@@ -36,6 +36,7 @@ Object.keys(plugins).forEach((key) => {
3636
3737overrideDefaultVideojsComponents ( ) ;
3838
39+
3940class VideoPlayer extends Utils . mixin ( Eventable ) {
4041
4142 static all ( selector , ...args ) {
@@ -93,6 +94,7 @@ class VideoPlayer extends Utils.mixin(Eventable) {
9394 this . _initPlugins ( ) ;
9495 this . _initJumpButtons ( ) ;
9596 this . _initPictureInPicture ( ) ;
97+ this . _initMobileTouchHandler ( ) ;
9698 this . _setVideoJsListeners ( ready ) ;
9799 }
98100
@@ -406,6 +408,202 @@ class VideoPlayer extends Utils.mixin(Eventable) {
406408 }
407409 }
408410
411+ _initMobileTouchHandler ( ) {
412+ // Check if this is a mobile device
413+ this . _isMobile = videojs . browser . IS_IOS || videojs . browser . IS_ANDROID ||
414+ / M o b i | A n d r o i d | w e b O S | i P h o n e | i P a d | i P o d | B l a c k B e r r y | I E M o b i l e | O p e r a M i n i / i. test ( navigator . userAgent ) ;
415+
416+ if ( ! this . _isMobile ) {
417+ return ;
418+ }
419+
420+ if ( this . _mobileTouchHandlerSetup ) {
421+ return ;
422+ }
423+
424+ // Initialize mobile interaction tracking
425+ this . _mobileInteractionActive = false ;
426+
427+ const setupTouchHandler = ( ) => {
428+ const playerElement = this . videojs . el ( ) ;
429+
430+ if ( ! playerElement ) {
431+ setTimeout ( setupTouchHandler , 100 ) ;
432+ return ;
433+ }
434+
435+ // Add touch event listener for mobile devices
436+ const handleMobileTouch = ( event ) => {
437+ // Only handle touch events on the player container, not on controls
438+ const target = event . target ;
439+ const isControlElement = target . closest ( '.vjs-control-bar' ) ||
440+ target . closest ( '.vjs-menu' ) ||
441+ target . matches ( '.vjs-control, .vjs-button' ) ;
442+
443+ if ( isControlElement ) {
444+ return ;
445+ }
446+
447+ // Check if video has started (show big play button for both playing and paused states)
448+ const hasStarted = this . videojs . hasStarted ( ) ;
449+
450+ if ( hasStarted ) {
451+ // Show the touch overlay and mark interaction as active
452+ this . videojs . addClass ( 'cld-mobile-touch-active' ) ;
453+ this . _mobileInteractionActive = true ;
454+
455+ // Update mobile touch state based on current playback
456+ this . _updateMobileTouchState ( ) ;
457+
458+ // Remove the overlay after timeout
459+ this . videojs . clearTimeout ( this . _mobileTouchTimeout ) ;
460+ this . _mobileTouchTimeout = this . videojs . setTimeout ( ( ) => {
461+ // First remove the visibility class to start fade-out
462+ this . videojs . removeClass ( 'cld-mobile-touch-active' ) ;
463+
464+ // Wait for CSS transition to complete before removing pause icon class
465+ setTimeout ( ( ) => {
466+ this . videojs . removeClass ( 'cld-mobile-touch-playing' ) ;
467+ this . _removeMobileBigPlayButtonHandler ( ) ;
468+ this . _mobileInteractionActive = false ; // End mobile interaction session
469+ } , 250 ) ; // Wait slightly longer than the 0.2s CSS transition
470+ } , 3000 ) ;
471+ }
472+ } ;
473+
474+ playerElement . addEventListener ( 'touchend' , handleMobileTouch , { passive : true } ) ;
475+
476+ // Listen to play/pause events to update mobile touch state (only during active mobile interactions)
477+ this . videojs . on ( 'play' , ( ) => {
478+ if ( this . _isMobile && this . _mobileInteractionActive ) {
479+ this . _updateMobileTouchState ( ) ;
480+ }
481+ } ) ;
482+
483+ this . videojs . on ( 'pause' , ( ) => {
484+ if ( this . _isMobile && this . _mobileInteractionActive ) {
485+ this . _updateMobileTouchState ( ) ;
486+ }
487+ } ) ;
488+
489+ // Mark as setup complete
490+ this . _mobileTouchHandlerSetup = true ;
491+
492+ // Clean up on dispose
493+ this . videojs . on ( 'dispose' , ( ) => {
494+ playerElement . removeEventListener ( 'touchend' , handleMobileTouch ) ;
495+ this . videojs . clearTimeout ( this . _mobileTouchTimeout ) ;
496+ this . _removeMobileBigPlayButtonHandler ( ) ;
497+ this . _mobileTouchHandlerSetup = false ;
498+ this . _mobileInteractionActive = false ;
499+ } ) ;
500+ } ;
501+
502+ // Initialize when VideoJS is ready
503+ if ( this . videojs && this . videojs . isReady_ ) {
504+ setupTouchHandler ( ) ;
505+ } else if ( this . videojs ) {
506+ this . videojs . ready ( setupTouchHandler ) ;
507+ } else {
508+ setTimeout ( ( ) => this . _initMobileTouchHandler ( ) , 200 ) ;
509+ }
510+ }
511+
512+ _updateMobileTouchState ( ) {
513+ const isPlaying = ! this . videojs . paused ( ) ;
514+
515+ if ( isPlaying ) {
516+ this . videojs . addClass ( 'cld-mobile-touch-playing' ) ;
517+ this . _setupMobileBigPlayButtonHandler ( true ) ;
518+ } else {
519+ this . videojs . removeClass ( 'cld-mobile-touch-playing' ) ;
520+ this . _removeMobileBigPlayButtonHandler ( ) ;
521+ }
522+ }
523+
524+ _setupMobileBigPlayButtonHandler ( isPlaying ) {
525+ if ( ! isPlaying ) {
526+ return ; // Use default play behavior when paused
527+ }
528+
529+ // Remove any existing handler first
530+ this . _removeMobileBigPlayButtonHandler ( ) ;
531+
532+ // Find the big play button
533+ const bigPlayButton = this . videojs . el ( ) . querySelector ( '.vjs-big-play-button' ) ;
534+ if ( ! bigPlayButton ) {
535+ return ;
536+ }
537+
538+ // Create custom pause handler that completely overrides default behavior
539+ this . _mobilePauseHandler = ( event ) => {
540+ event . preventDefault ( ) ;
541+ event . stopPropagation ( ) ;
542+ event . stopImmediatePropagation ( ) ;
543+
544+ // Pause the video
545+ if ( ! this . videojs . paused ( ) ) {
546+ this . videojs . pause ( ) ;
547+
548+ // Immediately update the mobile touch state
549+ setTimeout ( ( ) => {
550+ this . _updateMobileTouchState ( ) ;
551+
552+ // Force show the button briefly to give visual feedback
553+ if ( this . videojs . hasClass ( 'cld-mobile-touch-active' ) ) {
554+ this . videojs . clearTimeout ( this . _mobileTouchTimeout ) ;
555+ this . _mobileTouchTimeout = this . videojs . setTimeout ( ( ) => {
556+ this . videojs . removeClass ( 'cld-mobile-touch-active' ) ;
557+ } , 2000 ) ;
558+ }
559+ } , 50 ) ;
560+ }
561+
562+ return false ;
563+ } ;
564+
565+ // Add multiple event listeners to ensure we catch the click
566+ bigPlayButton . addEventListener ( 'click' , this . _mobilePauseHandler , { capture : true } ) ;
567+ bigPlayButton . addEventListener ( 'touchend' , this . _mobilePauseHandler , { capture : true } ) ;
568+ bigPlayButton . addEventListener ( 'mousedown' , this . _mobilePauseHandler , { capture : true } ) ;
569+
570+ // Also disable the VideoJS component's click handling temporarily
571+ const bigPlayButtonComponent = this . videojs . getChild ( 'bigPlayButton' ) ;
572+ if ( bigPlayButtonComponent ) {
573+ this . _originalHandleClick = bigPlayButtonComponent . handleClick ;
574+ bigPlayButtonComponent . handleClick = ( ) => {
575+ // Override with our pause behavior
576+ this . _mobilePauseHandler ( {
577+ preventDefault : ( ) => { } ,
578+ stopPropagation : ( ) => { } ,
579+ stopImmediatePropagation : ( ) => { }
580+ } ) ;
581+ } ;
582+ }
583+
584+ // Store reference to the button for cleanup
585+ this . _mobilePauseButton = bigPlayButton ;
586+ }
587+
588+ _removeMobileBigPlayButtonHandler ( ) {
589+ if ( this . _mobilePauseHandler && this . _mobilePauseButton ) {
590+ // Remove all event listeners
591+ this . _mobilePauseButton . removeEventListener ( 'click' , this . _mobilePauseHandler , { capture : true } ) ;
592+ this . _mobilePauseButton . removeEventListener ( 'touchend' , this . _mobilePauseHandler , { capture : true } ) ;
593+ this . _mobilePauseButton . removeEventListener ( 'mousedown' , this . _mobilePauseHandler , { capture : true } ) ;
594+
595+ // Restore original VideoJS component behavior
596+ const bigPlayButtonComponent = this . videojs . getChild ( 'bigPlayButton' ) ;
597+ if ( bigPlayButtonComponent && this . _originalHandleClick ) {
598+ bigPlayButtonComponent . handleClick = this . _originalHandleClick ;
599+ this . _originalHandleClick = null ;
600+ }
601+
602+ this . _mobilePauseHandler = null ;
603+ this . _mobilePauseButton = null ;
604+ }
605+ }
606+
409607 _initCloudinary ( ) {
410608 const cloudinaryConfig = this . playerOptions . cloudinary ;
411609 cloudinaryConfig . chainTarget = this ;
0 commit comments