@gcorevideo/player 2.24.1 → 2.24.3
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/assets/big-mute-button/big-mute-button.ejs +2 -2
- package/assets/bottom-gear/gear-sub-menu.scss +1 -0
- package/dist/core.js +1 -1
- package/dist/index.css +754 -753
- package/dist/index.js +151 -130
- package/dist/player.d.ts +72 -21
- package/docs/api/player.bigmutebutton.md +13 -1
- package/docs/api/player.clapprstatssettings.md +51 -4
- package/docs/api/player.clapprstatssettings.runeach.md +16 -0
- package/docs/api/player.clipspluginsettings.md +1 -1
- package/docs/api/player.clipspluginsettings.text.md +1 -1
- package/docs/api/player.cmcdconfig.exportids.md +4 -0
- package/docs/api/player.cmcdconfig.md +19 -105
- package/docs/api/{player.cmcdconfig.version.md → player.cmcdconfigoptions.contentid.md} +5 -3
- package/docs/api/player.cmcdconfigoptions.md +79 -0
- package/docs/api/{player.cmcdconfigpluginsettings.md → player.cmcdconfigoptions.sessionid.md} +4 -6
- package/docs/api/player.extendedevents.md +9 -0
- package/docs/api/player.md +37 -31
- package/docs/api/player.mediacontrol.getavailableheight.md +24 -0
- package/docs/api/player.mediacontrol.md +14 -0
- package/docs/api/{player.cmcdconfig.name.md → player.posterpluginsettings.custom.md} +4 -3
- package/docs/api/player.posterpluginsettings.md +108 -7
- package/docs/api/player.posterpluginsettings.showfornoop.md +16 -0
- package/docs/api/player.posterpluginsettings.showonvideoend.md +16 -0
- package/docs/api/{player.cmcdconfig.bindevents.md → player.posterpluginsettings.url.md} +4 -7
- package/lib/plugins/big-mute-button/BigMuteButton.d.ts +15 -13
- package/lib/plugins/big-mute-button/BigMuteButton.d.ts.map +1 -1
- package/lib/plugins/big-mute-button/BigMuteButton.js +68 -83
- package/lib/plugins/bottom-gear/BottomGear.d.ts +1 -0
- package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
- package/lib/plugins/bottom-gear/BottomGear.js +17 -17
- package/lib/plugins/clappr-stats/ClapprStats.d.ts +6 -2
- package/lib/plugins/clappr-stats/ClapprStats.d.ts.map +1 -1
- package/lib/plugins/clips/Clips.d.ts +1 -1
- package/lib/plugins/clips/Clips.d.ts.map +1 -1
- package/lib/plugins/clips/Clips.js +2 -1
- package/lib/plugins/cmcd-config/CmcdConfig.d.ts +34 -11
- package/lib/plugins/cmcd-config/CmcdConfig.d.ts.map +1 -1
- package/lib/plugins/cmcd-config/CmcdConfig.js +28 -18
- package/lib/plugins/media-control/MediaControl.d.ts +11 -0
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.js +19 -5
- package/lib/plugins/poster/Poster.d.ts +7 -3
- package/lib/plugins/poster/Poster.d.ts.map +1 -1
- package/lib/plugins/source-controller/SourceController.d.ts +1 -0
- package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
- package/lib/plugins/source-controller/SourceController.js +20 -9
- package/lib/testUtils.d.ts +1 -0
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +3 -0
- package/package.json +1 -1
- package/src/plugins/big-mute-button/BigMuteButton.ts +75 -110
- package/src/plugins/big-mute-button/__tests__/BigMuteButton.test.ts +38 -0
- package/src/plugins/big-mute-button/__tests__/__snapshots__/BigMuteButton.test.ts.snap +8 -0
- package/src/plugins/bottom-gear/BottomGear.ts +40 -28
- package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +34 -7
- package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +5 -2
- package/src/plugins/clappr-stats/ClapprStats.ts +5 -1
- package/src/plugins/clips/Clips.ts +3 -2
- package/src/plugins/cmcd-config/CmcdConfig.ts +33 -27
- package/src/plugins/media-control/MediaControl.ts +23 -6
- package/src/plugins/poster/Poster.ts +6 -2
- package/src/plugins/source-controller/SourceController.ts +25 -9
- package/src/plugins/source-controller/__tests__/SourceController.test.ts +28 -8
- package/src/testUtils.ts +3 -0
- package/temp/player.api.json +229 -154
- package/tsconfig.tsbuildinfo +1 -1
- package/docs/api/player.cmcdconfig.supportedversion.md +0 -14
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Events, template, UICorePlugin, Utils } from '@clappr/core'
|
|
2
2
|
import { trace } from '@gcorevideo/utils'
|
|
3
|
+
import assert from 'assert'
|
|
3
4
|
|
|
4
5
|
import { CLAPPR_VERSION } from '../../build.js'
|
|
5
|
-
import { ZeptoResult } from '../../types.js'
|
|
6
6
|
|
|
7
7
|
import volumeMuteIcon from '../../../assets/icons/new/volume-off.svg'
|
|
8
|
-
import
|
|
8
|
+
import templateHtml from '../../../assets/big-mute-button/big-mute-button.ejs'
|
|
9
9
|
import '../../../assets/big-mute-button/big-mute-button.scss'
|
|
10
10
|
|
|
11
11
|
const T = 'plugins.big_mute_button'
|
|
@@ -13,19 +13,22 @@ const T = 'plugins.big_mute_button'
|
|
|
13
13
|
// TODO rewrite as a container plugin
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* `PLUGIN` that displays a big mute button over the video when it's muted.
|
|
17
|
-
* Once pressed, it unmutes the video.
|
|
16
|
+
* `PLUGIN` that displays a big mute button over the video when it's being played muted.
|
|
18
17
|
* @beta
|
|
18
|
+
* @remarks
|
|
19
|
+
* When pressed, it unmutes the video.
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { BigMuteButton } from '@gcorevideo/player'
|
|
23
|
+
* Player.registerPlugin(BigMuteButton)
|
|
24
|
+
* ```
|
|
19
25
|
*/
|
|
20
26
|
export class BigMuteButton extends UICorePlugin {
|
|
21
|
-
private
|
|
27
|
+
private hidden = false
|
|
22
28
|
|
|
29
|
+
// TODO get back to the ads-related logic later
|
|
23
30
|
private _adIsPlaying = false
|
|
24
31
|
|
|
25
|
-
private $bigMuteBtnContainer: ZeptoResult | null = null
|
|
26
|
-
|
|
27
|
-
private $bigMuteButton: ZeptoResult | null = null
|
|
28
|
-
|
|
29
32
|
/**
|
|
30
33
|
* @internal
|
|
31
34
|
*/
|
|
@@ -40,15 +43,14 @@ export class BigMuteButton extends UICorePlugin {
|
|
|
40
43
|
return { min: CLAPPR_VERSION }
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
private static readonly template = template(
|
|
46
|
+
private static readonly template = template(templateHtml)
|
|
44
47
|
|
|
45
48
|
/**
|
|
46
49
|
* @internal
|
|
47
50
|
*/
|
|
48
51
|
override get events() {
|
|
49
52
|
return {
|
|
50
|
-
'click
|
|
51
|
-
'click .big-mute-icon-wrapper': 'destroyBigMuteBtn',
|
|
53
|
+
'click': 'clicked',
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -57,156 +59,119 @@ export class BigMuteButton extends UICorePlugin {
|
|
|
57
59
|
*/
|
|
58
60
|
override bindEvents() {
|
|
59
61
|
this.listenTo(this.core, Events.CORE_READY, this.onCoreReady)
|
|
62
|
+
this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.onContainerChanged)
|
|
60
63
|
this.listenTo(this.core, 'core:advertisement:start', this.onStartAd)
|
|
61
64
|
this.listenTo(this.core, 'core:advertisement:finish', this.onFinishAd)
|
|
62
|
-
trace(`${T} bindEvents`, {
|
|
63
|
-
mediacontrol: !!this.core.mediaControl,
|
|
64
|
-
})
|
|
65
|
-
// TOOD use core.getPlugin('media_control')
|
|
66
|
-
this.listenTo(
|
|
67
|
-
this.core.mediaControl,
|
|
68
|
-
Events.MEDIACONTROL_RENDERED,
|
|
69
|
-
this.mediaControlRendered,
|
|
70
|
-
)
|
|
71
65
|
}
|
|
72
66
|
|
|
73
67
|
private onCoreReady() {
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private onContainerChanged() {
|
|
74
72
|
this.listenTo(
|
|
75
73
|
this.core.activeContainer,
|
|
76
74
|
Events.CONTAINER_VOLUME,
|
|
77
75
|
this.onContainerVolume,
|
|
78
76
|
)
|
|
79
|
-
this.listenTo(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
)
|
|
77
|
+
// this.listenTo(
|
|
78
|
+
// this.core.activeContainer,
|
|
79
|
+
// Events.CONTAINER_READY,
|
|
80
|
+
// this.onContainerReady,
|
|
81
|
+
// )
|
|
84
82
|
this.listenTo(
|
|
85
83
|
this.core.activePlayback,
|
|
86
84
|
Events.PLAYBACK_ENDED,
|
|
87
85
|
this.onPlaybackEnded,
|
|
88
86
|
)
|
|
87
|
+
this.listenTo(
|
|
88
|
+
this.core.activeContainer,
|
|
89
|
+
Events.CONTAINER_PLAY,
|
|
90
|
+
this.onPlay
|
|
91
|
+
)
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
private
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
private onPlay(_: string, { autoPlay }: { autoPlay?: boolean}) {
|
|
95
|
+
const container = this.core.activeContainer
|
|
96
|
+
const { volume } = container
|
|
97
|
+
const { wasMuted } = this.options
|
|
98
|
+
trace(`${T} onPlay`, {
|
|
99
|
+
autoPlay,
|
|
100
|
+
wasMuted,
|
|
101
|
+
volume,
|
|
102
|
+
})
|
|
103
|
+
if (autoPlay && !wasMuted && volume === 0) {
|
|
104
|
+
this.mount()
|
|
105
|
+
} else {
|
|
106
|
+
this.destroy()
|
|
94
107
|
}
|
|
95
108
|
}
|
|
96
109
|
|
|
97
|
-
private
|
|
98
|
-
if (
|
|
99
|
-
this.
|
|
110
|
+
private onContainerVolume(value: number) {
|
|
111
|
+
if (value !== 0) {
|
|
112
|
+
this.destroy()
|
|
100
113
|
}
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
private onPlaybackEnded() {
|
|
104
|
-
this.
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
private mediaControlRendered() {
|
|
108
|
-
const container = this.core.activeContainer
|
|
109
|
-
|
|
110
|
-
trace(`${T} mediaControlRendered`, {
|
|
111
|
-
container: !!container,
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
if (container) {
|
|
115
|
-
this.listenTo(container.playback, Events.PLAYBACK_PLAY, () => {
|
|
116
|
-
trace(`${T} PLAYBACK_PLAY`)
|
|
117
|
-
this.render()
|
|
118
|
-
})
|
|
119
|
-
}
|
|
117
|
+
this.hide()
|
|
120
118
|
}
|
|
121
119
|
|
|
122
120
|
private onStartAd() {
|
|
123
121
|
this._adIsPlaying = true
|
|
124
|
-
|
|
125
|
-
this.$bigMuteBtnContainer.addClass('hide')
|
|
126
|
-
}
|
|
122
|
+
this.hide()
|
|
127
123
|
}
|
|
128
124
|
|
|
129
125
|
private onFinishAd() {
|
|
130
126
|
this._adIsPlaying = false
|
|
131
|
-
|
|
132
|
-
this.$bigMuteBtnContainer.removeClass('hide')
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
private shouldRender() {
|
|
137
|
-
const container = this.core.activeContainer
|
|
138
|
-
|
|
139
|
-
if (!container) {
|
|
140
|
-
return false
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const { autoPlay, wasMuted } = this.options
|
|
144
|
-
const volume = container.volume
|
|
145
|
-
|
|
146
|
-
trace(`${T} shouldRender`, {
|
|
147
|
-
autoPlay,
|
|
148
|
-
wasMuted,
|
|
149
|
-
volume,
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
return autoPlay && !wasMuted && volume === 0
|
|
127
|
+
this.show()
|
|
153
128
|
}
|
|
154
129
|
|
|
155
130
|
/**
|
|
156
131
|
* @internal
|
|
157
132
|
*/
|
|
158
133
|
override render() {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
this.$bigMuteBtnContainer = this.$el.find(
|
|
166
|
-
'.big-mute-icon-wrapper[data-big-mute]',
|
|
167
|
-
)
|
|
168
|
-
this._adIsPlaying && this.$bigMuteBtnContainer.addClass('hide')
|
|
169
|
-
|
|
170
|
-
this.$bigMuteButton = this.$bigMuteBtnContainer.find('.big-mute-icon')
|
|
171
|
-
this.$bigMuteButton.append(volumeMuteIcon)
|
|
172
|
-
|
|
173
|
-
const container = this.core.activeContainer
|
|
174
|
-
|
|
175
|
-
container.$el.append(this.$el.get(0))
|
|
176
|
-
}
|
|
134
|
+
trace(`${T} render`)
|
|
135
|
+
this.$el.html(BigMuteButton.template())
|
|
136
|
+
this.$el.find('#gplayer-big-mute-icon').append(volumeMuteIcon)
|
|
137
|
+
|
|
138
|
+
// TODO
|
|
139
|
+
// this._adIsPlaying && this.hide()
|
|
177
140
|
|
|
178
141
|
return this
|
|
179
142
|
}
|
|
180
143
|
|
|
181
|
-
private
|
|
182
|
-
this.
|
|
183
|
-
this
|
|
144
|
+
private mount() {
|
|
145
|
+
this.core.activeContainer.$el.append(this.$el)
|
|
146
|
+
this.show()
|
|
184
147
|
}
|
|
185
148
|
|
|
186
|
-
private
|
|
187
|
-
this.
|
|
188
|
-
|
|
189
|
-
this.$bigMuteBtnContainer.removeClass('hide')
|
|
190
|
-
}
|
|
149
|
+
private hide() {
|
|
150
|
+
this.hidden = true
|
|
151
|
+
this.$el.find('#gplayer-big-mute-button')?.addClass('hide')
|
|
191
152
|
}
|
|
192
153
|
|
|
193
|
-
private
|
|
194
|
-
this.
|
|
195
|
-
|
|
196
|
-
if (e && e.stopPropagation) {
|
|
197
|
-
e.stopPropagation()
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
this.destroy()
|
|
154
|
+
private show() {
|
|
155
|
+
this.hidden = false
|
|
156
|
+
this.$el.find('#gplayer-big-mute-button')?.removeClass('hide')
|
|
201
157
|
}
|
|
202
158
|
|
|
203
159
|
private clicked(e: MouseEvent) {
|
|
160
|
+
trace(`${T} clicked`)
|
|
161
|
+
const mediaControl = this.core.getPlugin('media_control')
|
|
162
|
+
// TODO delegate to media_control plugin
|
|
204
163
|
const localVolume = Utils.Config.restore('volume')
|
|
205
164
|
const volume = !isNaN(localVolume) ? localVolume : 100
|
|
165
|
+
const unmuted = volume === 0 ? 100 : volume
|
|
206
166
|
|
|
207
|
-
|
|
208
|
-
|
|
167
|
+
if (mediaControl) {
|
|
168
|
+
mediaControl.setVolume(unmuted)
|
|
169
|
+
} else {
|
|
170
|
+
this.core.activeContainer.setVolume(unmuted)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
e.stopPropagation?.()
|
|
209
174
|
|
|
210
|
-
this.
|
|
175
|
+
this.destroy()
|
|
211
176
|
}
|
|
212
177
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { beforeEach, describe, it, expect } from 'vitest'
|
|
2
|
+
import { Events } from '@clappr/core'
|
|
3
|
+
|
|
4
|
+
import { BigMuteButton } from '../BigMuteButton.js'
|
|
5
|
+
import { createMockCore } from '../../../testUtils.js'
|
|
6
|
+
|
|
7
|
+
describe('BigMuteButton', () => {
|
|
8
|
+
let core: any
|
|
9
|
+
let bmb: BigMuteButton
|
|
10
|
+
describe('basically', () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
core = createMockCore({})
|
|
13
|
+
bmb = new BigMuteButton(core)
|
|
14
|
+
// core.emit('core:ready')
|
|
15
|
+
// core.emit('core:active:container:changed')
|
|
16
|
+
})
|
|
17
|
+
it('should render', () => {
|
|
18
|
+
expect(bmb.$el.html()).toMatchSnapshot()
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
describe('when container starts playing', () => {
|
|
22
|
+
describe.each([
|
|
23
|
+
['muted autoplay', 0, { autoPlay: true }, true],
|
|
24
|
+
['audible autoplay', 50, { autoPlay: true }, false],
|
|
25
|
+
['muted not autoplay', 0, { }, false],
|
|
26
|
+
['audible not autoplay', 1, {}, false],
|
|
27
|
+
])("%s", (_, volume, playMetadata, shouldMount) => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED)
|
|
30
|
+
core.activeContainer.volume = volume
|
|
31
|
+
core.activeContainer.emit(Events.CONTAINER_PLAY, 'Container', playMetadata)
|
|
32
|
+
})
|
|
33
|
+
it(`should ${shouldMount ? 'mount' : 'not mount'} to container`, () => {
|
|
34
|
+
expect(core.activeContainer.$el.find('#gplayer-big-mute-button').length).toBe(shouldMount ? 1 : 0)
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
})
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`BigMuteButton > basically > should render 1`] = `
|
|
4
|
+
"<div class="big-mute-icon-wrapper" data-big-mute="" id="gplayer-big-mute-button">
|
|
5
|
+
<div class="big-mute-icon gcore-skin-border-color" data-big-mute-icon="" id="gplayer-big-mute-icon">/assets/icons/new/volume-off.svg</div>
|
|
6
|
+
</div>
|
|
7
|
+
"
|
|
8
|
+
`;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
UICorePlugin,
|
|
3
|
+
template,
|
|
4
|
+
Events as ClapprEvents,
|
|
5
|
+
$,
|
|
6
|
+
Container,
|
|
7
|
+
} from '@clappr/core'
|
|
2
8
|
import { trace } from '@gcorevideo/utils'
|
|
3
9
|
import assert from 'assert'
|
|
4
10
|
|
|
@@ -16,6 +22,9 @@ const VERSION = '2.19.12'
|
|
|
16
22
|
|
|
17
23
|
const T = 'plugins.bottom_gear'
|
|
18
24
|
|
|
25
|
+
const MENU_VMARGIN = 12
|
|
26
|
+
const MENU_BACKLINK_HEIGHT = 44
|
|
27
|
+
|
|
19
28
|
/**
|
|
20
29
|
* Events triggered by the plugin
|
|
21
30
|
* @beta
|
|
@@ -27,8 +36,6 @@ export enum GearEvents {
|
|
|
27
36
|
RENDERED = 'rendered',
|
|
28
37
|
}
|
|
29
38
|
|
|
30
|
-
// TODO disabled if no items added
|
|
31
|
-
|
|
32
39
|
/**
|
|
33
40
|
* `PLUGIN` that adds a button to extend the media controls UI with extra options.
|
|
34
41
|
* @beta
|
|
@@ -100,9 +107,9 @@ export enum GearEvents {
|
|
|
100
107
|
export class BottomGear extends UICorePlugin {
|
|
101
108
|
private hd = false
|
|
102
109
|
|
|
103
|
-
private numItems = 0
|
|
110
|
+
private numItems = 0
|
|
104
111
|
|
|
105
|
-
private collapsed = true
|
|
112
|
+
private collapsed = true
|
|
106
113
|
|
|
107
114
|
/**
|
|
108
115
|
* @internal
|
|
@@ -175,7 +182,7 @@ export class BottomGear extends UICorePlugin {
|
|
|
175
182
|
* ```
|
|
176
183
|
*/
|
|
177
184
|
addItem(name: string, $subMenu?: ZeptoResult): ZeptoResult {
|
|
178
|
-
const $existingItem = this.$el.find(`#gear-options li[data-${name}`)
|
|
185
|
+
const $existingItem = this.$el.find(`#gear-options li[data-${name}]`)
|
|
179
186
|
if ($existingItem.length) {
|
|
180
187
|
trace(`${T} addItem already exists`, { name })
|
|
181
188
|
return $existingItem
|
|
@@ -190,21 +197,18 @@ export class BottomGear extends UICorePlugin {
|
|
|
190
197
|
.hide()
|
|
191
198
|
.appendTo(this.$el.find('#gear-options-wrapper'))
|
|
192
199
|
$item.on('click', (e: MouseEvent) => {
|
|
193
|
-
trace(`${T} addItem submenu clicked`, { name })
|
|
194
200
|
e.stopPropagation()
|
|
201
|
+
this.alignSubmenu($subMenu)
|
|
195
202
|
$subMenu.show()
|
|
196
203
|
this.$el.find('#gear-options').hide()
|
|
197
204
|
})
|
|
198
205
|
}
|
|
199
|
-
this.numItems
|
|
200
|
-
|
|
201
|
-
this.$el.show()
|
|
202
|
-
}
|
|
206
|
+
this.numItems++
|
|
207
|
+
this.$el.show()
|
|
203
208
|
return $item
|
|
204
209
|
}
|
|
205
210
|
|
|
206
211
|
private bindContainerEvents(container: Container) {
|
|
207
|
-
trace(`${T} bindContainerEvents`)
|
|
208
212
|
this.listenTo(
|
|
209
213
|
container,
|
|
210
214
|
ClapprEvents.CONTAINER_HIGHDEFINITIONUPDATE,
|
|
@@ -225,14 +229,13 @@ export class BottomGear extends UICorePlugin {
|
|
|
225
229
|
* @internal
|
|
226
230
|
*/
|
|
227
231
|
override render() {
|
|
228
|
-
trace(`${T} render`)
|
|
229
232
|
const mediaControl = this.core.getPlugin('media_control')
|
|
230
233
|
if (!mediaControl) {
|
|
231
234
|
return this // TODO test
|
|
232
235
|
}
|
|
233
236
|
const icon = this.hd ? gearHdIcon : gearIcon
|
|
234
|
-
this.collapsed = true
|
|
235
|
-
this.numItems = 0
|
|
237
|
+
this.collapsed = true
|
|
238
|
+
this.numItems = 0
|
|
236
239
|
this.$el
|
|
237
240
|
.html(BottomGear.template({ icon }))
|
|
238
241
|
.hide() // until numItems > 0
|
|
@@ -264,19 +267,19 @@ export class BottomGear extends UICorePlugin {
|
|
|
264
267
|
this.core
|
|
265
268
|
.getPlugin('media_control')
|
|
266
269
|
.trigger(ExtendedEvents.MEDIACONTROL_MENU_COLLAPSE, this.name)
|
|
267
|
-
this.collapsed = !this.collapsed
|
|
270
|
+
this.collapsed = !this.collapsed
|
|
268
271
|
if (this.collapsed) {
|
|
269
272
|
this.$el.find('#gear-options-wrapper').hide()
|
|
270
273
|
} else {
|
|
271
274
|
this.$el.find('#gear-options-wrapper').show()
|
|
272
275
|
}
|
|
273
|
-
this.$el
|
|
274
|
-
|
|
276
|
+
this.$el
|
|
277
|
+
.find('#gear-button')
|
|
278
|
+
.attr('aria-expanded', (!this.collapsed).toString())
|
|
275
279
|
}
|
|
276
280
|
|
|
277
281
|
private collapse() {
|
|
278
|
-
|
|
279
|
-
this.collapsed = true;
|
|
282
|
+
this.collapsed = true
|
|
280
283
|
this.$el.find('#gear-options-wrapper').hide()
|
|
281
284
|
this.$el.find('#gear-button').attr('aria-expanded', 'false')
|
|
282
285
|
// TODO hide submenus
|
|
@@ -284,7 +287,6 @@ export class BottomGear extends UICorePlugin {
|
|
|
284
287
|
}
|
|
285
288
|
|
|
286
289
|
private onCoreReady() {
|
|
287
|
-
trace(`${T} onCoreReady`)
|
|
288
290
|
const mediaControl = this.core.getPlugin('media_control')
|
|
289
291
|
assert(mediaControl, 'media_control plugin is required')
|
|
290
292
|
this.listenTo(
|
|
@@ -293,9 +295,13 @@ export class BottomGear extends UICorePlugin {
|
|
|
293
295
|
this.onMediaControlRendered,
|
|
294
296
|
)
|
|
295
297
|
this.listenTo(mediaControl, ClapprEvents.MEDIACONTROL_HIDE, this.collapse)
|
|
296
|
-
this.listenTo(
|
|
297
|
-
|
|
298
|
-
|
|
298
|
+
this.listenTo(
|
|
299
|
+
mediaControl,
|
|
300
|
+
ClapprEvents.MEDIACONTROL_CONTAINERCHANGED,
|
|
301
|
+
() => {
|
|
302
|
+
this.bindContainerEvents(mediaControl.container)
|
|
303
|
+
},
|
|
304
|
+
)
|
|
299
305
|
this.listenTo(
|
|
300
306
|
mediaControl,
|
|
301
307
|
ExtendedEvents.MEDIACONTROL_MENU_COLLAPSE,
|
|
@@ -309,15 +315,21 @@ export class BottomGear extends UICorePlugin {
|
|
|
309
315
|
}
|
|
310
316
|
|
|
311
317
|
private onMediaControlRendered() {
|
|
312
|
-
trace(`${T} onMediaControlRendered`)
|
|
313
318
|
this.mount()
|
|
314
319
|
}
|
|
315
320
|
|
|
316
321
|
private mount() {
|
|
317
|
-
trace(`${T} mount`, {
|
|
318
|
-
numItems: this.numItems,
|
|
319
|
-
})
|
|
320
322
|
const mediaControl = this.core.getPlugin('media_control')
|
|
321
323
|
mediaControl.mount('gear', this.$el)
|
|
322
324
|
}
|
|
325
|
+
|
|
326
|
+
private alignSubmenu($subMenu: ZeptoResult) {
|
|
327
|
+
const availableHeight =
|
|
328
|
+
this.core.getPlugin('media_control').getAvailableHeight() -
|
|
329
|
+
MENU_VMARGIN * 2
|
|
330
|
+
$subMenu.css('max-height', `${availableHeight}px`)
|
|
331
|
+
$subMenu
|
|
332
|
+
.find('.gear-sub-menu')
|
|
333
|
+
.css('max-height', `${availableHeight - MENU_BACKLINK_HEIGHT}px`)
|
|
334
|
+
}
|
|
323
335
|
}
|
|
@@ -31,6 +31,15 @@ describe('BottomGear', () => {
|
|
|
31
31
|
bottomGear.on(GearEvents.RENDERED, onGearRendered, null)
|
|
32
32
|
core.emit(Events.CORE_READY)
|
|
33
33
|
bottomGear.addItem('test', null).html('<button>test</button>')
|
|
34
|
+
const $moreOptions = $(
|
|
35
|
+
`<div>
|
|
36
|
+
<button id="more-options-back">< back</button>
|
|
37
|
+
<ul class="gear-sub-menu" id="more-options"><li>Item</li><li>Item</li><li>Item</li></ul>
|
|
38
|
+
</div>`,
|
|
39
|
+
)
|
|
40
|
+
bottomGear
|
|
41
|
+
.addItem('more', $moreOptions)
|
|
42
|
+
.html('<button id="more-button">more options</button>')
|
|
34
43
|
})
|
|
35
44
|
it('should render', () => {
|
|
36
45
|
expect(bottomGear.el.innerHTML).toMatchSnapshot()
|
|
@@ -99,6 +108,26 @@ describe('BottomGear', () => {
|
|
|
99
108
|
)
|
|
100
109
|
})
|
|
101
110
|
})
|
|
111
|
+
describe('when submenu is open', () => {
|
|
112
|
+
beforeEach(async () => {
|
|
113
|
+
mediaControl.getAvailableHeight.mockReturnValue(198)
|
|
114
|
+
bottomGear.$el.find('#gear-button').click()
|
|
115
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
116
|
+
bottomGear.$el.find('#more-button').click()
|
|
117
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
118
|
+
})
|
|
119
|
+
it('should show submenu', () => {
|
|
120
|
+
expect(
|
|
121
|
+
bottomGear.$el.find('#more-options').parent().css('display'),
|
|
122
|
+
).not.toBe('none')
|
|
123
|
+
})
|
|
124
|
+
it('should align nicely within container', () => {
|
|
125
|
+
const submenu = bottomGear.$el.find('#more-options')
|
|
126
|
+
const wrapper = submenu.parent()
|
|
127
|
+
expect(wrapper.css('max-height')).toBe('174px') // available height minus vertical margins
|
|
128
|
+
expect(submenu.css('max-height')).toBe('130px') // wrapper height minus backlink height
|
|
129
|
+
})
|
|
130
|
+
})
|
|
102
131
|
})
|
|
103
132
|
describe('when there are no items', () => {
|
|
104
133
|
beforeEach(() => {
|
|
@@ -128,9 +157,9 @@ describe('BottomGear', () => {
|
|
|
128
157
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
129
158
|
})
|
|
130
159
|
it('should collapse the gear menu', () => {
|
|
131
|
-
expect(
|
|
132
|
-
'
|
|
133
|
-
)
|
|
160
|
+
expect(
|
|
161
|
+
bottomGear.$el.find('#gear-options-wrapper').css('display'),
|
|
162
|
+
).toBe('none')
|
|
134
163
|
expect(bottomGear.$el.find('#gear-button').attr('aria-expanded')).toBe(
|
|
135
164
|
'false',
|
|
136
165
|
)
|
|
@@ -140,14 +169,12 @@ describe('BottomGear', () => {
|
|
|
140
169
|
describe('when submenu is open', () => {
|
|
141
170
|
beforeEach(async () => {
|
|
142
171
|
// bottomGear.$el.find('#test-submenu').click()
|
|
143
|
-
bottomGear.$el.find('#test-options').show()
|
|
172
|
+
bottomGear.$el.find('#test-options').show() // as if it was clicked
|
|
144
173
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
145
174
|
mediaControl.container.trigger(Events.CONTAINER_CLICK)
|
|
146
175
|
})
|
|
147
176
|
it('should collapse it as well', () => {
|
|
148
|
-
expect(bottomGear.$el.find('#test-options').css('display')).toBe(
|
|
149
|
-
'none',
|
|
150
|
-
)
|
|
177
|
+
expect(bottomGear.$el.find('#test-options').css('display')).toBe('none')
|
|
151
178
|
expect(bottomGear.$el.find('#gear-options').css('display')).not.toBe(
|
|
152
179
|
'none',
|
|
153
180
|
)
|
|
@@ -5,7 +5,10 @@ exports[`BottomGear > basically > should render 1`] = `
|
|
|
5
5
|
/assets/icons/new/gear.svg
|
|
6
6
|
</button>
|
|
7
7
|
<div class="gear-wrapper gcore-skin-bg-color" id="gear-options-wrapper" style="display: none;">
|
|
8
|
-
<ul class="gear-options-list" id="gear-options" role="menu"><li data-test=""><button>test</button></li></ul>
|
|
9
|
-
|
|
8
|
+
<ul class="gear-options-list" id="gear-options" role="menu"><li data-test=""><button>test</button></li><li data-more=""><button id="more-button">more options</button></li></ul>
|
|
9
|
+
<div class="gear-sub-menu-wrapper" style="display: none;">
|
|
10
|
+
<button id="more-options-back">< back</button>
|
|
11
|
+
<ul class="gear-sub-menu" id="more-options"><li>Item</li><li>Item</li><li>Item</li></ul>
|
|
12
|
+
</div></div>
|
|
10
13
|
"
|
|
11
14
|
`;
|
|
@@ -20,7 +20,11 @@ import { isFullscreen } from '../utils/fullscreen.js'
|
|
|
20
20
|
|
|
21
21
|
// const T = 'plugins.clappr_stats'
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Config options for the {@link ClapprStats} plugin
|
|
25
|
+
* @beta
|
|
26
|
+
*/
|
|
27
|
+
export interface ClapprStatsSettings {
|
|
24
28
|
/**
|
|
25
29
|
* The interval in milliseconds of periodic measurements.
|
|
26
30
|
* The plugin will emit a {@link ClapprStatsEvents.REPORT} event with the collected metrics at the specified interval.
|
|
@@ -17,7 +17,7 @@ const T = 'plugins.clips'
|
|
|
17
17
|
*/
|
|
18
18
|
export interface ClipsPluginSettings {
|
|
19
19
|
/**
|
|
20
|
-
* The compiled text of the clips description, one clip per line in format
|
|
20
|
+
* The compiled text of the clips description, one clip per line in format:
|
|
21
21
|
* `HH:MM:SS text` or `MM:SS text` or `SS text`
|
|
22
22
|
*/
|
|
23
23
|
text: string
|
|
@@ -123,7 +123,8 @@ export class Clips extends UICorePlugin {
|
|
|
123
123
|
* @returns The text of the clip at the given time
|
|
124
124
|
*/
|
|
125
125
|
getText(time: TimeValue): string | undefined {
|
|
126
|
-
return this.clips.find((clip) => clip.start <= time && clip.end >= time)
|
|
126
|
+
return this.clips.find((clip) => clip.start <= time && clip.end >= time)
|
|
127
|
+
?.text
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
private onCoreReady() {
|