@bubo-squared/gyroview 0.1.0

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 (228) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +216 -0
  4. package/dist/define.d.ts +3 -0
  5. package/dist/define.js +4 -0
  6. package/dist/index.d.ts +959 -0
  7. package/dist/index.js +13 -0
  8. package/dist/packages/adapters/blob/src/BlobRandomAccessSource.js +37 -0
  9. package/dist/packages/adapters/blob/src/index.js +2 -0
  10. package/dist/packages/adapters/fetch/src/HttpRangeSource.js +194 -0
  11. package/dist/packages/adapters/fetch/src/HttpResourceLocator.js +35 -0
  12. package/dist/packages/adapters/fetch/src/httpRequest.js +126 -0
  13. package/dist/packages/adapters/fetch/src/index.js +3 -0
  14. package/dist/packages/adapters/mediabunny/src/MediabunnyAudioSegments.js +133 -0
  15. package/dist/packages/adapters/mediabunny/src/MediabunnyDemuxer.js +63 -0
  16. package/dist/packages/adapters/mediabunny/src/MediabunnyVideoTrackReader.js +84 -0
  17. package/dist/packages/adapters/mediabunny/src/SegmentChannel.js +84 -0
  18. package/dist/packages/adapters/mediabunny/src/bufferSources.js +12 -0
  19. package/dist/packages/adapters/mediabunny/src/index.js +2 -0
  20. package/dist/packages/adapters/mse-audio/src/MediaSourceAudioClock.js +139 -0
  21. package/dist/packages/adapters/mse-audio/src/SourceBufferFeeder.js +174 -0
  22. package/dist/packages/adapters/mse-audio/src/events.js +26 -0
  23. package/dist/packages/adapters/mse-audio/src/index.js +2 -0
  24. package/dist/packages/adapters/mse-audio/src/mediaSourceSupport.js +82 -0
  25. package/dist/packages/adapters/three/src/ThreeFrameRenderer.js +274 -0
  26. package/dist/packages/adapters/three/src/compileAndProve.js +14 -0
  27. package/dist/packages/adapters/three/src/fullscreenPass.js +52 -0
  28. package/dist/packages/adapters/three/src/index.js +2 -0
  29. package/dist/packages/adapters/three/src/lensTextures.js +25 -0
  30. package/dist/packages/adapters/three/src/pictureMaterials.js +44 -0
  31. package/dist/packages/adapters/three/src/renderInto.js +15 -0
  32. package/dist/packages/adapters/three/src/rendererUniforms.js +157 -0
  33. package/dist/packages/adapters/three/src/samplingStrategies.js +26 -0
  34. package/dist/packages/adapters/three/src/seamMeter/SeamMeterPass.js +79 -0
  35. package/dist/packages/adapters/three/src/seamMeter/rowMeans.js +38 -0
  36. package/dist/packages/adapters/three/src/shaderPrograms.js +66 -0
  37. package/dist/packages/adapters/three/src/shaders/analysis.frag.js +4 -0
  38. package/dist/packages/adapters/three/src/shaders/equirectangularRays.js +4 -0
  39. package/dist/packages/adapters/three/src/shaders/fixedJoin.js +4 -0
  40. package/dist/packages/adapters/three/src/shaders/fullscreen.vert.js +4 -0
  41. package/dist/packages/adapters/three/src/shaders/header.js +4 -0
  42. package/dist/packages/adapters/three/src/shaders/lensModels.js +4 -0
  43. package/dist/packages/adapters/three/src/shaders/lensSampling.js +4 -0
  44. package/dist/packages/adapters/three/src/shaders/lensTextures.js +4 -0
  45. package/dist/packages/adapters/three/src/shaders/rawLenses.frag.js +4 -0
  46. package/dist/packages/adapters/three/src/shaders/rectilinearRays.js +4 -0
  47. package/dist/packages/adapters/three/src/shaders/screenAreas.js +4 -0
  48. package/dist/packages/adapters/three/src/shaders/stitch.frag.js +4 -0
  49. package/dist/packages/adapters/three/src/webglRenderer.js +37 -0
  50. package/dist/packages/adapters/webcodecs/src/WebCodecsVideoDecoderPort.js +135 -0
  51. package/dist/packages/adapters/webcodecs/src/index.js +2 -0
  52. package/dist/packages/core/src/application/gainMatching/GainMatching.js +66 -0
  53. package/dist/packages/core/src/application/gainMatching/GainMatchingFrameSink.js +62 -0
  54. package/dist/packages/core/src/application/playback/ClockWatch.js +76 -0
  55. package/dist/packages/core/src/application/playback/DecodePipeline.js +188 -0
  56. package/dist/packages/core/src/application/playback/DecodeRun.js +93 -0
  57. package/dist/packages/core/src/application/playback/FramePairQueue.js +93 -0
  58. package/dist/packages/core/src/application/playback/FramePairer.js +60 -0
  59. package/dist/packages/core/src/application/playback/PlaybackSession.js +417 -0
  60. package/dist/packages/core/src/application/playback/SeekOrder.js +36 -0
  61. package/dist/packages/core/src/application/playback/SessionLifecycle.js +42 -0
  62. package/dist/packages/core/src/application/playback/StartGate.js +55 -0
  63. package/dist/packages/core/src/application/playback/TimeUpdates.js +31 -0
  64. package/dist/packages/core/src/application/playback/WallClock.js +46 -0
  65. package/dist/packages/core/src/application/playback/keyframeTimeAt.js +15 -0
  66. package/dist/packages/core/src/application/playback/probeDecoding.js +168 -0
  67. package/dist/packages/core/src/application/recording/frameTimesOf.js +22 -0
  68. package/dist/packages/core/src/application/recording/inspectLayout.js +21 -0
  69. package/dist/packages/core/src/application/recording/inspectRecording.js +124 -0
  70. package/dist/packages/core/src/application/recording/locateOtherLensFile.js +18 -0
  71. package/dist/packages/core/src/application/recording/motionOf.js +39 -0
  72. package/dist/packages/core/src/application/recording/readRecording.js +31 -0
  73. package/dist/packages/core/src/application/recording/timeRecording.js +57 -0
  74. package/dist/packages/core/src/application/stabilization/StabilizingFrameSink.js +31 -0
  75. package/dist/packages/core/src/domain/format/boxes/BoxLayout.js +7 -0
  76. package/dist/packages/core/src/domain/format/boxes/boxConstants.js +16 -0
  77. package/dist/packages/core/src/domain/format/boxes/scanBoxes.js +59 -0
  78. package/dist/packages/core/src/domain/format/calibration/CalibrationVersion.js +13 -0
  79. package/dist/packages/core/src/domain/format/calibration/layouts/CalibrationStringLayout.js +34 -0
  80. package/dist/packages/core/src/domain/format/calibration/layouts/LegacyCalibrationLayout.js +44 -0
  81. package/dist/packages/core/src/domain/format/calibration/layouts/MeiCalibrationLayout.js +45 -0
  82. package/dist/packages/core/src/domain/format/calibration/layouts/PolynomialCalibrationLayout.js +44 -0
  83. package/dist/packages/core/src/domain/format/calibration/offsetTokens.js +65 -0
  84. package/dist/packages/core/src/domain/format/calibration/parseOffsetString.js +52 -0
  85. package/dist/packages/core/src/domain/format/calibration/selectCalibration.js +46 -0
  86. package/dist/packages/core/src/domain/format/captureOrigin.js +20 -0
  87. package/dist/packages/core/src/domain/format/constants.js +33 -0
  88. package/dist/packages/core/src/domain/format/info/infoFields.js +110 -0
  89. package/dist/packages/core/src/domain/format/info/parseInfoRecord.js +90 -0
  90. package/dist/packages/core/src/domain/format/info/readInfoRecord.js +14 -0
  91. package/dist/packages/core/src/domain/format/layout/detectLensLayout.js +122 -0
  92. package/dist/packages/core/src/domain/format/naming/RecordingFileName.js +67 -0
  93. package/dist/packages/core/src/domain/format/records/TrailerRecords.js +83 -0
  94. package/dist/packages/core/src/domain/format/records/exposure/parseExposureRecord.js +48 -0
  95. package/dist/packages/core/src/domain/format/records/gyro/FloatGyroSampleLayout.js +39 -0
  96. package/dist/packages/core/src/domain/format/records/gyro/RawGyroSampleLayout.js +55 -0
  97. package/dist/packages/core/src/domain/format/records/gyro/SampleColumns.js +82 -0
  98. package/dist/packages/core/src/domain/format/records/gyro/gyroLayouts.js +12 -0
  99. package/dist/packages/core/src/domain/format/records/gyro/parseGyroRecord.js +129 -0
  100. package/dist/packages/core/src/domain/format/trailer/RecordHeader.js +23 -0
  101. package/dist/packages/core/src/domain/format/trailer/Trailer.js +23 -0
  102. package/dist/packages/core/src/domain/format/trailer/TrailerFooter.js +27 -0
  103. package/dist/packages/core/src/domain/format/trailer/locateRecords.js +80 -0
  104. package/dist/packages/core/src/domain/format/trailer/parseRecordIndex.js +28 -0
  105. package/dist/packages/core/src/domain/format/trailer/readTrailer.js +32 -0
  106. package/dist/packages/core/src/domain/motion/exposure/ExposureRecord.js +53 -0
  107. package/dist/packages/core/src/domain/motion/gyro/GyroTrack.js +60 -0
  108. package/dist/packages/core/src/domain/motion/gyro/medianStep.js +11 -0
  109. package/dist/packages/core/src/domain/motion/gyro/repairedTimeline.js +101 -0
  110. package/dist/packages/core/src/domain/motion/imu/ImuFrame.js +112 -0
  111. package/dist/packages/core/src/domain/motion/orientation/OrientationTrack.js +146 -0
  112. package/dist/packages/core/src/domain/motion/orientation/estimateGyroBias.js +64 -0
  113. package/dist/packages/core/src/domain/motion/orientation/gravity.js +97 -0
  114. package/dist/packages/core/src/domain/motion/stabilization/Stabilizer.js +13 -0
  115. package/dist/packages/core/src/domain/motion/stabilization/stabilizers.js +86 -0
  116. package/dist/packages/core/src/domain/motion/timing/CaptureClock.js +41 -0
  117. package/dist/packages/core/src/domain/motion/timing/FrameTimes.js +92 -0
  118. package/dist/packages/core/src/domain/motion/timing/frameTimeSources.js +61 -0
  119. package/dist/packages/core/src/domain/motion/timing/resolveFrameTimes.js +68 -0
  120. package/dist/packages/core/src/domain/optics/EquidistantModel.js +39 -0
  121. package/dist/packages/core/src/domain/optics/MeiModel.js +45 -0
  122. package/dist/packages/core/src/domain/optics/PolynomialModel.js +44 -0
  123. package/dist/packages/core/src/domain/optics/gainMatch.js +62 -0
  124. package/dist/packages/core/src/domain/optics/lensGeometry.js +18 -0
  125. package/dist/packages/core/src/domain/optics/lensPose.js +35 -0
  126. package/dist/packages/core/src/domain/optics/opticsConstants.js +5 -0
  127. package/dist/packages/core/src/domain/playback/PlayerState.js +83 -0
  128. package/dist/packages/core/src/domain/stitching/LensLayout.js +21 -0
  129. package/dist/packages/core/src/domain/stitching/StitchingSetup.js +78 -0
  130. package/dist/packages/core/src/domain/stitching/seamDisparity.js +4 -0
  131. package/dist/packages/core/src/domain/stitching/seamDisparityField.js +5 -0
  132. package/dist/packages/core/src/domain/stitching/seamJoin.js +16 -0
  133. package/dist/packages/core/src/domain/stitching/seamMismatch.js +1 -0
  134. package/dist/packages/core/src/domain/stitching/seamRing.js +4 -0
  135. package/dist/packages/core/src/domain/stitching/seamStrip.js +26 -0
  136. package/dist/packages/core/src/domain/view/Framing.js +31 -0
  137. package/dist/packages/core/src/domain/view/PictureQuality.js +26 -0
  138. package/dist/packages/core/src/domain/view/ViewMode.js +9 -0
  139. package/dist/packages/core/src/domain/view/ViewState.js +48 -0
  140. package/dist/packages/core/src/domain/view/lensTilesView.js +63 -0
  141. package/dist/packages/core/src/domain/view/magnification.js +103 -0
  142. package/dist/packages/core/src/domain/view/normalView.js +29 -0
  143. package/dist/packages/core/src/domain/view/panoramaView.js +99 -0
  144. package/dist/packages/core/src/domain/view/rectilinear.js +40 -0
  145. package/dist/packages/core/src/domain/view/screenLayout.js +113 -0
  146. package/dist/packages/core/src/domain/view/viewGestures.js +98 -0
  147. package/dist/packages/core/src/domain/view/viewModes.js +14 -0
  148. package/dist/packages/core/src/index.js +59 -0
  149. package/dist/packages/core/src/ports/FramePair.js +6 -0
  150. package/dist/packages/core/src/shared/async/Deferred.js +42 -0
  151. package/dist/packages/core/src/shared/async/RunStop.js +40 -0
  152. package/dist/packages/core/src/shared/binary/ByteRange.js +38 -0
  153. package/dist/packages/core/src/shared/binary/ByteReader.js +77 -0
  154. package/dist/packages/core/src/shared/errors/GyroViewError.js +81 -0
  155. package/dist/packages/core/src/shared/errors/reportLater.js +11 -0
  156. package/dist/packages/core/src/shared/events/Outbox.js +68 -0
  157. package/dist/packages/core/src/shared/events/TypedEmitter.js +55 -0
  158. package/dist/packages/core/src/shared/keysOf.js +10 -0
  159. package/dist/packages/core/src/shared/lazy.js +11 -0
  160. package/dist/packages/core/src/shared/math/Matrix3.js +93 -0
  161. package/dist/packages/core/src/shared/math/Quaternion.js +133 -0
  162. package/dist/packages/core/src/shared/math/Vector3.js +54 -0
  163. package/dist/packages/core/src/shared/math/clamp.js +9 -0
  164. package/dist/packages/core/src/shared/protobuf/ProtobufMessage.js +157 -0
  165. package/dist/packages/core/src/shared/text/urlPath.js +31 -0
  166. package/dist/packages/core/src/shared/text/utf8.js +12 -0
  167. package/dist/packages/core/src/shared/units/angle.js +22 -0
  168. package/dist/packages/core/src/shared/units/time.js +13 -0
  169. package/dist/packages/player/src/PlayerSource.js +15 -0
  170. package/dist/packages/player/src/browserPlayer.js +18 -0
  171. package/dist/packages/player/src/choices.js +25 -0
  172. package/dist/packages/player/src/composition/Disposables.js +31 -0
  173. package/dist/packages/player/src/composition/browserPorts.js +46 -0
  174. package/dist/packages/player/src/composition/buildPipeline.js +153 -0
  175. package/dist/packages/player/src/composition/ensureDecodable.js +63 -0
  176. package/dist/packages/player/src/composition/openInputs.js +179 -0
  177. package/dist/packages/player/src/composition/openRecording.js +42 -0
  178. package/dist/packages/player/src/controls/ChoiceMenu.js +113 -0
  179. package/dist/packages/player/src/controls/PictureMenus.js +47 -0
  180. package/dist/packages/player/src/controls/SeekBar.js +125 -0
  181. package/dist/packages/player/src/controls/SoundControls.js +41 -0
  182. package/dist/packages/player/src/controls/TransportButtons.js +31 -0
  183. package/dist/packages/player/src/controls/ViewGestures.js +136 -0
  184. package/dist/packages/player/src/controls/Wording.js +78 -0
  185. package/dist/packages/player/src/controls/controlParts.js +36 -0
  186. package/dist/packages/player/src/controls/controls.js +4 -0
  187. package/dist/packages/player/src/controls/controlsBar.js +23 -0
  188. package/dist/packages/player/src/controls/controlsMarkup.js +85 -0
  189. package/dist/packages/player/src/controls/customControls.js +59 -0
  190. package/dist/packages/player/src/controls/formatTime.js +19 -0
  191. package/dist/packages/player/src/controls/icons.js +40 -0
  192. package/dist/packages/player/src/controls/keyboard.js +118 -0
  193. package/dist/packages/player/src/controls/messages.js +94 -0
  194. package/dist/packages/player/src/controls/parseMarkup.js +31 -0
  195. package/dist/packages/player/src/controls/viewButtons.js +14 -0
  196. package/dist/packages/player/src/element/ElementLoads.js +119 -0
  197. package/dist/packages/player/src/element/FullscreenToggle.js +39 -0
  198. package/dist/packages/player/src/element/GyroViewElement.js +313 -0
  199. package/dist/packages/player/src/element/IdleWatcher.js +71 -0
  200. package/dist/packages/player/src/element/TypedEventElement.js +22 -0
  201. package/dist/packages/player/src/element/accessibleRegion.js +19 -0
  202. package/dist/packages/player/src/element/attributeNames.js +51 -0
  203. package/dist/packages/player/src/element/attributes.js +101 -0
  204. package/dist/packages/player/src/element/defineGyroView.js +21 -0
  205. package/dist/packages/player/src/element/earlyProperties.js +36 -0
  206. package/dist/packages/player/src/element/elementSource.js +23 -0
  207. package/dist/packages/player/src/element/liveSettings.js +138 -0
  208. package/dist/packages/player/src/element/playbackAttributes.js +53 -0
  209. package/dist/packages/player/src/element/playerEventMirror.js +33 -0
  210. package/dist/packages/player/src/element/reflectedProperties.js +78 -0
  211. package/dist/packages/player/src/element/styles.js +4 -0
  212. package/dist/packages/player/src/element/template.js +47 -0
  213. package/dist/packages/player/src/index.js +10 -0
  214. package/dist/packages/player/src/inspectRecording.js +22 -0
  215. package/dist/packages/player/src/player/DrawingBufferFit.js +77 -0
  216. package/dist/packages/player/src/player/FrameLoop.js +40 -0
  217. package/dist/packages/player/src/player/PictureSettings.js +60 -0
  218. package/dist/packages/player/src/player/Player.js +377 -0
  219. package/dist/packages/player/src/player/PlayerEvents.js +28 -0
  220. package/dist/packages/player/src/player/PlayerPhase.js +21 -0
  221. package/dist/packages/player/src/player/PlayerSound.js +61 -0
  222. package/dist/packages/player/src/player/PlayerView.js +113 -0
  223. package/dist/packages/player/src/player/SessionRelay.js +66 -0
  224. package/dist/packages/player/src/player/ensureFinite.js +26 -0
  225. package/dist/packages/player/src/player/loadRecording.js +65 -0
  226. package/dist/packages/player/src/player/transportEvents.js +29 -0
  227. package/dist/standalone.js +4919 -0
  228. package/package.json +73 -0
