trtc-cloud-js-sdk 1.0.13 → 2.0.0
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 +88 -0
- package/.prettierrc +5 -0
- package/CHANGELOG.md +58 -0
- package/build/jsdoc/clean-doc.js +12 -0
- package/build/jsdoc/fix-doc.js +141 -0
- package/build/jsdoc/jsdoc.json +42 -0
- package/build/package-bundle.js +29 -0
- package/build/rollup.config.dev.js +88 -0
- package/build/rollup.config.prod.js +93 -0
- package/build/rollup.js +359 -0
- package/build/template/npm-package/package.json +24 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/base.css +213 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/index.html +80 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/prettify.css +1 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/prettify.js +1 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/sort-arrow-sprite.png +0 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/sorter.js +158 -0
- package/examples/apiExample/.env +2 -0
- package/examples/apiExample/README.md +70 -0
- package/examples/apiExample/package-lock.json +30915 -0
- package/examples/apiExample/package.json +51 -0
- package/examples/apiExample/public/audio.js +195 -0
- package/examples/apiExample/public/audio.js.map +7 -0
- package/examples/apiExample/public/av_processing.js +1 -0
- package/examples/apiExample/public/basic/av_processing.wasm +0 -0
- package/examples/apiExample/public/basic/worker.js +10434 -0
- package/examples/apiExample/public/favicon.ico +0 -0
- package/examples/apiExample/public/index.html +47 -0
- package/examples/apiExample/public/logo192.png +0 -0
- package/examples/apiExample/public/logo512.png +0 -0
- package/examples/apiExample/public/manifest.json +25 -0
- package/examples/apiExample/public/robots.txt +3 -0
- package/examples/apiExample/src/App.css +37 -0
- package/examples/apiExample/src/App.js +25 -0
- package/examples/apiExample/src/api/http.js +127 -0
- package/examples/apiExample/src/api/nav.js +44 -0
- package/examples/apiExample/src/components/BasicInfoComponent.css +16 -0
- package/examples/apiExample/src/components/BasicInfoComponent.js +27 -0
- package/examples/apiExample/src/config/gen-test-user-sig.js +64 -0
- package/examples/apiExample/src/config/lib-generate-test-usersig.min.js +7052 -0
- package/examples/apiExample/src/config/nav.js +136 -0
- package/examples/apiExample/src/home.js +16 -0
- package/examples/apiExample/src/index.css +21 -0
- package/examples/apiExample/src/index.js +12 -0
- package/examples/apiExample/src/logo.svg +1 -0
- package/examples/apiExample/src/page/basic/screen-share/index.css +52 -0
- package/examples/apiExample/src/page/basic/screen-share/index.js +223 -0
- package/examples/apiExample/src/page/basic/setDevice/index.js +262 -0
- package/examples/apiExample/src/page/basic/setDevice/index.scss +93 -0
- package/examples/apiExample/src/page/basic/video-call/index.js +521 -0
- package/examples/apiExample/src/page/basic/video-call/index.scss +93 -0
- package/examples/apiExample/src/page/basic/video-call-init/index.js +382 -0
- package/examples/apiExample/src/page/basic/video-call-init/index.scss +93 -0
- package/examples/apiExample/src/page/basic/video-live/index.css +37 -0
- package/examples/apiExample/src/page/basic/video-live/index.js +188 -0
- package/examples/apiExample/src/page/layout.js +22 -0
- package/examples/apiExample/src/page/layout.scss +76 -0
- package/examples/apiExample/src/utils/utils.js +35 -0
- package/examples/jsExample/assets/css/bootstrap-material-design.css +12169 -0
- package/examples/jsExample/assets/css/bootstrap-material-design.min.css +8 -0
- package/examples/jsExample/assets/css/common.css +48 -0
- package/examples/jsExample/assets/icon/iconfont.js +1 -0
- package/examples/jsExample/assets/js/bootstrap-material-design.js +6939 -0
- package/examples/jsExample/assets/js/bootstrap-material-design.js.map +1 -0
- package/examples/jsExample/assets/js/bootstrap-material-design.min.js +1 -0
- package/examples/jsExample/assets/js/graph.js +695 -0
- package/examples/jsExample/assets/js/jquery-3.2.1.min.js +4 -0
- package/examples/jsExample/assets/js/jquery-3.2.1.slim.min.js +4 -0
- package/examples/jsExample/assets/js/lib-generate-test-usersig.min.js +2 -0
- package/examples/jsExample/assets/js/popper.js +2442 -0
- package/examples/jsExample/index.html +57 -0
- package/examples/jsExample/rtc/css/common.css +82 -0
- package/examples/jsExample/rtc/index.html +107 -0
- package/examples/jsExample/rtc/js/index.js +142 -0
- package/examples/vueDemo/LICENSE +21 -0
- package/examples/vueDemo/README.md +144 -0
- package/examples/vueDemo/README_EN.md +136 -0
- package/examples/vueDemo/av_processing.wasm +0 -0
- package/examples/vueDemo/index.html +23 -0
- package/examples/vueDemo/package-lock.json +1375 -0
- package/examples/vueDemo/package.json +36 -0
- package/examples/vueDemo/src/App.vue +12 -0
- package/examples/vueDemo/src/api/index.js +59 -0
- package/examples/vueDemo/src/assets/css/color-dark.css +28 -0
- package/examples/vueDemo/src/assets/css/icon.css +4 -0
- package/examples/vueDemo/src/assets/css/main.css +177 -0
- package/examples/vueDemo/src/assets/img/img.jpg +0 -0
- package/examples/vueDemo/src/assets/img/login-bg.jpg +0 -0
- package/examples/vueDemo/src/components/Header.vue +172 -0
- package/examples/vueDemo/src/components/Sidebar.vue +117 -0
- package/examples/vueDemo/src/components/Tags.vue +174 -0
- package/examples/vueDemo/src/components/tendency.vue +206 -0
- package/examples/vueDemo/src/components/trtc/main-menu.vue +50 -0
- package/examples/vueDemo/src/components/trtc/nav-bar.vue +53 -0
- package/examples/vueDemo/src/components/trtc/show-screen-capture.vue +118 -0
- package/examples/vueDemo/src/components/trtc/trtc-state-check.vue +117 -0
- package/examples/vueDemo/src/config/gen-test-user-sig.js +67 -0
- package/examples/vueDemo/src/config/lib-generate-test-usersig.min.js +7052 -0
- package/examples/vueDemo/src/main.js +11 -0
- package/examples/vueDemo/src/plugins/element.js +17 -0
- package/examples/vueDemo/src/router/index.js +73 -0
- package/examples/vueDemo/src/store/sidebar.js +17 -0
- package/examples/vueDemo/src/store/tags.js +48 -0
- package/examples/vueDemo/src/utils/i18n.js +24 -0
- package/examples/vueDemo/src/utils/request.js +34 -0
- package/examples/vueDemo/src/utils/utils.js +35 -0
- package/examples/vueDemo/src/views/Home.vue +46 -0
- package/examples/vueDemo/src/views/I18n.vue +40 -0
- package/examples/vueDemo/src/views/Icon.vue +229 -0
- package/examples/vueDemo/src/views/basic/trtc.vue +194 -0
- package/examples/vueDemo/src/views/feature/index.vue +259 -0
- package/examples/vueDemo/src/views/github/index.vue +243 -0
- package/examples/vueDemo/src/views/improve/live-index.vue +256 -0
- package/examples/vueDemo/src/views/improve/live-room-anchor.vue +689 -0
- package/examples/vueDemo/src/views/improve/live-room-audience.vue +383 -0
- package/examples/vueDemo/src/views/sdkAppId/index.vue +284 -0
- package/examples/vueDemo/vite.config.js +18 -0
- package/examples/vueDemo/worker.js +22 -0
- package/karma.conf.js +99 -0
- package/package.json +57 -7
- package/scripts/publish.js +86 -0
- package/src/Camera.ts +80 -0
- package/src/Mic.ts +145 -0
- package/src/common/IError.ts +6 -0
- package/src/common/ITRTCCloud.ts +68 -0
- package/src/common/constants.ts +116 -0
- package/src/common/trtc-code.ts +43 -0
- package/src/common/trtc-define.ts +1007 -0
- package/src/common/trtc-event.ts +29 -0
- package/src/index.ts +1672 -0
- package/src/utils/environment.js +297 -0
- package/src/utils/raf.js +131 -0
- package/src/utils/time.js +22 -0
- package/src/utils/utils.ts +71 -0
- package/src/utils/uuid.js +12 -0
- package/test/unit/env.test.js +25 -0
- package/test/unit/get-user-media.test.js +40 -0
- package/test/unit/ice-parser.test.js +23 -0
- package/test/unit/sdp.test.js +45 -0
- package/test/unit/signal.test.js +78 -0
- package/tsconfig.json +32 -0
- package/trtc-cloud-js-sdk.js +0 -1
- /package/{README.md → build/template/npm-package/README.md} +0 -0
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a BSD-style license
|
|
5
|
+
* that can be found in the LICENSE file in the root of the source
|
|
6
|
+
* tree.
|
|
7
|
+
*/
|
|
8
|
+
// taken from chrome://webrtc-internals with jshint adaptions
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
/* exported TimelineDataSeries, TimelineGraphView */
|
|
12
|
+
|
|
13
|
+
// The maximum number of data points bufferred for each stats. Old data points
|
|
14
|
+
// will be shifted out when the buffer is full.
|
|
15
|
+
const MAX_STATS_DATA_POINT_BUFFER_SIZE = 1000;
|
|
16
|
+
|
|
17
|
+
const TimelineDataSeries = (function() {
|
|
18
|
+
/**
|
|
19
|
+
* @constructor
|
|
20
|
+
*/
|
|
21
|
+
function TimelineDataSeries() {
|
|
22
|
+
// List of DataPoints in chronological order.
|
|
23
|
+
this.dataPoints_ = [];
|
|
24
|
+
|
|
25
|
+
// Default color. Should always be overridden prior to display.
|
|
26
|
+
this.color_ = 'red';
|
|
27
|
+
// Whether or not the data series should be drawn.
|
|
28
|
+
this.isVisible_ = true;
|
|
29
|
+
|
|
30
|
+
this.cacheStartTime_ = null;
|
|
31
|
+
this.cacheStepSize_ = 0;
|
|
32
|
+
this.cacheValues_ = [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
TimelineDataSeries.prototype = {
|
|
36
|
+
/**
|
|
37
|
+
* @override
|
|
38
|
+
*/
|
|
39
|
+
toJSON: function() {
|
|
40
|
+
if (this.dataPoints_.length < 1) {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let values = [];
|
|
45
|
+
for (let i = 0; i < this.dataPoints_.length; ++i) {
|
|
46
|
+
values.push(this.dataPoints_[i].value);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
startTime: this.dataPoints_[0].time,
|
|
50
|
+
endTime: this.dataPoints_[this.dataPoints_.length - 1].time,
|
|
51
|
+
values: JSON.stringify(values),
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Adds a DataPoint to |this| with the specified time and value.
|
|
57
|
+
* DataPoints are assumed to be received in chronological order.
|
|
58
|
+
*/
|
|
59
|
+
addPoint: function(timeTicks, value) {
|
|
60
|
+
let time = new Date(timeTicks);
|
|
61
|
+
this.dataPoints_.push(new DataPoint(time, value));
|
|
62
|
+
|
|
63
|
+
if (this.dataPoints_.length > MAX_STATS_DATA_POINT_BUFFER_SIZE) {
|
|
64
|
+
this.dataPoints_.shift();
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
isVisible: function() {
|
|
69
|
+
return this.isVisible_;
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
show: function(isVisible) {
|
|
73
|
+
this.isVisible_ = isVisible;
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
getColor: function() {
|
|
77
|
+
return this.color_;
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
setColor: function(color) {
|
|
81
|
+
this.color_ = color;
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
getCount: function() {
|
|
85
|
+
return this.dataPoints_.length;
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* Returns a list containing the values of the data series at |count|
|
|
89
|
+
* points, starting at |startTime|, and |stepSize| milliseconds apart.
|
|
90
|
+
* Caches values, so showing/hiding individual data series is fast.
|
|
91
|
+
*/
|
|
92
|
+
getValues: function(startTime, stepSize, count) {
|
|
93
|
+
// Use cached values, if we can.
|
|
94
|
+
if (this.cacheStartTime_ === startTime &&
|
|
95
|
+
this.cacheStepSize_ === stepSize &&
|
|
96
|
+
this.cacheValues_.length === count) {
|
|
97
|
+
return this.cacheValues_;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Do all the work.
|
|
101
|
+
this.cacheValues_ = this.getValuesInternal_(startTime, stepSize, count);
|
|
102
|
+
this.cacheStartTime_ = startTime;
|
|
103
|
+
this.cacheStepSize_ = stepSize;
|
|
104
|
+
|
|
105
|
+
return this.cacheValues_;
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Returns the cached |values| in the specified time period.
|
|
110
|
+
*/
|
|
111
|
+
getValuesInternal_: function(startTime, stepSize, count) {
|
|
112
|
+
let values = [];
|
|
113
|
+
let nextPoint = 0;
|
|
114
|
+
let currentValue = 0;
|
|
115
|
+
let time = startTime;
|
|
116
|
+
for (let i = 0; i < count; ++i) {
|
|
117
|
+
while (nextPoint < this.dataPoints_.length &&
|
|
118
|
+
this.dataPoints_[nextPoint].time < time) {
|
|
119
|
+
currentValue = this.dataPoints_[nextPoint].value;
|
|
120
|
+
++nextPoint;
|
|
121
|
+
}
|
|
122
|
+
values[i] = currentValue;
|
|
123
|
+
time += stepSize;
|
|
124
|
+
}
|
|
125
|
+
return values;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* A single point in a data series. Each point has a time, in the form of
|
|
131
|
+
* milliseconds since the Unix epoch, and a numeric value.
|
|
132
|
+
* @constructor
|
|
133
|
+
*/
|
|
134
|
+
function DataPoint(time, value) {
|
|
135
|
+
this.time = time;
|
|
136
|
+
this.value = value;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return TimelineDataSeries;
|
|
140
|
+
})();
|
|
141
|
+
|
|
142
|
+
const TimelineGraphView = (function() {
|
|
143
|
+
// Maximum number of labels placed vertically along the sides of the graph.
|
|
144
|
+
let MAX_VERTICAL_LABELS = 6;
|
|
145
|
+
|
|
146
|
+
// Vertical spacing between labels and between the graph and labels.
|
|
147
|
+
let LABEL_VERTICAL_SPACING = 4;
|
|
148
|
+
// Horizontal spacing between vertically placed labels and the edges of the
|
|
149
|
+
// graph.
|
|
150
|
+
let LABEL_HORIZONTAL_SPACING = 3;
|
|
151
|
+
// Horizintal spacing between two horitonally placed labels along the bottom
|
|
152
|
+
// of the graph.
|
|
153
|
+
// var LABEL_LABEL_HORIZONTAL_SPACING = 25;
|
|
154
|
+
|
|
155
|
+
// Length of ticks, in pixels, next to y-axis labels. The x-axis only has
|
|
156
|
+
// one set of labels, so it can use lines instead.
|
|
157
|
+
let Y_AXIS_TICK_LENGTH = 10;
|
|
158
|
+
|
|
159
|
+
let GRID_COLOR = '#CCC';
|
|
160
|
+
let TEXT_COLOR = '#000';
|
|
161
|
+
let BACKGROUND_COLOR = '#FFF';
|
|
162
|
+
|
|
163
|
+
let MAX_DECIMAL_PRECISION = 2;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @constructor
|
|
167
|
+
*/
|
|
168
|
+
function TimelineGraphView(divId, canvasId) {
|
|
169
|
+
this.scrollbar_ = {position_: 0, range_: 0};
|
|
170
|
+
|
|
171
|
+
this.graphDiv_ = document.getElementById(divId);
|
|
172
|
+
this.canvas_ = document.getElementById(canvasId);
|
|
173
|
+
|
|
174
|
+
// Set the range and scale of the graph. Times are in milliseconds since
|
|
175
|
+
// the Unix epoch.
|
|
176
|
+
|
|
177
|
+
// All measurements we have must be after this time.
|
|
178
|
+
this.startTime_ = 0;
|
|
179
|
+
// The current rightmost position of the graph is always at most this.
|
|
180
|
+
this.endTime_ = 1;
|
|
181
|
+
|
|
182
|
+
this.graph_ = null;
|
|
183
|
+
|
|
184
|
+
// Horizontal scale factor, in terms of milliseconds per pixel.
|
|
185
|
+
this.scale_ = 1000;
|
|
186
|
+
|
|
187
|
+
// Initialize the scrollbar.
|
|
188
|
+
this.updateScrollbarRange_(true);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
TimelineGraphView.prototype = {
|
|
192
|
+
setScale: function(scale) {
|
|
193
|
+
this.scale_ = scale;
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
// Returns the total length of the graph, in pixels.
|
|
197
|
+
getLength_: function() {
|
|
198
|
+
let timeRange = this.endTime_ - this.startTime_;
|
|
199
|
+
// Math.floor is used to ignore the last partial area, of length less
|
|
200
|
+
// than this.scale_.
|
|
201
|
+
return Math.floor(timeRange / this.scale_);
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Returns true if the graph is scrolled all the way to the right.
|
|
206
|
+
*/
|
|
207
|
+
graphScrolledToRightEdge_: function() {
|
|
208
|
+
return this.scrollbar_.position_ === this.scrollbar_.range_;
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Update the range of the scrollbar. If |resetPosition| is true, also
|
|
213
|
+
* sets the slider to point at the rightmost position and triggers a
|
|
214
|
+
* repaint.
|
|
215
|
+
*/
|
|
216
|
+
updateScrollbarRange_: function(resetPosition) {
|
|
217
|
+
let scrollbarRange = this.getLength_() - this.canvas_.width;
|
|
218
|
+
if (scrollbarRange < 0) {
|
|
219
|
+
scrollbarRange = 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// If we've decreased the range to less than the current scroll position,
|
|
223
|
+
// we need to move the scroll position.
|
|
224
|
+
if (this.scrollbar_.position_ > scrollbarRange) {
|
|
225
|
+
resetPosition = true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
this.scrollbar_.range_ = scrollbarRange;
|
|
229
|
+
if (resetPosition) {
|
|
230
|
+
this.scrollbar_.position_ = scrollbarRange;
|
|
231
|
+
this.repaint();
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Sets the date range displayed on the graph, switches to the default
|
|
237
|
+
* scale factor, and moves the scrollbar all the way to the right.
|
|
238
|
+
*/
|
|
239
|
+
setDateRange: function(startDate, endDate) {
|
|
240
|
+
this.startTime_ = startDate.getTime();
|
|
241
|
+
this.endTime_ = endDate.getTime();
|
|
242
|
+
|
|
243
|
+
// Safety check.
|
|
244
|
+
if (this.endTime_ <= this.startTime_) {
|
|
245
|
+
this.startTime_ = this.endTime_ - 1;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
this.updateScrollbarRange_(true);
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Updates the end time at the right of the graph to be the current time.
|
|
253
|
+
* Specifically, updates the scrollbar's range, and if the scrollbar is
|
|
254
|
+
* all the way to the right, keeps it all the way to the right. Otherwise,
|
|
255
|
+
* leaves the view as-is and doesn't redraw anything.
|
|
256
|
+
*/
|
|
257
|
+
updateEndDate: function(optDate) {
|
|
258
|
+
this.endTime_ = optDate || (new Date()).getTime();
|
|
259
|
+
this.updateScrollbarRange_(this.graphScrolledToRightEdge_());
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
getStartDate: function() {
|
|
263
|
+
return new Date(this.startTime_);
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Replaces the current TimelineDataSeries with |dataSeries|.
|
|
268
|
+
*/
|
|
269
|
+
setDataSeries: function(dataSeries) {
|
|
270
|
+
// Simply recreates the Graph.
|
|
271
|
+
this.graph_ = new Graph();
|
|
272
|
+
for (let i = 0; i < dataSeries.length; ++i) {
|
|
273
|
+
this.graph_.addDataSeries(dataSeries[i]);
|
|
274
|
+
}
|
|
275
|
+
this.repaint();
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Adds |dataSeries| to the current graph.
|
|
280
|
+
*/
|
|
281
|
+
addDataSeries: function(dataSeries) {
|
|
282
|
+
if (!this.graph_) {
|
|
283
|
+
this.graph_ = new Graph();
|
|
284
|
+
}
|
|
285
|
+
this.graph_.addDataSeries(dataSeries);
|
|
286
|
+
this.repaint();
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Draws the graph on |canvas_|.
|
|
291
|
+
*/
|
|
292
|
+
repaint: function() {
|
|
293
|
+
this.repaintTimerRunning_ = false;
|
|
294
|
+
|
|
295
|
+
let width = this.canvas_.width;
|
|
296
|
+
let height = this.canvas_.height;
|
|
297
|
+
let context = this.canvas_.getContext('2d');
|
|
298
|
+
|
|
299
|
+
// Clear the canvas.
|
|
300
|
+
context.fillStyle = BACKGROUND_COLOR;
|
|
301
|
+
context.fillRect(0, 0, width, height);
|
|
302
|
+
|
|
303
|
+
// Try to get font height in pixels. Needed for layout.
|
|
304
|
+
let fontHeightString = context.font.match(/([0-9]+)px/)[1];
|
|
305
|
+
let fontHeight = parseInt(fontHeightString);
|
|
306
|
+
|
|
307
|
+
// Safety check, to avoid drawing anything too ugly.
|
|
308
|
+
if (fontHeightString.length === 0 || fontHeight <= 0 ||
|
|
309
|
+
fontHeight * 4 > height || width < 50) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Save current transformation matrix so we can restore it later.
|
|
314
|
+
context.save();
|
|
315
|
+
|
|
316
|
+
// The center of an HTML canvas pixel is technically at (0.5, 0.5). This
|
|
317
|
+
// makes near straight lines look bad, due to anti-aliasing. This
|
|
318
|
+
// translation reduces the problem a little.
|
|
319
|
+
context.translate(0.5, 0.5);
|
|
320
|
+
|
|
321
|
+
// Figure out what time values to display.
|
|
322
|
+
let position = this.scrollbar_.position_;
|
|
323
|
+
// If the entire time range is being displayed, align the right edge of
|
|
324
|
+
// the graph to the end of the time range.
|
|
325
|
+
if (this.scrollbar_.range_ === 0) {
|
|
326
|
+
position = this.getLength_() - this.canvas_.width;
|
|
327
|
+
}
|
|
328
|
+
let visibleStartTime = this.startTime_ + position * this.scale_;
|
|
329
|
+
|
|
330
|
+
// Make space at the bottom of the graph for the time labels, and then
|
|
331
|
+
// draw the labels.
|
|
332
|
+
let textHeight = height;
|
|
333
|
+
height -= fontHeight + LABEL_VERTICAL_SPACING;
|
|
334
|
+
this.drawTimeLabels(context, width, height, textHeight, visibleStartTime);
|
|
335
|
+
|
|
336
|
+
// Draw outline of the main graph area.
|
|
337
|
+
context.strokeStyle = GRID_COLOR;
|
|
338
|
+
context.strokeRect(0, 0, width - 1, height - 1);
|
|
339
|
+
|
|
340
|
+
if (this.graph_) {
|
|
341
|
+
// Layout graph and have them draw their tick marks.
|
|
342
|
+
this.graph_.layout(
|
|
343
|
+
width, height, fontHeight, visibleStartTime, this.scale_);
|
|
344
|
+
this.graph_.drawTicks(context);
|
|
345
|
+
|
|
346
|
+
// Draw the lines of all graphs, and then draw their labels.
|
|
347
|
+
this.graph_.drawLines(context);
|
|
348
|
+
this.graph_.drawLabels(context);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Restore original transformation matrix.
|
|
352
|
+
context.restore();
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Draw time labels below the graph. Takes in start time as an argument
|
|
357
|
+
* since it may not be |startTime_|, when we're displaying the entire
|
|
358
|
+
* time range.
|
|
359
|
+
*/
|
|
360
|
+
drawTimeLabels: function(context, width, height, textHeight, startTime) {
|
|
361
|
+
// Draw the labels 1 minute apart.
|
|
362
|
+
let timeStep = 1000 * 60;
|
|
363
|
+
|
|
364
|
+
// Find the time for the first label. This time is a perfect multiple of
|
|
365
|
+
// timeStep because of how UTC times work.
|
|
366
|
+
let time = Math.ceil(startTime / timeStep) * timeStep;
|
|
367
|
+
|
|
368
|
+
context.textBaseline = 'bottom';
|
|
369
|
+
context.textAlign = 'center';
|
|
370
|
+
context.fillStyle = TEXT_COLOR;
|
|
371
|
+
context.strokeStyle = GRID_COLOR;
|
|
372
|
+
|
|
373
|
+
// Draw labels and vertical grid lines.
|
|
374
|
+
while (true) {
|
|
375
|
+
let x = Math.round((time - startTime) / this.scale_);
|
|
376
|
+
if (x >= width) {
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
let text = (new Date(time)).toLocaleTimeString();
|
|
380
|
+
context.fillText(text, x, textHeight);
|
|
381
|
+
context.beginPath();
|
|
382
|
+
context.lineTo(x, 0);
|
|
383
|
+
context.lineTo(x, height);
|
|
384
|
+
context.stroke();
|
|
385
|
+
time += timeStep;
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
getDataSeriesCount: function() {
|
|
390
|
+
if (this.graph_) {
|
|
391
|
+
return this.graph_.dataSeries_.length;
|
|
392
|
+
}
|
|
393
|
+
return 0;
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
hasDataSeries: function(dataSeries) {
|
|
397
|
+
if (this.graph_) {
|
|
398
|
+
return this.graph_.hasDataSeries(dataSeries);
|
|
399
|
+
}
|
|
400
|
+
return false;
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* A Graph is responsible for drawing all the TimelineDataSeries that have
|
|
407
|
+
* the same data type. Graphs are responsible for scaling the values, laying
|
|
408
|
+
* out labels, and drawing both labels and lines for its data series.
|
|
409
|
+
*/
|
|
410
|
+
const Graph = (function() {
|
|
411
|
+
/**
|
|
412
|
+
* @constructor
|
|
413
|
+
*/
|
|
414
|
+
function Graph() {
|
|
415
|
+
this.dataSeries_ = [];
|
|
416
|
+
|
|
417
|
+
// Cached properties of the graph, set in layout.
|
|
418
|
+
this.width_ = 0;
|
|
419
|
+
this.height_ = 0;
|
|
420
|
+
this.fontHeight_ = 0;
|
|
421
|
+
this.startTime_ = 0;
|
|
422
|
+
this.scale_ = 0;
|
|
423
|
+
|
|
424
|
+
// The lowest/highest values adjusted by the vertical label step size
|
|
425
|
+
// in the displayed range of the graph. Used for scaling and setting
|
|
426
|
+
// labels. Set in layoutLabels.
|
|
427
|
+
this.min_ = 0;
|
|
428
|
+
this.max_ = 0;
|
|
429
|
+
|
|
430
|
+
// Cached text of equally spaced labels. Set in layoutLabels.
|
|
431
|
+
this.labels_ = [];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* A Label is the label at a particular position along the y-axis.
|
|
436
|
+
* @constructor
|
|
437
|
+
*/
|
|
438
|
+
/*
|
|
439
|
+
function Label(height, text) {
|
|
440
|
+
this.height = height;
|
|
441
|
+
this.text = text;
|
|
442
|
+
}
|
|
443
|
+
*/
|
|
444
|
+
|
|
445
|
+
Graph.prototype = {
|
|
446
|
+
addDataSeries: function(dataSeries) {
|
|
447
|
+
this.dataSeries_.push(dataSeries);
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
hasDataSeries: function(dataSeries) {
|
|
451
|
+
for (let i = 0; i < this.dataSeries_.length; ++i) {
|
|
452
|
+
if (this.dataSeries_[i] === dataSeries) {
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return false;
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Returns a list of all the values that should be displayed for a given
|
|
461
|
+
* data series, using the current graph layout.
|
|
462
|
+
*/
|
|
463
|
+
getValues: function(dataSeries) {
|
|
464
|
+
if (!dataSeries.isVisible()) {
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
return dataSeries.getValues(this.startTime_, this.scale_, this.width_);
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Updates the graph's layout. In particular, both the max value and
|
|
472
|
+
* label positions are updated. Must be called before calling any of the
|
|
473
|
+
* drawing functions.
|
|
474
|
+
*/
|
|
475
|
+
layout: function(width, height, fontHeight, startTime, scale) {
|
|
476
|
+
this.width_ = width;
|
|
477
|
+
this.height_ = height;
|
|
478
|
+
this.fontHeight_ = fontHeight;
|
|
479
|
+
this.startTime_ = startTime;
|
|
480
|
+
this.scale_ = scale;
|
|
481
|
+
|
|
482
|
+
// Find largest value.
|
|
483
|
+
let max = 0;
|
|
484
|
+
let min = 0;
|
|
485
|
+
for (let i = 0; i < this.dataSeries_.length; ++i) {
|
|
486
|
+
let values = this.getValues(this.dataSeries_[i]);
|
|
487
|
+
if (!values) {
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
for (let j = 0; j < values.length; ++j) {
|
|
491
|
+
if (values[j] > max) {
|
|
492
|
+
max = values[j];
|
|
493
|
+
} else if (values[j] < min) {
|
|
494
|
+
min = values[j];
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
this.layoutLabels_(min, max);
|
|
500
|
+
},
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Lays out labels and sets |max_|/|min_|, taking the time units into
|
|
504
|
+
* consideration. |maxValue| is the actual maximum value, and
|
|
505
|
+
* |max_| will be set to the value of the largest label, which
|
|
506
|
+
* will be at least |maxValue|. Similar for |min_|.
|
|
507
|
+
*/
|
|
508
|
+
layoutLabels_: function(minValue, maxValue) {
|
|
509
|
+
if (maxValue - minValue < 1024) {
|
|
510
|
+
this.layoutLabelsBasic_(minValue, maxValue, MAX_DECIMAL_PRECISION);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Find appropriate units to use.
|
|
515
|
+
let units = ['', 'k', 'M', 'G', 'T', 'P'];
|
|
516
|
+
// Units to use for labels. 0 is '1', 1 is K, etc.
|
|
517
|
+
// We start with 1, and work our way up.
|
|
518
|
+
let unit = 1;
|
|
519
|
+
minValue /= 1024;
|
|
520
|
+
maxValue /= 1024;
|
|
521
|
+
while (units[unit + 1] && maxValue - minValue >= 1024) {
|
|
522
|
+
minValue /= 1024;
|
|
523
|
+
maxValue /= 1024;
|
|
524
|
+
++unit;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Calculate labels.
|
|
528
|
+
this.layoutLabelsBasic_(minValue, maxValue, MAX_DECIMAL_PRECISION);
|
|
529
|
+
|
|
530
|
+
// Append units to labels.
|
|
531
|
+
for (let i = 0; i < this.labels_.length; ++i) {
|
|
532
|
+
this.labels_[i] += ' ' + units[unit];
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Convert |min_|/|max_| back to unit '1'.
|
|
536
|
+
this.min_ *= Math.pow(1024, unit);
|
|
537
|
+
this.max_ *= Math.pow(1024, unit);
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Same as layoutLabels_, but ignores units. |maxDecimalDigits| is the
|
|
542
|
+
* maximum number of decimal digits allowed. The minimum allowed
|
|
543
|
+
* difference between two adjacent labels is 10^-|maxDecimalDigits|.
|
|
544
|
+
*/
|
|
545
|
+
layoutLabelsBasic_: function(minValue, maxValue, maxDecimalDigits) {
|
|
546
|
+
this.labels_ = [];
|
|
547
|
+
let range = maxValue - minValue;
|
|
548
|
+
// No labels if the range is 0.
|
|
549
|
+
if (range === 0) {
|
|
550
|
+
this.min_ = this.max_ = maxValue;
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// The maximum number of equally spaced labels allowed. |fontHeight_|
|
|
555
|
+
// is doubled because the top two labels are both drawn in the same
|
|
556
|
+
// gap.
|
|
557
|
+
let minLabelSpacing = 2 * this.fontHeight_ + LABEL_VERTICAL_SPACING;
|
|
558
|
+
|
|
559
|
+
// The + 1 is for the top label.
|
|
560
|
+
let maxLabels = 1 + this.height_ / minLabelSpacing;
|
|
561
|
+
if (maxLabels < 2) {
|
|
562
|
+
maxLabels = 2;
|
|
563
|
+
} else if (maxLabels > MAX_VERTICAL_LABELS) {
|
|
564
|
+
maxLabels = MAX_VERTICAL_LABELS;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Initial try for step size between conecutive labels.
|
|
568
|
+
let stepSize = Math.pow(10, -maxDecimalDigits);
|
|
569
|
+
// Number of digits to the right of the decimal of |stepSize|.
|
|
570
|
+
// Used for formating label strings.
|
|
571
|
+
let stepSizeDecimalDigits = maxDecimalDigits;
|
|
572
|
+
|
|
573
|
+
// Pick a reasonable step size.
|
|
574
|
+
while (true) {
|
|
575
|
+
// If we use a step size of |stepSize| between labels, we'll need:
|
|
576
|
+
//
|
|
577
|
+
// Math.ceil(range / stepSize) + 1
|
|
578
|
+
//
|
|
579
|
+
// labels. The + 1 is because we need labels at both at 0 and at
|
|
580
|
+
// the top of the graph.
|
|
581
|
+
|
|
582
|
+
// Check if we can use steps of size |stepSize|.
|
|
583
|
+
if (Math.ceil(range / stepSize) + 1 <= maxLabels) {
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
// Check |stepSize| * 2.
|
|
587
|
+
if (Math.ceil(range / (stepSize * 2)) + 1 <= maxLabels) {
|
|
588
|
+
stepSize *= 2;
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
// Check |stepSize| * 5.
|
|
592
|
+
if (Math.ceil(range / (stepSize * 5)) + 1 <= maxLabels) {
|
|
593
|
+
stepSize *= 5;
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
stepSize *= 10;
|
|
597
|
+
if (stepSizeDecimalDigits > 0) {
|
|
598
|
+
--stepSizeDecimalDigits;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Set the min/max so it's an exact multiple of the chosen step size.
|
|
603
|
+
this.max_ = Math.ceil(maxValue / stepSize) * stepSize;
|
|
604
|
+
this.min_ = Math.floor(minValue / stepSize) * stepSize;
|
|
605
|
+
|
|
606
|
+
// Create labels.
|
|
607
|
+
for (let label = this.max_; label >= this.min_; label -= stepSize) {
|
|
608
|
+
this.labels_.push(label.toFixed(stepSizeDecimalDigits));
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Draws tick marks for each of the labels in |labels_|.
|
|
614
|
+
*/
|
|
615
|
+
drawTicks: function(context) {
|
|
616
|
+
let x1;
|
|
617
|
+
let x2;
|
|
618
|
+
x1 = this.width_ - 1;
|
|
619
|
+
x2 = this.width_ - 1 - Y_AXIS_TICK_LENGTH;
|
|
620
|
+
|
|
621
|
+
context.fillStyle = GRID_COLOR;
|
|
622
|
+
context.beginPath();
|
|
623
|
+
for (let i = 1; i < this.labels_.length - 1; ++i) {
|
|
624
|
+
// The rounding is needed to avoid ugly 2-pixel wide anti-aliased
|
|
625
|
+
// lines.
|
|
626
|
+
let y = Math.round(this.height_ * i / (this.labels_.length - 1));
|
|
627
|
+
context.moveTo(x1, y);
|
|
628
|
+
context.lineTo(x2, y);
|
|
629
|
+
}
|
|
630
|
+
context.stroke();
|
|
631
|
+
},
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Draws a graph line for each of the data series.
|
|
635
|
+
*/
|
|
636
|
+
drawLines: function(context) {
|
|
637
|
+
// Factor by which to scale all values to convert them to a number from
|
|
638
|
+
// 0 to height - 1.
|
|
639
|
+
let scale = 0;
|
|
640
|
+
let bottom = this.height_ - 1;
|
|
641
|
+
if (this.max_) {
|
|
642
|
+
scale = bottom / (this.max_ - this.min_);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Draw in reverse order, so earlier data series are drawn on top of
|
|
646
|
+
// subsequent ones.
|
|
647
|
+
for (let i = this.dataSeries_.length - 1; i >= 0; --i) {
|
|
648
|
+
let values = this.getValues(this.dataSeries_[i]);
|
|
649
|
+
if (!values) {
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
context.strokeStyle = this.dataSeries_[i].getColor();
|
|
653
|
+
context.beginPath();
|
|
654
|
+
for (let x = 0; x < values.length; ++x) {
|
|
655
|
+
// The rounding is needed to avoid ugly 2-pixel wide anti-aliased
|
|
656
|
+
// horizontal lines.
|
|
657
|
+
context.lineTo(
|
|
658
|
+
x, bottom - Math.round((values[x] - this.min_) * scale));
|
|
659
|
+
}
|
|
660
|
+
context.stroke();
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Draw labels in |labels_|.
|
|
666
|
+
*/
|
|
667
|
+
drawLabels: function(context) {
|
|
668
|
+
if (this.labels_.length === 0) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
let x = this.width_ - LABEL_HORIZONTAL_SPACING;
|
|
672
|
+
|
|
673
|
+
// Set up the context.
|
|
674
|
+
context.fillStyle = TEXT_COLOR;
|
|
675
|
+
context.textAlign = 'right';
|
|
676
|
+
|
|
677
|
+
// Draw top label, which is the only one that appears below its tick
|
|
678
|
+
// mark.
|
|
679
|
+
context.textBaseline = 'top';
|
|
680
|
+
context.fillText(this.labels_[0], x, 0);
|
|
681
|
+
|
|
682
|
+
// Draw all the other labels.
|
|
683
|
+
context.textBaseline = 'bottom';
|
|
684
|
+
let step = (this.height_ - 1) / (this.labels_.length - 1);
|
|
685
|
+
for (let i = 1; i < this.labels_.length; ++i) {
|
|
686
|
+
context.fillText(this.labels_[i], x, step * i);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
return Graph;
|
|
692
|
+
})();
|
|
693
|
+
|
|
694
|
+
return TimelineGraphView;
|
|
695
|
+
})();
|