diff --git a/scripts/youtube.js b/scripts/youtube.js
index 2492fe3..2c1e44b 100644
--- a/scripts/youtube.js
+++ b/scripts/youtube.js
@@ -22,6 +22,150 @@ H5P.VideoYouTube = (function ($) {
text: l10n.loading,
html: `
`
}).appendTo($wrapper);
+ var $sphericalDragOverlay;
+ var sphericalDragStart;
+
+ /**
+ * Remove custom 360 drag handling.
+ *
+ * @private
+ */
+ var teardownSphericalDrag = function () {
+ $(document).off('.' + id);
+ if ($sphericalDragOverlay) {
+ $sphericalDragOverlay.remove();
+ $sphericalDragOverlay = undefined;
+ }
+ sphericalDragStart = undefined;
+ };
+
+ /**
+ * Add mouse drag support for 360° videos when YouTube controls are hidden.
+ * The YouTube embed player used to handle this itself, but H5P Interactive
+ * Video hides native YouTube controls and depends on H5P controls instead.
+ *
+ * @private
+ */
+ var setupSphericalDrag = function () {
+ if (options.controls || $sphericalDragOverlay ||
+ !player || !player.getSphericalProperties || !player.setSphericalProperties) {
+ return;
+ }
+
+ var sphericalProperties = player.getSphericalProperties();
+ if (!sphericalProperties || !Object.keys(sphericalProperties).length) {
+ return;
+ }
+
+ var $host = $placeholder.children('div').first();
+ $sphericalDragOverlay = $('', {
+ 'class': 'h5p-youtube-360-drag',
+ 'aria-hidden': 'true'
+ }).css({
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ cursor: 'grab',
+ 'touch-action': 'none',
+ 'z-index': 2
+ }).appendTo($host);
+
+ var normalizeYaw = function (yaw) {
+ yaw = yaw % 360;
+ return yaw < 0 ? yaw + 360 : yaw;
+ };
+
+ var clampPitch = function (pitch) {
+ return Math.max(-90, Math.min(90, pitch));
+ };
+
+ $sphericalDragOverlay.on('pointerdown', function (event) {
+ var originalEvent = event.originalEvent || event;
+ if (originalEvent.button !== undefined && originalEvent.button !== 0) {
+ return;
+ }
+
+ var current = player.getSphericalProperties();
+ if (!current || !Object.keys(current).length) {
+ return;
+ }
+
+ sphericalDragStart = {
+ x: originalEvent.clientX,
+ y: originalEvent.clientY,
+ yaw: current.yaw || 0,
+ pitch: current.pitch || 0,
+ fov: current.fov || 100,
+ moved: false
+ };
+
+ if (this.setPointerCapture && originalEvent.pointerId !== undefined) {
+ this.setPointerCapture(originalEvent.pointerId);
+ }
+
+ $sphericalDragOverlay.css('cursor', 'grabbing');
+ event.preventDefault();
+ });
+
+ $(document).on('pointermove.' + id, function (event) {
+ if (!sphericalDragStart) {
+ return;
+ }
+
+ var originalEvent = event.originalEvent || event;
+ var dx = originalEvent.clientX - sphericalDragStart.x;
+ var dy = originalEvent.clientY - sphericalDragStart.y;
+ sphericalDragStart.moved = sphericalDragStart.moved ||
+ Math.abs(dx) > 2 || Math.abs(dy) > 2;
+
+ var sensitivity = sphericalDragStart.fov / 650;
+ player.setSphericalProperties({
+ yaw: normalizeYaw(sphericalDragStart.yaw - (dx * sensitivity)),
+ pitch: clampPitch(sphericalDragStart.pitch + (dy * sensitivity)),
+ roll: 0,
+ fov: sphericalDragStart.fov
+ });
+
+ event.preventDefault();
+ });
+
+ $(document).on('pointerup.' + id + ' pointercancel.' + id, function () {
+ if (!sphericalDragStart) {
+ return;
+ }
+
+ if (!sphericalDragStart.moved && player.getPlayerState) {
+ if (player.getPlayerState() === H5P.Video.PLAYING) {
+ player.pauseVideo();
+ }
+ else {
+ player.playVideo();
+ }
+ }
+
+ sphericalDragStart = undefined;
+ $sphericalDragOverlay.css('cursor', 'grab');
+ });
+ };
+
+ /**
+ * Try to install custom drag handling after YouTube has detected that the
+ * current video is spherical. YouTube may not expose spherical properties
+ * immediately on ready.
+ *
+ * @private
+ */
+ var scheduleSphericalDragSetup = function () {
+ if (options.controls || $sphericalDragOverlay) {
+ return;
+ }
+
+ setupSphericalDrag();
+ setTimeout(setupSphericalDrag, 500);
+ setTimeout(setupSphericalDrag, 1500);
+ };
// Optional placeholder
// var $placeholder = $('').appendTo($wrapper);
@@ -78,6 +222,7 @@ H5P.VideoYouTube = (function ($) {
onReady: function () {
self.trigger('ready');
self.trigger('loaded');
+ scheduleSphericalDragSetup();
if (!options.autoplay) {
self.toPause = true;
@@ -134,6 +279,7 @@ H5P.VideoYouTube = (function ($) {
// End IE11 fix
self.trigger('stateChange', state.data);
+ scheduleSphericalDragSetup();
}
},
onPlaybackQualityChange: function (quality) {
@@ -312,6 +458,7 @@ H5P.VideoYouTube = (function ($) {
player.pauseVideo();
self.trigger('stateChange', H5P.Video.PAUSED);
}
+ teardownSphericalDrag();
player.destroy();
player = undefined;
}