@qispace/vue3-player 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +18 -0
  2. package/dist/components/apartmentChooser/QiApartmentChooser.vue.d.ts +4 -0
  3. package/dist/components/apartmentChooser/QiApartmentChooser.vue.d.ts.map +1 -0
  4. package/dist/components/apartmentChooser/QiApartmentChooserNavigation.vue.d.ts +4 -0
  5. package/dist/components/apartmentChooser/QiApartmentChooserNavigation.vue.d.ts.map +1 -0
  6. package/dist/components/apartmentChooser/QiApartmentChooserRaster.vue.d.ts +4 -0
  7. package/dist/components/apartmentChooser/QiApartmentChooserRaster.vue.d.ts.map +1 -0
  8. package/dist/components/apartmentChooser/QiApartmentChooserRotator.vue.d.ts +4 -0
  9. package/dist/components/apartmentChooser/QiApartmentChooserRotator.vue.d.ts.map +1 -0
  10. package/dist/components/apartmentChooser/QiApartmentChooserSvg.vue.d.ts +4 -0
  11. package/dist/components/apartmentChooser/QiApartmentChooserSvg.vue.d.ts.map +1 -0
  12. package/dist/components/buildingPicker/QiBuildingChooser.vue.d.ts +3 -0
  13. package/dist/components/buildingPicker/QiBuildingChooser.vue.d.ts.map +1 -0
  14. package/dist/components/buildingPicker/QiBuildingChooserRaster.vue.d.ts +4 -0
  15. package/dist/components/buildingPicker/QiBuildingChooserRaster.vue.d.ts.map +1 -0
  16. package/dist/components/buildingPicker/QiBuildingChooserRasterOverlay.vue.d.ts +4 -0
  17. package/dist/components/buildingPicker/QiBuildingChooserRasterOverlay.vue.d.ts.map +1 -0
  18. package/dist/components/buildingPicker/QiBuildingChooserSceneImageStack.vue.d.ts +4 -0
  19. package/dist/components/buildingPicker/QiBuildingChooserSceneImageStack.vue.d.ts.map +1 -0
  20. package/dist/components/buildingPicker/QiBuildingChooserSunSimControl.vue.d.ts +5 -0
  21. package/dist/components/buildingPicker/QiBuildingChooserSunSimControl.vue.d.ts.map +1 -0
  22. package/dist/components/buildingPicker/QiBuildingChooserSvg.vue.d.ts +4 -0
  23. package/dist/components/buildingPicker/QiBuildingChooserSvg.vue.d.ts.map +1 -0
  24. package/dist/components/index.d.ts +4 -0
  25. package/dist/components/shared/QiFloatingCard.vue.d.ts +4 -0
  26. package/dist/components/shared/QiFloatingCard.vue.d.ts.map +1 -0
  27. package/dist/components/shared/QiHoverProbe.vue.d.ts +4 -0
  28. package/dist/components/shared/QiHoverProbe.vue.d.ts.map +1 -0
  29. package/dist/components/shared/QiNorthDirection.vue.d.ts +3 -0
  30. package/dist/components/shared/QiNorthDirection.vue.d.ts.map +1 -0
  31. package/dist/components/shared/QiZoomBox.vue.d.ts +4 -0
  32. package/dist/components/shared/QiZoomBox.vue.d.ts.map +1 -0
  33. package/dist/components/virtualTourV2/Disclaimer.vue.d.ts +4 -0
  34. package/dist/components/virtualTourV2/Disclaimer.vue.d.ts.map +1 -0
  35. package/dist/components/virtualTourV2/FullScreenButton.vue.d.ts +3 -0
  36. package/dist/components/virtualTourV2/FullScreenButton.vue.d.ts.map +1 -0
  37. package/dist/components/virtualTourV2/ManualSlideShow.vue.d.ts +4 -0
  38. package/dist/components/virtualTourV2/ManualSlideShow.vue.d.ts.map +1 -0
  39. package/dist/components/virtualTourV2/ManualSlideShowPlayer.vue.d.ts +4 -0
  40. package/dist/components/virtualTourV2/ManualSlideShowPlayer.vue.d.ts.map +1 -0
  41. package/dist/components/virtualTourV2/SingleImage.vue.d.ts +4 -0
  42. package/dist/components/virtualTourV2/SingleImage.vue.d.ts.map +1 -0
  43. package/dist/components/virtualTourV2/SocialShare.vue.d.ts +4 -0
  44. package/dist/components/virtualTourV2/SocialShare.vue.d.ts.map +1 -0
  45. package/dist/components/virtualTourV2/WaypointCarousel.vue.d.ts +4 -0
  46. package/dist/components/virtualTourV2/WaypointCarousel.vue.d.ts.map +1 -0
  47. package/dist/lib/virtualTour__/ApartmentConfig.d.ts +27 -0
  48. package/dist/lib/virtualTour__/ApartmentFiles.d.ts +7 -0
  49. package/dist/lib/virtualTour__/MinimapConfig.d.ts +23 -0
  50. package/dist/lib/virtualTour__/impl/HttpApartmentFiles.d.ts +19 -0
  51. package/dist/vue3-player.cjs.js +2 -0
  52. package/dist/vue3-player.esm.js +2992 -0
  53. package/dist/vue3-player.iife.js +3 -0
  54. package/dist/vue3-player.umd.js +3 -0
  55. package/package.json +65 -0
  56. package/src/components/apartmentChooser/QiApartmentChooser.vue +319 -0
  57. package/src/components/apartmentChooser/QiApartmentChooserNavigation.vue +88 -0
  58. package/src/components/apartmentChooser/QiApartmentChooserRaster.vue +408 -0
  59. package/src/components/apartmentChooser/QiApartmentChooserRotator.vue +202 -0
  60. package/src/components/apartmentChooser/QiApartmentChooserSvg.vue +257 -0
  61. package/src/components/apartmentChooser/index.js +5 -0
  62. package/src/components/buildingPicker/QiBuildingChooser.vue +61 -0
  63. package/src/components/buildingPicker/QiBuildingChooserRaster.vue +312 -0
  64. package/src/components/buildingPicker/QiBuildingChooserRasterOverlay.vue +96 -0
  65. package/src/components/buildingPicker/QiBuildingChooserSceneImageStack.vue +89 -0
  66. package/src/components/buildingPicker/QiBuildingChooserSunSimControl.vue +257 -0
  67. package/src/components/buildingPicker/QiBuildingChooserSvg.vue +414 -0
  68. package/src/components/buildingPicker/index.js +6 -0
  69. package/src/components/index.ts +4 -0
  70. package/src/components/shared/QiFloatingCard.vue +76 -0
  71. package/src/components/shared/QiHoverProbe.vue +22 -0
  72. package/src/components/shared/QiNorthDirection.vue +27 -0
  73. package/src/components/shared/QiZoomBox.vue +322 -0
  74. package/src/components/shared/index.js +4 -0
  75. package/src/components/virtualTourV2/Compass.vue +37 -0
  76. package/src/components/virtualTourV2/Disclaimer.vue +41 -0
  77. package/src/components/virtualTourV2/FloorLevel.vue +73 -0
  78. package/src/components/virtualTourV2/FullScreenButton.vue +23 -0
  79. package/src/components/virtualTourV2/InteriorSelector.vue +90 -0
  80. package/src/components/virtualTourV2/ManualSlideShow.vue +198 -0
  81. package/src/components/virtualTourV2/ManualSlideShowPlayer.vue +159 -0
  82. package/src/components/virtualTourV2/PlayerV2.vue +300 -0
  83. package/src/components/virtualTourV2/ScrollHelper.vue +74 -0
  84. package/src/components/virtualTourV2/SettingsUI.vue +355 -0
  85. package/src/components/virtualTourV2/SingleImage.vue +36 -0
  86. package/src/components/virtualTourV2/SocialShare.vue +163 -0
  87. package/src/components/virtualTourV2/TimeOfDay.vue +50 -0
  88. package/src/components/virtualTourV2/Tutorial.vue +690 -0
  89. package/src/components/virtualTourV2/ViewModeToggle.vue +24 -0
  90. package/src/components/virtualTourV2/VirtualTourV2.vue +876 -0
  91. package/src/components/virtualTourV2/WaypointCarousel.vue +162 -0
  92. package/src/components/virtualTourV2/index.js +1 -0
  93. package/src/components/virtualTourV2/minimapv2/MiniMapMap.vue +262 -0
  94. package/src/components/virtualTourV2/minimapv2/MiniMapV2.vue +110 -0
  95. package/src/components/virtualTourV2/minimapv2/MinimapCompass.vue +39 -0
  96. package/src/components/virtualTourV2/minimapv2/Moveable.vue +208 -0
  97. package/src/components/virtualTourV2/minimapv2/RotationMarkerV2.vue +79 -0
  98. package/src/components/virtualTourV2/minimapv2/SunsimulationSlider.vue +203 -0
  99. package/src/components/virtualTourV2/minimapv2/index.js +1 -0
  100. package/src/entry.esm.js +17 -0
  101. package/src/entry.js +13 -0
  102. package/src/entry.ts_ +17 -0
  103. package/src/lib/apartmentChooser/BuildingViewerModel.js +60 -0
  104. package/src/lib/apartmentChooser/CircularSlideshow.js +66 -0
  105. package/src/lib/apartmentChooser/RotationStep.js +31 -0
  106. package/src/lib/apartmentChooser/SceneRotator.js +25 -0
  107. package/src/lib/apartmentChooser/index.js +3 -0
  108. package/src/lib/apartmentChooser/throttle.js +15 -0
  109. package/src/lib/buildingPicker/BuildingMap.js +24 -0
  110. package/src/lib/buildingPicker/BuildingPickerResourceProvider.js +97 -0
  111. package/src/lib/buildingPicker/CanvasRaster.js +29 -0
  112. package/src/lib/buildingPicker/DayOfYearSelector.js +36 -0
  113. package/src/lib/buildingPicker/SampleRaster.js +14 -0
  114. package/src/lib/buildingPicker/index.js +5 -0
  115. package/src/lib/index.js +4 -0
  116. package/src/lib/shared/BatchLoadTracker.js +52 -0
  117. package/src/lib/shared/I18N.js +65 -0
  118. package/src/lib/shared/ResourceLoader.js +33 -0
  119. package/src/lib/shared/index.js +3 -0
  120. package/src/lib/virtualTour/CameraSnapshot.js +42 -0
  121. package/src/lib/virtualTour/FullscreenModel.js +69 -0
  122. package/src/lib/virtualTour/textures/arrow.png +0 -0
  123. package/src/lib/virtualTour/textures/compass-bg.png +0 -0
  124. package/src/lib/virtualTour/textures/compass-needle.png +0 -0
  125. package/src/lib/virtualTour/textures/compass-north.png +0 -0
  126. package/src/lib/virtualTour/textures/floor-1.svg +4 -0
  127. package/src/lib/virtualTour/textures/floor-2.svg +4 -0
  128. package/src/lib/virtualTour/textures/marker.png +0 -0
  129. package/src/lib/virtualTour/textures/tod-sun.png +0 -0
  130. package/src/lib/virtualTour__/ApartmentConfig.ts +80 -0
  131. package/src/lib/virtualTour__/ApartmentFiles.ts +8 -0
  132. package/src/lib/virtualTour__/CameraNavigator.js_ +74 -0
  133. package/src/lib/virtualTour__/CameraSnapshot.js +42 -0
  134. package/src/lib/virtualTour__/CoordConversions.js +43 -0
  135. package/src/lib/virtualTour__/FullscreenModel.js +69 -0
  136. package/src/lib/virtualTour__/MinimapConfig.ts +46 -0
  137. package/src/lib/virtualTour__/PlayerViewModel.js +423 -0
  138. package/src/lib/virtualTour__/config/ApartmentConfig.js +92 -0
  139. package/src/lib/virtualTour__/config/CameraConfig.js +97 -0
  140. package/src/lib/virtualTour__/config/Interaction.js +393 -0
  141. package/src/lib/virtualTour__/config/Panorama.js +78 -0
  142. package/src/lib/virtualTour__/config/PlayerConfig.js +812 -0
  143. package/src/lib/virtualTour__/config/rawinflate.export.js +833 -0
  144. package/src/lib/virtualTour__/config/shaders.js +24 -0
  145. package/src/lib/virtualTour__/impl/HttpApartmentFiles.ts +57 -0
  146. package/src/lib/virtualTour__/index.js +1 -0
  147. package/src/lib/virtualTour__/textures/arrow.png +0 -0
  148. package/src/lib/virtualTour__/textures/compass-bg.png +0 -0
  149. package/src/lib/virtualTour__/textures/compass-needle.png +0 -0
  150. package/src/lib/virtualTour__/textures/compass-north.png +0 -0
  151. package/src/lib/virtualTour__/textures/floor-1.svg +4 -0
  152. package/src/lib/virtualTour__/textures/floor-2.svg +4 -0
  153. package/src/lib/virtualTour__/textures/marker.png +0 -0
  154. package/src/lib/virtualTour__/textures/tod-sun.png +0 -0
  155. package/src/main.ts_ +24 -0
  156. package/src/shims-png.d.ts +4 -0
  157. package/src/shims-tsx.d.ts +11 -0
  158. package/src/shims-vue.d.ts +4 -0
  159. package/src/style.css +0 -0
  160. package/src/vite-env.d.ts +5 -0
