summaryrefslogtreecommitdiff
path: root/javascript/videojs/src/js/control-bar/progress-control/load-progress-bar.js
blob: 0da4ff2a80cda50a81eafb1d72006d117048833b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/**
 * @file load-progress-bar.js
 */
import Component from '../../component.js';
import * as Dom from '../../utils/dom.js';
import {clamp} from '../../utils/num';
import document from 'global/document';

/** @import Player from '../../player' */

// get the percent width of a time compared to the total end
const percentify = (time, end) => clamp((time / end) * 100, 0, 100).toFixed(2) + '%';

/**
 * Shows loading progress
 *
 * @extends Component
 */
class LoadProgressBar extends Component {

  /**
   * Creates an instance of this class.
   *
   * @param {Player} player
   *        The `Player` that this class should be attached to.
   *
   * @param {Object} [options]
   *        The key/value store of player options.
   */
  constructor(player, options) {
    super(player, options);
    this.partEls_ = [];
    this.on(player, 'progress', (e) => this.update(e));
  }

  /**
   * Create the `Component`'s DOM element
   *
   * @return {Element}
   *         The element that was created.
   */
  createEl() {
    const el = super.createEl('div', {className: 'vjs-load-progress'});
    const wrapper = Dom.createEl('span', {className: 'vjs-control-text'});
    const loadedText = Dom.createEl('span', {textContent: this.localize('Loaded')});
    const separator = document.createTextNode(': ');

    this.percentageEl_ = Dom.createEl('span', {
      className: 'vjs-control-text-loaded-percentage',
      textContent: '0%'
    });

    el.appendChild(wrapper);
    wrapper.appendChild(loadedText);
    wrapper.appendChild(separator);
    wrapper.appendChild(this.percentageEl_);

    return el;
  }

  dispose() {
    this.partEls_ = null;
    this.percentageEl_ = null;

    super.dispose();
  }

  /**
   * Update progress bar
   *
   * @param {Event} [event]
   *        The `progress` event that caused this function to run.
   *
   * @listens Player#progress
   */
  update(event) {
    this.requestNamedAnimationFrame('LoadProgressBar#update', () => {
      const liveTracker = this.player_.liveTracker;
      const buffered = this.player_.buffered();
      const duration = (liveTracker && liveTracker.isLive()) ? liveTracker.seekableEnd() : this.player_.duration();
      const bufferedEnd = this.player_.bufferedEnd();
      const children = this.partEls_;
      const percent = percentify(bufferedEnd, duration);

      if (this.percent_ !== percent) {
        // update the width of the progress bar
        this.el_.style.width = percent;
        // update the control-text
        Dom.textContent(this.percentageEl_, percent);
        this.percent_ = percent;
      }

      // add child elements to represent the individual buffered time ranges
      for (let i = 0; i < buffered.length; i++) {
        const start = buffered.start(i);
        const end = buffered.end(i);
        let part = children[i];

        if (!part) {
          part = this.el_.appendChild(Dom.createEl());
          children[i] = part;
        }

        //  only update if changed
        if (part.dataset.start === start && part.dataset.end === end) {
          continue;
        }

        part.dataset.start = start;
        part.dataset.end = end;

        // set the percent based on the width of the progress bar (bufferedEnd)
        part.style.left = percentify(start, bufferedEnd);
        part.style.width = percentify(end - start, bufferedEnd);
      }

      // remove unused buffered range elements
      for (let i = children.length; i > buffered.length; i--) {
        this.el_.removeChild(children[i - 1]);
      }
      children.length = buffered.length;
    });
  }
}

Component.registerComponent('LoadProgressBar', LoadProgressBar);
export default LoadProgressBar;