1. 1 : /**
  2. 2 : * @file track-list.js
  3. 3 : */
  4. 4 : import EventTarget from '../event-target';
  5. 5 : import {isEvented} from '../mixins/evented';
  6. 6 :
  7. 7 : /**
  8. 8 : * @typedef { import('./track').default } Track
  9. 9 : */
  10. 10 :
  11. 11 : /**
  12. 12 : * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
  13. 13 : * {@link VideoTrackList}
  14. 14 : *
  15. 15 : * @extends EventTarget
  16. 16 : */
  17. 17 : class TrackList extends EventTarget {
  18. 18 : /**
  19. 19 : * Create an instance of this class
  20. 20 : *
  21. 21 : * @param {Track[]} tracks
  22. 22 : * A list of tracks to initialize the list with.
  23. 23 : *
  24. 24 : * @abstract
  25. 25 : */
  26. 26 : constructor(tracks = []) {
  27. 27 : super();
  28. 28 :
  29. 29 : this.tracks_ = [];
  30. 30 :
  31. 31 : /**
  32. 32 : * @memberof TrackList
  33. 33 : * @member {number} length
  34. 34 : * The current number of `Track`s in the this Trackist.
  35. 35 : * @instance
  36. 36 : */
  37. 37 : Object.defineProperty(this, 'length', {
  38. 38 : get() {
  39. 39 : return this.tracks_.length;
  40. 40 : }
  41. 41 : });
  42. 42 :
  43. 43 : for (let i = 0; i < tracks.length; i++) {
  44. 44 : this.addTrack(tracks[i]);
  45. 45 : }
  46. 46 : }
  47. 47 :
  48. 48 : /**
  49. 49 : * Add a {@link Track} to the `TrackList`
  50. 50 : *
  51. 51 : * @param {Track} track
  52. 52 : * The audio, video, or text track to add to the list.
  53. 53 : *
  54. 54 : * @fires TrackList#addtrack
  55. 55 : */
  56. 56 : addTrack(track) {
  57. 57 : const index = this.tracks_.length;
  58. 58 :
  59. 59 : if (!('' + index in this)) {
  60. 60 : Object.defineProperty(this, index, {
  61. 61 : get() {
  62. 62 : return this.tracks_[index];
  63. 63 : }
  64. 64 : });
  65. 65 : }
  66. 66 :
  67. 67 : // Do not add duplicate tracks
  68. 68 : if (this.tracks_.indexOf(track) === -1) {
  69. 69 : this.tracks_.push(track);
  70. 70 : /**
  71. 71 : * Triggered when a track is added to a track list.
  72. 72 : *
  73. 73 : * @event TrackList#addtrack
  74. 74 : * @type {Event}
  75. 75 : * @property {Track} track
  76. 76 : * A reference to track that was added.
  77. 77 : */
  78. 78 : this.trigger({
  79. 79 : track,
  80. 80 : type: 'addtrack',
  81. 81 : target: this
  82. 82 : });
  83. 83 : }
  84. 84 :
  85. 85 : /**
  86. 86 : * Triggered when a track label is changed.
  87. 87 : *
  88. 88 : * @event TrackList#addtrack
  89. 89 : * @type {Event}
  90. 90 : * @property {Track} track
  91. 91 : * A reference to track that was added.
  92. 92 : */
  93. 93 : track.labelchange_ = () => {
  94. 94 : this.trigger({
  95. 95 : track,
  96. 96 : type: 'labelchange',
  97. 97 : target: this
  98. 98 : });
  99. 99 : };
  100. 100 :
  101. 101 : if (isEvented(track)) {
  102. 102 : track.addEventListener('labelchange', track.labelchange_);
  103. 103 : }
  104. 104 : }
  105. 105 :
  106. 106 : /**
  107. 107 : * Remove a {@link Track} from the `TrackList`
  108. 108 : *
  109. 109 : * @param {Track} rtrack
  110. 110 : * The audio, video, or text track to remove from the list.
  111. 111 : *
  112. 112 : * @fires TrackList#removetrack
  113. 113 : */
  114. 114 : removeTrack(rtrack) {
  115. 115 : let track;
  116. 116 :
  117. 117 : for (let i = 0, l = this.length; i < l; i++) {
  118. 118 : if (this[i] === rtrack) {
  119. 119 : track = this[i];
  120. 120 : if (track.off) {
  121. 121 : track.off();
  122. 122 : }
  123. 123 :
  124. 124 : this.tracks_.splice(i, 1);
  125. 125 :
  126. 126 : break;
  127. 127 : }
  128. 128 : }
  129. 129 :
  130. 130 : if (!track) {
  131. 131 : return;
  132. 132 : }
  133. 133 :
  134. 134 : /**
  135. 135 : * Triggered when a track is removed from track list.
  136. 136 : *
  137. 137 : * @event TrackList#removetrack
  138. 138 : * @type {Event}
  139. 139 : * @property {Track} track
  140. 140 : * A reference to track that was removed.
  141. 141 : */
  142. 142 : this.trigger({
  143. 143 : track,
  144. 144 : type: 'removetrack',
  145. 145 : target: this
  146. 146 : });
  147. 147 : }
  148. 148 :
  149. 149 : /**
  150. 150 : * Get a Track from the TrackList by a tracks id
  151. 151 : *
  152. 152 : * @param {string} id - the id of the track to get
  153. 153 : * @method getTrackById
  154. 154 : * @return {Track}
  155. 155 : * @private
  156. 156 : */
  157. 157 : getTrackById(id) {
  158. 158 : let result = null;
  159. 159 :
  160. 160 : for (let i = 0, l = this.length; i < l; i++) {
  161. 161 : const track = this[i];
  162. 162 :
  163. 163 : if (track.id === id) {
  164. 164 : result = track;
  165. 165 : break;
  166. 166 : }
  167. 167 : }
  168. 168 :
  169. 169 : return result;
  170. 170 : }
  171. 171 : }
  172. 172 :
  173. 173 : /**
  174. 174 : * Triggered when a different track is selected/enabled.
  175. 175 : *
  176. 176 : * @event TrackList#change
  177. 177 : * @type {Event}
  178. 178 : */
  179. 179 :
  180. 180 : /**
  181. 181 : * Events that can be called with on + eventName. See {@link EventHandler}.
  182. 182 : *
  183. 183 : * @property {Object} TrackList#allowedEvents_
  184. 184 : * @private
  185. 185 : */
  186. 186 : TrackList.prototype.allowedEvents_ = {
  187. 187 : change: 'change',
  188. 188 : addtrack: 'addtrack',
  189. 189 : removetrack: 'removetrack',
  190. 190 : labelchange: 'labelchange'
  191. 191 : };
  192. 192 :
  193. 193 : // emulate attribute EventHandler support to allow for feature detection
  194. 194 : for (const event in TrackList.prototype.allowedEvents_) {
  195. 195 : TrackList.prototype['on' + event] = null;
  196. 196 : }
  197. 197 :
  198. 198 : export default TrackList;