rx-player 3.30.0-dev.2023020100 → 3.30.0-dev.2023030200
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.
- package/.eslintrc.js +8 -0
- package/CHANGELOG.md +5 -1
- package/VERSION +1 -1
- package/dist/_esm5.processed/compat/browser_detection.d.ts +23 -12
- package/dist/_esm5.processed/compat/browser_detection.js +80 -38
- package/dist/_esm5.processed/compat/can_reuse_media_keys.js +2 -2
- package/dist/_esm5.processed/compat/eme/close_session.js +2 -2
- package/dist/_esm5.processed/compat/event_listeners.js +1 -1
- package/dist/_esm5.processed/config.d.ts +2 -0
- package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +4 -2
- package/dist/_esm5.processed/core/api/debug/buffer_graph.d.ts +28 -0
- package/dist/_esm5.processed/core/api/debug/buffer_graph.js +175 -0
- package/dist/_esm5.processed/core/api/debug/buffer_size_graph.d.ts +10 -0
- package/dist/_esm5.processed/core/api/debug/buffer_size_graph.js +104 -0
- package/dist/_esm5.processed/core/api/debug/constants.d.ts +2 -0
- package/dist/_esm5.processed/core/api/debug/constants.js +2 -0
- package/dist/_esm5.processed/core/api/debug/index.d.ts +2 -0
- package/dist/_esm5.processed/core/api/debug/index.js +2 -0
- package/dist/_esm5.processed/core/api/debug/modules/general_info.d.ts +3 -0
- package/dist/_esm5.processed/core/api/debug/modules/general_info.js +199 -0
- package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.d.ts +4 -0
- package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.js +121 -0
- package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.d.ts +3 -0
- package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.js +35 -0
- package/dist/_esm5.processed/core/api/debug/render.d.ts +3 -0
- package/dist/_esm5.processed/core/api/debug/render.js +32 -0
- package/dist/_esm5.processed/core/api/debug/utils.d.ts +39 -0
- package/dist/_esm5.processed/core/api/debug/utils.js +57 -0
- package/dist/_esm5.processed/core/api/playback_observer.js +3 -2
- package/dist/_esm5.processed/core/api/public_api.d.ts +3 -0
- package/dist/_esm5.processed/core/api/public_api.js +25 -14
- package/dist/_esm5.processed/core/api/utils.js +3 -3
- package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.d.ts +32 -13
- package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.js +28 -7
- package/dist/_esm5.processed/core/decrypt/attach_media_keys.js +1 -1
- package/dist/_esm5.processed/core/decrypt/content_decryptor.js +1 -1
- package/dist/_esm5.processed/core/decrypt/find_key_system.js +33 -24
- package/dist/_esm5.processed/core/decrypt/session_events_listener.js +54 -50
- package/dist/_esm5.processed/core/decrypt/utils/check_key_statuses.js +4 -0
- package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.d.ts +17 -8
- package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.js +10 -6
- package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +5 -4
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +22 -5
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +37 -21
- package/dist/_esm5.processed/core/fetchers/segment/task_prioritizer.js +21 -23
- package/dist/_esm5.processed/core/fetchers/utils/schedule_request.js +17 -7
- package/dist/_esm5.processed/core/init/directfile_content_initializer.js +1 -1
- package/dist/_esm5.processed/core/init/media_source_content_initializer.js +27 -31
- package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.js +2 -2
- package/dist/_esm5.processed/core/init/utils/create_media_source.js +3 -12
- package/dist/_esm5.processed/core/init/utils/end_of_stream.js +6 -3
- package/dist/_esm5.processed/core/init/utils/get_loaded_reference.js +2 -1
- package/dist/_esm5.processed/core/init/utils/initial_seek_and_play.js +9 -5
- package/dist/_esm5.processed/core/init/utils/initialize_content_decryption.js +2 -1
- package/dist/_esm5.processed/core/init/utils/media_duration_updater.js +23 -19
- package/dist/_esm5.processed/core/init/utils/rebuffering_controller.js +1 -1
- package/dist/_esm5.processed/core/init/utils/stream_events_emitter/stream_events_emitter.js +6 -4
- package/dist/_esm5.processed/core/init/utils/throw_on_media_error.js +1 -1
- package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +7 -10
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +4 -2
- package/dist/_esm5.processed/core/segment_buffers/segment_buffers_store.js +13 -9
- package/dist/_esm5.processed/core/stream/adaptation/adaptation_stream.js +25 -16
- package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +26 -12
- package/dist/_esm5.processed/core/stream/period/period_stream.js +11 -10
- package/dist/_esm5.processed/core/stream/representation/representation_stream.js +15 -15
- package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.js +1 -1
- package/dist/_esm5.processed/core/stream/representation/utils/downloading_queue.js +16 -6
- package/dist/_esm5.processed/default_config.d.ts +16 -0
- package/dist/_esm5.processed/default_config.js +19 -0
- package/dist/_esm5.processed/experimental/features/debug_element.d.ts +8 -0
- package/dist/_esm5.processed/experimental/features/debug_element.js +10 -0
- package/dist/_esm5.processed/experimental/features/index.d.ts +1 -0
- package/dist/_esm5.processed/experimental/features/index.js +1 -0
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.js +7 -4
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +8 -4
- package/dist/_esm5.processed/features/features_object.js +1 -0
- package/dist/_esm5.processed/features/initialize_features.js +13 -10
- package/dist/_esm5.processed/features/types.d.ts +3 -0
- package/dist/_esm5.processed/manifest/adaptation.js +4 -0
- package/dist/_esm5.processed/manifest/manifest.js +2 -0
- package/dist/_esm5.processed/manifest/representation.js +11 -4
- package/dist/_esm5.processed/parsers/manifest/dash/common/parse_adaptation_sets.js +105 -137
- package/dist/_esm5.processed/parsers/manifest/dash/common/parse_representations.js +25 -5
- package/dist/_esm5.processed/public_types.d.ts +1 -0
- package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +15 -11
- package/dist/_esm5.processed/transports/dash/low_latency_segment_loader.js +2 -2
- package/dist/_esm5.processed/transports/dash/manifest_parser.js +1 -1
- package/dist/_esm5.processed/transports/dash/segment_loader.js +4 -4
- package/dist/_esm5.processed/transports/local/segment_loader.js +13 -26
- package/dist/_esm5.processed/transports/smooth/segment_loader.js +4 -4
- package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.js +3 -3
- package/dist/_esm5.processed/utils/cancellable_sleep.js +4 -10
- package/dist/_esm5.processed/utils/create_cancellable_promise.d.ts +26 -0
- package/dist/_esm5.processed/utils/create_cancellable_promise.js +52 -0
- package/dist/_esm5.processed/utils/reference.js +6 -0
- package/dist/_esm5.processed/utils/request/xhr.js +1 -1
- package/dist/_esm5.processed/utils/task_canceller.d.ts +34 -15
- package/dist/_esm5.processed/utils/task_canceller.js +55 -22
- package/dist/rx-player.js +880 -677
- package/dist/rx-player.min.js +1 -1
- package/jest.config.js +1 -0
- package/package.json +19 -18
- package/scripts/build/constants.d.ts +1 -0
- package/scripts/report_build_sizes +4 -0
- package/sonar-project.properties +1 -1
- package/src/compat/browser_detection.ts +99 -52
- package/src/compat/can_reuse_media_keys.ts +5 -2
- package/src/compat/eme/close_session.ts +2 -2
- package/src/compat/event_listeners.ts +1 -1
- package/src/core/adaptive/adaptive_representation_selector.ts +4 -2
- package/src/core/api/debug/buffer_graph.ts +247 -0
- package/src/core/api/debug/buffer_size_graph.ts +130 -0
- package/src/core/api/debug/constants.ts +2 -0
- package/src/core/api/debug/index.ts +3 -0
- package/src/core/api/debug/modules/general_info.ts +204 -0
- package/src/core/api/debug/modules/segment_buffer_content.ts +155 -0
- package/src/core/api/debug/modules/segment_buffer_size.ts +48 -0
- package/src/core/api/debug/render.ts +40 -0
- package/src/core/api/debug/utils.ts +103 -0
- package/src/core/api/playback_observer.ts +4 -2
- package/src/core/api/public_api.ts +28 -14
- package/src/core/api/utils.ts +3 -3
- package/src/core/decrypt/__tests__/__global__/utils.ts +61 -40
- package/src/core/decrypt/attach_media_keys.ts +1 -1
- package/src/core/decrypt/content_decryptor.ts +1 -1
- package/src/core/decrypt/find_key_system.ts +36 -35
- package/src/core/decrypt/session_events_listener.ts +57 -56
- package/src/core/decrypt/utils/check_key_statuses.ts +6 -0
- package/src/core/fetchers/cdn_prioritizer.ts +18 -9
- package/src/core/fetchers/manifest/manifest_fetcher.ts +5 -4
- package/src/core/fetchers/segment/segment_fetcher.ts +36 -14
- package/src/core/fetchers/segment/task_prioritizer.ts +25 -30
- package/src/core/fetchers/utils/schedule_request.ts +18 -7
- package/src/core/init/directfile_content_initializer.ts +1 -1
- package/src/core/init/media_source_content_initializer.ts +37 -41
- package/src/core/init/utils/content_time_boundaries_observer.ts +2 -2
- package/src/core/init/utils/create_media_source.ts +4 -16
- package/src/core/init/utils/end_of_stream.ts +6 -3
- package/src/core/init/utils/get_loaded_reference.ts +2 -1
- package/src/core/init/utils/initial_seek_and_play.ts +9 -5
- package/src/core/init/utils/initialize_content_decryption.ts +2 -1
- package/src/core/init/utils/media_duration_updater.ts +25 -20
- package/src/core/init/utils/rebuffering_controller.ts +1 -1
- package/src/core/init/utils/stream_events_emitter/stream_events_emitter.ts +6 -4
- package/src/core/init/utils/throw_on_media_error.ts +1 -1
- package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +7 -11
- package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +4 -2
- package/src/core/segment_buffers/segment_buffers_store.ts +16 -13
- package/src/core/stream/adaptation/adaptation_stream.ts +28 -19
- package/src/core/stream/orchestrator/stream_orchestrator.ts +26 -12
- package/src/core/stream/period/period_stream.ts +11 -10
- package/src/core/stream/representation/representation_stream.ts +15 -15
- package/src/core/stream/representation/utils/append_segment_to_buffer.ts +1 -1
- package/src/core/stream/representation/utils/downloading_queue.ts +16 -4
- package/src/default_config.ts +30 -9
- package/src/experimental/features/__tests__/debug_element.test.ts +26 -0
- package/src/experimental/features/debug_element.ts +13 -0
- package/src/experimental/features/index.ts +1 -0
- package/src/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.ts +7 -4
- package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +8 -4
- package/src/features/__tests__/initialize_features.test.ts +11 -0
- package/src/features/features_object.ts +1 -0
- package/src/features/initialize_features.ts +15 -10
- package/src/features/types.ts +9 -0
- package/src/manifest/adaptation.ts +7 -0
- package/src/manifest/manifest.ts +4 -0
- package/src/manifest/representation.ts +10 -4
- package/src/parsers/manifest/dash/common/parse_adaptation_sets.ts +116 -151
- package/src/parsers/manifest/dash/common/parse_representations.ts +21 -4
- package/src/public_types.ts +1 -0
- package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +31 -22
- package/src/transports/dash/low_latency_segment_loader.ts +2 -2
- package/src/transports/dash/manifest_parser.ts +1 -1
- package/src/transports/dash/segment_loader.ts +4 -4
- package/src/transports/local/segment_loader.ts +14 -30
- package/src/transports/smooth/segment_loader.ts +4 -4
- package/src/transports/utils/call_custom_manifest_loader.ts +3 -3
- package/src/typings/globals.d.ts +2 -0
- package/src/utils/cancellable_sleep.ts +5 -14
- package/src/utils/create_cancellable_promise.ts +69 -0
- package/src/utils/reference.ts +6 -0
- package/src/utils/request/xhr.ts +1 -1
- package/src/utils/task_canceller.ts +63 -34
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maximum history of the buffer size that will be displayed, in milliseconds.
|
|
3
|
+
* For example, a value of `3000` indicates that we will just show at most the
|
|
4
|
+
* buffer size evolution during the last 3 seconds.
|
|
5
|
+
*/
|
|
6
|
+
const TIME_SAMPLES_MS = 30000;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* At minimum, that value will be taken in the chart as a maximum buffer size,
|
|
10
|
+
* in seconds.
|
|
11
|
+
* If samples go higher than this size, the chart will adapt automatically to
|
|
12
|
+
* a higher scale.
|
|
13
|
+
* However if values go below that value, the chart won't scale down more than
|
|
14
|
+
* this.
|
|
15
|
+
*/
|
|
16
|
+
const MINIMUM_MAX_BUFFER_SIZE = 20;
|
|
17
|
+
|
|
18
|
+
export default class BufferSizeGraph {
|
|
19
|
+
private _history : IHistoryItem[];
|
|
20
|
+
|
|
21
|
+
/** Canvas that will contain the buffer size graph itself. */
|
|
22
|
+
private readonly _canvasElt : HTMLCanvasElement;
|
|
23
|
+
|
|
24
|
+
private readonly _canvasCtxt : CanvasRenderingContext2D | null;
|
|
25
|
+
|
|
26
|
+
constructor(canvasElt : HTMLCanvasElement) {
|
|
27
|
+
this._canvasElt = canvasElt;
|
|
28
|
+
this._canvasCtxt = this._canvasElt.getContext("2d");
|
|
29
|
+
this._history = [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public pushBufferSize(bufferSize : number) : void {
|
|
33
|
+
const now = performance.now();
|
|
34
|
+
this._history.push({ timestamp: now, bufferSize });
|
|
35
|
+
if (this._history.length > 0) {
|
|
36
|
+
const minimumTime = now - TIME_SAMPLES_MS;
|
|
37
|
+
let i;
|
|
38
|
+
for (i = this._history.length - 1; i >= 1; i--) {
|
|
39
|
+
if (this._history[i].timestamp <= minimumTime) {
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
this._history = this._history.slice(i);
|
|
44
|
+
} else {
|
|
45
|
+
this._history = [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public clear() {
|
|
50
|
+
if (this._canvasCtxt !== null) {
|
|
51
|
+
this._canvasCtxt.clearRect(0, 0, this._canvasElt.width, this._canvasElt.height);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public reRender(width : number, height : number) : void {
|
|
56
|
+
this._canvasElt.style.width = `${width}px`;
|
|
57
|
+
this._canvasElt.style.height = `${height}px`;
|
|
58
|
+
this._canvasElt.width = width;
|
|
59
|
+
this._canvasElt.height = height;
|
|
60
|
+
this.clear();
|
|
61
|
+
const history = this._history;
|
|
62
|
+
const canvasCtx = this._canvasCtxt;
|
|
63
|
+
|
|
64
|
+
if (history.length === 0) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const currentMaxSize = getNewMaxBufferSize();
|
|
69
|
+
const minDate = history[0].timestamp;
|
|
70
|
+
|
|
71
|
+
const gridHeight = height / currentMaxSize;
|
|
72
|
+
const gridWidth = width / TIME_SAMPLES_MS;
|
|
73
|
+
|
|
74
|
+
drawData();
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get more appropriate maximum buffer size to put on top of the graph
|
|
78
|
+
* according to current history.
|
|
79
|
+
*/
|
|
80
|
+
function getNewMaxBufferSize() {
|
|
81
|
+
const maxPoint = Math.max(...history.map(d => d.bufferSize));
|
|
82
|
+
return Math.max(maxPoint + 5, MINIMUM_MAX_BUFFER_SIZE);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Draw all data contained in `history` in the canvas given.
|
|
87
|
+
*/
|
|
88
|
+
function drawData() {
|
|
89
|
+
if (canvasCtx === null) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
canvasCtx.beginPath();
|
|
93
|
+
canvasCtx.fillStyle = "rgb(200, 100, 200)";
|
|
94
|
+
for (let i = 1; i < history.length; i++) {
|
|
95
|
+
const diff = dateToX(history[i].timestamp) -
|
|
96
|
+
dateToX(history[i - 1].timestamp);
|
|
97
|
+
const y = height - bufferValueToHeight(history[i].bufferSize);
|
|
98
|
+
canvasCtx.fillRect(
|
|
99
|
+
dateToX(history[i - 1].timestamp),
|
|
100
|
+
y,
|
|
101
|
+
diff,
|
|
102
|
+
height);
|
|
103
|
+
}
|
|
104
|
+
canvasCtx.stroke();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Convert a value of a given data point, to a u coordinate in the canvas.
|
|
109
|
+
* @param {number} bufferVal - Value to convert
|
|
110
|
+
* @returns {number} - y coordinate
|
|
111
|
+
*/
|
|
112
|
+
function bufferValueToHeight(bufferVal : number) : number {
|
|
113
|
+
return height - (currentMaxSize - bufferVal) * gridHeight;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Convert a date of a given data point, to a x coordinate in the canvas.
|
|
118
|
+
* @param {number} date - Date to convert, in milliseconds
|
|
119
|
+
* @returns {number} - x coordinate
|
|
120
|
+
*/
|
|
121
|
+
function dateToX(date : number) : number {
|
|
122
|
+
return (date - minDate) * gridWidth;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
interface IHistoryItem {
|
|
128
|
+
timestamp : number;
|
|
129
|
+
bufferSize : number;
|
|
130
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { CancellationSignal } from "../../../../utils/task_canceller";
|
|
2
|
+
import RxPlayer from "../../public_api";
|
|
3
|
+
import { DEFAULT_REFRESH_INTERVAL } from "../constants";
|
|
4
|
+
import {
|
|
5
|
+
createCompositeElement,
|
|
6
|
+
createElement,
|
|
7
|
+
createMetricTitle,
|
|
8
|
+
isExtendedMode,
|
|
9
|
+
} from "../utils";
|
|
10
|
+
|
|
11
|
+
export default function constructDebugGeneralInfo(
|
|
12
|
+
instance : RxPlayer,
|
|
13
|
+
parentElt : HTMLElement,
|
|
14
|
+
cancelSignal : CancellationSignal
|
|
15
|
+
) : HTMLElement {
|
|
16
|
+
const generalInfoElt = createElement("div");
|
|
17
|
+
const adaptationsElt = createElement("div");
|
|
18
|
+
const representationsElt = createElement("div");
|
|
19
|
+
updateGeneralInfo();
|
|
20
|
+
const generalInfoItv = setInterval(() => {
|
|
21
|
+
updateGeneralInfo();
|
|
22
|
+
}, DEFAULT_REFRESH_INTERVAL);
|
|
23
|
+
cancelSignal.register(() => {
|
|
24
|
+
clearInterval(generalInfoItv);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return createCompositeElement("div", [
|
|
28
|
+
generalInfoElt,
|
|
29
|
+
adaptationsElt,
|
|
30
|
+
representationsElt,
|
|
31
|
+
]);
|
|
32
|
+
function updateGeneralInfo() {
|
|
33
|
+
const videoElement = instance.getVideoElement();
|
|
34
|
+
if (videoElement === null) {
|
|
35
|
+
// disposed player. Clean-up everything
|
|
36
|
+
generalInfoElt.innerHTML = "";
|
|
37
|
+
adaptationsElt.innerHTML = "";
|
|
38
|
+
representationsElt.innerHTML = "";
|
|
39
|
+
clearInterval(generalInfoItv);
|
|
40
|
+
return;
|
|
41
|
+
} else {
|
|
42
|
+
const currentTime = instance.getPosition();
|
|
43
|
+
const bufferGap = instance.getVideoBufferGap();
|
|
44
|
+
const bufferGapStr = bufferGap === Infinity ? "0" : bufferGap.toFixed(2);
|
|
45
|
+
const valuesLine1 : Array<[string, string]> = [
|
|
46
|
+
["ct", currentTime.toFixed(2)],
|
|
47
|
+
["bg", bufferGapStr],
|
|
48
|
+
["rs", String(videoElement.readyState)],
|
|
49
|
+
["pr", String(videoElement.playbackRate)],
|
|
50
|
+
["sp", String(instance.getPlaybackRate())],
|
|
51
|
+
["pa", String(videoElement.paused ? 1 : 0)],
|
|
52
|
+
["en", String(videoElement.ended ? 1 : 0)],
|
|
53
|
+
["li", String(instance.isLive() ? 1 : 0)],
|
|
54
|
+
["wba", String(instance.getWantedBufferAhead())],
|
|
55
|
+
["st", `"${instance.getPlayerState()}"`],
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const valuesLine2 : Array<[string, string]> = [];
|
|
59
|
+
const ks = instance.getKeySystemConfiguration();
|
|
60
|
+
if (ks !== null) {
|
|
61
|
+
valuesLine2.push(["ks", ks.keySystem]);
|
|
62
|
+
}
|
|
63
|
+
const mbb = instance.getMaxBufferBehind();
|
|
64
|
+
if (mbb !== Infinity) {
|
|
65
|
+
valuesLine2.push(["mbb", String(mbb)]);
|
|
66
|
+
}
|
|
67
|
+
const mba = instance.getMaxBufferAhead();
|
|
68
|
+
if (mba !== Infinity) {
|
|
69
|
+
valuesLine2.push(["mba", String(mba)]);
|
|
70
|
+
}
|
|
71
|
+
const mia = instance.getMinAudioBitrate();
|
|
72
|
+
if (mia !== 0) {
|
|
73
|
+
valuesLine2.push(["mia", String(mia)]);
|
|
74
|
+
}
|
|
75
|
+
const miv = instance.getMinVideoBitrate();
|
|
76
|
+
if (miv !== 0) {
|
|
77
|
+
valuesLine2.push(["miv", String(miv)]);
|
|
78
|
+
}
|
|
79
|
+
const maa = instance.getMaxAudioBitrate();
|
|
80
|
+
if (maa !== Infinity) {
|
|
81
|
+
valuesLine2.push(["maa", String(maa)]);
|
|
82
|
+
}
|
|
83
|
+
const mav = instance.getMaxVideoBitrate();
|
|
84
|
+
if (mav !== Infinity) {
|
|
85
|
+
valuesLine2.push(["mav", String(mav)]);
|
|
86
|
+
}
|
|
87
|
+
const fab = instance.getManualAudioBitrate();
|
|
88
|
+
if (fab >= 0) {
|
|
89
|
+
valuesLine2.push(["fab", String(fab)]);
|
|
90
|
+
}
|
|
91
|
+
const fvb = instance.getManualVideoBitrate();
|
|
92
|
+
if (fvb >= 0) {
|
|
93
|
+
valuesLine2.push(["fvb", String(fvb)]);
|
|
94
|
+
}
|
|
95
|
+
const mbs = instance.getMaxVideoBufferSize();
|
|
96
|
+
if (mbs !== Infinity) {
|
|
97
|
+
valuesLine2.push(["mbs", String(mbs)]);
|
|
98
|
+
}
|
|
99
|
+
const minPos = instance.getMinimumPosition();
|
|
100
|
+
if (minPos !== null) {
|
|
101
|
+
valuesLine1.push(["mip", minPos.toFixed(2)]);
|
|
102
|
+
valuesLine2.push(["dmi", (currentTime - minPos).toFixed(2)]);
|
|
103
|
+
}
|
|
104
|
+
const maxPos = instance.getMaximumPosition();
|
|
105
|
+
if (maxPos !== null) {
|
|
106
|
+
valuesLine1.push(["map", maxPos.toFixed(2)]);
|
|
107
|
+
valuesLine2.push(["dma", (maxPos - currentTime).toFixed(2)]);
|
|
108
|
+
}
|
|
109
|
+
const valuesLine3 : Array<[string, string]> = [];
|
|
110
|
+
const error = instance.getError();
|
|
111
|
+
if (error !== null) {
|
|
112
|
+
valuesLine3.push(["er", `"${String(error)}"`]);
|
|
113
|
+
}
|
|
114
|
+
generalInfoElt.innerHTML = "";
|
|
115
|
+
for (const valueSet of [valuesLine1, valuesLine2, valuesLine3]) {
|
|
116
|
+
if (valueSet.length > 0) {
|
|
117
|
+
const lineInfoElt = createElement("div");
|
|
118
|
+
for (const value of valueSet) {
|
|
119
|
+
lineInfoElt.appendChild(createMetricTitle(value[0]));
|
|
120
|
+
lineInfoElt.appendChild(createElement("span", {
|
|
121
|
+
textContent: value[1] + " ",
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
generalInfoElt.appendChild(lineInfoElt);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (isExtendedMode(parentElt)) {
|
|
128
|
+
const url = instance.getUrl();
|
|
129
|
+
if (url !== undefined) {
|
|
130
|
+
const reducedUrl = url.length > 100 ?
|
|
131
|
+
url.substring(0, 99) + "…" :
|
|
132
|
+
url;
|
|
133
|
+
|
|
134
|
+
generalInfoElt.appendChild(createCompositeElement("div", [
|
|
135
|
+
createMetricTitle("url"),
|
|
136
|
+
createElement("span", {
|
|
137
|
+
textContent: reducedUrl,
|
|
138
|
+
}),
|
|
139
|
+
]));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (isExtendedMode(parentElt)) {
|
|
144
|
+
const videoId = instance.getAvailableVideoTracks().map(({ id, active }) =>
|
|
145
|
+
active ? `*${id}` : id);
|
|
146
|
+
const audioId = instance.getAvailableAudioTracks().map(({ id, active }) =>
|
|
147
|
+
active ? `*${id}` : id);
|
|
148
|
+
const textId = instance.getAvailableTextTracks().map(({ id, active }) =>
|
|
149
|
+
active ? `*${id}` : id);
|
|
150
|
+
adaptationsElt.innerHTML = "";
|
|
151
|
+
if (videoId.length > 0) {
|
|
152
|
+
let textContent = `${videoId.length}:${videoId.join(" ")} `;
|
|
153
|
+
if (textContent.length > 100) {
|
|
154
|
+
textContent = textContent.substring(0, 98) + "… ";
|
|
155
|
+
}
|
|
156
|
+
const videoAdaps = createCompositeElement("div", [
|
|
157
|
+
createMetricTitle("vt"),
|
|
158
|
+
createElement("span", { textContent }),
|
|
159
|
+
]);
|
|
160
|
+
adaptationsElt.appendChild(videoAdaps);
|
|
161
|
+
}
|
|
162
|
+
if (audioId.length > 0) {
|
|
163
|
+
let textContent = `${audioId.length}:${audioId.join(" ")} `;
|
|
164
|
+
if (textContent.length > 100) {
|
|
165
|
+
textContent = textContent.substring(0, 98) + "… ";
|
|
166
|
+
}
|
|
167
|
+
const audioAdaps = createCompositeElement("div", [
|
|
168
|
+
createMetricTitle("at"),
|
|
169
|
+
createElement("span", { textContent }),
|
|
170
|
+
]);
|
|
171
|
+
adaptationsElt.appendChild(audioAdaps);
|
|
172
|
+
}
|
|
173
|
+
if (textId.length > 0) {
|
|
174
|
+
let textContent = `${textId.length}:${textId.join(" ")} `;
|
|
175
|
+
if (textContent.length > 100) {
|
|
176
|
+
textContent = textContent.substring(0, 98) + "… ";
|
|
177
|
+
}
|
|
178
|
+
const textAdaps = createCompositeElement("div", [
|
|
179
|
+
createMetricTitle("tt"),
|
|
180
|
+
createElement("span", { textContent }),
|
|
181
|
+
]);
|
|
182
|
+
adaptationsElt.appendChild(textAdaps);
|
|
183
|
+
}
|
|
184
|
+
const videoBitrates = instance.getAvailableVideoBitrates();
|
|
185
|
+
const audioBitrates = instance.getAvailableAudioBitrates();
|
|
186
|
+
representationsElt.innerHTML = "";
|
|
187
|
+
if (videoBitrates.length > 0) {
|
|
188
|
+
representationsElt.appendChild(createMetricTitle("vb"));
|
|
189
|
+
representationsElt.appendChild(createElement("span", {
|
|
190
|
+
textContent: videoBitrates.join(" ") + " ",
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
if (audioBitrates.length > 0) {
|
|
194
|
+
representationsElt.appendChild(createMetricTitle("ab"));
|
|
195
|
+
representationsElt.appendChild(createElement("span", {
|
|
196
|
+
textContent: audioBitrates.join(" ") + " ",
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
adaptationsElt.innerHTML = "";
|
|
201
|
+
representationsElt.innerHTML = "";
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IAdaptation,
|
|
3
|
+
IPeriod,
|
|
4
|
+
IRepresentation,
|
|
5
|
+
} from "../../../../public_types";
|
|
6
|
+
import isNullOrUndefined from "../../../../utils/is_null_or_undefined";
|
|
7
|
+
import { CancellationSignal } from "../../../../utils/task_canceller";
|
|
8
|
+
import { IBufferType } from "../../../segment_buffers";
|
|
9
|
+
import RxPlayer from "../../public_api";
|
|
10
|
+
import SegmentBufferGraph from "../buffer_graph";
|
|
11
|
+
import { DEFAULT_REFRESH_INTERVAL } from "../constants";
|
|
12
|
+
import {
|
|
13
|
+
createElement,
|
|
14
|
+
createGraphCanvas,
|
|
15
|
+
createMetricTitle,
|
|
16
|
+
isExtendedMode,
|
|
17
|
+
} from "../utils";
|
|
18
|
+
|
|
19
|
+
export default function createSegmentBufferGraph(
|
|
20
|
+
instance : RxPlayer,
|
|
21
|
+
bufferType : IBufferType,
|
|
22
|
+
title : string,
|
|
23
|
+
parentElt : HTMLElement,
|
|
24
|
+
cancelSignal : CancellationSignal
|
|
25
|
+
) : HTMLElement {
|
|
26
|
+
const bufferGraphWrapper = createElement("div");
|
|
27
|
+
const bufferTitle = createMetricTitle(title);
|
|
28
|
+
const canvasElt = createGraphCanvas();
|
|
29
|
+
const currentRangeRepInfoElt = createElement("div");
|
|
30
|
+
const loadingRangeRepInfoElt = createElement("div");
|
|
31
|
+
const bufferGraph = new SegmentBufferGraph(canvasElt);
|
|
32
|
+
const intervalId = setInterval(update, DEFAULT_REFRESH_INTERVAL);
|
|
33
|
+
cancelSignal.register(() => {
|
|
34
|
+
clearInterval(intervalId);
|
|
35
|
+
});
|
|
36
|
+
bufferGraphWrapper.appendChild(bufferTitle);
|
|
37
|
+
bufferGraphWrapper.appendChild(canvasElt);
|
|
38
|
+
bufferGraphWrapper.appendChild(currentRangeRepInfoElt);
|
|
39
|
+
bufferGraphWrapper.appendChild(loadingRangeRepInfoElt);
|
|
40
|
+
bufferGraphWrapper.style.padding = "5px 0px";
|
|
41
|
+
update();
|
|
42
|
+
return bufferGraphWrapper;
|
|
43
|
+
|
|
44
|
+
function update() {
|
|
45
|
+
if (instance.getVideoElement() === null) {
|
|
46
|
+
// disposed player. Clean-up everything
|
|
47
|
+
bufferGraphWrapper.style.display = "none";
|
|
48
|
+
bufferGraphWrapper.innerHTML = "";
|
|
49
|
+
clearInterval(intervalId);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const showAllInfo = isExtendedMode(parentElt);
|
|
53
|
+
const inventory = instance.__priv_getSegmentBufferContent(bufferType);
|
|
54
|
+
if (inventory === null) {
|
|
55
|
+
bufferGraphWrapper.style.display = "none";
|
|
56
|
+
currentRangeRepInfoElt.innerHTML = "";
|
|
57
|
+
loadingRangeRepInfoElt.innerHTML = "";
|
|
58
|
+
} else {
|
|
59
|
+
bufferGraphWrapper.style.display = "block";
|
|
60
|
+
const currentTime = instance.getPosition();
|
|
61
|
+
const width = Math.min(parentElt.clientWidth - 150, 600);
|
|
62
|
+
bufferGraph.update({
|
|
63
|
+
currentTime,
|
|
64
|
+
minimumPosition: instance.getMinimumPosition() ?? undefined,
|
|
65
|
+
maximumPosition: instance.getMaximumPosition() ?? undefined,
|
|
66
|
+
inventory,
|
|
67
|
+
width,
|
|
68
|
+
height: 10,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (!showAllInfo) {
|
|
72
|
+
currentRangeRepInfoElt.innerHTML = "";
|
|
73
|
+
loadingRangeRepInfoElt.innerHTML = "";
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
currentRangeRepInfoElt.innerHTML = "";
|
|
78
|
+
for (let i = 0; i < inventory.length; i++) {
|
|
79
|
+
const rangeInfo = inventory[i];
|
|
80
|
+
const { bufferedStart, bufferedEnd, infos } = rangeInfo;
|
|
81
|
+
if (bufferedStart !== undefined && bufferedEnd !== undefined &&
|
|
82
|
+
currentTime >= bufferedStart && currentTime < bufferedEnd)
|
|
83
|
+
{
|
|
84
|
+
currentRangeRepInfoElt.appendChild(createMetricTitle("play"));
|
|
85
|
+
currentRangeRepInfoElt.appendChild(createElement("span", {
|
|
86
|
+
textContent: constructRepresentationInfo(infos),
|
|
87
|
+
}));
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
loadingRangeRepInfoElt.innerHTML = "";
|
|
93
|
+
const rep = instance.getCurrentRepresentations()?.[bufferType];
|
|
94
|
+
const adap = instance.getCurrentAdaptations()?.[bufferType];
|
|
95
|
+
const manifest = instance.getManifest();
|
|
96
|
+
if (manifest !== null && !isNullOrUndefined(rep) && !isNullOrUndefined(adap)) {
|
|
97
|
+
const period = manifest.getPeriodForTime(currentTime);
|
|
98
|
+
if (period !== undefined) {
|
|
99
|
+
loadingRangeRepInfoElt.appendChild(createMetricTitle("load"));
|
|
100
|
+
loadingRangeRepInfoElt.appendChild(createElement("span", {
|
|
101
|
+
textContent: constructRepresentationInfo({
|
|
102
|
+
period,
|
|
103
|
+
adaptation: adap,
|
|
104
|
+
representation: rep,
|
|
105
|
+
}),
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function constructRepresentationInfo(
|
|
114
|
+
content : {
|
|
115
|
+
period : IPeriod;
|
|
116
|
+
adaptation : IAdaptation;
|
|
117
|
+
representation : IRepresentation;
|
|
118
|
+
}
|
|
119
|
+
) : string {
|
|
120
|
+
const period = content.period;
|
|
121
|
+
const { language,
|
|
122
|
+
isAudioDescription,
|
|
123
|
+
isClosedCaption,
|
|
124
|
+
isTrickModeTrack,
|
|
125
|
+
isSignInterpreted,
|
|
126
|
+
type: bufferType } = content.adaptation;
|
|
127
|
+
const { id, height, width, bitrate, codec } = content.representation;
|
|
128
|
+
let representationInfo = `"${id}" `;
|
|
129
|
+
if (height !== undefined && width !== undefined) {
|
|
130
|
+
representationInfo += `${width}x${height} `;
|
|
131
|
+
}
|
|
132
|
+
if (bitrate !== undefined) {
|
|
133
|
+
representationInfo += `(${(bitrate / 1000).toFixed(0)}kbps) `;
|
|
134
|
+
}
|
|
135
|
+
if (codec !== undefined) {
|
|
136
|
+
representationInfo += `c:"${codec}" `;
|
|
137
|
+
}
|
|
138
|
+
if (language !== undefined) {
|
|
139
|
+
representationInfo += `l:"${language}" `;
|
|
140
|
+
}
|
|
141
|
+
if (bufferType === "video" && typeof isSignInterpreted === "boolean") {
|
|
142
|
+
representationInfo += `si:${isSignInterpreted ? 1 : 0} `;
|
|
143
|
+
}
|
|
144
|
+
if (bufferType === "video" && typeof isTrickModeTrack === "boolean") {
|
|
145
|
+
representationInfo += `tm:${isTrickModeTrack ? 1 : 0} `;
|
|
146
|
+
}
|
|
147
|
+
if (bufferType === "audio" && typeof isAudioDescription === "boolean") {
|
|
148
|
+
representationInfo += `ad:${isAudioDescription ? 1 : 0} `;
|
|
149
|
+
}
|
|
150
|
+
if (bufferType === "text" && typeof isClosedCaption === "boolean") {
|
|
151
|
+
representationInfo += `cc:${isClosedCaption ? 1 : 0} `;
|
|
152
|
+
}
|
|
153
|
+
representationInfo += `p:${period.start}-${period.end ?? "?"}`;
|
|
154
|
+
return representationInfo;
|
|
155
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CancellationSignal } from "../../../../utils/task_canceller";
|
|
2
|
+
import RxPlayer from "../../public_api";
|
|
3
|
+
import BufferSizeGraph from "../buffer_size_graph";
|
|
4
|
+
import { DEFAULT_REFRESH_INTERVAL } from "../constants";
|
|
5
|
+
import {
|
|
6
|
+
createElement,
|
|
7
|
+
createGraphCanvas,
|
|
8
|
+
createMetricTitle,
|
|
9
|
+
} from "../utils";
|
|
10
|
+
|
|
11
|
+
export default function createSegmentBufferSizeGraph(
|
|
12
|
+
instance : RxPlayer,
|
|
13
|
+
parentElt : HTMLElement,
|
|
14
|
+
cancelSignal : CancellationSignal
|
|
15
|
+
) : HTMLElement {
|
|
16
|
+
const bufferSizeGraphWrapperElt = createElement("div");
|
|
17
|
+
const bufferSizeTitle = createMetricTitle("bgap");
|
|
18
|
+
const canvasElt = createGraphCanvas();
|
|
19
|
+
|
|
20
|
+
const bufferSizeGraph = new BufferSizeGraph(canvasElt);
|
|
21
|
+
const intervalId = setInterval(addBufferSize, DEFAULT_REFRESH_INTERVAL);
|
|
22
|
+
cancelSignal.register(() => {
|
|
23
|
+
clearInterval(intervalId);
|
|
24
|
+
});
|
|
25
|
+
bufferSizeGraphWrapperElt.appendChild(bufferSizeTitle);
|
|
26
|
+
bufferSizeGraphWrapperElt.appendChild(canvasElt);
|
|
27
|
+
bufferSizeGraphWrapperElt.style.padding = "7px 0px";
|
|
28
|
+
addBufferSize();
|
|
29
|
+
return bufferSizeGraphWrapperElt;
|
|
30
|
+
|
|
31
|
+
function addBufferSize() {
|
|
32
|
+
if (instance.getVideoElement() === null) {
|
|
33
|
+
// disposed player. Clean-up everything
|
|
34
|
+
bufferSizeGraphWrapperElt.innerHTML = "";
|
|
35
|
+
clearInterval(intervalId);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const bufferGap = instance.getVideoBufferGap();
|
|
39
|
+
if (bufferGap === Infinity) {
|
|
40
|
+
bufferSizeGraph.pushBufferSize(0);
|
|
41
|
+
} else {
|
|
42
|
+
bufferSizeGraph.pushBufferSize(bufferGap);
|
|
43
|
+
}
|
|
44
|
+
const width = Math.min(parentElt.clientWidth - 150, 600);
|
|
45
|
+
bufferSizeGraph.reRender(width, 10);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CancellationSignal } from "../../../utils/task_canceller";
|
|
2
|
+
import RxPlayer from "../public_api";
|
|
3
|
+
import constructDebugGeneralInfo from "./modules/general_info";
|
|
4
|
+
import createSegmentBufferGraph from "./modules/segment_buffer_content";
|
|
5
|
+
import createSegmentBufferSizeGraph from "./modules/segment_buffer_size";
|
|
6
|
+
import { createCompositeElement, createElement } from "./utils";
|
|
7
|
+
|
|
8
|
+
export default function renderDebugElement(
|
|
9
|
+
parentElt : HTMLElement,
|
|
10
|
+
instance : RxPlayer,
|
|
11
|
+
cancelSignal : CancellationSignal
|
|
12
|
+
) : void {
|
|
13
|
+
const debugElementTitleElt = createElement("div", {
|
|
14
|
+
textContent: "RxPlayer Debug Information",
|
|
15
|
+
});
|
|
16
|
+
debugElementTitleElt.style.fontWeight = "bold";
|
|
17
|
+
debugElementTitleElt.style.borderBottom = "1px solid white";
|
|
18
|
+
debugElementTitleElt.style.marginBottom = "5px";
|
|
19
|
+
debugElementTitleElt.style.fontStyle = "italic";
|
|
20
|
+
|
|
21
|
+
const debugWrapperElt = createCompositeElement("div", [
|
|
22
|
+
debugElementTitleElt,
|
|
23
|
+
constructDebugGeneralInfo(instance, parentElt, cancelSignal),
|
|
24
|
+
createSegmentBufferGraph(instance, "video", "vbuf", parentElt, cancelSignal),
|
|
25
|
+
createSegmentBufferGraph(instance, "audio", "abuf", parentElt, cancelSignal),
|
|
26
|
+
createSegmentBufferGraph(instance, "text", "tbuf", parentElt, cancelSignal),
|
|
27
|
+
createSegmentBufferSizeGraph(instance, parentElt, cancelSignal),
|
|
28
|
+
]);
|
|
29
|
+
debugWrapperElt.style.backgroundColor = "#00000099";
|
|
30
|
+
debugWrapperElt.style.padding = "7px";
|
|
31
|
+
debugWrapperElt.style.fontSize = "13px";
|
|
32
|
+
debugWrapperElt.style.fontFamily = "mono";
|
|
33
|
+
debugWrapperElt.style.color = "white";
|
|
34
|
+
debugWrapperElt.style.display = "inline-block";
|
|
35
|
+
debugWrapperElt.style.bottom = "0px";
|
|
36
|
+
parentElt.appendChild(debugWrapperElt);
|
|
37
|
+
cancelSignal.register(() => {
|
|
38
|
+
parentElt.removeChild(debugWrapperElt);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create an HTML element.
|
|
3
|
+
* @param {string} elementName - The element's name, like `"div"` for example.
|
|
4
|
+
* @param {Object} [options={}] - Optional attributes for the element.
|
|
5
|
+
* @param {string} [options.textContent] - Inner text for that element.
|
|
6
|
+
* @param {string} [options.className] - Value for a `class` attribute
|
|
7
|
+
* associated to this element.
|
|
8
|
+
* @param {string} [options.href] - Value for a `href` attribute
|
|
9
|
+
* associated to this element.
|
|
10
|
+
* @returns {HTMLElement}
|
|
11
|
+
*/
|
|
12
|
+
export function createElement(
|
|
13
|
+
elementName : "input",
|
|
14
|
+
opts? : CreateElementOptions | undefined
|
|
15
|
+
) : HTMLInputElement;
|
|
16
|
+
export function createElement(
|
|
17
|
+
elementName : "button",
|
|
18
|
+
opts? : CreateElementOptions | undefined
|
|
19
|
+
) : HTMLButtonElement;
|
|
20
|
+
export function createElement(
|
|
21
|
+
elementName : "a",
|
|
22
|
+
opts? : CreateElementOptions | undefined
|
|
23
|
+
) : HTMLLinkElement;
|
|
24
|
+
export function createElement(
|
|
25
|
+
elementName : "canvas",
|
|
26
|
+
opts? : CreateElementOptions | undefined
|
|
27
|
+
) : HTMLCanvasElement;
|
|
28
|
+
export function createElement(
|
|
29
|
+
elementName : string,
|
|
30
|
+
opts? : CreateElementOptions | undefined
|
|
31
|
+
) : HTMLElement;
|
|
32
|
+
export function createElement(
|
|
33
|
+
elementName : string,
|
|
34
|
+
{
|
|
35
|
+
textContent,
|
|
36
|
+
className,
|
|
37
|
+
} : CreateElementOptions | undefined = {}
|
|
38
|
+
) : HTMLElement {
|
|
39
|
+
const elt = document.createElement(elementName);
|
|
40
|
+
if (className !== undefined) {
|
|
41
|
+
elt.className = className;
|
|
42
|
+
}
|
|
43
|
+
if (textContent !== undefined) {
|
|
44
|
+
elt.textContent = textContent;
|
|
45
|
+
}
|
|
46
|
+
return elt;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface CreateElementOptions {
|
|
50
|
+
textContent? : string | undefined;
|
|
51
|
+
className? : string | undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create an HTML element which may contain mutiple HTML sub-elements.
|
|
56
|
+
* @param {string} rootElementName - The element's name, like `"div"` for
|
|
57
|
+
* example.
|
|
58
|
+
* @param {Array.<string|HTMLElement>} parts - The HTML sub-elements, in order.
|
|
59
|
+
* Those can also just be strings, in which case only text nodes (and no actual
|
|
60
|
+
* HTMLElement) will be added at this place.
|
|
61
|
+
* @param {Object} [options={}] - Optional attributes for the element.
|
|
62
|
+
* @param {string} [options.className] - Value for a `class` attribute
|
|
63
|
+
* associated to this element.
|
|
64
|
+
* @returns {HTMLElement}
|
|
65
|
+
*/
|
|
66
|
+
export function createCompositeElement(
|
|
67
|
+
rootElementName : string,
|
|
68
|
+
parts : Array<HTMLElement | string>,
|
|
69
|
+
{ className } : { className? : string } | undefined = {}
|
|
70
|
+
) : HTMLElement {
|
|
71
|
+
const elt = document.createElement(rootElementName);
|
|
72
|
+
if (className !== undefined) {
|
|
73
|
+
elt.className = className;
|
|
74
|
+
}
|
|
75
|
+
for (const subElt of parts) {
|
|
76
|
+
if (typeof subElt === "string") {
|
|
77
|
+
elt.appendChild(document.createTextNode(subElt));
|
|
78
|
+
} else {
|
|
79
|
+
elt.appendChild(subElt);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return elt;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function isExtendedMode(parentElt : HTMLElement) : boolean {
|
|
86
|
+
return parentElt.clientHeight > 400;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function createMetricTitle(title : string) : HTMLElement {
|
|
90
|
+
const elt = createElement("span", {
|
|
91
|
+
textContent: title + "/",
|
|
92
|
+
});
|
|
93
|
+
elt.style.fontWeight = "bold";
|
|
94
|
+
return elt;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function createGraphCanvas() : HTMLCanvasElement {
|
|
98
|
+
const canvasElt = createElement("canvas");
|
|
99
|
+
canvasElt.style.border = "1px solid white";
|
|
100
|
+
canvasElt.style.height = "15px";
|
|
101
|
+
canvasElt.style.marginLeft = "2px";
|
|
102
|
+
return canvasElt;
|
|
103
|
+
}
|