1 import videojs 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"
13 stroke="#FFFFFF" stroke-width="4" transform="rotate(-90)"
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>
26 export interface EndCardOptions extends videojs.ComponentOptions {
28 getTitle: () => string
33 condition: () => boolean
34 suspended: () => boolean
37 const Component = videojs.getComponent('Component')
38 class EndCard extends Component {
39 options_: EndCardOptions
44 upNextEvents = new videojs.EventTarget()
48 container: HTMLDivElement
50 autoplayRing: HTMLElement
51 cancelButton: HTMLElement
52 suspendedMessage: HTMLElement
53 nextButton: HTMLElement
55 constructor (player: videojs.Player, options: EndCardOptions) {
56 super(player, options)
58 this.totalTicks = this.options_.timeout / this.interval
60 player.on('ended', (_: any) => {
61 if (!this.options_.condition()) return
63 player.addClass('vjs-upnext--showing')
64 this.showCard((canceled: boolean) => {
65 player.removeClass('vjs-upnext--showing')
66 this.container.style.display = 'none'
73 player.on('playing', () => {
74 this.upNextEvents.trigger('playing')
79 const container = super.createEl('div', {
80 className: 'vjs-upnext-content',
81 innerHTML: getMainTemplate(this.options_)
84 this.container = container
85 container.style.display = 'none'
87 this.autoplayRing = container.getElementsByClassName('vjs-upnext-svg-autoplay-ring')[0] as HTMLElement
88 this.title = container.getElementsByClassName('vjs-upnext-title')[0] as HTMLElement
89 this.cancelButton = container.getElementsByClassName('vjs-upnext-cancel-button')[0] as HTMLElement
90 this.suspendedMessage = container.getElementsByClassName('vjs-upnext-suspended')[0] as HTMLElement
91 this.nextButton = container.getElementsByClassName('vjs-upnext-autoplay-icon')[0] as HTMLElement
93 this.cancelButton.onclick = () => {
94 this.upNextEvents.trigger('cancel')
97 this.nextButton.onclick = () => {
98 this.upNextEvents.trigger('next')
104 showCard (cb: (value: boolean) => void) {
107 this.autoplayRing.setAttribute('stroke-dasharray', `${this.dashOffsetStart}`)
108 this.autoplayRing.setAttribute('stroke-dashoffset', `${-this.dashOffsetStart}`)
110 this.title.innerHTML = this.options_.getTitle()
112 this.upNextEvents.one('cancel', () => {
113 clearTimeout(timeout)
117 this.upNextEvents.one('playing', () => {
118 clearTimeout(timeout)
122 this.upNextEvents.one('next', () => {
123 clearTimeout(timeout)
127 const goToPercent = (percent: number) => {
128 const newOffset = Math.max(-this.dashOffsetTotal, -this.dashOffsetStart - percent * this.dashOffsetTotal / 2 / 100)
129 this.autoplayRing.setAttribute('stroke-dashoffset', '' + newOffset)
133 goToPercent((this.ticks++) * 100 / this.totalTicks)
136 const update = () => {
137 if (this.options_.suspended()) {
138 this.suspendedMessage.innerText = this.options_.suspendedText
141 timeout = setTimeout(update.bind(this), 300) // checks once supsended can be a bit longer
142 } else if (this.ticks >= this.totalTicks) {
143 clearTimeout(timeout)
146 this.suspendedMessage.innerText = ''
148 timeout = setTimeout(update.bind(this), this.interval)
152 this.container.style.display = 'block'
153 timeout = setTimeout(update.bind(this), this.interval)
157 videojs.registerComponent('EndCard', EndCard)