1 import videojs, { VideoJsPlayer } from 'video.js'
3 function getMainTemplate (options: any) {
5 <div class="vjs-upnext-top">
6 <span class="vjs-upnext-headtext">${options.headText}</span>
7 <div class="vjs-upnext-title"></div>
9 <div class="vjs-upnext-autoplay-icon">
10 <svg height="100%" version="1.1" viewbox="0 0 98 98" width="100%">
11 <circle class="vjs-upnext-svg-autoplay-circle" cx="49" cy="49" fill="#000" fill-opacity="0.8" r="48"></circle>
12 <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>
13 <polygon class="vjs-upnext-svg-autoplay-triangle" fill="#fff" points="32,27 72,49 32,71"></polygon></svg>
15 <span class="vjs-upnext-bottom">
16 <span class="vjs-upnext-cancel">
17 <button class="vjs-upnext-cancel-button" tabindex="0" aria-label="Cancel autoplay">${options.cancelText}</button>
19 <span class="vjs-upnext-suspended">${options.suspendedText}</span>
24 export interface EndCardOptions extends videojs.ComponentOptions {
26 getTitle: () => string
31 condition: () => boolean
32 suspended: () => boolean
35 const Component = videojs.getComponent('Component')
36 class EndCard extends Component {
37 options_: EndCardOptions
42 upNextEvents = new videojs.EventTarget()
46 container: HTMLDivElement
48 autoplayRing: HTMLElement
49 cancelButton: HTMLElement
50 suspendedMessage: HTMLElement
51 nextButton: HTMLElement
53 constructor (player: VideoJsPlayer, options: EndCardOptions) {
54 super(player, options)
56 this.totalTicks = this.options_.timeout / this.interval
58 player.on('ended', (_: any) => {
59 if (!this.options_.condition()) return
61 player.addClass('vjs-upnext--showing')
62 this.showCard((canceled: boolean) => {
63 player.removeClass('vjs-upnext--showing')
64 this.container.style.display = 'none'
71 player.on('playing', () => {
72 this.upNextEvents.trigger('playing')
77 const container = super.createEl('div', {
78 className: 'vjs-upnext-content',
79 innerHTML: getMainTemplate(this.options_)
82 this.container = container
83 container.style.display = 'none'
85 this.autoplayRing = container.getElementsByClassName('vjs-upnext-svg-autoplay-ring')[0] as HTMLElement
86 this.title = container.getElementsByClassName('vjs-upnext-title')[0] as HTMLElement
87 this.cancelButton = container.getElementsByClassName('vjs-upnext-cancel-button')[0] as HTMLElement
88 this.suspendedMessage = container.getElementsByClassName('vjs-upnext-suspended')[0] as HTMLElement
89 this.nextButton = container.getElementsByClassName('vjs-upnext-autoplay-icon')[0] as HTMLElement
91 this.cancelButton.onclick = () => {
92 this.upNextEvents.trigger('cancel')
95 this.nextButton.onclick = () => {
96 this.upNextEvents.trigger('next')
102 showCard (cb: Function) {
105 this.autoplayRing.setAttribute('stroke-dasharray', '' + this.dashOffsetStart)
106 this.autoplayRing.setAttribute('stroke-dashoffset', '' + -this.dashOffsetStart)
108 this.title.innerHTML = this.options_.getTitle()
110 this.upNextEvents.one('cancel', () => {
111 clearTimeout(timeout)
115 this.upNextEvents.one('playing', () => {
116 clearTimeout(timeout)
120 this.upNextEvents.one('next', () => {
121 clearTimeout(timeout)
125 const goToPercent = (percent: number) => {
126 const newOffset = Math.max(-this.dashOffsetTotal, - this.dashOffsetStart - percent * this.dashOffsetTotal / 2 / 100)
127 this.autoplayRing.setAttribute('stroke-dashoffset', '' + newOffset)
131 goToPercent((this.ticks++) * 100 / this.totalTicks)
134 const update = () => {
135 if (this.options_.suspended()) {
136 this.suspendedMessage.innerText = this.options_.suspendedText
139 timeout = setTimeout(update.bind(this), 300) // checks once supsended can be a bit longer
140 } else if (this.ticks >= this.totalTicks) {
141 clearTimeout(timeout)
144 this.suspendedMessage.innerText = ''
146 timeout = setTimeout(update.bind(this), this.interval)
150 this.container.style.display = 'block'
151 timeout = setTimeout(update.bind(this), this.interval)
155 videojs.registerComponent('EndCard', EndCard)