@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.
- package/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/README.md +216 -0
- package/dist/define.d.ts +3 -0
- package/dist/define.js +4 -0
- package/dist/index.d.ts +959 -0
- package/dist/index.js +13 -0
- package/dist/packages/adapters/blob/src/BlobRandomAccessSource.js +37 -0
- package/dist/packages/adapters/blob/src/index.js +2 -0
- package/dist/packages/adapters/fetch/src/HttpRangeSource.js +194 -0
- package/dist/packages/adapters/fetch/src/HttpResourceLocator.js +35 -0
- package/dist/packages/adapters/fetch/src/httpRequest.js +126 -0
- package/dist/packages/adapters/fetch/src/index.js +3 -0
- package/dist/packages/adapters/mediabunny/src/MediabunnyAudioSegments.js +133 -0
- package/dist/packages/adapters/mediabunny/src/MediabunnyDemuxer.js +63 -0
- package/dist/packages/adapters/mediabunny/src/MediabunnyVideoTrackReader.js +84 -0
- package/dist/packages/adapters/mediabunny/src/SegmentChannel.js +84 -0
- package/dist/packages/adapters/mediabunny/src/bufferSources.js +12 -0
- package/dist/packages/adapters/mediabunny/src/index.js +2 -0
- package/dist/packages/adapters/mse-audio/src/MediaSourceAudioClock.js +139 -0
- package/dist/packages/adapters/mse-audio/src/SourceBufferFeeder.js +174 -0
- package/dist/packages/adapters/mse-audio/src/events.js +26 -0
- package/dist/packages/adapters/mse-audio/src/index.js +2 -0
- package/dist/packages/adapters/mse-audio/src/mediaSourceSupport.js +82 -0
- package/dist/packages/adapters/three/src/ThreeFrameRenderer.js +274 -0
- package/dist/packages/adapters/three/src/compileAndProve.js +14 -0
- package/dist/packages/adapters/three/src/fullscreenPass.js +52 -0
- package/dist/packages/adapters/three/src/index.js +2 -0
- package/dist/packages/adapters/three/src/lensTextures.js +25 -0
- package/dist/packages/adapters/three/src/pictureMaterials.js +44 -0
- package/dist/packages/adapters/three/src/renderInto.js +15 -0
- package/dist/packages/adapters/three/src/rendererUniforms.js +157 -0
- package/dist/packages/adapters/three/src/samplingStrategies.js +26 -0
- package/dist/packages/adapters/three/src/seamMeter/SeamMeterPass.js +79 -0
- package/dist/packages/adapters/three/src/seamMeter/rowMeans.js +38 -0
- package/dist/packages/adapters/three/src/shaderPrograms.js +66 -0
- package/dist/packages/adapters/three/src/shaders/analysis.frag.js +4 -0
- package/dist/packages/adapters/three/src/shaders/equirectangularRays.js +4 -0
- package/dist/packages/adapters/three/src/shaders/fixedJoin.js +4 -0
- package/dist/packages/adapters/three/src/shaders/fullscreen.vert.js +4 -0
- package/dist/packages/adapters/three/src/shaders/header.js +4 -0
- package/dist/packages/adapters/three/src/shaders/lensModels.js +4 -0
- package/dist/packages/adapters/three/src/shaders/lensSampling.js +4 -0
- package/dist/packages/adapters/three/src/shaders/lensTextures.js +4 -0
- package/dist/packages/adapters/three/src/shaders/rawLenses.frag.js +4 -0
- package/dist/packages/adapters/three/src/shaders/rectilinearRays.js +4 -0
- package/dist/packages/adapters/three/src/shaders/screenAreas.js +4 -0
- package/dist/packages/adapters/three/src/shaders/stitch.frag.js +4 -0
- package/dist/packages/adapters/three/src/webglRenderer.js +37 -0
- package/dist/packages/adapters/webcodecs/src/WebCodecsVideoDecoderPort.js +135 -0
- package/dist/packages/adapters/webcodecs/src/index.js +2 -0
- package/dist/packages/core/src/application/gainMatching/GainMatching.js +66 -0
- package/dist/packages/core/src/application/gainMatching/GainMatchingFrameSink.js +62 -0
- package/dist/packages/core/src/application/playback/ClockWatch.js +76 -0
- package/dist/packages/core/src/application/playback/DecodePipeline.js +188 -0
- package/dist/packages/core/src/application/playback/DecodeRun.js +93 -0
- package/dist/packages/core/src/application/playback/FramePairQueue.js +93 -0
- package/dist/packages/core/src/application/playback/FramePairer.js +60 -0
- package/dist/packages/core/src/application/playback/PlaybackSession.js +417 -0
- package/dist/packages/core/src/application/playback/SeekOrder.js +36 -0
- package/dist/packages/core/src/application/playback/SessionLifecycle.js +42 -0
- package/dist/packages/core/src/application/playback/StartGate.js +55 -0
- package/dist/packages/core/src/application/playback/TimeUpdates.js +31 -0
- package/dist/packages/core/src/application/playback/WallClock.js +46 -0
- package/dist/packages/core/src/application/playback/keyframeTimeAt.js +15 -0
- package/dist/packages/core/src/application/playback/probeDecoding.js +168 -0
- package/dist/packages/core/src/application/recording/frameTimesOf.js +22 -0
- package/dist/packages/core/src/application/recording/inspectLayout.js +21 -0
- package/dist/packages/core/src/application/recording/inspectRecording.js +124 -0
- package/dist/packages/core/src/application/recording/locateOtherLensFile.js +18 -0
- package/dist/packages/core/src/application/recording/motionOf.js +39 -0
- package/dist/packages/core/src/application/recording/readRecording.js +31 -0
- package/dist/packages/core/src/application/recording/timeRecording.js +57 -0
- package/dist/packages/core/src/application/stabilization/StabilizingFrameSink.js +31 -0
- package/dist/packages/core/src/domain/format/boxes/BoxLayout.js +7 -0
- package/dist/packages/core/src/domain/format/boxes/boxConstants.js +16 -0
- package/dist/packages/core/src/domain/format/boxes/scanBoxes.js +59 -0
- package/dist/packages/core/src/domain/format/calibration/CalibrationVersion.js +13 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/CalibrationStringLayout.js +34 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/LegacyCalibrationLayout.js +44 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/MeiCalibrationLayout.js +45 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/PolynomialCalibrationLayout.js +44 -0
- package/dist/packages/core/src/domain/format/calibration/offsetTokens.js +65 -0
- package/dist/packages/core/src/domain/format/calibration/parseOffsetString.js +52 -0
- package/dist/packages/core/src/domain/format/calibration/selectCalibration.js +46 -0
- package/dist/packages/core/src/domain/format/captureOrigin.js +20 -0
- package/dist/packages/core/src/domain/format/constants.js +33 -0
- package/dist/packages/core/src/domain/format/info/infoFields.js +110 -0
- package/dist/packages/core/src/domain/format/info/parseInfoRecord.js +90 -0
- package/dist/packages/core/src/domain/format/info/readInfoRecord.js +14 -0
- package/dist/packages/core/src/domain/format/layout/detectLensLayout.js +122 -0
- package/dist/packages/core/src/domain/format/naming/RecordingFileName.js +67 -0
- package/dist/packages/core/src/domain/format/records/TrailerRecords.js +83 -0
- package/dist/packages/core/src/domain/format/records/exposure/parseExposureRecord.js +48 -0
- package/dist/packages/core/src/domain/format/records/gyro/FloatGyroSampleLayout.js +39 -0
- package/dist/packages/core/src/domain/format/records/gyro/RawGyroSampleLayout.js +55 -0
- package/dist/packages/core/src/domain/format/records/gyro/SampleColumns.js +82 -0
- package/dist/packages/core/src/domain/format/records/gyro/gyroLayouts.js +12 -0
- package/dist/packages/core/src/domain/format/records/gyro/parseGyroRecord.js +129 -0
- package/dist/packages/core/src/domain/format/trailer/RecordHeader.js +23 -0
- package/dist/packages/core/src/domain/format/trailer/Trailer.js +23 -0
- package/dist/packages/core/src/domain/format/trailer/TrailerFooter.js +27 -0
- package/dist/packages/core/src/domain/format/trailer/locateRecords.js +80 -0
- package/dist/packages/core/src/domain/format/trailer/parseRecordIndex.js +28 -0
- package/dist/packages/core/src/domain/format/trailer/readTrailer.js +32 -0
- package/dist/packages/core/src/domain/motion/exposure/ExposureRecord.js +53 -0
- package/dist/packages/core/src/domain/motion/gyro/GyroTrack.js +60 -0
- package/dist/packages/core/src/domain/motion/gyro/medianStep.js +11 -0
- package/dist/packages/core/src/domain/motion/gyro/repairedTimeline.js +101 -0
- package/dist/packages/core/src/domain/motion/imu/ImuFrame.js +112 -0
- package/dist/packages/core/src/domain/motion/orientation/OrientationTrack.js +146 -0
- package/dist/packages/core/src/domain/motion/orientation/estimateGyroBias.js +64 -0
- package/dist/packages/core/src/domain/motion/orientation/gravity.js +97 -0
- package/dist/packages/core/src/domain/motion/stabilization/Stabilizer.js +13 -0
- package/dist/packages/core/src/domain/motion/stabilization/stabilizers.js +86 -0
- package/dist/packages/core/src/domain/motion/timing/CaptureClock.js +41 -0
- package/dist/packages/core/src/domain/motion/timing/FrameTimes.js +92 -0
- package/dist/packages/core/src/domain/motion/timing/frameTimeSources.js +61 -0
- package/dist/packages/core/src/domain/motion/timing/resolveFrameTimes.js +68 -0
- package/dist/packages/core/src/domain/optics/EquidistantModel.js +39 -0
- package/dist/packages/core/src/domain/optics/MeiModel.js +45 -0
- package/dist/packages/core/src/domain/optics/PolynomialModel.js +44 -0
- package/dist/packages/core/src/domain/optics/gainMatch.js +62 -0
- package/dist/packages/core/src/domain/optics/lensGeometry.js +18 -0
- package/dist/packages/core/src/domain/optics/lensPose.js +35 -0
- package/dist/packages/core/src/domain/optics/opticsConstants.js +5 -0
- package/dist/packages/core/src/domain/playback/PlayerState.js +83 -0
- package/dist/packages/core/src/domain/stitching/LensLayout.js +21 -0
- package/dist/packages/core/src/domain/stitching/StitchingSetup.js +78 -0
- package/dist/packages/core/src/domain/stitching/seamDisparity.js +4 -0
- package/dist/packages/core/src/domain/stitching/seamDisparityField.js +5 -0
- package/dist/packages/core/src/domain/stitching/seamJoin.js +16 -0
- package/dist/packages/core/src/domain/stitching/seamMismatch.js +1 -0
- package/dist/packages/core/src/domain/stitching/seamRing.js +4 -0
- package/dist/packages/core/src/domain/stitching/seamStrip.js +26 -0
- package/dist/packages/core/src/domain/view/Framing.js +31 -0
- package/dist/packages/core/src/domain/view/PictureQuality.js +26 -0
- package/dist/packages/core/src/domain/view/ViewMode.js +9 -0
- package/dist/packages/core/src/domain/view/ViewState.js +48 -0
- package/dist/packages/core/src/domain/view/lensTilesView.js +63 -0
- package/dist/packages/core/src/domain/view/magnification.js +103 -0
- package/dist/packages/core/src/domain/view/normalView.js +29 -0
- package/dist/packages/core/src/domain/view/panoramaView.js +99 -0
- package/dist/packages/core/src/domain/view/rectilinear.js +40 -0
- package/dist/packages/core/src/domain/view/screenLayout.js +113 -0
- package/dist/packages/core/src/domain/view/viewGestures.js +98 -0
- package/dist/packages/core/src/domain/view/viewModes.js +14 -0
- package/dist/packages/core/src/index.js +59 -0
- package/dist/packages/core/src/ports/FramePair.js +6 -0
- package/dist/packages/core/src/shared/async/Deferred.js +42 -0
- package/dist/packages/core/src/shared/async/RunStop.js +40 -0
- package/dist/packages/core/src/shared/binary/ByteRange.js +38 -0
- package/dist/packages/core/src/shared/binary/ByteReader.js +77 -0
- package/dist/packages/core/src/shared/errors/GyroViewError.js +81 -0
- package/dist/packages/core/src/shared/errors/reportLater.js +11 -0
- package/dist/packages/core/src/shared/events/Outbox.js +68 -0
- package/dist/packages/core/src/shared/events/TypedEmitter.js +55 -0
- package/dist/packages/core/src/shared/keysOf.js +10 -0
- package/dist/packages/core/src/shared/lazy.js +11 -0
- package/dist/packages/core/src/shared/math/Matrix3.js +93 -0
- package/dist/packages/core/src/shared/math/Quaternion.js +133 -0
- package/dist/packages/core/src/shared/math/Vector3.js +54 -0
- package/dist/packages/core/src/shared/math/clamp.js +9 -0
- package/dist/packages/core/src/shared/protobuf/ProtobufMessage.js +157 -0
- package/dist/packages/core/src/shared/text/urlPath.js +31 -0
- package/dist/packages/core/src/shared/text/utf8.js +12 -0
- package/dist/packages/core/src/shared/units/angle.js +22 -0
- package/dist/packages/core/src/shared/units/time.js +13 -0
- package/dist/packages/player/src/PlayerSource.js +15 -0
- package/dist/packages/player/src/browserPlayer.js +18 -0
- package/dist/packages/player/src/choices.js +25 -0
- package/dist/packages/player/src/composition/Disposables.js +31 -0
- package/dist/packages/player/src/composition/browserPorts.js +46 -0
- package/dist/packages/player/src/composition/buildPipeline.js +153 -0
- package/dist/packages/player/src/composition/ensureDecodable.js +63 -0
- package/dist/packages/player/src/composition/openInputs.js +179 -0
- package/dist/packages/player/src/composition/openRecording.js +42 -0
- package/dist/packages/player/src/controls/ChoiceMenu.js +113 -0
- package/dist/packages/player/src/controls/PictureMenus.js +47 -0
- package/dist/packages/player/src/controls/SeekBar.js +125 -0
- package/dist/packages/player/src/controls/SoundControls.js +41 -0
- package/dist/packages/player/src/controls/TransportButtons.js +31 -0
- package/dist/packages/player/src/controls/ViewGestures.js +136 -0
- package/dist/packages/player/src/controls/Wording.js +78 -0
- package/dist/packages/player/src/controls/controlParts.js +36 -0
- package/dist/packages/player/src/controls/controls.js +4 -0
- package/dist/packages/player/src/controls/controlsBar.js +23 -0
- package/dist/packages/player/src/controls/controlsMarkup.js +85 -0
- package/dist/packages/player/src/controls/customControls.js +59 -0
- package/dist/packages/player/src/controls/formatTime.js +19 -0
- package/dist/packages/player/src/controls/icons.js +40 -0
- package/dist/packages/player/src/controls/keyboard.js +118 -0
- package/dist/packages/player/src/controls/messages.js +94 -0
- package/dist/packages/player/src/controls/parseMarkup.js +31 -0
- package/dist/packages/player/src/controls/viewButtons.js +14 -0
- package/dist/packages/player/src/element/ElementLoads.js +119 -0
- package/dist/packages/player/src/element/FullscreenToggle.js +39 -0
- package/dist/packages/player/src/element/GyroViewElement.js +313 -0
- package/dist/packages/player/src/element/IdleWatcher.js +71 -0
- package/dist/packages/player/src/element/TypedEventElement.js +22 -0
- package/dist/packages/player/src/element/accessibleRegion.js +19 -0
- package/dist/packages/player/src/element/attributeNames.js +51 -0
- package/dist/packages/player/src/element/attributes.js +101 -0
- package/dist/packages/player/src/element/defineGyroView.js +21 -0
- package/dist/packages/player/src/element/earlyProperties.js +36 -0
- package/dist/packages/player/src/element/elementSource.js +23 -0
- package/dist/packages/player/src/element/liveSettings.js +138 -0
- package/dist/packages/player/src/element/playbackAttributes.js +53 -0
- package/dist/packages/player/src/element/playerEventMirror.js +33 -0
- package/dist/packages/player/src/element/reflectedProperties.js +78 -0
- package/dist/packages/player/src/element/styles.js +4 -0
- package/dist/packages/player/src/element/template.js +47 -0
- package/dist/packages/player/src/index.js +10 -0
- package/dist/packages/player/src/inspectRecording.js +22 -0
- package/dist/packages/player/src/player/DrawingBufferFit.js +77 -0
- package/dist/packages/player/src/player/FrameLoop.js +40 -0
- package/dist/packages/player/src/player/PictureSettings.js +60 -0
- package/dist/packages/player/src/player/Player.js +377 -0
- package/dist/packages/player/src/player/PlayerEvents.js +28 -0
- package/dist/packages/player/src/player/PlayerPhase.js +21 -0
- package/dist/packages/player/src/player/PlayerSound.js +61 -0
- package/dist/packages/player/src/player/PlayerView.js +113 -0
- package/dist/packages/player/src/player/SessionRelay.js +66 -0
- package/dist/packages/player/src/player/ensureFinite.js +26 -0
- package/dist/packages/player/src/player/loadRecording.js +65 -0
- package/dist/packages/player/src/player/transportEvents.js +29 -0
- package/dist/standalone.js +4919 -0
- 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 };
|