Home Reference Source

js/controls/picture-in-picture-toggle.js

/**
 * @file picture-in-picture-toggle.js
 * @since 3.5.0
 */

import videojs from 'video.js';

import Event from '../event';

const Button = videojs.getComponent('Button');
const Component = videojs.getComponent('Component');

/**
 * Button to toggle Picture-in-Picture mode.
 *
 * @class
 * @augments videojs.Button
*/
class PictureInPictureToggle extends Button {
    /**
     * The constructor function for the class.
     *
     * @private
     * @param {(videojs.Player|Object)} player - Video.js player instance.
     * @param {Object} options - Player options.
     */
    constructor(player, options) {
        super(player, options);

        // listen for events
        this.on(this.player_, Event.ENTER_PIP, this.onStart);
        this.on(this.player_, Event.LEAVE_PIP, this.onStop);
    }

    /**
     * Builds the default DOM `className`.
     *
     * @return {string}
     *         The DOM `className` for this object.
     */
    buildCSSClass() {
        return 'vjs-pip-button vjs-control vjs-button vjs-icon-picture-in-picture-start';
    }

    /**
     * Show the `PictureInPictureToggle` element if it is hidden by removing the
     * 'vjs-hidden' class name from it.
     */
    show() {
        if (this.layoutExclude && this.layoutExclude === true) {
            // ignore
            return;
        }
        super.show();
    }

    /**
     * This gets called when the button is clicked.
     *
     * @param {EventTarget~Event} event
     *        The `tap` or `click` event that caused this function to be
     *        called.
     *
     * @listens tap
     * @listens click
     */
    async handleClick(event) {
        let recorder = this.player_.record();

        // disable button during picture-in-picture switch
        this.disable();

        // switch picture-in-picture mode
        try {
            if (recorder.mediaElement !== document.pictureInPictureElement) {
                // request picture-in-picture
                await recorder.mediaElement.requestPictureInPicture();
            } else {
                // exit picture-in-picture
                await document.exitPictureInPicture();
            }
        } catch (error) {
            // notify listeners
            this.player_.trigger(Event.ERROR, error);
        } finally {
            // switch completed
            this.enable();
        }
    }

    /**
     * Add the vjs-icon-picture-in-picture-stop class to the element so it can
     * change appearance.
     *
     * @param {EventTarget~Event} [event]
     *        The event that caused this function to run.
     *
     * @listens Player#enterPIP
     */
    onStart(event) {
        // replace element class so it can change appearance
        this.removeClass('vjs-icon-picture-in-picture-start');
        this.addClass('vjs-icon-picture-in-picture-stop');
    }

    /**
     * Add the vjs-icon-picture-in-picture-start class to the element so it can
     * change appearance.
     *
     * @param {EventTarget~Event} [event]
     *        The event that caused this function to run.
     *
     * @listens Player#leavePIP
     */
    onStop(event) {
        // replace element class so it can change appearance
        this.removeClass('vjs-icon-picture-in-picture-stop');
        this.addClass('vjs-icon-picture-in-picture-start');
    }
}

/**
 * The text that should display over the `PictureInPictureToggle`s controls.
 *
 * Added for localization.
 *
 * @type {string}
 * @private
 */
PictureInPictureToggle.prototype.controlText_ = 'Picture in Picture';

Component.registerComponent('PictureInPictureToggle', PictureInPictureToggle);

export default PictureInPictureToggle;