import { DemosPayload, DimensionsUnitPayload, GetDemosCallback, GetDimensionsUnitCallback, GetLanguageInformationCallback, GetScreenshotCallback, GetVariantInformationCallback, LanguageInformationPayload, Message, statusLevels, StatusPayload, DimensionsUnit, VariantInformationPayload, ViewerAPIMessage, ViewerAPIMessageSource, ViewerElement, ViewerStatus, ViewerStatusListenerCallback, ScreenshotPayload, ScreenshotOptions, defaultScreenshotOptions, GetAnimationInformationCallback, AnimationInformationPayload, AnimationOptions, } from './types'; import { logErrorBlock } from './logging'; import { listenToMessages, parseIncomingMessage } from './utils'; import { Callbacks } from './callbacks'; import { base64ToBlob } from './base64ToBlob'; /** * The Visao class is the starting point of the Visao viewer api. * It encapsulates all the necessary calls to interact with your viewers from your * own code base. */ export class Visao { private id: string; private viewerElementInternal: ViewerElement = null; private status: ViewerStatus = ViewerStatus.UNMOUNTED; private readonly callbacks: Callbacks; private get viewerElement(): ViewerElement { if (this.viewerElementInternal) { return this.viewerElementInternal; } if (this.id) { this.viewerElementInternal = document.getElementById( this.id, ) as HTMLIFrameElement; return this.viewerElementInternal; } return null; } /** * #### Description * The constructor requires an element id given to the iframe tag/component * defined in your application. Internally, we take care of finding the element * in the DOM once the component is mounted. * * #### Usage * * ```typescript * const instance = new Visao("viewer-iframe-id"); * ``` * * @param id * The id of the viewer element. */ constructor(id: string) { this.id = id; this.callbacks = new Callbacks({ [ViewerAPIMessage.GET_STATUS]: [], [ViewerAPIMessage.GET_LANGUAGE_INFO]: [], [ViewerAPIMessage.GET_VARIANT_INFO]: [], [ViewerAPIMessage.GET_ANIMATION_INFO]: [], [ViewerAPIMessage.GET_DEMOS]: [], [ViewerAPIMessage.GET_DIMENSIONS_UNIT]: [], [ViewerAPIMessage.GET_SCREENSHOT]: [], }); listenToMessages(this.handleIncomingMessage); } /** * #### Description * > Manually set the reference of the viewer from the id. * * #### Usage: * * ```typescript * visao.setViewerElementFromId('viewer-id'); * ``` * * @param id * HTML element id representing the viewer */ public setViewerElementFromId(id: string): void { this.id = id; this.viewerElementInternal = document.getElementById( id, ) as HTMLIFrameElement; } /** * #### Description * > Manually set the reference of the viewer element. * * #### Usage: * * ```typescript * const element = document.getElementById('viewer-id'); * visao.setViewerElement(element); * ``` * * @param viewerElement * HTML element representing the viewer */ public setViewerElement(viewerElement: ViewerElement): void { this.viewerElementInternal = viewerElement; } /** * #### Description * > Listens to the state of the viewer. It will notify by calling * the provided parameter callback providing the new state everytime * the state changes. * * #### Usage * * ```typescript * visao.listenToViewerStatus((newViewerStatus: ViewerStatus) => { * // Do Something * }); * ``` * * @param callback * Callback to execute when a change happen */ public listenToViewerStatus(callback: ViewerStatusListenerCallback): void { this.callbacks.addTo(ViewerAPIMessage.GET_STATUS, callback); } /** * #### Description * > Removes a viewer status listener. * * #### Usage * * ```typescript * visao.unListenToViewerStatus(callback); * ``` * * @param callback * Callback which matches the one provided to a previously called {@link listenToViewerStatus} */ public unListenToViewerStatus(callback: ViewerStatusListenerCallback): void { this.callbacks.removeByCallbackFunctionFrom( ViewerAPIMessage.GET_STATUS, callback, ); } /** * #### Description * > Listens to any change to variants in the viewer. It will notify by calling * the provided parameter callback providing the new state everytime * the state changes. * * #### Usage * * ```typescript * visao.listenToVariantInfoChange((variantInformation: VariantInformationPayload) => { * // Do Something * }); * ``` * * @param callback * Callback to execute when a change happen */ public listenToVariantInfoChange( callback: GetVariantInformationCallback, ): void { this.callbacks.addTo(ViewerAPIMessage.GET_VARIANT_INFO, callback); } /** * #### Description * > Removes a variant info change listener. * * #### Usage * * ```typescript * visao.unListenToVariantInfoChange(callback); * ``` * * @param callback * Callback which matches the one provided to a previously called {@link listenToVariantInfoChange} */ public unListenToVariantInfoChange( callback: GetVariantInformationCallback, ): void { this.callbacks.removeByCallbackFunctionFrom( ViewerAPIMessage.GET_VARIANT_INFO, callback, ); } /** * #### Description * > Listens to any change to animations in the viewer. It will notify by calling * the provided parameter callback providing the new state everytime * the state changes. * * #### Usage * * ```typescript * visao.listenToAnimationInfoChange((animationInformation: AnimationInformationPayload) => { * // Do Something * }); * ``` * * @param callback * Callback to execute when a change happen */ public listenToAnimationInfoChange( callback: GetAnimationInformationCallback, ): void { this.callbacks.addTo(ViewerAPIMessage.GET_ANIMATION_INFO, callback); } /** * #### Description * > Removes an animation info change listener. * * #### Usage * * ```typescript * visao.unListenToAnimationInfoChange(callback); * ``` * * @param callback * Callback which matches the one provided to a previously called {@link listenToAnimationInfoChange} */ public unListenToAnimationInfoChange( callback: GetAnimationInformationCallback, ): void { this.callbacks.removeByCallbackFunctionFrom( ViewerAPIMessage.GET_ANIMATION_INFO, callback, ); } /** * #### Description * > Listens to any change to the language in the viewer. It will notify by calling * the provided parameter callback providing the new state everytime * the state changes. * * #### Usage * * ```typescript * visao.listenToLanguageInfoChange((languageInformation: LanguageInformationPayload) => { * // Do Something * }); * ``` * * @param callback * Callback to execute when a change happen */ public listenToLanguageInfoChange( callback: GetLanguageInformationCallback, ): void { this.callbacks.addTo(ViewerAPIMessage.GET_LANGUAGE_INFO, callback); } /** * #### Description * > Removes a language info change listener. * * #### Usage * * ```typescript * visao.unListenToLanguageInfoChange(callback); * ``` * * @param callback * Callback which matches the one provided to a previously called {@link listenToLanguageInfoChange} */ public unListenToLanguageInfoChange( callback: GetLanguageInformationCallback, ): void { this.callbacks.removeByCallbackFunctionFrom( ViewerAPIMessage.GET_LANGUAGE_INFO, callback, ); } /** * #### Description * > Listens to any change to dimensions unit in the viewer. It will notify by calling * the provided parameter callback providing the new state everytime * the state changes. * * #### Usage * * ```typescript * visao.listenToDimensionsUnitChange((dimensionsUnitPayload: DimensionsUnitPayload) => { * // Do Something * }); * ``` * * @param callback * Callback to execute when a change happen */ public listenToDimensionsUnitChange( callback: GetDimensionsUnitCallback, ): void { this.callbacks.addTo(ViewerAPIMessage.GET_DIMENSIONS_UNIT, callback); } /** * #### Description * > Removes a dimensions unit change listener. * * #### Usage * * ```typescript * visao.unListenToDimensionsUnitChange(callback); * ``` * * @param callback * Callback which matches the one provided to a previously called {@link listenToDimensionsUnitChange} */ public unListenToDimensionsUnitChange( callback: GetDimensionsUnitCallback, ): void { this.callbacks.removeByCallbackFunctionFrom( ViewerAPIMessage.GET_DIMENSIONS_UNIT, callback, ); } /** * #### Description * > Listens to any change to the demos in the viewer. It will notify by calling * the provided parameter callback providing the new state everytime * the state changes. * * #### Usage * * ```typescript * visao.listenToDemosChange((demos: DemosPayload) => { * // Do Something * }); * ``` * * @param callback * Callback to execute when a change happen */ public listenToDemosChange(callback: GetDemosCallback): void { this.callbacks.addTo(ViewerAPIMessage.GET_DEMOS, callback); } /** * #### Description * > Removes a demos change listener. * * #### Usage * * ```typescript * visao.unListenToDemosChange(callback); * ``` * * @param callback * Callback which matches the one provided to a previously called {@link listenToDemosChange} */ public unListenToDemosChange(callback: GetDemosCallback): void { this.callbacks.removeByCallbackFunctionFrom( ViewerAPIMessage.GET_DEMOS, callback, ); } /** * #### Description * > Get the data for all the demos and their steps. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * // Get the demos data * let demos; * visao.getDemos((payload) => { * demos = payload.demos; * // Do something with the data * }); * ``` * @param callback * Callback with a payload as parameter which includes the list of demos. */ public getDemos(callback: GetDemosCallback): void { const callbackId = this.callbacks.addTo( ViewerAPIMessage.GET_DEMOS, callback, ); this.executeAction( { type: ViewerAPIMessage.GET_DEMOS, callbackId, }, ViewerStatus.LOADED, ); } /** * #### Description * > Displays the provided demo step. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * * ```typescript * const stepUUID = '6ed63afa-2dc6-4182-9f36-f9d623423821'; * visao.showStep(stepUUID); * * // or * * const stepLocalizedTitleEN = 'Shoe laces'; * visao.showStep(stepLocalizedTitleEN); * * // or * * const stepLocalizedTitleFR = 'Lacets de chaussures'; * visao.showStep(stepLocalizedTitleFR); * ``` * * @param step The step’s **ID** (recommended) or one of it's **localized titles**. * Using the ID is preferred because it’s always unique. * The title is a localized field and may not be unique. The first matching step found will be activated. */ public showStep(step: string): void { this.executeAction( { type: ViewerAPIMessage.SHOW_STEP, payload: { step, }, }, ViewerStatus.LOADED, ); } /** * #### Description * > Closes currently displayed demo step. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.closeStep(); * ``` */ public closeStep(): void { this.executeAction( { type: ViewerAPIMessage.CLOSE_STEP, }, ViewerStatus.LOADED, ); } /** * #### Description * > Displays the previous demo step. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.previousStep(); * ``` */ public previousStep(): void { this.executeAction( { type: ViewerAPIMessage.PREVIOUS_STEP, }, ViewerStatus.LOADED, ); } /** * #### Description * > Displays the next demo step. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.nextStep(); * ``` */ public nextStep(): void { this.executeAction( { type: ViewerAPIMessage.NEXT_STEP, }, ViewerStatus.LOADED, ); } /** * #### Description * > Starts the demo steps player. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.play(); * ``` */ public play(): void { this.executeAction( { type: ViewerAPIMessage.PLAY, }, ViewerStatus.LOADED, ); } /** * #### Description * > Pauses the demo steps player. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.pause(); * ``` */ public pause(): void { this.executeAction( { type: ViewerAPIMessage.PAUSE, }, ViewerStatus.LOADED, ); } /** * #### Description * > Gets the current language information * * #### Usage * > :warning: **{@link ViewerStatus}** must be either at **{@link ViewerStatus.MOUNTED}**, * **{@link ViewerStatus.LOADING}**, **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.getLanguageInformation((info) => { * const currentLanguage = info.language; * const availableLanguages = info.languages; * }); * ``` * * @param callback * The callback will contain the currently displayed languages as well as all the supported languages in the viewer. */ public getLanguageInformation( callback: GetLanguageInformationCallback, ): void { const callbackId = this.callbacks.addTo( ViewerAPIMessage.GET_LANGUAGE_INFO, callback, ); this.executeAction( { type: ViewerAPIMessage.GET_LANGUAGE_INFO, callbackId, }, ViewerStatus.MOUNTED, ); } /** * #### Description * > Tells the viewer to change the language of the different localized content if it can be found. * This will be visible on UI components like tooltips, menus, flyers, etc... * * #### Usage * > :warning: **{@link ViewerStatus}** must be either at **{@link ViewerStatus.MOUNTED}**, * **{@link ViewerStatus.LOADING}**, **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * // Change the viewer language to french * visao.changeLanguage('fr'); * ``` * * @param language * The language abbreviation to apply. An exhaustive list of all valid languages * can be found **[here](https://github.com/annexare/Countries/blob/master/data/languages.json)**. */ public changeLanguage(language: string): void { this.executeAction( { type: ViewerAPIMessage.UPDATE_LANGUAGE, payload: { language, }, }, ViewerStatus.MOUNTED, ); } /** * #### Description * > Display one or more configuration/variant(s) * of the current 3D model if they can be found. * * #### Usage: * A 3D model of a shoe may support multiple independent variants, * such as color options for the shoe body, laces, and sole. * You can apply a single variant (e.g., just the body color), * or multiple variants at once (e.g., body, laces, and sole). * * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * // Change only the shoe body color to green * visao.showModelVariant('green'); * * // Apply multiple variants: green body, white laces, and black sole * visao.showModelVariant(['green', 'white_laces', 'black_sole']); * ``` * * @param modelVariants * A unique key or array of keys representing one or more variants that can be matched in the current 3D model schema. */ public showModelVariant(modelVariants: string | string[]): void { this.executeAction( { type: ViewerAPIMessage.UPDATE_VARIANT, payload: { modelVariants, }, }, ViewerStatus.LOADED, ); } /** * #### Description * > Remove one or more applied configuration/variant(s) * from the current 3D model if they are currently active. * * #### Usage: * A 3D model of a shoe may have multiple variants applied simultaneously, * such as different colors for the body, laces, and sole. * You can remove a single variant, or several at once. * * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * // Remove the green body color variant * visao.removeModelVariant('green'); * * // Remove multiple variants: white laces and black sole * visao.removeModelVariant(['white_laces', 'black_sole']); * ``` * * @param modelVariants * A unique key that can be matched inside the current 3D model schema. */ public removeModelVariant(modelVariants: string | string[]): void { this.executeAction( { type: ViewerAPIMessage.REMOVE_VARIANT, payload: { modelVariants, }, }, ViewerStatus.LOADED, ); } /** * #### Description * > Get the 3D model's variants/configurations information from the Visao viewer. * A configuration/variant is a structure with selected properties within the 3D model file, * which will determine how the 3D model will be displayed. * * #### Usage: * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * A 3D model of a shoe may have multiple variants applied simultaneously, * such as different colors for the body, laces, and sole. * * ```typescript * // Get the configurations/variants information * let variantsDisplayed; * let availableVariants; * visao.getVariantInformation((info) => { * variantsDisplayed = info.selectedModelVariants; * availableVariants = info.availableModelVariants; * // Do something with the data * }); * ``` * * @param callback * The configuration/variant information will be the parameter of the provided callback once called. */ public getVariantInformation(callback: GetVariantInformationCallback): void { const callbackId = this.callbacks.addTo( ViewerAPIMessage.GET_VARIANT_INFO, callback, ); this.executeAction( { type: ViewerAPIMessage.GET_VARIANT_INFO, callbackId, }, ViewerStatus.LOADED, ); } /** * #### Description * > Get the 3D model's animation information from the Visao viewer. * * #### Usage: * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * // Get the animation information * let availableAnimations; * let currentAnimation; * visao.getAnimationInformation((info) => { * availableAnimations = info.animations; * currentAnimation = info.animation; * // Do something with the data * }); * ``` * * @param callback * The animation information will be the parameter of the provided callback once called. */ public getAnimationInformation( callback: GetAnimationInformationCallback, ): void { const callbackId = this.callbacks.addTo( ViewerAPIMessage.GET_ANIMATION_INFO, callback, ); this.executeAction( { type: ViewerAPIMessage.GET_ANIMATION_INFO, callbackId, }, ViewerStatus.LOADED, ); } /** * #### Description * > Start the passed animation. * * #### Usage: * Default or step animations will be overridden by the new animation. * * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * // Start the animation * visao.startAnimation(animation); * ``` * ```typescript * // Start the animation with options * visaoAPI.startAnimation(anim, { * repetitionType: AnimationRepetitionType.REPEAT, * repetitionQuantity: 2, * }) * ``` * * @param animation * The animation name to start * * * @param options * The animation options */ public startAnimation( animation: string, options: AnimationOptions = {}, ): void { this.executeAction( { type: ViewerAPIMessage.START_ANIMATION, payload: { animation, options, }, }, ViewerStatus.LOADED, ); } /** * #### Description * > Stop the current animation. * * #### Usage: * Viewer preset animations (default or step) will be restored. This can be prevented by parameter preventPresets being set to true. * * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * // Stop the animation (preset animation will be restored if available) * visao.stopAnimation(); * ``` * * ```typescript * // Stop the animation (preset animation will not be restored) * visao.stopAnimation(true); * ``` * * @param preventPresets * Whether preset (default & step) animations should be prevented from being restored, Default: false */ public stopAnimation(preventPresets = false): void { this.executeAction( { type: ViewerAPIMessage.STOP_ANIMATION, payload: { preventPresets, }, }, ViewerStatus.LOADED, ); } /** * #### Description * > Sets the dimensions unit. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * const unit = 'm'; * visao.setDimensionsUnit(unit); * ``` * * @param unit * The new unit. */ public setDimensionsUnit(unit: DimensionsUnit): void { this.executeAction( { type: ViewerAPIMessage.UPDATE_DIMENSIONS_UNIT, payload: { unit, }, }, ViewerStatus.LOADED, ); } /** * #### Description * > Starts the augmented reality feature. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. This feature only * works on devices supporting augmented reality. * * ```typescript * visao.startAR(); * ``` */ public startAR(): void { this.executeAction( { type: ViewerAPIMessage.START_AR, }, ViewerStatus.LOADED, ); } /** * #### Description * > Reset the viewer's camera to its original state. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.resetCamera(); * ``` */ public resetCamera(): void { this.executeAction( { type: ViewerAPIMessage.RESET_CAMERA, }, ViewerStatus.LOADED, ); } /** * #### Description * > Captures a screenshot of the viewer's 3D model. * This will trigger a download of the image. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.captureScreenshot({ * mimeType: 'image/png', * }); * ``` * @param options * {@link ScreenshotOptions} * Optional screenshot options which can include the `mimeType` and `fileFormat`. * The default values are `mimeType: 'image/png'` and `fileFormat: 'blob'`. */ public captureScreenshot(options: Partial = {}): void { this.executeAction( { type: ViewerAPIMessage.CAPTURE_SCREENSHOT, payload: { ...defaultScreenshotOptions, ...options, }, }, ViewerStatus.LOADED, ); } /** * #### Description * > Captures a screenshot of the viewer's 3D model and then returns the image blob. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * // Get the screenshot blob * visao.getScreenshot((screenshot: Blob | string) => { * // Do something with the screenshot * }, { * mimeType: 'image/png', * fileFormat: 'blob', * }); * ``` * @param callback * Callback which includes the captured screenshot blob as parameter. * @param options * {@link ScreenshotOptions} * Optional screenshot options which can include the `mimeType` and `fileFormat`. * The default values are `mimeType: 'image/png'` and `fileFormat: 'blob'`. */ public getScreenshot( callback: GetScreenshotCallback, options: Partial = {}, ): void { const callbackId = this.callbacks.addTo( ViewerAPIMessage.GET_SCREENSHOT, callback, ); this.executeAction( { type: ViewerAPIMessage.GET_SCREENSHOT, callbackId, payload: { ...defaultScreenshotOptions, ...options, }, }, ViewerStatus.LOADED, ); } /** * #### Description * > Locks the viewer's camera controls. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.lockCamera(); * ``` */ public lockCamera(): void { this.executeAction( { type: ViewerAPIMessage.LOCK_CAMERA, }, ViewerStatus.LOADED, ); } /** * #### Description * > Unlocks the viewer's camera controls. * * #### Usage * > :warning: **{@link ViewerStatus}** must be either at **{@link ViewerStatus.MOUNTED}**, * **{@link ViewerStatus.LOADING}**, **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.unlockCamera(); * ``` */ public unlockCamera(): void { this.executeAction( { type: ViewerAPIMessage.UNLOCK_CAMERA, }, ViewerStatus.MOUNTED, ); } /** * #### Description * > Shows the viewer's help panel. * * #### Usage * > :warning: **{@link ViewerStatus}** must be either at **{@link ViewerStatus.MOUNTED}**, * **{@link ViewerStatus.LOADING}**, **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.showHelp(); * ``` */ public showHelp(): void { this.executeAction( { type: ViewerAPIMessage.SHOW_HELP, }, ViewerStatus.MOUNTED, ); } /** * #### Description * > Closes the viewer's help panel. * * #### Usage * > :warning: **{@link ViewerStatus}** must be either at **{@link ViewerStatus.MOUNTED}**, * **{@link ViewerStatus.LOADING}**, **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.closeHelp(); * ``` */ public closeHelp(): void { this.executeAction( { type: ViewerAPIMessage.CLOSE_HELP, }, ViewerStatus.MOUNTED, ); } /** * #### Description * > Shows the viewer's QR code panel. * * #### Usage * > :warning: **{@link ViewerStatus}** must be either at **{@link ViewerStatus.MOUNTED}**, * **{@link ViewerStatus.LOADING}**, **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.showQR(); * ``` */ public showQR(): void { this.executeAction( { type: ViewerAPIMessage.SHOW_QR, }, ViewerStatus.MOUNTED, ); } /** * #### Description * > Closes the viewer's QR code panel. * * #### Usage * > :warning: **{@link ViewerStatus}** must be either at **{@link ViewerStatus.MOUNTED}**, * **{@link ViewerStatus.LOADING}**, **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.closeQR(); * ``` */ public closeQR(): void { this.executeAction( { type: ViewerAPIMessage.CLOSE_QR, }, ViewerStatus.MOUNTED, ); } /** * #### Description * > Shows the viewer's dimensions. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.showDimensions(); * ``` */ public showDimensions(): void { this.executeAction( { type: ViewerAPIMessage.SHOW_DIMENSIONS, }, ViewerStatus.LOADED, ); } /** * #### Description * > Closes the viewer's dimensions. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.closeDimensions(); * ``` */ public closeDimensions(): void { this.executeAction( { type: ViewerAPIMessage.CLOSE_DIMENSIONS, }, ViewerStatus.LOADED, ); } /** * #### Description * > Starts the viewer's auto-rotation. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.startAutoRotate(); * ``` */ public startAutoRotate(): void { this.executeAction( { type: ViewerAPIMessage.START_AUTO_ROTATE, }, ViewerStatus.LOADED, ); } /** * #### Description * > Stops the viewer's auto-rotation. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.LOADED}** to execute. * * ```typescript * visao.stopAutoRotate(); * ``` */ public stopAutoRotate(): void { this.executeAction( { type: ViewerAPIMessage.STOP_AUTO_ROTATE, }, ViewerStatus.LOADED, ); } /** * #### Description * > Mutes the viewer's voiceover. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.MOUNTED}** to execute. * * ```typescript * visao.muteVoiceover(); * ``` */ public muteVoiceover(): void { this.executeAction( { type: ViewerAPIMessage.MUTE_VOICEOVER, }, ViewerStatus.MOUNTED, ); } /** * #### Description * > Un-mutes the viewer's voiceover. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.MOUNTED}** to execute. * * ```typescript * visao.unmuteVoiceover(); * ``` */ public unmuteVoiceover(): void { this.executeAction( { type: ViewerAPIMessage.UNMUTE_VOICEOVER, }, ViewerStatus.MOUNTED, ); } /** * #### Description * > Sets to volume level of the viewer's voiceover. * * #### Usage * > :warning: **{@link ViewerStatus}** must be at **{@link ViewerStatus.MOUNTED}** to execute. * * ```typescript * const volume = 0.75; // 75% volume * visao.setVoiceoverVolume(volume); * ``` * * @param volume * The new volume. Between `0.00` and `1.00`. Representing `0%` and `100%` volume respectively. */ public setVoiceoverVolume(volume: number): void { this.executeAction( { type: ViewerAPIMessage.UPDATE_VOICEOVER_VOLUME, payload: { volume, }, }, ViewerStatus.MOUNTED, ); } private handleIncomingMessage = (event: MessageEvent): void => { const message = parseIncomingMessage(event, ViewerAPIMessageSource.VIEWER); if (!message) { return; } switch (message.type) { case ViewerAPIMessage.SEND_STATUS: { const { status } = message.payload as StatusPayload; this.status = status; if (status === ViewerStatus.MOUNTED) { this.setViewerElement( document.getElementById(this.id) as HTMLIFrameElement, ); } this.callbacks.executeFor(ViewerAPIMessage.GET_STATUS, status); break; } case ViewerAPIMessage.SEND_LANGUAGE_INFO: { const languageInformation = message.payload as LanguageInformationPayload; this.callbacks.executeFor( ViewerAPIMessage.GET_LANGUAGE_INFO, languageInformation, ); this.callbacks.removeByCallbackIdFrom( ViewerAPIMessage.GET_LANGUAGE_INFO, message.callbackId, ); break; } case ViewerAPIMessage.SEND_VARIANT_INFO: { const variantInformation = message.payload as VariantInformationPayload; this.callbacks.executeFor( ViewerAPIMessage.GET_VARIANT_INFO, variantInformation, ); this.callbacks.removeByCallbackIdFrom( ViewerAPIMessage.GET_VARIANT_INFO, message.callbackId, ); break; } case ViewerAPIMessage.SEND_ANIMATION_INFO: { const animationInformation = message.payload as AnimationInformationPayload; this.callbacks.executeFor( ViewerAPIMessage.GET_ANIMATION_INFO, animationInformation, ); this.callbacks.removeByCallbackIdFrom( ViewerAPIMessage.GET_ANIMATION_INFO, message.callbackId, ); break; } case ViewerAPIMessage.SEND_DIMENSIONS_UNIT: { const dimensionsUnitPayload = message.payload as DimensionsUnitPayload; this.callbacks.executeFor( ViewerAPIMessage.GET_DIMENSIONS_UNIT, dimensionsUnitPayload, ); this.callbacks.removeByCallbackIdFrom( ViewerAPIMessage.GET_DIMENSIONS_UNIT, message.callbackId, ); break; } case ViewerAPIMessage.SEND_SCREENSHOT: { const { screenshot, options: { fileFormat }, } = message.payload as ScreenshotPayload; try { const formattedScreenshot = fileFormat === 'blob' ? base64ToBlob(screenshot) : screenshot; this.callbacks.executeFor( ViewerAPIMessage.GET_SCREENSHOT, formattedScreenshot, ); } catch (e) { logErrorBlock(['Unable to capture the screenshot image']); } this.callbacks.removeByCallbackIdFrom( ViewerAPIMessage.GET_SCREENSHOT, message.callbackId, ); break; } case ViewerAPIMessage.SEND_DEMOS: { const demos = message.payload as DemosPayload; this.callbacks.executeFor(ViewerAPIMessage.GET_DEMOS, demos); this.callbacks.removeByCallbackIdFrom( ViewerAPIMessage.GET_DEMOS, message.callbackId, ); break; } } }; private executeAction( action: Omit, statusNeeded?: ViewerStatus, ): void { this.logInvalidViewerElement(); this.logForInsufficientStatusLevel(statusNeeded); const message: Message = { ...action, source: ViewerAPIMessageSource.API }; this.viewerElement?.contentWindow?.postMessage( JSON.stringify(message), '*', ); } private logInvalidViewerElement(): void { if (!this.viewerElement) { logErrorBlock([ `Viewer HTML element cannot be found in the DOM with the id "${this.id}".`, 'Make sure the visao iframe is properly mounted in the DOM before calling this method.', ]); } } private logForInsufficientStatusLevel(statusNeeded?: ViewerStatus): void { if ( statusNeeded && !this.validateStatusHasReachedNeededLevel(statusNeeded) ) { logErrorBlock([ 'The viewer is not ready for this action.', `Minimum viewer status needed: [${statusNeeded}]`, `Current viewer status: [${this.status}]`, ]); } } private validateStatusHasReachedNeededLevel( statusNeeded: ViewerStatus, ): boolean { return statusLevels[this.status] >= statusLevels[statusNeeded]; } }