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.
Files changed (143) hide show
  1. package/.eslintrc.js +88 -0
  2. package/.prettierrc +5 -0
  3. package/CHANGELOG.md +58 -0
  4. package/build/jsdoc/clean-doc.js +12 -0
  5. package/build/jsdoc/fix-doc.js +141 -0
  6. package/build/jsdoc/jsdoc.json +42 -0
  7. package/build/package-bundle.js +29 -0
  8. package/build/rollup.config.dev.js +88 -0
  9. package/build/rollup.config.prod.js +93 -0
  10. package/build/rollup.js +359 -0
  11. package/build/template/npm-package/package.json +24 -0
  12. package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/base.css +213 -0
  13. package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/index.html +80 -0
  14. package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/prettify.css +1 -0
  15. package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/prettify.js +1 -0
  16. package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/sort-arrow-sprite.png +0 -0
  17. package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/sorter.js +158 -0
  18. package/examples/apiExample/.env +2 -0
  19. package/examples/apiExample/README.md +70 -0
  20. package/examples/apiExample/package-lock.json +30915 -0
  21. package/examples/apiExample/package.json +51 -0
  22. package/examples/apiExample/public/audio.js +195 -0
  23. package/examples/apiExample/public/audio.js.map +7 -0
  24. package/examples/apiExample/public/av_processing.js +1 -0
  25. package/examples/apiExample/public/basic/av_processing.wasm +0 -0
  26. package/examples/apiExample/public/basic/worker.js +10434 -0
  27. package/examples/apiExample/public/favicon.ico +0 -0
  28. package/examples/apiExample/public/index.html +47 -0
  29. package/examples/apiExample/public/logo192.png +0 -0
  30. package/examples/apiExample/public/logo512.png +0 -0
  31. package/examples/apiExample/public/manifest.json +25 -0
  32. package/examples/apiExample/public/robots.txt +3 -0
  33. package/examples/apiExample/src/App.css +37 -0
  34. package/examples/apiExample/src/App.js +25 -0
  35. package/examples/apiExample/src/api/http.js +127 -0
  36. package/examples/apiExample/src/api/nav.js +44 -0
  37. package/examples/apiExample/src/components/BasicInfoComponent.css +16 -0
  38. package/examples/apiExample/src/components/BasicInfoComponent.js +27 -0
  39. package/examples/apiExample/src/config/gen-test-user-sig.js +64 -0
  40. package/examples/apiExample/src/config/lib-generate-test-usersig.min.js +7052 -0
  41. package/examples/apiExample/src/config/nav.js +136 -0
  42. package/examples/apiExample/src/home.js +16 -0
  43. package/examples/apiExample/src/index.css +21 -0
  44. package/examples/apiExample/src/index.js +12 -0
  45. package/examples/apiExample/src/logo.svg +1 -0
  46. package/examples/apiExample/src/page/basic/screen-share/index.css +52 -0
  47. package/examples/apiExample/src/page/basic/screen-share/index.js +223 -0
  48. package/examples/apiExample/src/page/basic/setDevice/index.js +262 -0
  49. package/examples/apiExample/src/page/basic/setDevice/index.scss +93 -0
  50. package/examples/apiExample/src/page/basic/video-call/index.js +521 -0
  51. package/examples/apiExample/src/page/basic/video-call/index.scss +93 -0
  52. package/examples/apiExample/src/page/basic/video-call-init/index.js +382 -0
  53. package/examples/apiExample/src/page/basic/video-call-init/index.scss +93 -0
  54. package/examples/apiExample/src/page/basic/video-live/index.css +37 -0
  55. package/examples/apiExample/src/page/basic/video-live/index.js +188 -0
  56. package/examples/apiExample/src/page/layout.js +22 -0
  57. package/examples/apiExample/src/page/layout.scss +76 -0
  58. package/examples/apiExample/src/utils/utils.js +35 -0
  59. package/examples/jsExample/assets/css/bootstrap-material-design.css +12169 -0
  60. package/examples/jsExample/assets/css/bootstrap-material-design.min.css +8 -0
  61. package/examples/jsExample/assets/css/common.css +48 -0
  62. package/examples/jsExample/assets/icon/iconfont.js +1 -0
  63. package/examples/jsExample/assets/js/bootstrap-material-design.js +6939 -0
  64. package/examples/jsExample/assets/js/bootstrap-material-design.js.map +1 -0
  65. package/examples/jsExample/assets/js/bootstrap-material-design.min.js +1 -0
  66. package/examples/jsExample/assets/js/graph.js +695 -0
  67. package/examples/jsExample/assets/js/jquery-3.2.1.min.js +4 -0
  68. package/examples/jsExample/assets/js/jquery-3.2.1.slim.min.js +4 -0
  69. package/examples/jsExample/assets/js/lib-generate-test-usersig.min.js +2 -0
  70. package/examples/jsExample/assets/js/popper.js +2442 -0
  71. package/examples/jsExample/index.html +57 -0
  72. package/examples/jsExample/rtc/css/common.css +82 -0
  73. package/examples/jsExample/rtc/index.html +107 -0
  74. package/examples/jsExample/rtc/js/index.js +142 -0
  75. package/examples/vueDemo/LICENSE +21 -0
  76. package/examples/vueDemo/README.md +144 -0
  77. package/examples/vueDemo/README_EN.md +136 -0
  78. package/examples/vueDemo/av_processing.wasm +0 -0
  79. package/examples/vueDemo/index.html +23 -0
  80. package/examples/vueDemo/package-lock.json +1375 -0
  81. package/examples/vueDemo/package.json +36 -0
  82. package/examples/vueDemo/src/App.vue +12 -0
  83. package/examples/vueDemo/src/api/index.js +59 -0
  84. package/examples/vueDemo/src/assets/css/color-dark.css +28 -0
  85. package/examples/vueDemo/src/assets/css/icon.css +4 -0
  86. package/examples/vueDemo/src/assets/css/main.css +177 -0
  87. package/examples/vueDemo/src/assets/img/img.jpg +0 -0
  88. package/examples/vueDemo/src/assets/img/login-bg.jpg +0 -0
  89. package/examples/vueDemo/src/components/Header.vue +172 -0
  90. package/examples/vueDemo/src/components/Sidebar.vue +117 -0
  91. package/examples/vueDemo/src/components/Tags.vue +174 -0
  92. package/examples/vueDemo/src/components/tendency.vue +206 -0
  93. package/examples/vueDemo/src/components/trtc/main-menu.vue +50 -0
  94. package/examples/vueDemo/src/components/trtc/nav-bar.vue +53 -0
  95. package/examples/vueDemo/src/components/trtc/show-screen-capture.vue +118 -0
  96. package/examples/vueDemo/src/components/trtc/trtc-state-check.vue +117 -0
  97. package/examples/vueDemo/src/config/gen-test-user-sig.js +67 -0
  98. package/examples/vueDemo/src/config/lib-generate-test-usersig.min.js +7052 -0
  99. package/examples/vueDemo/src/main.js +11 -0
  100. package/examples/vueDemo/src/plugins/element.js +17 -0
  101. package/examples/vueDemo/src/router/index.js +73 -0
  102. package/examples/vueDemo/src/store/sidebar.js +17 -0
  103. package/examples/vueDemo/src/store/tags.js +48 -0
  104. package/examples/vueDemo/src/utils/i18n.js +24 -0
  105. package/examples/vueDemo/src/utils/request.js +34 -0
  106. package/examples/vueDemo/src/utils/utils.js +35 -0
  107. package/examples/vueDemo/src/views/Home.vue +46 -0
  108. package/examples/vueDemo/src/views/I18n.vue +40 -0
  109. package/examples/vueDemo/src/views/Icon.vue +229 -0
  110. package/examples/vueDemo/src/views/basic/trtc.vue +194 -0
  111. package/examples/vueDemo/src/views/feature/index.vue +259 -0
  112. package/examples/vueDemo/src/views/github/index.vue +243 -0
  113. package/examples/vueDemo/src/views/improve/live-index.vue +256 -0
  114. package/examples/vueDemo/src/views/improve/live-room-anchor.vue +689 -0
  115. package/examples/vueDemo/src/views/improve/live-room-audience.vue +383 -0
  116. package/examples/vueDemo/src/views/sdkAppId/index.vue +284 -0
  117. package/examples/vueDemo/vite.config.js +18 -0
  118. package/examples/vueDemo/worker.js +22 -0
  119. package/karma.conf.js +99 -0
  120. package/package.json +57 -7
  121. package/scripts/publish.js +86 -0
  122. package/src/Camera.ts +80 -0
  123. package/src/Mic.ts +145 -0
  124. package/src/common/IError.ts +6 -0
  125. package/src/common/ITRTCCloud.ts +68 -0
  126. package/src/common/constants.ts +116 -0
  127. package/src/common/trtc-code.ts +43 -0
  128. package/src/common/trtc-define.ts +1007 -0
  129. package/src/common/trtc-event.ts +29 -0
  130. package/src/index.ts +1672 -0
  131. package/src/utils/environment.js +297 -0
  132. package/src/utils/raf.js +131 -0
  133. package/src/utils/time.js +22 -0
  134. package/src/utils/utils.ts +71 -0
  135. package/src/utils/uuid.js +12 -0
  136. package/test/unit/env.test.js +25 -0
  137. package/test/unit/get-user-media.test.js +40 -0
  138. package/test/unit/ice-parser.test.js +23 -0
  139. package/test/unit/sdp.test.js +45 -0
  140. package/test/unit/signal.test.js +78 -0
  141. package/tsconfig.json +32 -0
  142. package/trtc-cloud-js-sdk.js +0 -1
  143. /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
+ })();