1 : /**
2 : * @file picture-in-picture-toggle.js
3 : */
4 : import Button from '../button.js';
5 : import Component from '../component.js';
6 : import document from 'global/document';
7 :
8 : /**
9 : * Toggle Picture-in-Picture mode
10 : *
11 : * @extends Button
12 : */
13 : class PictureInPictureToggle extends Button {
14 :
15 : /**
16 : * Creates an instance of this class.
17 : *
18 : * @param {Player} player
19 : * The `Player` that this class should be attached to.
20 : *
21 : * @param {Object} [options]
22 : * The key/value store of player options.
23 : *
24 : * @listens Player#enterpictureinpicture
25 : * @listens Player#leavepictureinpicture
26 : */
27 : constructor(player, options) {
28 : super(player, options);
29 : this.on(player, ['enterpictureinpicture', 'leavepictureinpicture'], (e) => this.handlePictureInPictureChange(e));
30 : this.on(player, ['disablepictureinpicturechanged', 'loadedmetadata'], (e) => this.handlePictureInPictureEnabledChange(e));
31 :
32 : this.on(player, ['loadedmetadata', 'audioonlymodechange', 'audiopostermodechange'], () => {
33 : // This audio detection will not detect HLS or DASH audio-only streams because there was no reliable way to detect them at the time
34 : const isSourceAudio = player.currentType().substring(0, 5) === 'audio';
35 :
36 : if (isSourceAudio || player.audioPosterMode() || player.audioOnlyMode()) {
37 : if (player.isInPictureInPicture()) {
38 : player.exitPictureInPicture();
39 : }
40 : this.hide();
41 : } else {
42 : this.show();
43 : }
44 :
45 : });
46 :
47 : // TODO: Deactivate button on player emptied event.
48 : this.disable();
49 : }
50 :
51 : /**
52 : * Builds the default DOM `className`.
53 : *
54 : * @return {string}
55 : * The DOM `className` for this object.
56 : */
57 : buildCSSClass() {
58 : return `vjs-picture-in-picture-control ${super.buildCSSClass()}`;
59 : }
60 :
61 : /**
62 : * Enables or disables button based on document.pictureInPictureEnabled property value
63 : * or on value returned by player.disablePictureInPicture() method.
64 : */
65 : handlePictureInPictureEnabledChange() {
66 : if (document.pictureInPictureEnabled && this.player_.disablePictureInPicture() === false) {
67 : this.enable();
68 : } else {
69 : this.disable();
70 : }
71 : }
72 :
73 : /**
74 : * Handles enterpictureinpicture and leavepictureinpicture on the player and change control text accordingly.
75 : *
76 : * @param {EventTarget~Event} [event]
77 : * The {@link Player#enterpictureinpicture} or {@link Player#leavepictureinpicture} event that caused this function to be
78 : * called.
79 : *
80 : * @listens Player#enterpictureinpicture
81 : * @listens Player#leavepictureinpicture
82 : */
83 : handlePictureInPictureChange(event) {
84 : if (this.player_.isInPictureInPicture()) {
85 : this.controlText('Exit Picture-in-Picture');
86 : } else {
87 : this.controlText('Picture-in-Picture');
88 : }
89 : this.handlePictureInPictureEnabledChange();
90 : }
91 :
92 : /**
93 : * This gets called when an `PictureInPictureToggle` is "clicked". See
94 : * {@link ClickableComponent} for more detailed information on what a click can be.
95 : *
96 : * @param {EventTarget~Event} [event]
97 : * The `keydown`, `tap`, or `click` event that caused this function to be
98 : * called.
99 : *
100 : * @listens tap
101 : * @listens click
102 : */
103 : handleClick(event) {
104 : if (!this.player_.isInPictureInPicture()) {
105 : this.player_.requestPictureInPicture();
106 : } else {
107 : this.player_.exitPictureInPicture();
108 : }
109 : }
110 :
111 : }
112 :
113 : /**
114 : * The text that should display over the `PictureInPictureToggle`s controls. Added for localization.
115 : *
116 : * @type {string}
117 : * @private
118 : */
119 : PictureInPictureToggle.prototype.controlText_ = 'Picture-in-Picture';
120 :
121 : Component.registerComponent('PictureInPictureToggle', PictureInPictureToggle);
122 : export default PictureInPictureToggle;