@@ -0,0 +1,85 @@
1
+ import { STABILIZATION_MODES } from "../../../core/src/domain/motion/stabilization/Stabilizer.js";
2
+ import { VIEW_MODES } from "../../../core/src/domain/view/ViewMode.js";
3
+ import "../../../core/src/index.js";
4
+ import controls_default from "./controls.js";
5
+ import { ICONS } from "./icons.js";
6
+ //#region ../../packages/player/src/controls/controlsMarkup.ts
7
+ /**
8
+ * The bar's choice menus, by the name their parts are found under.
9
+ */
10
+ var ChoiceMenuName = {
11
+ Stabilization: "stabilization",
12
+ ViewMode: "view-mode"
13
+ };
14
+ /**
15
+ * The class names of a choice menu's button and popup, for the markup and the query alike.
16
+ */
17
+ function choiceMenuClasses(name) {
18
+ return {
19
+ button: `${name}-button`,
20
+ popup: `${name}-menu`
21
+ };
22
+ }
23
+ function menuItemsOf(choices) {
24
+ return choices.map((choice) => `<button type="button" role="menuitemradio" aria-checked="false" tabindex="-1" data-choice="${choice}"></button>`).join("");
25
+ }
26
+ /**
27
+ * A button opening a popup of choices; the popup's title repeats the button's label for sight.
28
+ * The words are the wording's to fill in.
29
+ */
30
+ function choiceMenuOf(menu) {
31
+ const classes = choiceMenuClasses(menu.name);
32
+ return `<div class="choice">
33
+ <button class="${classes.button}" type="button" data-label="${menu.title}" aria-haspopup="menu" aria-expanded="false">${menu.icon}</button>
34
+ <div class="popup ${classes.popup}" hidden>
35
+ <p class="popup-title" aria-hidden="true" data-text="${menu.title}"></p>
36
+ <div role="menu" data-label="${menu.title}" data-choices="${menu.choices}">${menu.items}</div>
37
+ </div>
38
+ </div>`;
39
+ }
40
+ function pictureMenusMarkup() {
41
+ return choiceMenuOf({
42
+ name: ChoiceMenuName.Stabilization,
43
+ title: "stabilization",
44
+ icon: ICONS.stabilization,
45
+ choices: "stabilizationModes",
46
+ items: menuItemsOf(STABILIZATION_MODES)
47
+ }) + choiceMenuOf({
48
+ name: ChoiceMenuName.ViewMode,
49
+ title: "viewMode",
50
+ icon: ICONS.viewMode,
51
+ choices: "viewModes",
52
+ items: menuItemsOf(VIEW_MODES)
53
+ });
54
+ }
55
+ /**
56
+ * The controls' stylesheet, which the element adopts beside its own.
57
+ */
58
+ var CONTROLS_STYLES = controls_default;
59
+ /**
60
+ * The big play button and the control bar, for the element's stage; built when first asked for,
61
+ * so a page importing only the package's types and errors carries none of it. Class names are
62
+ * the contract with `queryControlParts` and `controls.css`. It holds no words, which the
63
+ * wording fills in, nor the play and mute buttons' state-dependent labels and icons, which
64
+ * those buttons give themselves when they are bound.
65
+ */
66
+ function controlsMarkup() {
67
+ return `
68
+ <button class="big-play" part="big-play" type="button">${ICONS.play}</button>
69
+ <div class="controls" part="controls">
70
+ <input class="seek" type="range" min="0" max="0" step="0.01" value="0" data-label="seek" />
71
+ <div class="row">
72
+ <button class="play" type="button"></button>
73
+ <button class="stop" type="button" data-label="stop">${ICONS.stop}</button>
74
+ <span class="time">0:00 / 0:00</span>
75
+ <span class="spacer"></span>
76
+ <button class="mute" type="button" data-label="mute"></button>
77
+ <input class="volume" type="range" min="0" max="1" step="0.01" value="1" data-label="volume" />
78
+ <button class="reset-view" type="button" data-label="resetView">${ICONS.resetView}</button>
79
+ ${pictureMenusMarkup()}
80
+ <button class="fullscreen" type="button" data-label="fullscreen">${ICONS.fullscreen}</button>
81
+ </div>
82
+ </div>`;
83
+ }
84
+ //#endregion
85
+ export { CONTROLS_STYLES, ChoiceMenuName, choiceMenuClasses, controlsMarkup };
@@ -0,0 +1,59 @@
1
+ import { messageOf } from "../../../core/src/shared/errors/GyroViewError.js";
2
+ import "../../../core/src/index.js";
3
+ import { bindKeyboard } from "./keyboard.js";
4
+ import { ViewGestures } from "./ViewGestures.js";
5
+ //#region ../../packages/player/src/controls/customControls.ts
6
+ /**
7
+ * The gestures `<gyro-view>` answers, for an interface of a page's own around the headless
8
+ * player: a drag looks around (or moves a zoomed picture), a pinch or a wheel turn zooms toward
9
+ * the fingers or the pointer, a tap plays or pauses. `surface` is the canvas or an element over
10
+ * it. Returns what removes them.
11
+ */
12
+ function attachViewGestures(surface, player, options = {}) {
13
+ const onTap = options.onTap ?? (() => {
14
+ togglePlayback(player);
15
+ });
16
+ const gestures = new ViewGestures(surface, player, onTap);
17
+ return () => {
18
+ gestures.detach();
19
+ };
20
+ }
21
+ /**
22
+ * The keyboard shortcuts `<gyro-view>` answers, on `target`, which a page makes focusable: space
23
+ * or K play and pause, J and L seek, S stops, the arrows look around (with Shift they seek), plus
24
+ * and minus zoom, 0 resets the view, M mutes, F fills the screen and Escape leaves it. Returns
25
+ * what removes them.
26
+ */
27
+ function attachKeyboard(target, player, options = {}) {
28
+ const isFullscreen = () => target.matches(":fullscreen");
29
+ return bindKeyboard(target, {
30
+ player,
31
+ togglePlay: () => {
32
+ togglePlayback(player);
33
+ },
34
+ toggleFullscreen: options.toggleFullscreen ?? (() => {
35
+ isFullscreen() ? document.exitFullscreen() : target.requestFullscreen();
36
+ }),
37
+ isFullscreen,
38
+ exitFullscreen: () => {
39
+ document.exitFullscreen();
40
+ }
41
+ });
42
+ }
43
+ /**
44
+ * Pauses, or starts playing and reports a refused start as the player's `warning`.
45
+ */
46
+ function togglePlayback(player) {
47
+ if (!player.isPaused) {
48
+ player.pause();
49
+ return;
50
+ }
51
+ player.play().catch((error) => {
52
+ player.events.emit("warning", {
53
+ code: "playback-failed",
54
+ message: `playback could not start: ${messageOf(error)}`
55
+ });
56
+ });
57
+ }
58
+ //#endregion
59
+ export { attachKeyboard, attachViewGestures, togglePlayback };
@@ -0,0 +1,19 @@
1
+ //#region ../../packages/player/src/controls/formatTime.ts
2
+ var SECONDS_PER_MINUTE = 60;
3
+ var MINUTES_PER_HOUR = 60;
4
+ /**
5
+ * `m:ss`, or `h:mm:ss` from one hour on, as media players show it. Negative or unknown times
6
+ * read as zero.
7
+ */
8
+ function formatTime(totalSeconds) {
9
+ const whole = Number.isFinite(totalSeconds) ? Math.max(0, Math.floor(totalSeconds)) : 0;
10
+ const seconds = whole % SECONDS_PER_MINUTE;
11
+ const minutes = Math.floor(whole / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
12
+ const hours = Math.floor(whole / 3600);
13
+ return hours > 0 ? `${hours}:${padded(minutes)}:${padded(seconds)}` : `${minutes}:${padded(seconds)}`;
14
+ }
15
+ function padded(value) {
16
+ return String(value).padStart(2, "0");
17
+ }
18
+ //#endregion
19
+ export { formatTime };
@@ -0,0 +1,40 @@
1
+ import { parseMarkup } from "./parseMarkup.js";
2
+ //#region ../../packages/player/src/controls/icons.ts
3
+ /**
4
+ * Wraps an icon's shapes in a 24-unit square. Shapes are stroked in `currentColor` unless they
5
+ * say otherwise, so every icon takes the controls' text colour.
6
+ */
7
+ function icon(shapes) {
8
+ return `<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">${shapes}</svg>`;
9
+ }
10
+ var SPEAKER = "<path d=\"M4 9.5v5h3.5L12 19V5L7.5 9.5z\" fill=\"currentColor\"/>";
11
+ /**
12
+ * The controls' icons, drawn as inline SVG in `currentColor` only: a text glyph is left to the
13
+ * platform's fonts, some of which draw it as a colour emoji.
14
+ */
15
+ var ICONS = {
16
+ play: icon("<path d=\"M8 5v14l11-7z\" fill=\"currentColor\"/>"),
17
+ pause: icon("<rect x=\"6\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" fill=\"currentColor\" stroke=\"none\"/><rect x=\"14\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" fill=\"currentColor\" stroke=\"none\"/>"),
18
+ stop: icon("<rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"1.5\" fill=\"currentColor\" stroke=\"none\"/>"),
19
+ sound: icon(`${SPEAKER}<path d="M15.5 9a4 4 0 0 1 0 6"/><path d="M18.5 6a8 8 0 0 1 0 12"/>`),
20
+ muted: icon(`${SPEAKER}<path d="M16 9.5l5 5M21 9.5l-5 5"/>`),
21
+ resetView: icon("<path d=\"M4.5 12a7.5 7.5 0 1 0 2.2-5.3\"/><path d=\"M4.5 4.5v4h4\"/>"),
22
+ stabilization: icon("<rect x=\"2.5\" y=\"8.5\" width=\"19\" height=\"7\" rx=\"3.5\"/><path d=\"M8.5 8.5v7M15.5 8.5v7\"/><circle cx=\"12\" cy=\"12\" r=\"1.8\" fill=\"currentColor\" stroke=\"none\"/>"),
23
+ viewMode: icon("<circle cx=\"12\" cy=\"12\" r=\"8.5\"/><ellipse cx=\"12\" cy=\"12\" rx=\"3.5\" ry=\"8.5\"/><path d=\"M3.5 12h17\"/>"),
24
+ fullscreen: icon("<path d=\"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5\"/>")
25
+ };
26
+ var parsedIcons = /* @__PURE__ */ new Map();
27
+ /**
28
+ * A copy of an icon for a button to show; each is parsed once per page, so changing a button's
29
+ * icon parses nothing.
30
+ */
31
+ function iconNode(name) {
32
+ let parsed = parsedIcons.get(name);
33
+ if (!parsed) {
34
+ parsed = parseMarkup(ICONS[name]);
35
+ parsedIcons.set(name, parsed);
36
+ }
37
+ return parsed.cloneNode(true);
38
+ }
39
+ //#endregion
40
+ export { ICONS, iconNode };
@@ -0,0 +1,118 @@
1
+ //#region ../../packages/player/src/controls/keyboard.ts
2
+ /**
3
+ * Keys as `KeyboardEvent.key` reports them. Space and K play, J and L seek, S stops, the arrows
4
+ * look around (with Shift they seek), M mutes, F fills the screen, 0 resets the view, plus and
5
+ * minus zoom. Escape leaves fullscreen.
6
+ */
7
+ var PLAIN_KEYS = /* @__PURE__ */ new Map([
8
+ [" ", "toggle-play"],
9
+ ["k", "toggle-play"],
10
+ ["j", "seek-back"],
11
+ ["l", "seek-forward"],
12
+ ["s", "stop"],
13
+ ["m", "toggle-mute"],
14
+ ["f", "toggle-fullscreen"],
15
+ ["0", "reset-view"],
16
+ ["+", "zoom-in"],
17
+ ["=", "zoom-in"],
18
+ ["-", "zoom-out"],
19
+ ["ArrowLeft", "look-left"],
20
+ ["ArrowRight", "look-right"],
21
+ ["ArrowUp", "look-up"],
22
+ ["ArrowDown", "look-down"]
23
+ ]);
24
+ var SHIFTED_KEYS = /* @__PURE__ */ new Map([["ArrowLeft", "seek-back"], ["ArrowRight", "seek-forward"]]);
25
+ /**
26
+ * The player command a key press means, or undefined when the press is not a shortcut. A
27
+ * character already carries what Shift did to it (`+` is Shift and `=` on a US keyboard), so
28
+ * Shift picks other commands for the named keys only.
29
+ */
30
+ function shortcutFor(press) {
31
+ if (press.hasSystemModifier) return void 0;
32
+ if (press.key.length === 1) return PLAIN_KEYS.get(press.key.toLowerCase());
33
+ return press.isShiftPressed ? SHIFTED_KEYS.get(press.key) : PLAIN_KEYS.get(press.key);
34
+ }
35
+ var LOOK_STEP_DEGREES = 5;
36
+ function seekBy(delta) {
37
+ return ({ player }) => {
38
+ player.seek(player.currentTime + delta);
39
+ };
40
+ }
41
+ function lookBy(yawDelta, pitchDelta) {
42
+ return ({ player }) => {
43
+ player.turn(yawDelta, pitchDelta);
44
+ };
45
+ }
46
+ /**
47
+ * What each command does; a command without an action does not compile.
48
+ */
49
+ var ACTIONS = {
50
+ "toggle-play": (host) => {
51
+ host.togglePlay();
52
+ },
53
+ stop: ({ player }) => {
54
+ player.stop();
55
+ },
56
+ "seek-back": seekBy(-5),
57
+ "seek-forward": seekBy(5),
58
+ "toggle-mute": ({ player }) => {
59
+ player.setMuted(!player.isMuted);
60
+ },
61
+ "toggle-fullscreen": (host) => {
62
+ host.toggleFullscreen();
63
+ },
64
+ "reset-view": ({ player }) => {
65
+ player.resetView();
66
+ },
67
+ "zoom-in": ({ player }) => {
68
+ player.zoom(1);
69
+ },
70
+ "zoom-out": ({ player }) => {
71
+ player.zoom(-1);
72
+ },
73
+ "look-left": lookBy(-5, 0),
74
+ "look-right": lookBy(LOOK_STEP_DEGREES, 0),
75
+ "look-up": lookBy(0, LOOK_STEP_DEGREES),
76
+ "look-down": lookBy(0, -5)
77
+ };
78
+ /**
79
+ * Turns key presses on the element into player commands until the returned function is called,
80
+ * leaving the keys a focused control handles itself and the browser's shortcuts alone.
81
+ */
82
+ function bindKeyboard(element, host) {
83
+ const listening = new AbortController();
84
+ element.addEventListener("keydown", (event) => {
85
+ if (event.key === "Escape" && host.isFullscreen()) {
86
+ host.exitFullscreen();
87
+ return;
88
+ }
89
+ const command = shortcutFor({
90
+ key: event.key,
91
+ isShiftPressed: event.shiftKey,
92
+ hasSystemModifier: event.ctrlKey || event.altKey || event.metaKey
93
+ });
94
+ if (!command || isControlsOwnKey(event)) return;
95
+ event.preventDefault();
96
+ ACTIONS[command](host);
97
+ }, { signal: listening.signal });
98
+ return () => {
99
+ listening.abort();
100
+ };
101
+ }
102
+ var SLIDER_KEYS = /* @__PURE__ */ new Set([
103
+ "ArrowLeft",
104
+ "ArrowRight",
105
+ "ArrowUp",
106
+ "ArrowDown"
107
+ ]);
108
+ var BUTTON_KEY = " ";
109
+ /**
110
+ * A slider steps with the arrows and a button presses with Space. A press from the shadow tree
111
+ * reaches the element retargeted to it, so the control it started at is read from its path.
112
+ */
113
+ function isControlsOwnKey(event) {
114
+ const [origin] = event.composedPath();
115
+ return origin instanceof HTMLInputElement ? SLIDER_KEYS.has(event.key) : origin instanceof HTMLButtonElement && event.key === BUTTON_KEY;
116
+ }
117
+ //#endregion
118
+ export { bindKeyboard, shortcutFor };
@@ -0,0 +1,94 @@
1
+ //#region ../../packages/player/src/controls/messages.ts
2
+ var UNREACHABLE = "The video could not be loaded.";
3
+ var UNSUPPORTED_BROWSER = "This browser cannot play this video.";
4
+ var UNREADABLE = "This file cannot be played.";
5
+ var FAILED = "The video could not be played.";
6
+ var DEFAULT_MESSAGES = {
7
+ labels: {
8
+ player: "360° video player",
9
+ loading: "Loading",
10
+ play: "Play",
11
+ pause: "Pause",
12
+ stop: "Stop",
13
+ seek: "Seek",
14
+ position: "{time} of {duration}",
15
+ mute: "Mute",
16
+ volume: "Volume",
17
+ resetView: "Reset view",
18
+ stabilization: "Stabilization",
19
+ viewMode: "View",
20
+ fullscreen: "Fullscreen"
21
+ },
22
+ stabilizationModes: {
23
+ off: "Off",
24
+ lock: "Lock",
25
+ horizon: "Horizon",
26
+ follow: "Follow"
27
+ },
28
+ viewModes: {
29
+ "raw-lenses": "Raw lenses",
30
+ equirectangular: "Equirectangular",
31
+ normal: "Normal"
32
+ },
33
+ errors: {
34
+ "binary-out-of-bounds": UNREADABLE,
35
+ "binary-unsafe-integer": UNREADABLE,
36
+ "codec-unsupported": UNSUPPORTED_BROWSER,
37
+ cors: UNREACHABLE,
38
+ decode: FAILED,
39
+ "embed-destroyed": FAILED,
40
+ "index-out-of-range": FAILED,
41
+ "invalid-argument": FAILED,
42
+ "invalid-byte-range": UNREACHABLE,
43
+ "invalid-calibration": UNREADABLE,
44
+ "invalid-protobuf": UNREADABLE,
45
+ "invalid-trailer": UNREADABLE,
46
+ "invariant-violation": FAILED,
47
+ "missing-second-file": "Part of this recording is missing.",
48
+ "no-calibration": UNREADABLE,
49
+ "no-info-record": UNREADABLE,
50
+ "no-key-frame": UNREADABLE,
51
+ "playback-blocked": FAILED,
52
+ "range-unsupported": UNREACHABLE,
53
+ "render-unavailable": UNSUPPORTED_BROWSER,
54
+ "source-truncated": UNREACHABLE,
55
+ "source-unreadable": UNREACHABLE,
56
+ "unsupported-calibration": UNREADABLE,
57
+ "unsupported-container": UNREADABLE,
58
+ "unsupported-gyro-record": UNREADABLE,
59
+ "unsupported-info-format": UNREADABLE,
60
+ "unsupported-layout": UNREADABLE
61
+ }
62
+ };
63
+ /**
64
+ * The default words with the page's own in their place. What is not a string is left out, so a
65
+ * partial or mistyped table still leaves every word said.
66
+ */
67
+ function messagesWith(overrides) {
68
+ const given = isRecord(overrides) ? overrides : {};
69
+ return {
70
+ labels: tableWith(DEFAULT_MESSAGES.labels, given["labels"]),
71
+ stabilizationModes: tableWith(DEFAULT_MESSAGES.stabilizationModes, given["stabilizationModes"]),
72
+ viewModes: tableWith(DEFAULT_MESSAGES.viewModes, given["viewModes"]),
73
+ errors: tableWith(DEFAULT_MESSAGES.errors, given["errors"])
74
+ };
75
+ }
76
+ function isLabelName(name) {
77
+ return name !== null && Object.hasOwn(DEFAULT_MESSAGES.labels, name);
78
+ }
79
+ function isChoiceTable(name) {
80
+ return name === "stabilizationModes" || name === "viewModes";
81
+ }
82
+ function tableWith(defaults, overrides) {
83
+ if (!isRecord(overrides)) return defaults;
84
+ const words = Object.entries(defaults).map(([key, word]) => {
85
+ const given = overrides[key];
86
+ return [key, typeof given === "string" ? given : word];
87
+ });
88
+ return Object.fromEntries(words);
89
+ }
90
+ function isRecord(value) {
91
+ return typeof value === "object" && value !== null;
92
+ }
93
+ //#endregion
94
+ export { DEFAULT_MESSAGES, isChoiceTable, isLabelName, messagesWith };
@@ -0,0 +1,31 @@
1
+ import { lazy } from "../../../core/src/shared/lazy.js";
2
+ import "../../../core/src/index.js";
3
+ //#region ../../packages/player/src/controls/parseMarkup.ts
4
+ /**
5
+ * The Trusted Types policy the element parses its own markup through. A page that enforces
6
+ * Trusted Types allows it by name in its CSP: `trusted-types gyroview`.
7
+ */
8
+ var MARKUP_POLICY_NAME = "gyroview";
9
+ /**
10
+ * Parses markup this package built from its own constants into nodes to clone. Where the browser
11
+ * has Trusted Types the markup goes through the policy, which passes it on as it is: a page that
12
+ * enforces them refuses a plain string.
13
+ */
14
+ function parseMarkup(markup) {
15
+ const template = document.createElement("template");
16
+ template.innerHTML = markupPolicy()?.createHTML(markup) ?? markup;
17
+ return template.content;
18
+ }
19
+ /**
20
+ * Made once per page: a second policy of the same name is refused where the CSP names it.
21
+ */
22
+ var markupPolicy = lazy(() => {
23
+ const factory = globalThis.trustedTypes;
24
+ try {
25
+ return factory?.createPolicy(MARKUP_POLICY_NAME, { createHTML: (markup) => markup });
26
+ } catch {
27
+ return;
28
+ }
29
+ });
30
+ //#endregion
31
+ export { MARKUP_POLICY_NAME, parseMarkup };
@@ -0,0 +1,14 @@
1
+ //#region ../../packages/player/src/controls/viewButtons.ts
2
+ /**
3
+ * Binds Reset view, which brings the current view mode back to how it starts, and Fullscreen.
4
+ */
5
+ function bindViewButtons(parts, host) {
6
+ parts.resetView.addEventListener("click", () => {
7
+ host.player.resetView();
8
+ });
9
+ parts.fullscreen.addEventListener("click", () => {
10
+ host.toggleFullscreen();
11
+ });
12
+ }
13
+ //#endregion
14
+ export { bindViewButtons };
@@ -0,0 +1,119 @@
1
+ import { Deferred } from "../../../core/src/shared/async/Deferred.js";
2
+ import "../../../core/src/index.js";
3
+ import { PlaybackAttribute } from "./attributeNames.js";
4
+ import { shouldPreload } from "./attributes.js";
5
+ import { elementSourceOf } from "./elementSource.js";
6
+ //#region ../../packages/player/src/element/ElementLoads.ts
7
+ /**
8
+ * When `<gyro-view>` loads, and what. Attribute changes are read together a microtask after they
9
+ * arrive, so a `src` set with its `src2` loads once; files handed in replace the attributes until
10
+ * `src` or `src2` change. Only a connected element loads: one out of the document owes its load
11
+ * to the next connection, and lets its recording go a microtask after a removal unless it was
12
+ * only moved. A seek asked for while a load waits starts the new recording there.
13
+ */
14
+ var ElementLoads = class {
15
+ element;
16
+ player;
17
+ scheduled;
18
+ /**
19
+ * The recording the attributes name is still to be loaded once connected: at first, after a
20
+ * removal let it go, and after a change of source while out of the document.
21
+ */
22
+ isOwed = true;
23
+ /**
24
+ * A `load()` asked for out of the document, settled by the load the connection starts.
25
+ */
26
+ awaited;
27
+ startTime;
28
+ files;
29
+ constructor(element, player) {
30
+ this.element = element;
31
+ this.player = player;
32
+ }
33
+ connected() {
34
+ if (this.isOwed) this.schedule();
35
+ this.awaited?.follow(this.scheduled ?? Promise.resolve());
36
+ this.awaited = void 0;
37
+ }
38
+ /**
39
+ * A move within the document disconnects and connects again at once: only an element still out
40
+ * of it a microtask later lets its recording go.
41
+ */
42
+ async disconnected() {
43
+ await Promise.resolve();
44
+ if (this.element.isConnected) return;
45
+ this.player.unload();
46
+ this.isOwed = true;
47
+ }
48
+ sourceChanged() {
49
+ this.files = void 0;
50
+ this.schedule();
51
+ }
52
+ loadFiles(files) {
53
+ this.files = files;
54
+ this.schedule();
55
+ }
56
+ /**
57
+ * The change already waiting to be read, or else a load now; out of the document, the load the
58
+ * next connection starts.
59
+ */
60
+ load() {
61
+ return this.element.isConnected ? this.scheduled ?? this.reload() : this.loadOnConnection();
62
+ }
63
+ /**
64
+ * Seeks there, or starts the recording the waiting load brings there.
65
+ */
66
+ seek(time) {
67
+ if (this.scheduled) this.startTime = time;
68
+ else this.player.seek(time);
69
+ }
70
+ /**
71
+ * The waiting load, however it ends: a failure has been dispatched as an `error` already.
72
+ */
73
+ async settled() {
74
+ try {
75
+ await this.scheduled;
76
+ } catch {}
77
+ }
78
+ loadOnConnection() {
79
+ this.isOwed = true;
80
+ this.awaited ??= new Deferred();
81
+ return this.awaited.promise;
82
+ }
83
+ schedule() {
84
+ this.isOwed = !this.element.isConnected;
85
+ if (this.scheduled || !this.element.isConnected) return;
86
+ const scheduled = this.loadAfterPendingChanges();
87
+ this.scheduled = scheduled;
88
+ scheduled.catch(ignoreReportedFailure);
89
+ }
90
+ async loadAfterPendingChanges() {
91
+ await Promise.resolve();
92
+ this.scheduled = void 0;
93
+ if (this.element.isConnected) await this.reload();
94
+ }
95
+ reload() {
96
+ this.isOwed = false;
97
+ const read = (attribute) => this.element.getAttribute(attribute);
98
+ const source = elementSourceOf(read, document.baseURI, this.files);
99
+ delete this.element.dataset["hasFrame"];
100
+ const startTime = this.startTime;
101
+ this.startTime = void 0;
102
+ if (!source) {
103
+ this.player.unload();
104
+ return Promise.resolve();
105
+ }
106
+ const loading = this.player.load(source, {
107
+ autoplay: this.element.autoplay,
108
+ preload: shouldPreload(read(PlaybackAttribute.Preload))
109
+ });
110
+ if (startTime !== void 0) this.player.seek(startTime);
111
+ return loading;
112
+ }
113
+ };
114
+ /**
115
+ * A failed load has already been dispatched as an `error` event; the promise adds nothing.
116
+ */
117
+ function ignoreReportedFailure() {}
118
+ //#endregion
119
+ export { ElementLoads };
@@ -0,0 +1,39 @@
1
+ //#region ../../packages/player/src/element/FullscreenToggle.ts
2
+ var FILL_ATTRIBUTE = "data-fill";
3
+ /**
4
+ * Fills the screen with the element through the Fullscreen API where there is one, and by
5
+ * pinning the element over the page where there is not (iPhone Safari has none for anything
6
+ * but a video). Escape leaves either way.
7
+ */
8
+ var FullscreenToggle = class {
9
+ element;
10
+ constructor(element) {
11
+ this.element = element;
12
+ }
13
+ get isActive() {
14
+ return this.isNativelyFullscreen() || this.element.hasAttribute(FILL_ATTRIBUTE);
15
+ }
16
+ async toggle() {
17
+ await (this.isActive ? this.exit() : this.enter());
18
+ }
19
+ async exit() {
20
+ if (this.isNativelyFullscreen()) await document.exitFullscreen();
21
+ this.element.removeAttribute(FILL_ATTRIBUTE);
22
+ }
23
+ /**
24
+ * Asked of the element itself: `document.fullscreenElement` names the outermost shadow host
25
+ * instead when the player sits inside another component.
26
+ */
27
+ isNativelyFullscreen() {
28
+ return this.element.matches(":fullscreen");
29
+ }
30
+ async enter() {
31
+ if (document.fullscreenEnabled) try {
32
+ await this.element.requestFullscreen();
33
+ return;
34
+ } catch {}
35
+ this.element.setAttribute(FILL_ATTRIBUTE, "");
36
+ }
37
+ };
38
+ //#endregion
39
+ export { FullscreenToggle };