mobility-toolbox-js 2.0.0 → 2.0.1-beta.13
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/api/RoutingAPI.js +15 -0
- package/api/RoutingAPI.test.js +25 -0
- package/api/StopsAPI.js +12 -0
- package/api/StopsAPI.test.js +22 -0
- package/api/TralisAPI.js +359 -0
- package/api/TralisAPI.test.js +67 -0
- package/api/{tralis/TralisAPIUtils.js → TralisAPIUtils.js} +2 -32
- package/api/index.js +3 -3
- package/{ol/README.md → api/typedefs.js} +0 -0
- package/common/Tracker.js +14 -118
- package/common/api/HttpAPI.js +30 -0
- package/common/api/HttpAPI.test.js +50 -0
- package/common/api/WebSocketAPI.js +175 -0
- package/{api/tralis/WebSocketConnector.test.js → common/api/WebSocketAPI.test.js} +100 -145
- package/common/controls/Control.js +26 -91
- package/common/controls/Control.test.js +32 -43
- package/common/index.js +4 -0
- package/common/layers/Layer.js +53 -244
- package/common/layers/Layer.test.js +185 -244
- package/common/mixins/CopyrightMixin.js +20 -44
- package/common/mixins/SearchMixin.js +100 -166
- package/common/mixins/TralisLayerMixin.js +443 -894
- package/common/styles/index.js +4 -4
- package/common/styles/trackerDefaultStyle.js +39 -175
- package/common/styles/trackerDelayStyle.js +2 -11
- package/common/styles/trackerSimpleStyle.js +4 -8
- package/common/trackerConfig.js +61 -99
- package/common/trackerConfig.test.js +15 -17
- package/common/typedefs.js +0 -23
- package/common/utils/createTrackerFilters.js +10 -41
- package/common/utils/createTrackerFilters.test.js +40 -56
- package/common/utils/getMapboxMapCopyrights.js +3 -16
- package/common/utils/getMapboxMapCopyrights.test.js +32 -39
- package/common/utils/getMapboxStyleUrl.js +3 -13
- package/common/utils/getVehiclePosition.js +3 -33
- package/common/utils/index.js +5 -6
- package/common/utils/removeDuplicate.js +3 -17
- package/common/utils/removeDuplicate.test.js +17 -20
- package/common/utils/sortByDelay.js +2 -7
- package/common/utils/timeUtils.js +8 -32
- package/common/utils/timeUtils.test.js +7 -13
- package/index.js +8 -2
- package/mapbox/controls/CopyrightControl.js +9 -38
- package/mapbox/controls/index.js +1 -0
- package/mapbox/index.js +4 -3
- package/mapbox/layers/Layer.js +15 -76
- package/mapbox/layers/Layer.test.js +81 -101
- package/mapbox/layers/TralisLayer.js +46 -193
- package/mapbox/layers/TralisLayer.test.js +12 -14
- package/mapbox/layers/index.js +2 -0
- package/mapbox/utils.js +7 -21
- package/mbt.js +50444 -0
- package/mbt.js.map +7 -0
- package/mbt.min.js +1005 -0
- package/mbt.min.js.map +7 -0
- package/ol/controls/CopyrightControl.js +8 -46
- package/ol/controls/CopyrightControl.test.js +75 -121
- package/ol/controls/RoutingControl.js +167 -532
- package/ol/controls/RoutingControl.test.js +99 -164
- package/ol/controls/StopFinderControl.js +3 -31
- package/ol/controls/StopFinderControl.test.js +18 -29
- package/ol/controls/index.js +3 -0
- package/ol/index.js +5 -13
- package/ol/layers/Layer.js +23 -128
- package/ol/layers/Layer.test.js +79 -102
- package/ol/layers/MapboxLayer.js +62 -237
- package/ol/layers/MapboxLayer.test.js +58 -84
- package/ol/layers/MapboxStyleLayer.js +38 -268
- package/ol/layers/MapboxStyleLayer.test.js +97 -128
- package/ol/layers/MaplibreLayer.js +46 -187
- package/ol/layers/RoutingLayer.js +21 -51
- package/ol/layers/RoutingLayer.test.js +15 -24
- package/ol/layers/TralisLayer.js +102 -276
- package/ol/layers/TralisLayer.test.js +32 -50
- package/ol/layers/VectorLayer.js +3 -24
- package/ol/layers/VectorLayer.test.js +34 -45
- package/ol/layers/WMSLayer.js +15 -57
- package/ol/layers/WMSLayer.test.js +35 -43
- package/ol/layers/index.js +8 -0
- package/ol/styles/fullTrajectoryDelayStyle.js +11 -15
- package/ol/styles/fullTrajectoryStyle.js +17 -25
- package/ol/styles/index.js +2 -2
- package/package.json +35 -62
- package/api/routing/RoutingAPI.js +0 -44
- package/api/routing/RoutingAPI.test.js +0 -41
- package/api/stops/StopsAPI.js +0 -41
- package/api/stops/StopsAPI.test.js +0 -34
- package/api/tralis/TralisAPI.js +0 -731
- package/api/tralis/TralisAPI.test.js +0 -75
- package/api/tralis/WebSocketConnector.js +0 -338
- package/api/tralis/typedefs.js +0 -81
- package/common/api/api.js +0 -64
- package/common/api/api.test.js +0 -68
- package/index.js.map +0 -1
- package/module.js +0 -23
- package/ol/controls/snapshots/RoutingControlRouteGen10.json +0 -58
- package/ol/controls/snapshots/RoutingControlRouteGen100.json +0 -292
- package/ol/controls/snapshots/RoutingControlRouteGen30.json +0 -69
- package/ol/controls/snapshots/RoutingControlRouteGen5.json +0 -58
- package/ol/controls/snapshots/RoutingControlRouteOSM.json +0 -759
- package/ol/controls/snapshots/RoutingControlStation1.json +0 -60
- package/ol/controls/snapshots/RoutingControlStation2.json +0 -49
package/common/Tracker.js
CHANGED
|
@@ -1,43 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import getVehiclePosition from './utils/getVehiclePosition';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Tracker. This class stores and allows to draw trajectories on a canvas.
|
|
7
|
-
* @class
|
|
8
|
-
* @param {Object} options
|
|
9
|
-
* @private
|
|
10
|
-
*/
|
|
1
|
+
import { compose, apply, create } from "ol/transform";
|
|
2
|
+
import getVehiclePosition from "./utils/getVehiclePosition";
|
|
11
3
|
export default class Tracker {
|
|
12
|
-
/**
|
|
13
|
-
* @private
|
|
14
|
-
*/
|
|
15
4
|
constructor(options) {
|
|
16
|
-
/**
|
|
17
|
-
* Function use to style the features displayed.
|
|
18
|
-
* @type {function}
|
|
19
|
-
*/
|
|
20
5
|
this.style = options.style;
|
|
21
|
-
|
|
22
|
-
// we draw directly on the canvas since openlayers is too slow.
|
|
23
|
-
/**
|
|
24
|
-
* HTML <canvas> element.
|
|
25
|
-
* @type {Canvas}
|
|
26
|
-
*/
|
|
27
|
-
this.canvas = options.canvas || document.createElement('canvas');
|
|
6
|
+
this.canvas = options.canvas || document.createElement("canvas");
|
|
28
7
|
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Draw all the trajectories available to the canvas.
|
|
32
|
-
* @param {ViewState} trajectories An array of trajectories.
|
|
33
|
-
* @param {ViewState} viewState The view state of the map.
|
|
34
|
-
* @param {boolean} options.hoverVehicleId The id of the vehicle to highlight.
|
|
35
|
-
* @param {boolean} options.selectedVehicleId The id of the vehicle to select.
|
|
36
|
-
* @param {boolean} options.noInterpolate If true trajectories are not interpolated but
|
|
37
|
-
* drawn at the last known coordinate. Use this for performance optimization
|
|
38
|
-
* during map navigation.
|
|
39
|
-
* @private
|
|
40
|
-
*/
|
|
41
8
|
renderTrajectories(trajectories, viewState, options) {
|
|
42
9
|
const {
|
|
43
10
|
time = Date.now(),
|
|
@@ -45,44 +12,25 @@ export default class Tracker {
|
|
|
45
12
|
center,
|
|
46
13
|
resolution,
|
|
47
14
|
rotation = 0,
|
|
48
|
-
pixelRatio
|
|
15
|
+
pixelRatio
|
|
49
16
|
} = viewState;
|
|
50
17
|
const {
|
|
51
18
|
noInterpolate = false,
|
|
52
19
|
hoverVehicleId,
|
|
53
|
-
selectedVehicleId
|
|
20
|
+
selectedVehicleId
|
|
54
21
|
} = options;
|
|
55
|
-
|
|
56
22
|
const { canvas } = this;
|
|
57
|
-
const context = canvas.getContext(
|
|
23
|
+
const context = canvas.getContext("2d");
|
|
58
24
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
59
|
-
|
|
60
25
|
const [width, height] = size;
|
|
61
|
-
if (
|
|
62
|
-
width &&
|
|
63
|
-
height &&
|
|
64
|
-
(canvas.width !== width || canvas.height !== height)
|
|
65
|
-
) {
|
|
26
|
+
if (width && height && (canvas.width !== width || canvas.height !== height)) {
|
|
66
27
|
[canvas.width, canvas.height] = [width * pixelRatio, height * pixelRatio];
|
|
67
28
|
}
|
|
68
|
-
|
|
69
|
-
const coordinateToPixelTransform = compose(
|
|
70
|
-
create(),
|
|
71
|
-
size[0] / 2,
|
|
72
|
-
size[1] / 2,
|
|
73
|
-
1 / resolution,
|
|
74
|
-
-1 / resolution,
|
|
75
|
-
-rotation,
|
|
76
|
-
-center[0],
|
|
77
|
-
-center[1],
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
// Offscreen canvas has not style attribute
|
|
29
|
+
const coordinateToPixelTransform = compose(create(), size[0] / 2, size[1] / 2, 1 / resolution, -1 / resolution, -rotation, -center[0], -center[1]);
|
|
81
30
|
if (canvas.style) {
|
|
82
31
|
canvas.style.width = `${canvas.width / pixelRatio}px`;
|
|
83
32
|
canvas.style.height = `${canvas.height / pixelRatio}px`;
|
|
84
33
|
}
|
|
85
|
-
|
|
86
34
|
let hoverVehicleImg;
|
|
87
35
|
let hoverVehiclePx;
|
|
88
36
|
let hoverVehicleWidth;
|
|
@@ -92,106 +40,54 @@ export default class Tracker {
|
|
|
92
40
|
let selectedVehicleWidth;
|
|
93
41
|
let selectedVehicleHeight;
|
|
94
42
|
let nbRendered = 0;
|
|
95
|
-
|
|
96
43
|
for (let i = trajectories.length - 1; i >= 0; i -= 1) {
|
|
97
44
|
const trajectory = trajectories[i];
|
|
98
|
-
|
|
99
|
-
// We simplify the trajectory object
|
|
100
45
|
const { train_id: id, timeOffset } = trajectory.properties;
|
|
101
|
-
|
|
102
|
-
// if rotation === null that seems there is no rotation available.
|
|
103
|
-
const { coord, rotation: rotationIcon } = getVehiclePosition(
|
|
104
|
-
time - (timeOffset || 0),
|
|
105
|
-
trajectory,
|
|
106
|
-
noInterpolate,
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
// We store the current vehicle position to the trajectory.
|
|
46
|
+
const { coord, rotation: rotationIcon } = getVehiclePosition(time - (timeOffset || 0), trajectory, noInterpolate);
|
|
110
47
|
trajectories[i].properties.coordinate = coord;
|
|
111
48
|
trajectories[i].properties.rotation = rotationIcon;
|
|
112
|
-
|
|
113
49
|
if (!coord) {
|
|
114
|
-
// eslint-disable-next-line no-continue
|
|
115
50
|
continue;
|
|
116
51
|
}
|
|
117
|
-
|
|
118
52
|
let px = apply(coordinateToPixelTransform, [...coord]);
|
|
119
53
|
if (!px) {
|
|
120
|
-
// eslint-disable-next-line no-continue
|
|
121
54
|
continue;
|
|
122
55
|
}
|
|
123
|
-
|
|
124
56
|
px = px.map((p) => p * pixelRatio);
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
px[0] < 0 ||
|
|
128
|
-
px[0] > canvas.width ||
|
|
129
|
-
px[1] < 0 ||
|
|
130
|
-
px[1] > canvas.height
|
|
131
|
-
) {
|
|
132
|
-
// eslint-disable-next-line no-continue
|
|
57
|
+
if (px[0] < 0 || px[0] > canvas.width || px[1] < 0 || px[1] > canvas.height) {
|
|
133
58
|
continue;
|
|
134
59
|
}
|
|
135
|
-
|
|
136
60
|
const vehicleImg = this.style(trajectory, viewState, options);
|
|
137
61
|
if (!vehicleImg) {
|
|
138
|
-
// eslint-disable-next-line no-continue
|
|
139
62
|
continue;
|
|
140
63
|
}
|
|
141
|
-
|
|
142
64
|
nbRendered += 1;
|
|
143
|
-
|
|
144
65
|
const imgWidth = vehicleImg.width;
|
|
145
66
|
const imgHeight = vehicleImg.height;
|
|
146
|
-
|
|
147
67
|
if (hoverVehicleId !== id && selectedVehicleId !== id) {
|
|
148
|
-
context.drawImage(
|
|
149
|
-
vehicleImg,
|
|
150
|
-
px[0] - imgWidth / 2,
|
|
151
|
-
px[1] - imgHeight / 2,
|
|
152
|
-
imgWidth,
|
|
153
|
-
imgHeight,
|
|
154
|
-
);
|
|
68
|
+
context.drawImage(vehicleImg, px[0] - imgWidth / 2, px[1] - imgHeight / 2, imgWidth, imgHeight);
|
|
155
69
|
}
|
|
156
|
-
|
|
157
70
|
if (hoverVehicleId && hoverVehicleId === id) {
|
|
158
|
-
// Store the canvas to draw it at the end
|
|
159
71
|
hoverVehicleImg = vehicleImg;
|
|
160
72
|
hoverVehiclePx = px;
|
|
161
73
|
hoverVehicleWidth = imgWidth;
|
|
162
74
|
hoverVehicleHeight = imgHeight;
|
|
163
75
|
}
|
|
164
|
-
|
|
165
76
|
if (selectedVehicleId && selectedVehicleId === id) {
|
|
166
|
-
// Store the canvas to draw it at the end
|
|
167
77
|
selectedVehicleImg = vehicleImg;
|
|
168
78
|
selectedVehiclePx = px;
|
|
169
79
|
selectedVehicleWidth = imgWidth;
|
|
170
80
|
selectedVehicleHeight = imgHeight;
|
|
171
81
|
}
|
|
172
82
|
}
|
|
173
|
-
|
|
174
83
|
if (selectedVehicleImg) {
|
|
175
|
-
context.drawImage(
|
|
176
|
-
selectedVehicleImg,
|
|
177
|
-
selectedVehiclePx[0] - selectedVehicleWidth / 2,
|
|
178
|
-
selectedVehiclePx[1] - selectedVehicleHeight / 2,
|
|
179
|
-
selectedVehicleWidth,
|
|
180
|
-
selectedVehicleHeight,
|
|
181
|
-
);
|
|
84
|
+
context.drawImage(selectedVehicleImg, selectedVehiclePx[0] - selectedVehicleWidth / 2, selectedVehiclePx[1] - selectedVehicleHeight / 2, selectedVehicleWidth, selectedVehicleHeight);
|
|
182
85
|
}
|
|
183
|
-
|
|
184
86
|
if (hoverVehicleImg) {
|
|
185
|
-
context.drawImage(
|
|
186
|
-
hoverVehicleImg,
|
|
187
|
-
hoverVehiclePx[0] - hoverVehicleWidth / 2,
|
|
188
|
-
hoverVehiclePx[1] - hoverVehicleHeight / 2,
|
|
189
|
-
hoverVehicleWidth,
|
|
190
|
-
hoverVehicleHeight,
|
|
191
|
-
);
|
|
87
|
+
context.drawImage(hoverVehicleImg, hoverVehiclePx[0] - hoverVehicleWidth / 2, hoverVehiclePx[1] - hoverVehicleHeight / 2, hoverVehicleWidth, hoverVehicleHeight);
|
|
192
88
|
}
|
|
193
89
|
return {
|
|
194
|
-
nbTrajectoriesRendered: nbRendered
|
|
90
|
+
nbTrajectoriesRendered: nbRendered
|
|
195
91
|
};
|
|
196
92
|
}
|
|
197
93
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import qs from "query-string";
|
|
2
|
+
import BaseObject from "ol/Object";
|
|
3
|
+
class HttpApi extends BaseObject {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
super();
|
|
6
|
+
this.url = options.url;
|
|
7
|
+
this.apiKey = options.apiKey;
|
|
8
|
+
}
|
|
9
|
+
fetch(path, params, config) {
|
|
10
|
+
const urlParams = { ...params || {}, key: this.apiKey };
|
|
11
|
+
const clone = { ...urlParams };
|
|
12
|
+
Object.keys(urlParams).forEach((key) => (clone[key] === void 0 || clone[key] === null) && delete clone[key]);
|
|
13
|
+
if (!this.apiKey) {
|
|
14
|
+
return Promise.reject(new Error(`No apiKey defined for request to ${this.url}`));
|
|
15
|
+
}
|
|
16
|
+
return fetch(`${this.url}${path || ""}?${qs.stringify(clone)}`, config).then((response) => {
|
|
17
|
+
try {
|
|
18
|
+
return response.json().then((data) => {
|
|
19
|
+
if (data.error) {
|
|
20
|
+
throw new Error(data.error);
|
|
21
|
+
}
|
|
22
|
+
return data;
|
|
23
|
+
});
|
|
24
|
+
} catch (err) {
|
|
25
|
+
return Promise.reject(new Error(err));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export default HttpApi;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import fetch from "jest-fetch-mock";
|
|
2
|
+
import API from "./HttpAPI";
|
|
3
|
+
let api;
|
|
4
|
+
describe("HttpAPI", () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
global.fetch = fetch;
|
|
7
|
+
fetch.resetMocks();
|
|
8
|
+
api = new API({ url: "https://foo.ch", apiKey: "apiKey" });
|
|
9
|
+
});
|
|
10
|
+
describe("#fetch", () => {
|
|
11
|
+
test("should success", () => {
|
|
12
|
+
fetch.mockResponseOnce(JSON.stringify({ foo: "bar" }));
|
|
13
|
+
return api.fetch("/path", {
|
|
14
|
+
q: "Bern",
|
|
15
|
+
fooUndefined: void 0,
|
|
16
|
+
fooNull: null,
|
|
17
|
+
fooEmpty: ""
|
|
18
|
+
}).then((response) => {
|
|
19
|
+
expect(fetch.mock.calls[0][0]).toEqual("https://foo.ch/path?fooEmpty=&key=apiKey&q=Bern");
|
|
20
|
+
expect(response).toEqual({ foo: "bar" });
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe("should display error message", () => {
|
|
24
|
+
test("reject error", (done) => {
|
|
25
|
+
fetch.mockRejectOnce(new Error("Fake error message"));
|
|
26
|
+
return api.fetch().catch((err) => {
|
|
27
|
+
expect(err.name).toEqual("Error");
|
|
28
|
+
expect(err.message).toEqual("Fake error message");
|
|
29
|
+
done();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
test("if the response is invalid json", (done) => {
|
|
33
|
+
fetch.mockResponseOnce("invalid json");
|
|
34
|
+
api.fetch().catch((err) => {
|
|
35
|
+
expect(err.name).toEqual("FetchError");
|
|
36
|
+
expect(err.message).toEqual("invalid json response body at reason: Unexpected token i in JSON at position 0");
|
|
37
|
+
done();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
test("if the response contains an error message", (done) => {
|
|
41
|
+
fetch.mockResponseOnce('{"error":"foo2"}');
|
|
42
|
+
api.fetch().catch((err) => {
|
|
43
|
+
expect(err.name).toEqual("Error");
|
|
44
|
+
expect(err.message).toEqual("foo2");
|
|
45
|
+
done();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
class WebSocketApi {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.defineProperties();
|
|
4
|
+
}
|
|
5
|
+
defineProperties() {
|
|
6
|
+
Object.defineProperties(this, {
|
|
7
|
+
closed: {
|
|
8
|
+
get: () => !!(this.websocket && this.websocket.readyState === this.websocket.CLOSED)
|
|
9
|
+
},
|
|
10
|
+
closing: {
|
|
11
|
+
get: () => !!(this.websocket && this.websocket.readyState === this.websocket.CLOSING)
|
|
12
|
+
},
|
|
13
|
+
connecting: {
|
|
14
|
+
get: () => !!(this.websocket && this.websocket.readyState === this.websocket.CONNECTING)
|
|
15
|
+
},
|
|
16
|
+
open: {
|
|
17
|
+
get: () => !!(this.websocket && this.websocket.readyState === this.websocket.OPEN)
|
|
18
|
+
},
|
|
19
|
+
messagesOnOpen: {
|
|
20
|
+
value: [],
|
|
21
|
+
writable: true
|
|
22
|
+
},
|
|
23
|
+
subscriptions: {
|
|
24
|
+
value: [],
|
|
25
|
+
writable: true
|
|
26
|
+
},
|
|
27
|
+
subscribed: {
|
|
28
|
+
value: {},
|
|
29
|
+
writable: true
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
static getRequestString(method, params) {
|
|
34
|
+
let reqStr = `${method} ${params.channel}`;
|
|
35
|
+
reqStr += params.args ? ` ${params.args}` : "";
|
|
36
|
+
reqStr += params.id ? ` ${params.id}` : "";
|
|
37
|
+
return reqStr.trim();
|
|
38
|
+
}
|
|
39
|
+
connect(url, onOpen = () => {
|
|
40
|
+
}) {
|
|
41
|
+
if (this.websocket && !this.closed) {
|
|
42
|
+
this.websocket.close();
|
|
43
|
+
}
|
|
44
|
+
this.websocket = new WebSocket(url);
|
|
45
|
+
if (!this.open) {
|
|
46
|
+
this.websocket.addEventListener("open", () => {
|
|
47
|
+
onOpen();
|
|
48
|
+
this.subscribePreviousSubscriptions();
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
onOpen();
|
|
52
|
+
this.subscribePreviousSubscriptions();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
close() {
|
|
56
|
+
if (this.websocket) {
|
|
57
|
+
this.websocket.onclose = null;
|
|
58
|
+
this.websocket.close();
|
|
59
|
+
this.websocket = null;
|
|
60
|
+
this.messagesOnOpen = [];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
send(message) {
|
|
64
|
+
if (!this.websocket) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const send = () => {
|
|
68
|
+
this.websocket.send(message);
|
|
69
|
+
};
|
|
70
|
+
if (!this.open) {
|
|
71
|
+
if (!this.messagesOnOpen.includes(message)) {
|
|
72
|
+
this.messagesOnOpen.push(message);
|
|
73
|
+
this.websocket.addEventListener("open", () => {
|
|
74
|
+
this.messagesOnOpen = [];
|
|
75
|
+
send();
|
|
76
|
+
});
|
|
77
|
+
this.websocket.addEventListener("close", () => {
|
|
78
|
+
this.messagesOnOpen = [];
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
} else if (!this.messagesOnOpen.includes(message)) {
|
|
82
|
+
send();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
listen(params, cb, errorCb) {
|
|
86
|
+
this.unlisten(params, cb);
|
|
87
|
+
const onMessage = (evt) => {
|
|
88
|
+
let data = {};
|
|
89
|
+
try {
|
|
90
|
+
data = JSON.parse(evt.data);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.error("WebSocket: unable to parse JSON data", err, evt.data);
|
|
93
|
+
}
|
|
94
|
+
let source = params.channel;
|
|
95
|
+
source += params.args ? ` ${params.args}` : "";
|
|
96
|
+
const contents = data.source === "buffer" ? data.content : [data];
|
|
97
|
+
contents.forEach((content) => {
|
|
98
|
+
if (content?.source === source && (!params.id || params.id === data.client_reference)) {
|
|
99
|
+
cb(content);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
if (this.websocket) {
|
|
104
|
+
this.websocket.addEventListener("message", onMessage);
|
|
105
|
+
if (errorCb) {
|
|
106
|
+
this.websocket.addEventListener("error", errorCb);
|
|
107
|
+
this.websocket.addEventListener("close", errorCb);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { onMessageCb: onMessage, onErrorCb: errorCb };
|
|
111
|
+
}
|
|
112
|
+
unlisten(params, cb) {
|
|
113
|
+
if (!this.websocket) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.subscriptions.filter((s) => s.params.channel === params.channel && (!cb || s.cb === cb)).forEach(({ onMessageCb, onErrorCb }) => {
|
|
117
|
+
if (this.websocket) {
|
|
118
|
+
this.websocket.removeEventListener("message", onMessageCb);
|
|
119
|
+
if (onErrorCb) {
|
|
120
|
+
this.websocket.removeEventListener("error", onErrorCb);
|
|
121
|
+
this.websocket.removeEventListener("close", onErrorCb);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
get(params, cb, errorCb) {
|
|
127
|
+
const reqStr = WebSocketApi.getRequestString("GET", params);
|
|
128
|
+
this.send(reqStr);
|
|
129
|
+
this.listen(params, cb, errorCb);
|
|
130
|
+
}
|
|
131
|
+
subscribe(params, cb, errorCb, quiet = false) {
|
|
132
|
+
const { onMessageCb, onErrorCb } = this.listen(params, cb, errorCb);
|
|
133
|
+
const reqStr = WebSocketApi.getRequestString("", params);
|
|
134
|
+
const index = this.subscriptions.findIndex((subcr) => params.channel === subcr.params.channel && cb === subcr.cb);
|
|
135
|
+
const newSubscr = { params, cb, errorCb, onMessageCb, onErrorCb, quiet };
|
|
136
|
+
if (index > -1) {
|
|
137
|
+
this.subscriptions[index] = newSubscr;
|
|
138
|
+
} else {
|
|
139
|
+
this.subscriptions.push(newSubscr);
|
|
140
|
+
}
|
|
141
|
+
if (!this.subscribed[reqStr]) {
|
|
142
|
+
if (!newSubscr.quiet) {
|
|
143
|
+
this.send(`GET ${reqStr}`);
|
|
144
|
+
this.send(`SUB ${reqStr}`);
|
|
145
|
+
}
|
|
146
|
+
this.subscribed[reqStr] = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
unsubscribe(source, cb) {
|
|
150
|
+
const toRemove = this.subscriptions.filter((s) => s.params.channel === source && (!cb || s.cb === cb));
|
|
151
|
+
toRemove.forEach(({ onMessageCb, onErrorCb }) => {
|
|
152
|
+
if (this.websocket) {
|
|
153
|
+
this.websocket.removeEventListener("message", onMessageCb);
|
|
154
|
+
if (onErrorCb) {
|
|
155
|
+
this.websocket.removeEventListener("error", onErrorCb);
|
|
156
|
+
this.websocket.removeEventListener("close", onErrorCb);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
this.subscriptions = this.subscriptions.filter((s) => s.params.channel !== source || cb && s.cb !== cb);
|
|
161
|
+
if (source && this.subscribed[source] && !this.subscriptions.find((s) => s.params.channel === source) && toRemove.find((subscr) => !subscr.quiet)) {
|
|
162
|
+
this.send(`DEL ${source}`);
|
|
163
|
+
this.subscribed[source] = false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
subscribePreviousSubscriptions() {
|
|
167
|
+
Object.keys(this.subscribed).forEach((key) => {
|
|
168
|
+
this.subscribed[key] = false;
|
|
169
|
+
});
|
|
170
|
+
[...this.subscriptions].forEach((s) => {
|
|
171
|
+
this.subscribe(s.params, s.cb, s.errorCb, s.quiet);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export default WebSocketApi;
|