@playkit-js/rapt 0.4.10-canary.0-878cdb7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,549 @@
1
+ import {Dispatcher} from "./helpers/Dispatcher";
2
+ import {KalturaPlayer, PlayersFactory} from "./PlayersFactory";
3
+ import {Persistency, RaptNode} from "./PlayersManager";
4
+ import {log} from "./helpers/logger";
5
+ import {getInternetExplorerVersion, isWindows7} from "./helpers/detectIe";
6
+
7
+ interface BufferItem {
8
+ player: KalturaPlayer;
9
+ isReady: boolean;
10
+ isRunning: boolean;
11
+ bufferingTimeoutToken: number | null;
12
+ entryId: string;
13
+ startTime?: number;
14
+ }
15
+
16
+ export class PlayersBufferManager extends Dispatcher {
17
+ private shortEntryThreshold: number = 6;
18
+ private bufferThreshold: number = 2.5;
19
+ private isIe11Win7 = getInternetExplorerVersion() === 11 && isWindows7();
20
+ readonly secondsToBuffer: number;
21
+ private bufferList: BufferItem[] = [];
22
+ private persistenceObj: {
23
+ muted?: boolean;
24
+ rate?: number;
25
+ captions?: string;
26
+ volume?: number;
27
+ audio?: string;
28
+ } = {};
29
+ private _isAvailable: boolean;
30
+
31
+ constructor(private raptData: any, private playersFactory: PlayersFactory) {
32
+ super();
33
+ this.secondsToBuffer = playersFactory.secondsToBuffer;
34
+ this.initializeAvailablity();
35
+ }
36
+
37
+ private initializeAvailablity(): void {
38
+ // prevent caching on Safari and IE11-Win7 and if config set to no-cache
39
+ const browser = this.playersFactory.playerLibrary.core.Env.browser.name;
40
+ // TODO 9 will be used later to exclude other OS & browsers
41
+ // const browserVersion = this.playersFactory.playerLibrary.core.Env.major;
42
+ // const os = this.playersFactory.playerLibrary.core.Env.os.name;
43
+ const model =
44
+ this.playersFactory.playerLibrary.core.Env.device &&
45
+ this.playersFactory.playerLibrary.core.Env.device.model; // desktops will be undefined
46
+ this._isAvailable = true;
47
+ // On Safari desktop use pre-buffer, on ipad and iphone - don't because autoplay (defaultPath) needs user-gesture
48
+ if (model === "iPad" || model === "iPhone" || model === "Nexus 9") {
49
+ this._isAvailable = false;
50
+ log("log", "pbm_initializeAvailablity", "disabling prefetch", model);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Look if there is a relevant player that was created already
56
+ * @param playerId
57
+ */
58
+ public getPlayer(
59
+ entryId: string,
60
+ playImmediate: boolean,
61
+ showPoster?: boolean,
62
+ seekTo?: number
63
+ ): KalturaPlayer {
64
+ log("log", "pbm_getPlayer", "executed", {entryId, playImmediate});
65
+ if (!this._isAvailable) {
66
+ throw new Error("BufferManager is not available");
67
+ }
68
+
69
+ let result: KalturaPlayer;
70
+ let bufferedItem = this.bufferList.find(item => item.entryId === entryId);
71
+
72
+ if (bufferedItem) {
73
+ if (bufferedItem.player) {
74
+ log("log", "pbm_getPlayer", "found player in buffer list", {entryId});
75
+ result = bufferedItem.player;
76
+
77
+ log("log", "pbm_getPlayer", "seek player to the beginning or a specific time", {entryId});
78
+ result.player.currentTime = seekTo ? seekTo : 0;
79
+ // TODO 3 [eitan] for persistancy - assign only synced persistancy
80
+ if (playImmediate && !result.player.isPlaying) {
81
+ log("log", "pbm_getPlayer", "execute play command", {entryId});
82
+ result.player.play();
83
+ }
84
+ } else {
85
+ log(
86
+ "log",
87
+ "pbm_getPlayer",
88
+ "player buffered in pending mode, create player",
89
+ {entryId, trackBuffer: playImmediate}
90
+ );
91
+ bufferedItem.player = this.createPlayer(entryId, playImmediate, false, seekTo);
92
+ if (playImmediate) {
93
+ this.trackBufferOfItem(bufferedItem);
94
+ }
95
+ result = bufferedItem.player;
96
+ }
97
+ } else {
98
+ log(
99
+ "log",
100
+ "pbm_getPlayer",
101
+ "not found in buffer list, create player for entry",
102
+ {entryId}
103
+ );
104
+ result = this.createPlayer(entryId, playImmediate, showPoster, seekTo);
105
+ const newItem = {
106
+ entryId: entryId,
107
+ player: result,
108
+ isRunning: playImmediate,
109
+ bufferingTimeoutToken: NaN,
110
+ isReady: false
111
+ };
112
+
113
+ this.bufferList.push(newItem);
114
+
115
+ if (playImmediate) {
116
+ this.trackBufferOfItem(newItem);
117
+ }
118
+ }
119
+
120
+ return result;
121
+ }
122
+
123
+ public isAvailable(): boolean {
124
+ return this._isAvailable;
125
+ }
126
+
127
+ public disable(): void {
128
+ this._isAvailable = false;
129
+ }
130
+
131
+ private createPlayer(
132
+ entryId: string,
133
+ playImmediate: boolean,
134
+ showPoster?: boolean,
135
+ startTime?: number
136
+ ): KalturaPlayer {
137
+ log("log", "pbm_createPlayer", "create player for entry", {
138
+ entryId,
139
+ playImmediate,
140
+ showPoster,
141
+ startTime
142
+ });
143
+
144
+ // TODO 3 [eitan] for persistancy - apply async info
145
+ const kalturaPlayer = this.playersFactory.createPlayer(
146
+ entryId,
147
+ playImmediate,
148
+ this.persistenceObj,
149
+ showPoster,
150
+ startTime
151
+ );
152
+ return kalturaPlayer;
153
+ }
154
+
155
+ /**
156
+ * Anytime a new entry is requested to play (1st node, or on any nodeChange, by user or by defaultPath
157
+ * The function loads/play the nodes entry-player and handles buffering of next optional entries players
158
+ * @param nextNode
159
+ */
160
+ public switchPlayer(nextNode?: RaptNode) {
161
+ if (!this._isAvailable) {
162
+ log(
163
+ "warn",
164
+ "pbm_switchPlayer",
165
+ "buffer manager is disabled, ignore switch player request"
166
+ );
167
+ return;
168
+ }
169
+ if (nextNode) {
170
+ const nodesToBuffer = [nextNode, ...this.getNextNodes(nextNode)];
171
+ this.dispatch({type: "buffer:prebuffer", payload: nodesToBuffer});
172
+ const prevItemsMap = this.bufferList.reduce((acc, item) => {
173
+ acc[item.entryId] = item;
174
+ return acc;
175
+ }, {});
176
+
177
+ const prevItemsCount = this.bufferList.length;
178
+
179
+ this.bufferList = [];
180
+
181
+ log("log", "pbm_switchPlayer", "rebuliding buffer list", {
182
+ prevCount: prevItemsCount,
183
+ newCount: nodesToBuffer.length
184
+ });
185
+ nodesToBuffer.forEach(node => {
186
+ const existinItem = prevItemsMap[node.entryId];
187
+ if (existinItem) {
188
+ log(
189
+ "log",
190
+ "pbm_switchPlayer",
191
+ "entry id was in previous buffer list, copy to current list",
192
+ {entryId: node.entryId}
193
+ );
194
+ this.bufferList.push(existinItem);
195
+ delete prevItemsMap[node.entryId];
196
+ } else {
197
+ log("log", "pbm_switchPlayer", "add entry to buffer queue", {
198
+ entryId: node.entryId
199
+ });
200
+ this.bufferList.push({
201
+ entryId: node.entryId,
202
+ player: null,
203
+ isRunning: false,
204
+ bufferingTimeoutToken: null,
205
+ isReady: false,
206
+ startTime: node.startFrom
207
+ });
208
+ }
209
+ });
210
+ this.destroyBufferedItems(Object.values(prevItemsMap));
211
+ } else {
212
+ this.destroyBufferedItems(this.bufferList);
213
+ }
214
+ // remove duplicity items //todo 4 - prefer items that are better ready (has players, has token or isReady / isRunning
215
+ this.bufferList = this.bufferList.reduce((acc, item) => {
216
+ if (
217
+ !acc.some(
218
+ (bufferItem: BufferItem) => bufferItem.entryId === item.entryId
219
+ )
220
+ ) {
221
+ acc.push(item);
222
+ }
223
+ return acc;
224
+ }, []);
225
+ this.handleBufferList();
226
+ }
227
+
228
+ private handleBufferList(): void {
229
+ log("log", "pbm_handleBufferList", "executed");
230
+ const allReady =
231
+ this.bufferList.length === 0 ||
232
+ this.bufferList.every(item => item.isReady);
233
+ if (allReady) {
234
+ log("log", "pbm_handleBufferList", "all items are buffered", {
235
+ count: this.bufferList.length
236
+ });
237
+ this.dispatch({type: "buffer:allbuffered"});
238
+ return;
239
+ }
240
+
241
+ const isSomeoneBuffering = this.bufferList.some(item => item.isRunning);
242
+ // check if need to buffer an item (has items, no one is running and someone is not ready)
243
+ if (!isSomeoneBuffering) {
244
+ // find the 1st item on the list that is not buffering (list is ordered)
245
+ const unbufferedItem = this.bufferList.find(item => !item.isReady);
246
+ log("log", "pbm_handleBufferList", "start track item", {
247
+ entryId: unbufferedItem.entryId
248
+ });
249
+ this.executeItemBuffering(unbufferedItem);
250
+ } else {
251
+ log(
252
+ "log",
253
+ "pbm_handleBufferList",
254
+ "an item is already in buffer mode, nothing to do"
255
+ );
256
+ }
257
+ }
258
+
259
+ private trackBufferOfItem(item: BufferItem) {
260
+ item.isRunning = true;
261
+ item.bufferingTimeoutToken = window.setTimeout(
262
+ this.executeItemBuffering.bind(this, item),
263
+ 200
264
+ );
265
+ }
266
+
267
+ private executeItemBuffering(item: BufferItem): void {
268
+ if (item.bufferingTimeoutToken >= 100) {
269
+ log(
270
+ "log",
271
+ "pbm_executeItemBuffering",
272
+ "failed to prebuffer - release player",
273
+ {entryId: item.entryId}
274
+ );
275
+ item.isRunning = false;
276
+ item.isReady = true;
277
+ item.bufferingTimeoutToken = null;
278
+ this.dispatch({type: "buffer:bufferend", payload: item.entryId});
279
+ this.handleBufferList();
280
+ // stop! dont continue to loop on this entry. Let it go...
281
+ return;
282
+ }
283
+ item.bufferingTimeoutToken = null;
284
+
285
+ if (!item.isRunning) {
286
+ log("log", "pbm_executeItemBuffering", "start buffering entry", {
287
+ entryId: item.entryId
288
+ });
289
+ this.dispatch({type: "buffer:bufferstart", payload: item.entryId});
290
+ item.player = this.createPlayer(item.entryId, false, false, item.startTime);
291
+ this.trackBufferOfItem(item);
292
+ } else {
293
+ // has player ! find if we have duration
294
+ const player = item.player.player;
295
+ if (
296
+ player.duration &&
297
+ player.duration !== NaN &&
298
+ player.duration < this.shortEntryThreshold
299
+ ) {
300
+ log(
301
+ "log",
302
+ "pbm_executeItemBuffering",
303
+ "buffered not needed, mark entry as ready",
304
+ {entryId: item.entryId}
305
+ );
306
+ item.isReady = true;
307
+ item.isRunning = false;
308
+ this.handleBufferList();
309
+ } else if (
310
+ item.isRunning &&
311
+ player.buffered &&
312
+ player.buffered.length &&
313
+ player.buffered.end
314
+ ) {
315
+ // if we reached here we have something in the buffer. Let's check now if that's enough
316
+ // IE11 on Win7 sometimes has a very low buffer - it gets a special case
317
+ if (player.buffered.end(0) > this.secondsToBuffer - this.bufferThreshold ||
318
+ (this.isIe11Win7 && player.buffered.end(0) > 0.8)) {
319
+ log(
320
+ "log",
321
+ "pbm_executeItemBuffering",
322
+ "buffer completed, mark entry as ready",
323
+ {entryId: item.entryId}
324
+ );
325
+ item.isRunning = false;
326
+ item.isReady = true;
327
+ this.dispatch({type: "buffer:bufferend", payload: item.entryId});
328
+ this.handleBufferList();
329
+ } else {
330
+ this.trackBufferOfItem(item);
331
+ }
332
+ } else {
333
+ this.trackBufferOfItem(item);
334
+ }
335
+ }
336
+
337
+ // if has not running -> set running, create player and set a timeout on that item which will execute executeItemBuffering(item)
338
+ // if item is too short => set is ready and reexecute handlebufferlist
339
+ // if running -> check if ready mark as is ready and not running, and execute handleBufferList, otherwise, reset timeout
340
+ }
341
+
342
+ /**
343
+ * Clear all current players (but the players that are relevant for next node)
344
+ * @param nextNode
345
+ */
346
+ private destroyBufferedItems(items: BufferItem[]) {
347
+ log("log", "pbm_destroyBufferedItems", "executed", {
348
+ entries: items.map(item => item.entryId)
349
+ });
350
+ items.forEach(item => {
351
+ if (item.player) {
352
+ log(
353
+ "log",
354
+ "pbm_destroyBufferedItems",
355
+ "remove entry from buffer queue",
356
+ {entryId: item.entryId}
357
+ );
358
+ // TODO - maybe find a better way to unregister this. Consult Eran when we have more time
359
+ item.player.player.removeEventListener(
360
+ item.player.player.Event.Core.FIRST_PLAY,
361
+ e => this.playersFactory.onFirstPlay(e)
362
+ );
363
+ try {
364
+ item.player.destroy();
365
+ } catch (e) {
366
+ log(
367
+ "log",
368
+ "pbm_destroyBufferedItems",
369
+ "Failed to destroy player ",
370
+ {entryId: item.entryId , error : e}
371
+ );
372
+ }
373
+ }
374
+
375
+ if (item.bufferingTimeoutToken) {
376
+ log(
377
+ "log",
378
+ "pbm_destroyBufferedItems",
379
+ "cancel running buffer for entry",
380
+ {entryId: item.entryId}
381
+ );
382
+ clearTimeout(item.bufferingTimeoutToken);
383
+ }
384
+ });
385
+ }
386
+
387
+ public syncPlayersStatus(
388
+ attribute: Persistency,
389
+ value: number | string | boolean,
390
+ currentPlayer: any
391
+ ) {
392
+ log("log", "pbm_applyToPlayers", "executed", {
393
+ attribute,
394
+ value,
395
+ currentPlayer
396
+ });
397
+ // store to local
398
+ this.persistenceObj[attribute] = value;
399
+
400
+ const availablePlayers = this.bufferList
401
+ .filter(item => item.player)
402
+ .map(item => item.player);
403
+
404
+ availablePlayers.forEach(kalturaPlayer => {
405
+ const player: any = kalturaPlayer.player;
406
+
407
+ // no point applying the change to the player that triggered the change - it causes infinite loops
408
+ if (currentPlayer === player) {
409
+ return;
410
+ }
411
+
412
+ switch (attribute) {
413
+ case Persistency.captions:
414
+ // get current player text-tracks
415
+ const textTracks = player.getTracks(
416
+ this.playersFactory.playerLibrary.core.TrackType.TEXT
417
+ );
418
+ // find the track that has the language that the user selected
419
+ const textTrack = textTracks.find(
420
+ track => track.language === this.persistenceObj.captions
421
+ );
422
+ if (textTrack && textTrack._language !== "off") {
423
+ player.selectTrack(textTrack);
424
+ } else {
425
+ // if we did not find a track or if user selected "off" - turn off the captions
426
+ player.hideTextTrack();
427
+ }
428
+ break;
429
+
430
+ case Persistency.audioTrack:
431
+ // iterate all buffered players
432
+ const audioTracks = player.getTracks(
433
+ this.playersFactory.playerLibrary.core.TrackType.AUDIO
434
+ );
435
+ const audioTrack = audioTracks.find(
436
+ track => track.language === this.persistenceObj.audio
437
+ );
438
+ if (audioTrack) {
439
+ player.selectTrack(audioTrack);
440
+ }
441
+ break;
442
+
443
+ case Persistency.rate:
444
+ player.playbackRate = this.persistenceObj.rate;
445
+ break;
446
+ case Persistency.volume:
447
+ player.volume = this.persistenceObj.volume;
448
+ break;
449
+ case Persistency.mute:
450
+ player.muted = value;
451
+ break;
452
+ case Persistency.textStyle:
453
+ player.textStyle = value;
454
+ break;
455
+ }
456
+ });
457
+ }
458
+
459
+ /**
460
+ * Get a list of optional nodes for the given node
461
+ * @param node
462
+ */
463
+ private getNextNodes(node: RaptNode): RaptNode[] {
464
+ let nodes: RaptNode[] = node.prefetchNodeIds.length
465
+ ? node.prefetchNodeIds.map((nodeId: string) =>
466
+ this.getNodeByRaptId(nodeId)
467
+ )
468
+ : [];
469
+ // at this point we have a list of next nodes without default-path and without order according to appearance time
470
+ nodes = this.sortByApearenceTime(nodes, node);
471
+ return nodes;
472
+ }
473
+
474
+ /**
475
+ * Return a rapt node
476
+ * @param id
477
+ */
478
+ private getNodeByRaptId(id: string): RaptNode {
479
+ const nodes: RaptNode[] = this.raptData.nodes;
480
+ return nodes.find((item: RaptNode) => item.id === id);
481
+ }
482
+
483
+ /**
484
+ * Sort a given nodes-array by the appearance-order of the hotspots in that node
485
+ * @param arr of Nodes
486
+ * @param givenNode
487
+ */
488
+ private sortByApearenceTime(
489
+ arr: RaptNode[],
490
+ givenNode: RaptNode
491
+ ): RaptNode[] {
492
+ // get relevant hotspots (that has the givenNode as 'nodeId' ) and sort them by their showAt time
493
+ const hotspots: any[] = this.raptData.hotspots
494
+ .filter((hotSpot: any) => {
495
+ return (
496
+ hotSpot.nodeId === givenNode.id &&
497
+ hotSpot.onClick &&
498
+ hotSpot.onClick.find((itm: any) => itm.type === "project:jump") // filter out only-URL clicks
499
+ );
500
+ })
501
+ .sort((a: any, b: any) => a.showAt > b.showAt); // sort by appearance time
502
+
503
+
504
+ // find if there is any hotspot that has a startFrom attribute
505
+ const hotspotsWithStartFrom = hotspots.filter((hs: any) => {
506
+ return hs.onClick && hs.onClick.find((itm: any) => typeof itm.payload.startFrom === 'number');
507
+ });
508
+ const arrayToCache: RaptNode[] = [];
509
+ for (const hotSpot of hotspots) {
510
+ arrayToCache.push(
511
+ arr.find((itm: RaptNode) => {
512
+ // extract the onClick element with type 'project:jump'
513
+ const clickItem: any = hotSpot.onClick.find(
514
+ (itm: any) => itm.type === "project:jump"
515
+ );
516
+ return clickItem.payload.destination === itm.id;
517
+ })
518
+ );
519
+ }
520
+ // if there was a default-path on the current node - make sure it is returned as well
521
+ const defaultPathData: any = givenNode.onEnded.find(
522
+ (itm: any) => itm.type === "project:jump"
523
+ );
524
+ if (defaultPathData) {
525
+ const defaultPathNodeId: string = defaultPathData.payload.destination;
526
+ const defaultPathNode: RaptNode = this.raptData.nodes.find(
527
+ n => n.id === defaultPathNodeId
528
+ );
529
+ // add startFrom default path
530
+ if (defaultPathData.payload.startFrom) {
531
+ defaultPathNode.startFrom = defaultPathData.payload.startFrom
532
+ }
533
+ arrayToCache.push(defaultPathNode);
534
+ }
535
+ // add startTime if available
536
+ if (hotspotsWithStartFrom.length > 0) {
537
+ // fill startFrom attributes if they match a hotspot
538
+ for (const hsWithStartTime of hotspotsWithStartFrom) {
539
+ // find if we have a relevant start hotspot
540
+ const jumpingToData = hsWithStartTime.onClick.find((itm: any) => typeof itm.payload.startFrom === 'number').payload;
541
+ const matchingItem = arrayToCache.find((raptNode: RaptNode) => raptNode.id === jumpingToData.destination);
542
+ if (matchingItem) {
543
+ matchingItem.startFrom = jumpingToData.startFrom;
544
+ }
545
+ }
546
+ }
547
+ return arrayToCache;
548
+ }
549
+ }
@@ -0,0 +1,128 @@
1
+ import { createElement } from "./helpers/CreateElement";
2
+ import { KalturaPlayer } from "./PlayersFactory";
3
+ import { log } from "./helpers/logger";
4
+
5
+ export class PlayersDomManager {
6
+ private static raptPlayerCounter = 1;
7
+ private static kalturaPlayerCounter = 1;
8
+
9
+ private namespace: string;
10
+ private raptContainer: HTMLElement;
11
+
12
+ public getContainer() {
13
+ return this.raptContainer;
14
+ }
15
+ // add a class to the main container
16
+ public addClass(className) {
17
+ if (this.getContainer()) {
18
+ this.getContainer().classList.add(className);
19
+ }
20
+ }
21
+ // remove a class from the main container
22
+ public removeClass(className) {
23
+ if (this.getContainer()) {
24
+ this.getContainer().classList.remove(className);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Reparent the rapt layer into the player that is marked as .current-playing
30
+ */
31
+ public reparentRaptLayer(raptLayer:HTMLElement) {
32
+ const mainContainerId = this.getContainer().id;
33
+ const newParent = document.querySelector(
34
+ "#" + mainContainerId + " .current-playing .playkit-container"
35
+ );
36
+ if (newParent) {
37
+ newParent.appendChild(raptLayer);
38
+ }
39
+ }
40
+
41
+ private createElementName(suffix: string) {
42
+ return `${this.namespace}-${suffix}`;
43
+ }
44
+
45
+ public createDomElement(
46
+ type: string,
47
+ idSuffix: string,
48
+ initialClasses?: string
49
+ ): { id: string; domElement: HTMLElement } {
50
+ const newElement = document.createElement(type);
51
+ const newElementId = this.createElementName(idSuffix);
52
+ newElement.setAttribute("id", newElementId);
53
+
54
+ if (initialClasses) {
55
+ newElement.setAttribute("class", initialClasses);
56
+ }
57
+
58
+ this.raptContainer.appendChild(newElement);
59
+ return { id: newElementId, domElement: newElement };
60
+ }
61
+
62
+ constructor(parentId: string) {
63
+ this.createRaptPlayerElement(parentId);
64
+ }
65
+
66
+ public requestFullscreen(): void {
67
+ if (this.raptContainer && this.raptContainer.requestFullscreen) {
68
+ this.raptContainer.requestFullscreen();
69
+ }
70
+ }
71
+
72
+ public changeActivePlayer(player: KalturaPlayer): void {
73
+ log("log", "dm_changeActivePlayer", "executed", { id: player.id });
74
+ const allMarkedPlayers = this.raptContainer.querySelectorAll(
75
+ ".current-playing"
76
+ );
77
+ // IE cannot iterate normally with forEach
78
+ [].forEach.call(allMarkedPlayers, function(div) {
79
+ // do whatever
80
+ div.classList.remove("current-playing");
81
+ });
82
+ if (player) {
83
+ player.container.classList.add("current-playing");
84
+ }
85
+ }
86
+
87
+ public showRaptLayer() {
88
+ const mainContainerId = this.getContainer().id;
89
+ const raptLayer: HTMLElement = document.querySelector(
90
+ "#" + mainContainerId + " .kiv-rapt-engine"
91
+ );
92
+ raptLayer.classList.remove("kiv-hidden");
93
+ raptLayer.focus();
94
+ }
95
+ public hideRaptLayer() {
96
+ const mainContainerId = this.getContainer().id;
97
+ const raptLayer = document.querySelector(
98
+ "#" + mainContainerId + " .kiv-rapt-engine"
99
+ );
100
+ raptLayer.classList.add("kiv-hidden");
101
+ }
102
+
103
+ public createKalturaPlayerContainer(): {
104
+ id: string;
105
+ container: HTMLElement;
106
+ } {
107
+ const containerId = `kaltura-container__${
108
+ PlayersDomManager.kalturaPlayerCounter
109
+ }`;
110
+ PlayersDomManager.kalturaPlayerCounter++;
111
+
112
+ let playerClass = "kiv-player kiv-cache-player";
113
+ const kalturaContainer = createElement("div", containerId, playerClass);
114
+ this.raptContainer.appendChild(kalturaContainer);
115
+
116
+ return { id: containerId, container: kalturaContainer };
117
+ }
118
+
119
+ private createRaptPlayerElement(parentId: string) {
120
+ this.namespace = "kiv-container__" + PlayersDomManager.raptPlayerCounter;
121
+
122
+ // create a top-level container
123
+ this.raptContainer = createElement("div", this.namespace, "kiv-container");
124
+
125
+ PlayersDomManager.raptPlayerCounter++;
126
+ document.getElementById(parentId).appendChild(this.raptContainer);
127
+ }
128
+ }