summaryrefslogtreecommitdiff
path: root/javascript/videojs/src/js/control-bar/time-controls/time-display.js
blob: 2fa558dcfedb7a9bbd421bc780ff9729a2366f72 (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/**
 * @file time-display.js
 */
import document from 'global/document';
import Component from '../../component.js';
import * as Dom from '../../utils/dom.js';
import {formatTime} from '../../utils/time.js';
import log from '../../utils/log.js';

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

/**
 * Displays time information about the video
 *
 * @extends Component
 */
class TimeDisplay 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.on(player, ['timeupdate', 'ended', 'seeking'], (e) => this.update(e));
    this.updateTextNode_();
  }

  /**
   * Create the `Component`'s DOM element
   *
   * @return {Element}
   *         The element that was created.
   */
  createEl() {
    const className = this.buildCSSClass();
    const el = super.createEl('div', {
      className: `${className} vjs-time-control vjs-control`
    });
    const span = Dom.createEl('span', {
      className: 'vjs-control-text',
      textContent: `${this.localize(this.labelText_)}\u00a0`
    }, {
      role: 'presentation'
    });

    el.appendChild(span);

    this.contentEl_ = Dom.createEl('span', {
      className: `${className}-display`
    }, {
      // span elements have no implicit role, but some screen readers (notably VoiceOver)
      // treat them as a break between items in the DOM when using arrow keys
      // (or left-to-right swipes on iOS) to read contents of a page. Using
      // role='presentation' causes VoiceOver to NOT treat this span as a break.
      role: 'presentation'
    });

    el.appendChild(this.contentEl_);
    return el;
  }

  dispose() {
    this.contentEl_ = null;
    this.textNode_ = null;

    super.dispose();
  }

  /**
   * Updates the displayed time according to the `updateContent` function which is defined in the child class.
   *
   * @param {Event} [event]
   *          The `timeupdate`, `ended` or `seeking` (if enableSmoothSeeking is true) event that caused this function to be called.
   */
  update(event) {
    if (!this.player_.options_.enableSmoothSeeking && event.type === 'seeking') {
      return;
    }

    this.updateContent(event);
  }

  /**
   * Updates the time display text node with a new time
   *
   * @param {number} [time=0] the time to update to
   *
   * @private
   */
  updateTextNode_(time = 0) {
    time = formatTime(time);

    if (this.formattedTime_ === time) {
      return;
    }

    this.formattedTime_ = time;

    this.requestNamedAnimationFrame('TimeDisplay#updateTextNode_', () => {
      if (!this.contentEl_) {
        return;
      }

      let oldNode = this.textNode_;

      if (oldNode && this.contentEl_.firstChild !== oldNode) {
        oldNode = null;

        log.warn('TimeDisplay#updateTextnode_: Prevented replacement of text node element since it was no longer a child of this node. Appending a new node instead.');
      }

      this.textNode_ = document.createTextNode(this.formattedTime_);

      if (!this.textNode_) {
        return;
      }

      if (oldNode) {
        this.contentEl_.replaceChild(this.textNode_, oldNode);
      } else {
        this.contentEl_.appendChild(this.textNode_);
      }
    });
  }

  /**
   * To be filled out in the child class, should update the displayed time
   * in accordance with the fact that the current time has changed.
   *
   * @param {Event} [event]
   *        The `timeupdate`  event that caused this to run.
   *
   * @listens Player#timeupdate
   */
  updateContent(event) {}
}

/**
 * The text that is added to the `TimeDisplay` for screen reader users.
 *
 * @type {string}
 * @private
 */
TimeDisplay.prototype.labelText_ = 'Time';

/**
 * The text that should display over the `TimeDisplay`s controls. Added to for localization.
 *
 * @type {string}
 * @protected
 *
 * @deprecated in v7; controlText_ is not used in non-active display Components
 */
TimeDisplay.prototype.controlText_ = 'Time';

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