@@ -0,0 +1,74 @@
1
+ import Vue from 'vue'
2
+
3
+ export class CameraNavigator {
4
+ constructor() {
5
+ this._state = Vue.observable({
6
+ waypoints: [],
7
+ selectedWaypointIndex: null,
8
+ selectedCameraId: null,
9
+ })
10
+ }
11
+
12
+ initialize(waypoints, cameraId, selectedWaypointIndex = 0) {
13
+ this._state.waypoints = waypoints
14
+ this.selectedWaypointIndex = selectedWaypointIndex % waypoints.length
15
+ if (!this._state.selectedCameraId) this._state.selectedCameraId = cameraId
16
+ }
17
+
18
+ selectNextWaypoint() {
19
+ if (this.selectedWaypointIndex === this.waypointCount - 1) {
20
+ this.selectedWaypointIndex = 0
21
+ } else {
22
+ this.selectedWaypointIndex += 1
23
+ }
24
+ }
25
+
26
+ selectPrevWaypoint() {
27
+ if (this.selectedWaypointIndex === 0) {
28
+ this.selectedWaypointIndex = this.waypointCount - 1
29
+ } else {
30
+ this.selectedWaypointIndex -= 1
31
+ }
32
+ }
33
+
34
+ get waypointCount() {
35
+ return this._state.waypoints.length
36
+ }
37
+
38
+ get waypoints() {
39
+ return this._state.waypoints
40
+ }
41
+
42
+ get selectedWaypointIndex() {
43
+ return this._state.selectedWaypointIndex
44
+ }
45
+
46
+ set selectedWaypointIndex(value) {
47
+ this._state.selectedWaypointIndex = value
48
+ if (this.selectedWaypoint) {
49
+ this._state.selectedCameraId = this.selectedWaypoint.id
50
+ }
51
+ }
52
+
53
+ get selectedWaypoint() {
54
+ return this._state.waypoints[this._state.selectedWaypointIndex]
55
+ }
56
+
57
+ get selectedCameraId() {
58
+ return this._state.selectedCameraId
59
+ }
60
+
61
+ set selectedCameraId(value) {
62
+ this._state.selectedCameraId = value
63
+ const waypointIndex = this._waypointIndexById(value)
64
+ if (waypointIndex > -1) {
65
+ this._state.selectedWaypointIndex = waypointIndex
66
+ }
67
+ }
68
+
69
+ _waypointIndexById(cameraId) {
70
+ return this._state.waypoints.findIndex(
71
+ (cam) => cam.id.toUpperCase() === cameraId.toUpperCase()
72
+ )
73
+ }
74
+ }
@@ -0,0 +1,42 @@
1
+ function padded(num) {
2
+ return ('0' + num).slice(-2)
3
+ }
4
+
5
+ // Makes a string in the format of YYYYMMDD HHMMSS
6
+ function makeTimestampString() {
7
+ const now = new Date()
8
+ return [
9
+ now.getFullYear().toString(),
10
+ padded(now.getMonth() + 1),
11
+ padded(now.getDate()),
12
+ ' ',
13
+ padded(now.getHours()),
14
+ padded(now.getMinutes()),
15
+ padded(now.getSeconds()),
16
+ ].join('')
17
+ }
18
+
19
+ function makeDefaultFilename() {
20
+ return 'Visuado-Snapshot-' + makeTimestampString()
21
+ }
22
+
23
+ export class CameraSnapshot {
24
+ constructor(data, cameraParams) {
25
+ this.data = data
26
+ this.cameraParams = cameraParams
27
+ }
28
+
29
+ download(filename = null) {
30
+ filename = filename || makeDefaultFilename()
31
+ const link = document.createElement('a')
32
+ if (typeof link.download === 'string') {
33
+ document.body.appendChild(link) //Firefox requires the link to be in the body
34
+ link.download = filename
35
+ link.href = this.data
36
+ link.click()
37
+ document.body.removeChild(link)
38
+ } else {
39
+ location.replace(uri)
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,43 @@
1
+ import * as THREE from 'three'
2
+
3
+ function parseVectorString(str) {
4
+ return str.split(',', 3).map((c) => parseFloat(c))
5
+ }
6
+
7
+ export function clampAngle(angle) {
8
+ while (angle < -360) angle += 360
9
+ return angle % 360
10
+ }
11
+
12
+ export function toVector3([x, y, z]) {
13
+ return new THREE.Vector3(x, y, z)
14
+ }
15
+
16
+ export function positionFromUnity([x, y, z]) {
17
+ return [x, y, -z]
18
+ }
19
+
20
+ export function positionToUnity([x, y, z]) {
21
+ return [x, y, -z]
22
+ }
23
+
24
+ export function rotationFromUnity([rotX, rotY, rotZ]) {
25
+ if (rotX > 180) rotX = rotX - 360
26
+ return [-rotX, clampAngle(rotY + 270), rotZ]
27
+ }
28
+
29
+ export function rotationToUnity([rotX, rotY, rotZ]) {
30
+ rotX = -rotX
31
+ if (rotX < 0) rotX += 360
32
+ rotY = rotY - 270
33
+ if (rotY < 0) rotY += 360
34
+ return [rotX, rotY, rotZ]
35
+ }
36
+
37
+ export function stringToVector3ConvertPositionFromUnity(str) {
38
+ return toVector3(positionFromUnity(parseVectorString(str)))
39
+ }
40
+
41
+ export function stringToVector3ConvertRotationFromUnity(str) {
42
+ return toVector3(rotationFromUnity(parseVectorString(str)))
43
+ }
@@ -0,0 +1,69 @@
1
+ export class FullscreenModel {
2
+ constructor(document, elemId) {
3
+ this._doc = document
4
+ this._elemId = elemId
5
+ this._state = { isActive: false }
6
+ }
7
+
8
+ get isActive() {
9
+ return this._state.isActive
10
+ }
11
+
12
+ setup() {
13
+ const events = [
14
+ 'fullscreenchange',
15
+ 'webkitfullscreenchange',
16
+ 'mozfullscreenchange',
17
+ 'msfullscreenchange',
18
+ ]
19
+ const element = this._getElement()
20
+ events.forEach((event) => {
21
+ element.addEventListener(event, this._toggle.bind(this), false)
22
+ })
23
+ }
24
+
25
+ toggle() {
26
+ if (this.isActive) this._exit()
27
+ else this._enter()
28
+ }
29
+
30
+ _enter() {
31
+
32
+ const elem = this._getElement()
33
+ const method =
34
+ elem.requestFullscreen ||
35
+ elem.webkitRequestFullscreen ||
36
+ elem.mozRequestFullscreen ||
37
+ elem.msRequestFullscreen
38
+
39
+ if (method) {
40
+ method.apply(elem)
41
+ }
42
+
43
+ }
44
+
45
+ _exit() {
46
+ const method =
47
+ this._doc.exitFullscreen ||
48
+ this._doc.webkitExitFullscreen ||
49
+ this._doc.mozExitFullscreen ||
50
+ this._doc.document.msExitFullscreen
51
+ if (method) {
52
+ method.apply(document)
53
+ }
54
+ }
55
+
56
+ _getElement() {
57
+ return this._doc.getElementById(this._elemId)
58
+ }
59
+
60
+ _toggle() {
61
+ const activeState = !!(
62
+ this._doc.fullscreenElement ||
63
+ this._doc.webkitFullscreenElement ||
64
+ this._doc.mozFullscreenElement ||
65
+ this._doc.msFullscreenElement
66
+ )
67
+ this._state.isActive = activeState
68
+ }
69
+ }
@@ -0,0 +1,46 @@
1
+ import { ProjectFiles, ProjectFile } from './ApartmentFiles'
2
+
3
+
4
+ export interface Coordinate3D{
5
+ x: number,
6
+ y: number,
7
+ z: number
8
+ }
9
+
10
+ export interface MinimapStorey {
11
+ sunsimFilesTimeStamps: string[],
12
+ sunsimFiles: string[],
13
+ fovY: number,
14
+ rotation: Coordinate3D,
15
+ position: Coordinate3D,
16
+ }
17
+ export interface MinimapMetadata {
18
+ storeys: MinimapStorey[]
19
+ }
20
+
21
+ export class MinimapConfig {
22
+ constructor(private readonly apartmentFiles: ProjectFiles) {}
23
+
24
+ async getMinimapData(): Promise<Object | undefined> {
25
+ const metadata = await this.readMinimapMetadata()
26
+ if (!metadata) return undefined
27
+
28
+ const fileNames = metadata.storeys[0].sunsimFiles
29
+ const files = fileNames.map((name) => this.constructMinimapFile(name).toPath())
30
+
31
+ // TODO: for DUPLEX this will be different
32
+ const minimapData = metadata.storeys[0]
33
+
34
+ return {...minimapData, sunsimFiles: files }
35
+ }
36
+
37
+ private async readMinimapMetadata(): Promise<MinimapMetadata | undefined> {
38
+ const metadataFile = this.constructMinimapFile('minimap.json')
39
+ const metadata = await metadataFile.readJson<MinimapMetadata>()
40
+ return metadata
41
+ }
42
+
43
+ private constructMinimapFile(filename: string): ProjectFile {
44
+ return this.apartmentFiles.makeFile('minimap', filename)
45
+ }
46
+ }
@@ -0,0 +1,423 @@
1
+ import Vue from 'vue'
2
+ import axios from 'axios'
3
+ import PlayerConfig from './config/PlayerConfig'
4
+ import { CameraNavigator } from './CameraNavigator'
5
+
6
+ export const ViewModeVirtualTour = 'virtualTour'
7
+ export const ViewModeSlideShow = 'slideShow'
8
+ export const ViewModeManualSlideShow = 'manualSlideShow'
9
+
10
+ function parseTimeOfDay(floatValue) {
11
+ if (floatValue === undefined) return null
12
+
13
+ const hours = Math.floor(floatValue)
14
+ const minutes = Math.floor((hours - floatValue) * 60)
15
+ return { hours, minutes }
16
+ }
17
+
18
+ function getFirstCameraId(cameras) {
19
+ const camera = cameras && cameras[0]
20
+ return camera ? camera.id : undefined
21
+ }
22
+
23
+ function getAvailableViewModesFromUnitViewMode(unitViewMode) {
24
+ switch (unitViewMode) {
25
+ case 'slideShowOnly':
26
+ return [ViewModeSlideShow]
27
+ case 'virtualTourOnly':
28
+ return [ViewModeVirtualTour]
29
+ case 'virtualTourAndSlideShowDefaultVirtualTour':
30
+ case 'virtualTourAndSlideShowDefaultSlideShow':
31
+ return [ViewModeVirtualTour, ViewModeSlideShow]
32
+ case 'manualSlideShow':
33
+ return [ViewModeManualSlideShow]
34
+ default:
35
+ console.error('cannot initialize player without viewModes')
36
+ }
37
+ }
38
+
39
+ function getInitialViewModeFromUnitViewMode(unitViewMode) {
40
+ switch (unitViewMode) {
41
+ case 'slideShowOnly':
42
+ case 'virtualTourAndSlideShowDefaultSlideShow':
43
+ return ViewModeSlideShow
44
+ case 'virtualTourAndSlideShowDefaultVirtualTour':
45
+ case 'virtualTourOnly':
46
+ return ViewModeVirtualTour
47
+ case 'manualSlideShow':
48
+ return ViewModeManualSlideShow
49
+ default:
50
+ console.error('cannot initialize player without viewModes')
51
+ }
52
+ }
53
+
54
+ export default class PlayerViewModel {
55
+ static makeCdnModel(modelParams) {
56
+ return new CdnPlayerViewModel(modelParams)
57
+ }
58
+
59
+ constructor(initData) {
60
+ this._state = Vue.observable({
61
+ _initialized: false,
62
+ _viewMode: getInitialViewModeFromUnitViewMode(initData.unitViewMode),
63
+ _availableViewModes: getAvailableViewModesFromUnitViewMode(
64
+ initData.unitViewMode
65
+ ),
66
+ _selectedArchetypeIndex: initData.selectedArchetypeIndex || 0,
67
+ _cameras: [],
68
+ _timeOfDay: initData.timeOfDay,
69
+ _archetypes: initData.archetypes || [],
70
+ _cameraSnapshot: {
71
+ requested: null,
72
+ latest: null,
73
+ },
74
+ _panoramaOrientation: { lat: 0.0, lon: 0.0 },
75
+ adminMode: initData.adminMode || false,
76
+ })
77
+ this._navigator = new CameraNavigator()
78
+
79
+ this._cdnFileResolver =
80
+ typeof initData.cdnFileResolver === 'string'
81
+ ? (name) => initData.cdnFileResolver + name
82
+ : initData.cdnFileResolver
83
+ this._apartmentId = initData.apartmentId
84
+ this._websiteUrl = initData.websiteUrl
85
+ this._unitViewMode = initData.unitViewMode
86
+
87
+ this._manualSlideshowImages = initData.manualSlideshowImages
88
+
89
+ this._addBuildVersionToUrls = initData.addBuildVersionToUrls
90
+ this._showCompass = !!initData.showCompass
91
+ this._showMiniMap = initData.showMiniMap
92
+ this._onInformationButtonPress = initData.onInformationButtonPress
93
+ this._mouseSensitivityFactor = initData.mouseSensitivity ? initData.mouseSensitivity : 1
94
+
95
+ this._initData = undefined
96
+ }
97
+
98
+ takeCameraSnapshot() {
99
+ this._state._cameraSnapshot.requested = new Date()
100
+ }
101
+
102
+ async initializeData({ initialArchetypeId, initialWaypointIndex } = {}) {
103
+ if (this._state._viewMode === ViewModeManualSlideShow) {
104
+ this._initData = this._manualSlideshowImages
105
+ this._state._timeOfDay = undefined
106
+ this._state._cameras = this._manualSlideshowImages
107
+
108
+ this._navigator.initialize(
109
+ this._manualSlideshowImages,
110
+ getFirstCameraId(this._manualSlideshowImages),
111
+ 0
112
+ )
113
+ } else {
114
+ if (this.archetypes.length === 0) {
115
+ await this._loadArchetypesFromConfig(initialArchetypeId)
116
+ } else if (initialArchetypeId) {
117
+ let index = this.archetypes.findIndex(
118
+ ({ id }) => id === initialArchetypeId
119
+ )
120
+ if (index < 0) {
121
+ index = 0
122
+ }
123
+ this.selectArchetypeByIndex(index)
124
+ }
125
+
126
+ this._state._archetypes = await this._loadMinimaps()
127
+
128
+ // TODO: With Ulven project, apartment.json is an empty stub file
129
+ // The code needs to be ready for such a situation
130
+ const apartments = (
131
+ await axios.get(this.resolveApartmentFile('apartment.json'))
132
+ ).data
133
+ const thumbs = (await axios.get(this.resolveApartmentFile('thumbs.json')))
134
+ .data
135
+
136
+ this._initData = apartments
137
+ this._state._timeOfDay = parseTimeOfDay(apartments.timeInHours)
138
+ this._state._cameras = apartments.cameras
139
+
140
+ this._navigator.initialize(
141
+ thumbs.cameras,
142
+ getFirstCameraId(apartments.cameras),
143
+ initialWaypointIndex
144
+ )
145
+ }
146
+
147
+ this._state._initialized = true
148
+ }
149
+
150
+ async _loadArchetypesFromConfig(initialArchetypeId) {
151
+ const projectConfig = (await axios.get(this.getFilename('/config.json')))
152
+ .data
153
+
154
+ this._state._archetypes = projectConfig.archetypes.map(
155
+ ({ description, archetypeID, title, enabled }) => {
156
+ return {
157
+ id: archetypeID,
158
+ description,
159
+ label: title || archetypeID,
160
+ enabled,
161
+ }
162
+ }
163
+ )
164
+
165
+ let index = this.archetypes.findIndex(
166
+ ({ id }) =>
167
+ id ===
168
+ (initialArchetypeId
169
+ ? initialArchetypeId
170
+ : projectConfig.defaultArchetypeID)
171
+ )
172
+ if (index < 0) {
173
+ index = 0
174
+ }
175
+ this.selectArchetypeByIndex(index)
176
+ }
177
+
178
+ async _loadMinimaps() {
179
+ return Promise.all(
180
+ this.archetypes.map(async (a) => {
181
+ try {
182
+ const metadata = (
183
+ await axios.get(this.resolveApartmentFile('/minimap/minimap.json'))
184
+ ).data
185
+
186
+
187
+ let sunsimFilesByStorey = metadata.storeys.map((item) => { return item.sunsimFiles })
188
+
189
+ let sunsimFiles = []
190
+
191
+ for (let index = 0; index < sunsimFilesByStorey.length; index++) {
192
+ const element = sunsimFilesByStorey[index];
193
+ const fileUrls = element.map(name => this.resolveApartmentFile(`/minimap/${name}`))
194
+ sunsimFiles = [...sunsimFiles, fileUrls]
195
+ }
196
+
197
+ const minimapData = { data: metadata.storeys, sunsimFiles }
198
+ return { ...a, minimapData }
199
+ } catch {
200
+ return a
201
+ }
202
+ })
203
+ )
204
+ }
205
+
206
+ startPlayer(domElement, onStartedCb) {
207
+ const player = new PlayerConfig({
208
+ target: domElement,
209
+ addBuildVersionToUrls: this._addBuildVersionToUrls,
210
+ fileResolver: (name) => this.resolveApartmentFile(name),
211
+ onPlayerCameraChanged: (cameraID) => this.playerCameraChangedTo(cameraID),
212
+ panoramaOrientation: this._state._panoramaOrientation,
213
+ mouseSensitivityFactor: this._mouseSensitivityFactor
214
+ })
215
+
216
+ player.start(this._initData, this.getStartCameraId(), onStartedCb)
217
+ return player
218
+ }
219
+
220
+ playerCameraChangedTo(cameraId) {
221
+ this._navigator.selectedCameraId = cameraId
222
+ }
223
+
224
+ selectArchetypeByIndex(index) {
225
+ this._state._selectedArchetypeIndex = index
226
+ }
227
+
228
+ get isInitialized() {
229
+ return this._state._initialized
230
+ }
231
+
232
+ get viewMode() {
233
+ return this._state._viewMode
234
+ }
235
+
236
+ get availableViewModes() {
237
+ return this._state._availableViewModes
238
+ }
239
+
240
+ get isViewModeVirtualTour() {
241
+ return this._state._viewMode === ViewModeVirtualTour
242
+ }
243
+
244
+ get isViewModeSlideShow() {
245
+ return this._state._viewMode === ViewModeSlideShow
246
+ }
247
+
248
+ nextViewMode() {
249
+ if (this._state._availableViewModes.length > 1) {
250
+ const currentIndex = this._state._availableViewModes.findIndex(
251
+ (m) => m === this._state._viewMode
252
+ )
253
+ if (currentIndex === this._state._availableViewModes.length - 1) {
254
+ this._state._viewMode = this._state._availableViewModes[0]
255
+ } else {
256
+ this._state._viewMode =
257
+ this._state._availableViewModes[currentIndex + 1]
258
+ }
259
+ }
260
+ }
261
+
262
+ getStartCameraId() {
263
+ if (this._navigator.selectedCameraId)
264
+ return this._navigator.selectedCameraId
265
+ else throw new Error('Unable to detect a starting camera for virtual tour')
266
+ }
267
+
268
+ resolveCdnApartment() {
269
+ return `/${this.archetypeId}/${this.apartmentId}`
270
+ }
271
+
272
+ getFilename(name) {
273
+
274
+ if (this._state.adminMode) {
275
+ return this._cdnFileResolver(name) + `?v=${Date.now()}`
276
+ }
277
+
278
+ return this._cdnFileResolver(name)
279
+
280
+
281
+ }
282
+
283
+ resolveApartmentFile(name) {
284
+ if (!name.startsWith('/')) {
285
+ name = '/' + name
286
+ }
287
+ return this.getFilename(this.resolveCdnApartment() + name)
288
+ }
289
+
290
+ get apartmentId() {
291
+ return this._apartmentId
292
+ }
293
+
294
+ get archetype() {
295
+ const archetype = this.archetypes
296
+ ? this.archetypes[this._state._selectedArchetypeIndex]
297
+ : undefined
298
+ return archetype
299
+ }
300
+
301
+ get minimapData() {
302
+ return this.archetype && this.archetype.minimapData
303
+ }
304
+
305
+ get archetypeId() {
306
+ const archetype = this.archetypes
307
+ ? this.archetypes[this._state._selectedArchetypeIndex]
308
+ : undefined
309
+ return archetype && archetype.id
310
+ }
311
+
312
+ get selectedArchetypeIndex() {
313
+ return this._state._selectedArchetypeIndex
314
+ }
315
+
316
+ get websiteUrl() {
317
+ return this._websiteUrl
318
+ }
319
+
320
+ get archetypes() {
321
+ return this._state._archetypes
322
+ }
323
+
324
+ get thumbnails() {
325
+ if (this._state._viewMode === ViewModeManualSlideShow) {
326
+ return this._manualSlideshowImages
327
+ }
328
+
329
+ return this._navigator.waypoints.map((thumb) => ({
330
+ id: thumb.id,
331
+ url: this.getWaypointThumbnailUrl(this.archetypeId, thumb.id),
332
+ }))
333
+ }
334
+
335
+ getWaypointThumbnailUrl(interiorId, cameraId) {
336
+ return this.getFilename(
337
+ `/${interiorId}/${this.apartmentId}/thumbs/200/${cameraId}.jpg`
338
+ )
339
+ }
340
+
341
+ get selectedWaypointImageUrl() {
342
+ const selectedWaypoint = this._navigator.selectedWaypoint
343
+
344
+ if (this._state._viewMode === ViewModeManualSlideShow && selectedWaypoint) {
345
+ return selectedWaypoint.url
346
+ }
347
+
348
+ return selectedWaypoint
349
+ ? this.getFilename(
350
+ `/${this.archetypeId}/${this.apartmentId}/images/1280/${selectedWaypoint.id}.jpg`
351
+ )
352
+ : undefined
353
+ }
354
+
355
+ get panoramaOrientation() {
356
+ return this._state._panoramaOrientation
357
+ }
358
+
359
+ get cameraSnapshot() {
360
+ return this._state._cameraSnapshot.latest
361
+ }
362
+
363
+ set cameraSnapshot(value) {
364
+ this._state._cameraSnapshot.latest = value
365
+ }
366
+
367
+ get cameraSnapshotRequested() {
368
+ return this._state._cameraSnapshot.requested
369
+ }
370
+
371
+ get allCameras() {
372
+ return this._state._cameras
373
+ }
374
+
375
+ get showCompass() {
376
+ return this._showCompass
377
+ }
378
+
379
+ get showMiniMap() {
380
+ return this._showMiniMap
381
+ }
382
+
383
+ get waypointNavigator() {
384
+ return this._navigator
385
+ }
386
+
387
+ get timeOfDay() {
388
+ return this._state._timeOfDay
389
+ }
390
+
391
+ get hasInformationButtonCallback() {
392
+ return !!this._onInformationButtonPress
393
+ }
394
+
395
+ get onInformationButtonPress() {
396
+ return this._onInformationButtonPress
397
+ }
398
+ }
399
+
400
+ class CdnPlayerViewModel extends PlayerViewModel {
401
+ constructor(modelParams) {
402
+ super({
403
+ ...modelParams,
404
+ cdnFileResolver: (name) => modelParams.cdnRoot + name,
405
+ })
406
+ this._cdnRoot = modelParams.cdnRoot
407
+ }
408
+
409
+ dehydrate() {
410
+ return {
411
+ cdnRoot: this._cdnRoot,
412
+ apartmentId: this._apartmentId,
413
+ archetypes: this._state._archetypes,
414
+ selectedArchetypeIndex: this._state._selectedArchetypeIndex,
415
+ websiteUrl: this._websiteUrl,
416
+ unitViewMode: this._unitViewMode,
417
+ showCompass: this._showCompass,
418
+ showMiniMap: this._showMiniMap,
419
+ timeOfDay: this._timeOfDay,
420
+ onInformationButtonPress: this._onInformationButtonPress,
421
+ }
422
+ }
423
+ }