2 import * as videojs from 'video.js'
3 import { VideoJSComponentInterface } from '../peertube-videojs-typings'
5 function getMainTemplate (options: any) {
7 <div class="vjs-upnext-top">
8 <span class="vjs-upnext-headtext">${options.headText}</span>
9 <div class="vjs-upnext-title"></div>
11 <div class="vjs-upnext-autoplay-icon">
12 <svg height="100%" version="1.1" viewbox="0 0 98 98" width="100%">
13 <circle class="vjs-upnext-svg-autoplay-circle" cx="49" cy="49" fill="#000" fill-opacity="0.8" r="48"></circle>
14 <circle class="vjs-upnext-svg-autoplay-ring" cx="-49" cy="49" fill-opacity="0" r="46.5" stroke="#FFFFFF" stroke-width="4" transform="rotate(-90)"></circle>
15 <polygon class="vjs-upnext-svg-autoplay-triangle" fill="#fff" points="32,27 72,49 32,71"></polygon></svg>
17 <span class="vjs-upnext-bottom">
18 <span class="vjs-upnext-cancel">
19 <button class="vjs-upnext-cancel-button" tabindex="0" aria-label="Cancel autoplay">${options.cancelText}</button>
21 <span class="vjs-upnext-suspended">${options.suspendedText}</span>
27 const Component = videojs.getComponent('Component')
28 class EndCard extends Component {
33 upNextEvents = new videojs.EventTarget()
37 container: HTMLElement
39 autoplayRing: HTMLElement
40 cancelButton: HTMLElement
41 suspendedMessage: HTMLElement
42 nextButton: HTMLElement
44 constructor (player: videojs.Player, options: any) {
45 super(player, options)
47 this.totalTicks = this.options_.timeout / this.interval
49 player.on('ended', (_: any) => {
50 if (!this.options_.condition()) return
52 player.addClass('vjs-upnext--showing')
53 this.showCard((canceled: boolean) => {
54 player.removeClass('vjs-upnext--showing')
55 this.container.style.display = 'none'
62 player.on('playing', () => {
63 this.upNextEvents.trigger('playing')
68 const container = super.createEl('div', {
69 className: 'vjs-upnext-content',
70 innerHTML: getMainTemplate(this.options_)
73 this.container = container
74 container.style.display = 'none'
76 this.autoplayRing = container.getElementsByClassName('vjs-upnext-svg-autoplay-ring')[0]
77 this.title = container.getElementsByClassName('vjs-upnext-title')[0]
78 this.cancelButton = container.getElementsByClassName('vjs-upnext-cancel-button')[0]
79 this.suspendedMessage = container.getElementsByClassName('vjs-upnext-suspended')[0]
80 this.nextButton = container.getElementsByClassName('vjs-upnext-autoplay-icon')[0]
82 this.cancelButton.onclick = () => {
83 this.upNextEvents.trigger('cancel')
86 this.nextButton.onclick = () => {
87 this.upNextEvents.trigger('next')
93 showCard (cb: Function) {
96 this.autoplayRing.setAttribute('stroke-dasharray', '' + this.dashOffsetStart)
97 this.autoplayRing.setAttribute('stroke-dashoffset', '' + -this.dashOffsetStart)
99 this.title.innerHTML = this.options_.getTitle()
101 this.upNextEvents.one('cancel', () => {
102 clearTimeout(timeout)
106 this.upNextEvents.one('playing', () => {
107 clearTimeout(timeout)
111 this.upNextEvents.one('next', () => {
112 clearTimeout(timeout)
116 const goToPercent = (percent: number) => {
117 const newOffset = Math.max(-this.dashOffsetTotal, - this.dashOffsetStart - percent * this.dashOffsetTotal / 2 / 100)
118 this.autoplayRing.setAttribute('stroke-dashoffset', '' + newOffset)
122 goToPercent((this.ticks++) * 100 / this.totalTicks)
125 const update = () => {
126 if (this.options_.suspended()) {
127 this.suspendedMessage.innerText = this.options_.suspendedText
130 timeout = setTimeout(update.bind(this), 300) // checks once supsended can be a bit longer
131 } else if (this.ticks >= this.totalTicks) {
132 clearTimeout(timeout)
135 this.suspendedMessage.innerText = ''
137 timeout = setTimeout(update.bind(this), this.interval)
141 this.container.style.display = 'block'
142 timeout = setTimeout(update.bind(this), this.interval)
147 videojs.registerComponent('EndCard', EndCard)
149 const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin')
150 class UpNextPlugin extends Plugin {
151 constructor (player: videojs.Player, options: any = {}) {
154 getTitle: options.getTitle,
155 timeout: options.timeout || 5000,
156 cancelText: options.cancelText || 'Cancel',
157 headText: options.headText || 'Up Next',
158 suspendedText: options.suspendedText || 'Autoplay is suspended',
159 condition: options.condition,
160 suspended: options.suspended
163 super(player, settings)
165 this.player.ready(() => {
166 player.addClass('vjs-upnext')
169 player.addChild('EndCard', settings)
173 videojs.registerPlugin('upnext', UpNextPlugin)
174 export { UpNextPlugin }