gis-common 4.2.4 → 4.2.6
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/dist/core/Color.d.ts +67 -0
- package/dist/core/HashMap.d.ts +14 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/gis-common.es.js +1423 -1127
- package/dist/gis-common.umd.js +1 -1
- package/dist/utils/BrowserUtil.d.ts +65 -16
- package/dist/utils/CommUtil.d.ts +1 -1
- package/dist/utils/CoordsUtil.d.ts +14 -6
- package/dist/utils/index.d.ts +0 -1
- package/package.json +1 -1
- package/dist/utils/ColorUtil.d.ts +0 -37
package/dist/gis-common.es.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
var _a;
|
|
4
5
|
import { connect } from "mqtt-browser";
|
|
5
6
|
var EventType = /* @__PURE__ */ ((EventType2) => {
|
|
6
7
|
EventType2["MAP_RENDER"] = "mapRender";
|
|
@@ -165,580 +166,268 @@ class Cookie {
|
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
const CommUtils = {
|
|
170
|
+
/**
|
|
171
|
+
* 获取数据类型
|
|
172
|
+
*
|
|
173
|
+
* @param data 待判断的数据
|
|
174
|
+
* @returns 返回数据类型字符串
|
|
175
|
+
*/
|
|
176
|
+
getDataType(data) {
|
|
177
|
+
return Object.prototype.toString.call(data).slice(8, -1);
|
|
173
178
|
},
|
|
174
|
-
|
|
175
|
-
return
|
|
179
|
+
asArray(obj) {
|
|
180
|
+
return this.isEmpty(obj) ? [] : Array.isArray(obj) ? obj : [obj];
|
|
181
|
+
},
|
|
182
|
+
asNumber(a) {
|
|
183
|
+
return Number.isNaN(Number(a)) ? 0 : Number(a);
|
|
176
184
|
},
|
|
177
185
|
/**
|
|
178
|
-
*
|
|
186
|
+
* 将值转换为字符串
|
|
179
187
|
*
|
|
180
|
-
* @param
|
|
181
|
-
* @returns
|
|
188
|
+
* @param value 要转换的值
|
|
189
|
+
* @returns 转换后的字符串,如果值为空,则返回空字符串
|
|
182
190
|
*/
|
|
183
|
-
|
|
184
|
-
|
|
191
|
+
asString(value) {
|
|
192
|
+
if (this.isEmpty(value)) {
|
|
193
|
+
return "";
|
|
194
|
+
} else {
|
|
195
|
+
switch (this.getDataType(value)) {
|
|
196
|
+
case "Object":
|
|
197
|
+
case "Array":
|
|
198
|
+
return JSON.stringify(value);
|
|
199
|
+
default:
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
185
203
|
},
|
|
186
204
|
/**
|
|
187
|
-
*
|
|
205
|
+
* 判断传入的值是否为空
|
|
188
206
|
*
|
|
189
|
-
* @param
|
|
190
|
-
* @returns
|
|
207
|
+
* @param value 待判断的值
|
|
208
|
+
* @returns 返回布尔值,表示是否为空
|
|
191
209
|
*/
|
|
192
|
-
|
|
193
|
-
|
|
210
|
+
isEmpty(value) {
|
|
211
|
+
if (value == null) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
const type = this.getDataType(value);
|
|
215
|
+
switch (type) {
|
|
216
|
+
case "String":
|
|
217
|
+
return value.trim() === "";
|
|
218
|
+
case "Array":
|
|
219
|
+
return !value.length;
|
|
220
|
+
case "Object":
|
|
221
|
+
return !Object.keys(value).length;
|
|
222
|
+
case "Boolean":
|
|
223
|
+
return !value;
|
|
224
|
+
default:
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
194
227
|
},
|
|
195
|
-
|
|
196
|
-
|
|
228
|
+
/**
|
|
229
|
+
* 将JSON对象转换为FormData对象
|
|
230
|
+
*
|
|
231
|
+
* @param json 待转换的JSON对象,其属性值为字符串或Blob类型
|
|
232
|
+
* @returns 转换后的FormData对象
|
|
233
|
+
*/
|
|
234
|
+
json2form(json) {
|
|
235
|
+
const formData = new FormData();
|
|
236
|
+
Object.keys(json).forEach((key) => {
|
|
237
|
+
formData.append(key, json[key] instanceof Object ? JSON.stringify(json[key]) : json[key]);
|
|
238
|
+
});
|
|
239
|
+
return formData;
|
|
197
240
|
},
|
|
198
241
|
/**
|
|
199
|
-
*
|
|
242
|
+
* 生成GUID
|
|
200
243
|
*
|
|
201
|
-
* @
|
|
202
|
-
* @param min 最小值
|
|
203
|
-
* @param max 最大值
|
|
204
|
-
* @returns 返回限制后的数值
|
|
244
|
+
* @returns 返回一个由8个16进制数组成的GUID字符串
|
|
205
245
|
*/
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
246
|
+
guid() {
|
|
247
|
+
const S4 = function() {
|
|
248
|
+
return ((1 + Math.random()) * 65536 | 0).toString(16).substring(1);
|
|
249
|
+
};
|
|
250
|
+
return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4();
|
|
251
|
+
},
|
|
252
|
+
/**
|
|
253
|
+
* 将参数进行解码并返回解码后的字符串
|
|
254
|
+
*
|
|
255
|
+
* @param args 参数
|
|
256
|
+
* @returns 解码后的字符串
|
|
257
|
+
*/
|
|
258
|
+
decodeDict(...args) {
|
|
259
|
+
let res = "";
|
|
260
|
+
if (args.length > 1) {
|
|
261
|
+
const items = args.slice(1, args.length % 2 === 0 ? args.length - 1 : args.length);
|
|
262
|
+
for (let i = 0; i < items.length; i = i + 2) {
|
|
263
|
+
const item = items[i];
|
|
264
|
+
if (args[0] === item) {
|
|
265
|
+
res = items[i + 1];
|
|
266
|
+
}
|
|
217
267
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const canvas = el;
|
|
221
|
-
if (canvas.getContext) {
|
|
222
|
-
this.context = canvas.getContext("2d");
|
|
223
|
-
} else {
|
|
224
|
-
throw new Error("getContext is not available on this element");
|
|
268
|
+
if (!res && args.length % 2 === 0) {
|
|
269
|
+
res = args[args.length - 1];
|
|
225
270
|
}
|
|
226
271
|
} else {
|
|
227
|
-
|
|
272
|
+
res = args[0];
|
|
228
273
|
}
|
|
229
|
-
|
|
274
|
+
return res;
|
|
275
|
+
},
|
|
230
276
|
/**
|
|
231
|
-
*
|
|
277
|
+
* 将一个或多个对象的所有可枚举属性复制到目标对象。
|
|
232
278
|
*
|
|
233
|
-
* @param
|
|
234
|
-
* @param
|
|
235
|
-
* @
|
|
236
|
-
* @throws 当画布上下文不存在时抛出错误
|
|
279
|
+
* @param dest 目标对象,用于接收复制的属性。
|
|
280
|
+
* @param args 一个或多个源对象,用于提供要复制的属性。
|
|
281
|
+
* @returns 返回目标对象,包含所有复制的属性。
|
|
237
282
|
*/
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
283
|
+
extend(dest, ...args) {
|
|
284
|
+
let i, j, len, src;
|
|
285
|
+
for (j = 0, len = args.length; j < len; j++) {
|
|
286
|
+
src = args[j];
|
|
287
|
+
for (i in src) {
|
|
288
|
+
dest[i] = src[i];
|
|
289
|
+
}
|
|
241
290
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const color = options.color || "#000";
|
|
245
|
-
this.context.lineWidth = width;
|
|
246
|
-
this.context.strokeStyle = color;
|
|
247
|
-
this.context.moveTo(startX, startY);
|
|
248
|
-
this.context.lineTo(endX, endY);
|
|
249
|
-
this.context.stroke();
|
|
250
|
-
}
|
|
291
|
+
return dest;
|
|
292
|
+
},
|
|
251
293
|
/**
|
|
252
|
-
*
|
|
294
|
+
* 将扁平化数组转换为树形结构数组
|
|
253
295
|
*
|
|
254
|
-
* @param
|
|
255
|
-
* @param
|
|
256
|
-
* @param
|
|
257
|
-
* @param
|
|
258
|
-
* @
|
|
259
|
-
* @param anticlockwise 是否逆时针绘制
|
|
260
|
-
* @param isFill 是否填充
|
|
261
|
-
* @param bgColor 背景颜色
|
|
262
|
-
* @throws 当Canvas context为null或undefined时抛出错误
|
|
296
|
+
* @param data 扁平化数组
|
|
297
|
+
* @param idPropertyName 数据中标识id的字段名,默认为'id'
|
|
298
|
+
* @param parentIdPropertyName 数据中标识父节点id的字段名,默认为'parentId'
|
|
299
|
+
* @param childrenPropertyName 树形结构中标识子节点的字段名,默认为'children'
|
|
300
|
+
* @returns 转换后的树形结构数组
|
|
263
301
|
*/
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
this.context.beginPath();
|
|
276
|
-
this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
|
|
277
|
-
this.context.stroke();
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
static createCanvas(width = 1, height = 1) {
|
|
281
|
-
const canvas = document.createElement("canvas");
|
|
282
|
-
if (width) {
|
|
283
|
-
canvas.width = width;
|
|
284
|
-
}
|
|
285
|
-
if (height) {
|
|
286
|
-
canvas.height = height;
|
|
287
|
-
}
|
|
288
|
-
return canvas;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
class EventDispatcher {
|
|
292
|
-
constructor() {
|
|
293
|
-
__publicField(this, "_listeners");
|
|
294
|
-
__publicField(this, "_mutex", {});
|
|
295
|
-
__publicField(this, "_context");
|
|
296
|
-
}
|
|
297
|
-
addEventListener(type, listener, context, mutexStatus) {
|
|
298
|
-
if (this._listeners === void 0) this._listeners = {};
|
|
299
|
-
this._context = context;
|
|
300
|
-
const mutex = this._mutex;
|
|
301
|
-
const listeners = this._listeners;
|
|
302
|
-
if (listeners[type] === void 0) {
|
|
303
|
-
listeners[type] = [];
|
|
304
|
-
}
|
|
305
|
-
if (listeners[type].indexOf(listener) === -1) {
|
|
306
|
-
if (mutexStatus) {
|
|
307
|
-
mutex[type] = listener;
|
|
302
|
+
convertToTree2(data, idPropertyName = "id", parentIdPropertyName = "parentId", childrenPropertyName = "children") {
|
|
303
|
+
const result = [];
|
|
304
|
+
function buildChildren(item) {
|
|
305
|
+
const children = data.filter((item2) => item2[parentIdPropertyName] === item[idPropertyName]).map((child) => {
|
|
306
|
+
if (!result.some((r) => r[idPropertyName] === child[idPropertyName])) {
|
|
307
|
+
buildChildren(child);
|
|
308
|
+
}
|
|
309
|
+
return child;
|
|
310
|
+
});
|
|
311
|
+
if (children.length > 0) {
|
|
312
|
+
item[childrenPropertyName] = children;
|
|
308
313
|
}
|
|
309
|
-
listeners[type].push(listener);
|
|
310
|
-
}
|
|
311
|
-
return this;
|
|
312
|
-
}
|
|
313
|
-
hasEventListener(type, listener) {
|
|
314
|
-
if (this._listeners === null || this._listeners === void 0) return false;
|
|
315
|
-
const listeners = this._listeners;
|
|
316
|
-
return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
|
|
317
|
-
}
|
|
318
|
-
removeEventListener(type, listener) {
|
|
319
|
-
if (this._listeners === void 0) return;
|
|
320
|
-
const listeners = this._listeners;
|
|
321
|
-
const listenerArray = listeners[type];
|
|
322
|
-
if (this._mutex[type] === listener) {
|
|
323
|
-
this._mutex[type] = null;
|
|
324
314
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
315
|
+
data.forEach((item) => {
|
|
316
|
+
if (!data.some((other) => other[parentIdPropertyName] === item[idPropertyName])) {
|
|
317
|
+
buildChildren(item);
|
|
318
|
+
result.push(item);
|
|
329
319
|
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const find = array.find((item) => item === this._mutex[event.type]);
|
|
341
|
-
if (find) {
|
|
342
|
-
find.call(this._context || this, event);
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
for (let i = 0, l = array.length; i < l; i++) {
|
|
347
|
-
const item = array[i];
|
|
348
|
-
if (typeof item === "function") {
|
|
349
|
-
item.call(this._context || this, event);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
removeAllListener() {
|
|
355
|
-
this._mutex = {};
|
|
356
|
-
for (const key in this._listeners) {
|
|
357
|
-
this._listeners[key] = [];
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
class HashMap extends Map {
|
|
362
|
-
isEmpty() {
|
|
363
|
-
return this.size === 0;
|
|
364
|
-
}
|
|
365
|
-
_values() {
|
|
366
|
-
return Array.from(this.values());
|
|
367
|
-
}
|
|
368
|
-
_keys() {
|
|
369
|
-
return Array.from(this.keys());
|
|
370
|
-
}
|
|
371
|
-
_entries() {
|
|
372
|
-
return Array.from(this.entries());
|
|
373
|
-
}
|
|
374
|
-
fromEntries() {
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
HashMap.prototype.fromEntries = function(array = []) {
|
|
378
|
-
const hashMap = new HashMap();
|
|
379
|
-
array.forEach((element) => {
|
|
380
|
-
if (Array.isArray(element) && element.length === 2) {
|
|
381
|
-
hashMap.set(element[0], element[1]);
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
return hashMap;
|
|
385
|
-
};
|
|
386
|
-
class WebSocketClient extends EventDispatcher {
|
|
387
|
-
constructor(url = "ws://127.0.0.1:10088") {
|
|
388
|
-
super();
|
|
389
|
-
__publicField(this, "maxCheckTimes", 10);
|
|
390
|
-
__publicField(this, "url");
|
|
391
|
-
__publicField(this, "checkTimes", 0);
|
|
392
|
-
__publicField(this, "connectStatus", false);
|
|
393
|
-
__publicField(this, "client", null);
|
|
394
|
-
this.maxCheckTimes = 10;
|
|
395
|
-
this.url = url;
|
|
396
|
-
this.checkTimes = 0;
|
|
397
|
-
this.connect();
|
|
398
|
-
this.connCheckStatus(this.maxCheckTimes);
|
|
399
|
-
}
|
|
400
|
-
connect() {
|
|
401
|
-
this.disconnect();
|
|
402
|
-
if (this.url) {
|
|
320
|
+
});
|
|
321
|
+
return result;
|
|
322
|
+
},
|
|
323
|
+
/**
|
|
324
|
+
* 异步加载script
|
|
325
|
+
*
|
|
326
|
+
* @param {*} url
|
|
327
|
+
*/
|
|
328
|
+
asyncLoadScript(url) {
|
|
329
|
+
return new Promise((resolve, reject) => {
|
|
403
330
|
try {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
});
|
|
331
|
+
const oscript = document.createElement("script");
|
|
332
|
+
oscript.type = "text/javascript";
|
|
333
|
+
oscript.src = url;
|
|
334
|
+
if ("readyState" in oscript) {
|
|
335
|
+
oscript.onreadystatechange = function() {
|
|
336
|
+
if (oscript.readyState === "complete" || oscript.readyState === "loaded") {
|
|
337
|
+
resolve(oscript);
|
|
338
|
+
}
|
|
413
339
|
};
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
type: EventType.WEB_SOCKET_MESSAGE,
|
|
418
|
-
message
|
|
419
|
-
});
|
|
340
|
+
} else {
|
|
341
|
+
oscript.onload = function() {
|
|
342
|
+
resolve(oscript);
|
|
420
343
|
};
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
type: EventType.WEB_SOCKET_CLOSE,
|
|
424
|
-
message
|
|
425
|
-
});
|
|
344
|
+
oscript.onerror = function() {
|
|
345
|
+
reject(new Error("Script failed to load for URL: " + url));
|
|
426
346
|
};
|
|
427
|
-
if (this.checkTimes === this.maxCheckTimes) {
|
|
428
|
-
this.client.onerror = function(message) {
|
|
429
|
-
self.dispatchEvent({
|
|
430
|
-
type: EventType.WEB_SOCKET_ERROR,
|
|
431
|
-
message
|
|
432
|
-
});
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
347
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
disconnect() {
|
|
442
|
-
if (this.client) {
|
|
443
|
-
try {
|
|
444
|
-
console.log("ws断开连接" + this.url);
|
|
445
|
-
this.client.close();
|
|
446
|
-
this.client = null;
|
|
447
|
-
} catch (ex) {
|
|
448
|
-
this.client = null;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
connCheckStatus(times) {
|
|
453
|
-
if (this.checkTimes > times) return;
|
|
454
|
-
setTimeout(() => {
|
|
455
|
-
this.checkTimes++;
|
|
456
|
-
if (this.client && this.client.readyState !== 0 && this.client.readyState !== 1) {
|
|
457
|
-
this.connect();
|
|
458
|
-
}
|
|
459
|
-
this.connCheckStatus(times);
|
|
460
|
-
}, 2e3);
|
|
461
|
-
}
|
|
462
|
-
send(message) {
|
|
463
|
-
if (this.client && this.client.readyState === 1) {
|
|
464
|
-
this.client.send(message);
|
|
465
|
-
return true;
|
|
466
|
-
}
|
|
467
|
-
console.error(this.url + "消息发送失败:" + message);
|
|
468
|
-
return false;
|
|
469
|
-
}
|
|
470
|
-
heartbeat() {
|
|
471
|
-
setTimeout(() => {
|
|
472
|
-
if (this.client && this.client.readyState === 1) {
|
|
473
|
-
this.send("HeartBeat");
|
|
348
|
+
document.body.appendChild(oscript);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
reject(error);
|
|
474
351
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}, 1e3);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
const CommUtil = {
|
|
352
|
+
});
|
|
353
|
+
},
|
|
481
354
|
/**
|
|
482
|
-
*
|
|
355
|
+
* 加载样式文件
|
|
483
356
|
*
|
|
484
|
-
* @param
|
|
485
|
-
* @returns
|
|
357
|
+
* @param urls 样式文件URL数组
|
|
358
|
+
* @returns 无返回值
|
|
486
359
|
*/
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
360
|
+
loadStyle(urls) {
|
|
361
|
+
urls.forEach((url) => {
|
|
362
|
+
const css = document.createElement("link");
|
|
363
|
+
css.href = url;
|
|
364
|
+
css.rel = "stylesheet";
|
|
365
|
+
css.type = "text/css";
|
|
366
|
+
css.onerror = function() {
|
|
367
|
+
console.error(`Style loading failed for URL: ${url}`);
|
|
368
|
+
};
|
|
369
|
+
document.head.appendChild(css);
|
|
370
|
+
});
|
|
492
371
|
},
|
|
493
|
-
|
|
494
|
-
|
|
372
|
+
/**
|
|
373
|
+
* 将模板字符串中的占位符替换为给定对象中的值
|
|
374
|
+
*
|
|
375
|
+
* @param str 模板字符串
|
|
376
|
+
* @param data 包含替换值的对象
|
|
377
|
+
* @returns 替换后的字符串
|
|
378
|
+
* @throws 当对象中没有找到与占位符对应的值时,抛出错误
|
|
379
|
+
*/
|
|
380
|
+
template(str, data) {
|
|
381
|
+
const templateRe = /\{ *([\w_-]+) *\}/g;
|
|
382
|
+
return str.replace(templateRe, (match, key) => {
|
|
383
|
+
const value = data[key];
|
|
384
|
+
if (value === void 0) {
|
|
385
|
+
throw new Error(`${ErrorType.JSON_VALUE_ERROR}: ${match}`);
|
|
386
|
+
} else if (typeof value === "function") {
|
|
387
|
+
return value(data);
|
|
388
|
+
} else {
|
|
389
|
+
return value;
|
|
390
|
+
}
|
|
391
|
+
});
|
|
495
392
|
},
|
|
496
393
|
/**
|
|
497
|
-
*
|
|
394
|
+
* 删除对象中所有值为空的属性
|
|
498
395
|
*
|
|
499
|
-
* @param
|
|
500
|
-
* @returns
|
|
396
|
+
* @param data 待处理的对象
|
|
397
|
+
* @returns 返回处理后的对象
|
|
501
398
|
*/
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
399
|
+
deleteEmptyProperty(data) {
|
|
400
|
+
return Object.fromEntries(
|
|
401
|
+
Object.keys(data).filter((d) => !this.isEmpty(data[d])).map((i) => [i, data[i]])
|
|
402
|
+
);
|
|
403
|
+
},
|
|
404
|
+
deepAssign(target, ...sources) {
|
|
405
|
+
if (typeof target !== "object" || target === null) {
|
|
406
|
+
target = {};
|
|
407
|
+
}
|
|
408
|
+
for (const source of sources) {
|
|
409
|
+
if (typeof source === "object" && source !== null) {
|
|
410
|
+
for (const key in source) {
|
|
411
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
412
|
+
if (typeof source[key] === "object" && source[key] !== null) {
|
|
413
|
+
if (!target[key]) {
|
|
414
|
+
target[key] = Array.isArray(source[key]) ? [] : {};
|
|
415
|
+
}
|
|
416
|
+
this.deepAssign(target[key], source[key]);
|
|
417
|
+
} else {
|
|
418
|
+
target[key] = source[key];
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
512
422
|
}
|
|
513
423
|
}
|
|
424
|
+
return target;
|
|
514
425
|
},
|
|
515
426
|
/**
|
|
516
|
-
*
|
|
427
|
+
* 复制文本到剪贴板
|
|
517
428
|
*
|
|
518
|
-
* @param
|
|
519
|
-
* @returns
|
|
520
|
-
*/
|
|
521
|
-
isEmpty(value) {
|
|
522
|
-
if (value == null) {
|
|
523
|
-
return true;
|
|
524
|
-
}
|
|
525
|
-
const type = this.getDataType(value);
|
|
526
|
-
switch (type) {
|
|
527
|
-
case "String":
|
|
528
|
-
return value.trim() === "";
|
|
529
|
-
case "Array":
|
|
530
|
-
return !value.length;
|
|
531
|
-
case "Object":
|
|
532
|
-
return !Object.keys(value).length;
|
|
533
|
-
case "Boolean":
|
|
534
|
-
return !value;
|
|
535
|
-
default:
|
|
536
|
-
return false;
|
|
537
|
-
}
|
|
538
|
-
},
|
|
539
|
-
/**
|
|
540
|
-
* 将JSON对象转换为FormData对象
|
|
541
|
-
*
|
|
542
|
-
* @param json 待转换的JSON对象,其属性值为字符串或Blob类型
|
|
543
|
-
* @returns 转换后的FormData对象
|
|
544
|
-
*/
|
|
545
|
-
json2form(json) {
|
|
546
|
-
const formData = new FormData();
|
|
547
|
-
Object.keys(json).forEach((key) => {
|
|
548
|
-
formData.append(key, json[key] instanceof Object ? JSON.stringify(json[key]) : json[key]);
|
|
549
|
-
});
|
|
550
|
-
return formData;
|
|
551
|
-
},
|
|
552
|
-
/**
|
|
553
|
-
* 生成GUID
|
|
554
|
-
*
|
|
555
|
-
* @returns 返回一个由8个16进制数组成的GUID字符串
|
|
556
|
-
*/
|
|
557
|
-
guid() {
|
|
558
|
-
const S4 = function() {
|
|
559
|
-
return ((1 + Math.random()) * 65536 | 0).toString(16).substring(1);
|
|
560
|
-
};
|
|
561
|
-
return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4();
|
|
562
|
-
},
|
|
563
|
-
/**
|
|
564
|
-
* 将参数进行解码并返回解码后的字符串
|
|
565
|
-
*
|
|
566
|
-
* @param args 参数
|
|
567
|
-
* @returns 解码后的字符串
|
|
568
|
-
*/
|
|
569
|
-
decodeDict(...args) {
|
|
570
|
-
let res = "";
|
|
571
|
-
if (args.length > 1) {
|
|
572
|
-
const items = args.slice(1, args.length % 2 === 0 ? args.length - 1 : args.length);
|
|
573
|
-
for (let i = 0; i < items.length; i = i + 2) {
|
|
574
|
-
const item = items[i];
|
|
575
|
-
if (args[0] === item) {
|
|
576
|
-
res = items[i + 1];
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
if (!res && args.length % 2 === 0) {
|
|
580
|
-
res = args[args.length - 1];
|
|
581
|
-
}
|
|
582
|
-
} else {
|
|
583
|
-
res = args[0];
|
|
584
|
-
}
|
|
585
|
-
return res;
|
|
586
|
-
},
|
|
587
|
-
/**
|
|
588
|
-
* 将一个或多个对象的所有可枚举属性复制到目标对象。
|
|
589
|
-
*
|
|
590
|
-
* @param dest 目标对象,用于接收复制的属性。
|
|
591
|
-
* @param args 一个或多个源对象,用于提供要复制的属性。
|
|
592
|
-
* @returns 返回目标对象,包含所有复制的属性。
|
|
593
|
-
*/
|
|
594
|
-
extend(dest, ...args) {
|
|
595
|
-
let i, j, len, src;
|
|
596
|
-
for (j = 0, len = args.length; j < len; j++) {
|
|
597
|
-
src = args[j];
|
|
598
|
-
for (i in src) {
|
|
599
|
-
dest[i] = src[i];
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
return dest;
|
|
603
|
-
},
|
|
604
|
-
/**
|
|
605
|
-
* 将扁平化数组转换为树形结构数组
|
|
606
|
-
*
|
|
607
|
-
* @param data 扁平化数组
|
|
608
|
-
* @param idPropertyName 数据中标识id的字段名,默认为'id'
|
|
609
|
-
* @param parentIdPropertyName 数据中标识父节点id的字段名,默认为'parentId'
|
|
610
|
-
* @param childrenPropertyName 树形结构中标识子节点的字段名,默认为'children'
|
|
611
|
-
* @returns 转换后的树形结构数组
|
|
612
|
-
*/
|
|
613
|
-
convertToTree2(data, idPropertyName = "id", parentIdPropertyName = "parentId", childrenPropertyName = "children") {
|
|
614
|
-
const result = [];
|
|
615
|
-
function buildChildren(item) {
|
|
616
|
-
const children = data.filter((item2) => item2[parentIdPropertyName] === item[idPropertyName]).map((child) => {
|
|
617
|
-
if (!result.some((r) => r[idPropertyName] === child[idPropertyName])) {
|
|
618
|
-
buildChildren(child);
|
|
619
|
-
}
|
|
620
|
-
return child;
|
|
621
|
-
});
|
|
622
|
-
if (children.length > 0) {
|
|
623
|
-
item[childrenPropertyName] = children;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
data.forEach((item) => {
|
|
627
|
-
if (!data.some((other) => other[parentIdPropertyName] === item[idPropertyName])) {
|
|
628
|
-
buildChildren(item);
|
|
629
|
-
result.push(item);
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
|
-
return result;
|
|
633
|
-
},
|
|
634
|
-
/**
|
|
635
|
-
* 异步加载script
|
|
636
|
-
*
|
|
637
|
-
* @param {*} url
|
|
638
|
-
*/
|
|
639
|
-
asyncLoadScript(url) {
|
|
640
|
-
return new Promise((resolve, reject) => {
|
|
641
|
-
try {
|
|
642
|
-
const oscript = document.createElement("script");
|
|
643
|
-
oscript.type = "text/javascript";
|
|
644
|
-
oscript.src = url;
|
|
645
|
-
if ("readyState" in oscript) {
|
|
646
|
-
oscript.onreadystatechange = function() {
|
|
647
|
-
if (oscript.readyState === "complete" || oscript.readyState === "loaded") {
|
|
648
|
-
resolve(oscript);
|
|
649
|
-
}
|
|
650
|
-
};
|
|
651
|
-
} else {
|
|
652
|
-
oscript.onload = function() {
|
|
653
|
-
resolve(oscript);
|
|
654
|
-
};
|
|
655
|
-
oscript.onerror = function() {
|
|
656
|
-
reject(new Error("Script failed to load for URL: " + url));
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
document.body.appendChild(oscript);
|
|
660
|
-
} catch (error) {
|
|
661
|
-
reject(error);
|
|
662
|
-
}
|
|
663
|
-
});
|
|
664
|
-
},
|
|
665
|
-
/**
|
|
666
|
-
* 加载样式文件
|
|
667
|
-
*
|
|
668
|
-
* @param urls 样式文件URL数组
|
|
669
|
-
* @returns 无返回值
|
|
670
|
-
*/
|
|
671
|
-
loadStyle(urls) {
|
|
672
|
-
urls.forEach((url) => {
|
|
673
|
-
const css = document.createElement("link");
|
|
674
|
-
css.href = url;
|
|
675
|
-
css.rel = "stylesheet";
|
|
676
|
-
css.type = "text/css";
|
|
677
|
-
css.onerror = function() {
|
|
678
|
-
console.error(`Style loading failed for URL: ${url}`);
|
|
679
|
-
};
|
|
680
|
-
document.head.appendChild(css);
|
|
681
|
-
});
|
|
682
|
-
},
|
|
683
|
-
/**
|
|
684
|
-
* 将模板字符串中的占位符替换为给定对象中的值
|
|
685
|
-
*
|
|
686
|
-
* @param str 模板字符串
|
|
687
|
-
* @param data 包含替换值的对象
|
|
688
|
-
* @returns 替换后的字符串
|
|
689
|
-
* @throws 当对象中没有找到与占位符对应的值时,抛出错误
|
|
690
|
-
*/
|
|
691
|
-
template(str, data) {
|
|
692
|
-
const templateRe = /\{ *([\w_-]+) *\}/g;
|
|
693
|
-
return str.replace(templateRe, (match, key) => {
|
|
694
|
-
const value = data[key];
|
|
695
|
-
if (value === void 0) {
|
|
696
|
-
throw new Error(`${ErrorType.JSON_VALUE_ERROR}: ${match}`);
|
|
697
|
-
} else if (typeof value === "function") {
|
|
698
|
-
return value(data);
|
|
699
|
-
} else {
|
|
700
|
-
return value;
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
},
|
|
704
|
-
/**
|
|
705
|
-
* 删除对象中所有值为空的属性
|
|
706
|
-
*
|
|
707
|
-
* @param data 待处理的对象
|
|
708
|
-
* @returns 返回处理后的对象
|
|
709
|
-
*/
|
|
710
|
-
deleteEmptyProperty(data) {
|
|
711
|
-
return Object.fromEntries(
|
|
712
|
-
Object.keys(data).filter((d) => !this.isEmpty(data[d])).map((i) => [i, data[i]])
|
|
713
|
-
);
|
|
714
|
-
},
|
|
715
|
-
deepAssign(target, ...sources) {
|
|
716
|
-
if (typeof target !== "object" || target === null) {
|
|
717
|
-
target = {};
|
|
718
|
-
}
|
|
719
|
-
for (const source of sources) {
|
|
720
|
-
if (typeof source === "object" && source !== null) {
|
|
721
|
-
for (const key in source) {
|
|
722
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
723
|
-
if (typeof source[key] === "object" && source[key] !== null) {
|
|
724
|
-
if (!target[key]) {
|
|
725
|
-
target[key] = Array.isArray(source[key]) ? [] : {};
|
|
726
|
-
}
|
|
727
|
-
this.deepAssign(target[key], source[key]);
|
|
728
|
-
} else {
|
|
729
|
-
target[key] = source[key];
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
return target;
|
|
736
|
-
},
|
|
737
|
-
/**
|
|
738
|
-
* 复制文本到剪贴板
|
|
739
|
-
*
|
|
740
|
-
* @param text 要复制的文本
|
|
741
|
-
* @returns 返回一个Promise,表示复制操作的结果
|
|
429
|
+
* @param text 要复制的文本
|
|
430
|
+
* @returns 返回一个Promise,表示复制操作的结果
|
|
742
431
|
*/
|
|
743
432
|
handleCopyValue(text) {
|
|
744
433
|
if (navigator.clipboard && window.isSecureContext) {
|
|
@@ -818,434 +507,119 @@ const ObjectUtil = {
|
|
|
818
507
|
return JSON.parse(str);
|
|
819
508
|
}
|
|
820
509
|
};
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
const arr = [];
|
|
833
|
-
const obj = {};
|
|
834
|
-
this.forEach((item) => {
|
|
835
|
-
const val = f(item);
|
|
836
|
-
const key = String(val);
|
|
837
|
-
if (!obj[key]) {
|
|
838
|
-
obj[key] = true;
|
|
839
|
-
arr.push(item);
|
|
510
|
+
const ImageUtil = {
|
|
511
|
+
emptyImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
|
|
512
|
+
/**
|
|
513
|
+
*
|
|
514
|
+
* @param image image,类型可以是HTMLCanvasElement、ImageData
|
|
515
|
+
* @returns
|
|
516
|
+
*/
|
|
517
|
+
getURL(image) {
|
|
518
|
+
let _canvas;
|
|
519
|
+
if (/^data:/i.test(image.src)) {
|
|
520
|
+
return image.src;
|
|
840
521
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
};
|
|
859
|
-
myArray.asc = function(f = (d) => d) {
|
|
860
|
-
return this.sort((n1, n2) => f(n1) - f(n2));
|
|
861
|
-
};
|
|
862
|
-
myArray.remove = function(obj) {
|
|
863
|
-
const i = this.indexOf(obj);
|
|
864
|
-
if (i > -1) {
|
|
865
|
-
this.splice(i, 1);
|
|
866
|
-
}
|
|
867
|
-
return this;
|
|
868
|
-
};
|
|
869
|
-
const ArrayUtil = {
|
|
870
|
-
/**
|
|
871
|
-
* 创建指定长度的数组,并返回其索引数组
|
|
872
|
-
*
|
|
873
|
-
* @param length 数组长度
|
|
874
|
-
* @returns 索引数组
|
|
875
|
-
*/
|
|
876
|
-
create(length) {
|
|
877
|
-
return [...new Array(length).keys()];
|
|
878
|
-
},
|
|
879
|
-
/**
|
|
880
|
-
* 合并多个数组,并去重
|
|
881
|
-
*
|
|
882
|
-
* @param args 需要合并的数组
|
|
883
|
-
* @returns 合并后的去重数组
|
|
884
|
-
*/
|
|
885
|
-
union(...args) {
|
|
886
|
-
let res = [];
|
|
887
|
-
args.forEach((arg) => {
|
|
888
|
-
if (Array.isArray(arg)) {
|
|
889
|
-
res = res.concat(arg.filter((v) => !res.includes(v)));
|
|
522
|
+
if (typeof HTMLCanvasElement === "undefined") {
|
|
523
|
+
return image.src;
|
|
524
|
+
}
|
|
525
|
+
let canvas;
|
|
526
|
+
if (image instanceof HTMLCanvasElement) {
|
|
527
|
+
canvas = image;
|
|
528
|
+
} else {
|
|
529
|
+
if (_canvas === void 0) _canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
|
530
|
+
_canvas.width = image.width;
|
|
531
|
+
_canvas.height = image.height;
|
|
532
|
+
const context = _canvas.getContext("2d");
|
|
533
|
+
if (context) {
|
|
534
|
+
if (image instanceof ImageData) {
|
|
535
|
+
context.putImageData(image, 0, 0);
|
|
536
|
+
} else {
|
|
537
|
+
context.drawImage(image, 0, 0, image.width, image.height);
|
|
538
|
+
}
|
|
890
539
|
}
|
|
891
|
-
|
|
892
|
-
|
|
540
|
+
canvas = _canvas;
|
|
541
|
+
}
|
|
542
|
+
if (canvas.width > 2048 || canvas.height > 2048) {
|
|
543
|
+
console.warn("ImageUtil.getDataURL: Image converted to jpg for performance reasons", image);
|
|
544
|
+
return canvas.toDataURL("image/jpeg", 0.6);
|
|
545
|
+
} else {
|
|
546
|
+
return canvas.toDataURL("image/png");
|
|
547
|
+
}
|
|
893
548
|
},
|
|
894
549
|
/**
|
|
895
|
-
*
|
|
550
|
+
* 将图片的URL转换为Base64编码
|
|
896
551
|
*
|
|
897
|
-
* @param
|
|
898
|
-
* @
|
|
552
|
+
* @param url 图片的URL地址
|
|
553
|
+
* @param width 图片的宽度,默认为图片原始宽度
|
|
554
|
+
* @param height 图片的高度,默认为图片原始高度
|
|
555
|
+
* @returns 返回Promise对象,解析后得到包含Base64编码数据的对象
|
|
899
556
|
*/
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
557
|
+
getBase64(url) {
|
|
558
|
+
return new Promise((resolve, reject) => {
|
|
559
|
+
let image = new Image();
|
|
560
|
+
image.setAttribute("crossOrigin", "Anonymous");
|
|
561
|
+
image.src = url;
|
|
562
|
+
image.onload = () => {
|
|
563
|
+
let dataURL = this.getURL(image);
|
|
564
|
+
resolve(dataURL);
|
|
565
|
+
};
|
|
566
|
+
image.onerror = reject;
|
|
906
567
|
});
|
|
907
|
-
return res;
|
|
908
568
|
},
|
|
909
569
|
/**
|
|
910
|
-
*
|
|
570
|
+
* 解析base64编码
|
|
911
571
|
*
|
|
912
|
-
* @param
|
|
913
|
-
* @returns
|
|
572
|
+
* @param base64 base64编码字符串
|
|
573
|
+
* @returns 返回一个对象,包含type(类型)、ext(扩展名)和data(数据)字段,如果解析失败则返回null
|
|
914
574
|
*/
|
|
915
|
-
|
|
916
|
-
|
|
575
|
+
parseBase64(base64) {
|
|
576
|
+
let re = new RegExp("data:(?<type>.*?);base64,(?<data>.*)");
|
|
577
|
+
let res = re.exec(base64);
|
|
578
|
+
if (res && res.groups) {
|
|
579
|
+
return {
|
|
580
|
+
type: res.groups.type,
|
|
581
|
+
ext: res.groups.type.split("/").slice(-1)[0],
|
|
582
|
+
data: res.groups.data
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
return null;
|
|
917
586
|
},
|
|
918
587
|
/**
|
|
919
|
-
*
|
|
588
|
+
* 复制图片到剪贴板
|
|
920
589
|
*
|
|
921
|
-
* @param
|
|
922
|
-
* @returns
|
|
590
|
+
* @param url 图片的URL地址
|
|
591
|
+
* @returns 无返回值
|
|
592
|
+
* @throws 如果解析base64数据失败,则抛出异常
|
|
923
593
|
*/
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
constructor(url = `ws://${window.document.domain}:20007/mqtt`, config = {}) {
|
|
931
|
-
super();
|
|
932
|
-
__publicField(this, "state");
|
|
933
|
-
__publicField(this, "url");
|
|
934
|
-
__publicField(this, "context");
|
|
935
|
-
__publicField(this, "options");
|
|
936
|
-
__publicField(this, "client");
|
|
937
|
-
__publicField(this, "topics");
|
|
938
|
-
this.context = CommUtil.extend(_MqttClient.defaultContext, config);
|
|
939
|
-
this.options = {
|
|
940
|
-
connectTimeout: this.context.MQTT_TIMEOUTM,
|
|
941
|
-
clientId: CommUtil.guid(),
|
|
942
|
-
username: this.context.MQTT_USERNAME,
|
|
943
|
-
password: this.context.MQTT_PASSWORD,
|
|
944
|
-
clean: true
|
|
945
|
-
};
|
|
946
|
-
this.url = url;
|
|
947
|
-
this.client = connect(this.url, this.options);
|
|
948
|
-
this._onConnect();
|
|
949
|
-
this._onMessage();
|
|
950
|
-
this.state = 0;
|
|
951
|
-
this.topics = [];
|
|
952
|
-
}
|
|
953
|
-
_onConnect() {
|
|
954
|
-
this.client.on("connect", () => {
|
|
955
|
-
this.state = 1;
|
|
956
|
-
console.log("链接mqtt成功==>" + this.url);
|
|
957
|
-
this.dispatchEvent({ type: EventType.MQTT_CONNECT, message: this });
|
|
958
|
-
});
|
|
959
|
-
this.client.on("error", (err) => {
|
|
960
|
-
console.log("链接mqtt报错", err);
|
|
961
|
-
this.state = -1;
|
|
962
|
-
this.dispatchEvent({ type: EventType.MQTT_ERROR, message: this });
|
|
963
|
-
this.client.end();
|
|
964
|
-
this.client.reconnect();
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
_onMessage() {
|
|
968
|
-
this.client.on("message", (topic, message) => {
|
|
969
|
-
let dataString = message;
|
|
970
|
-
let data = "";
|
|
971
|
-
if (message instanceof Uint8Array) {
|
|
972
|
-
dataString = message.toString();
|
|
594
|
+
async copyImage(url) {
|
|
595
|
+
try {
|
|
596
|
+
const base64Result = await this.getBase64(url);
|
|
597
|
+
const parsedBase64 = this.parseBase64(base64Result.dataURL);
|
|
598
|
+
if (!parsedBase64) {
|
|
599
|
+
throw new Error("Failed to parse base64 data.");
|
|
973
600
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
601
|
+
let type = parsedBase64.type;
|
|
602
|
+
let bytes = atob(parsedBase64.data);
|
|
603
|
+
let ab = new ArrayBuffer(bytes.length);
|
|
604
|
+
let ua = new Uint8Array(ab);
|
|
605
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
606
|
+
ua[i] = bytes.charCodeAt(i);
|
|
978
607
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
});
|
|
984
|
-
}
|
|
985
|
-
sendMsg(topic, msg) {
|
|
986
|
-
if (!this.client.connected) {
|
|
987
|
-
console.error("客户端未连接");
|
|
988
|
-
return;
|
|
608
|
+
let blob = new Blob([ab], { type });
|
|
609
|
+
await navigator.clipboard.write([new ClipboardItem({ [type]: blob })]);
|
|
610
|
+
} catch (error) {
|
|
611
|
+
console.error("Failed to copy image to clipboard:", error);
|
|
989
612
|
}
|
|
990
|
-
this.client.publish(topic, msg, { qos: 1, retain: true });
|
|
991
|
-
}
|
|
992
|
-
subscribe(topic) {
|
|
993
|
-
this.state === 1 ? this.client.subscribe(topic, { qos: 1 }, (error, e) => {
|
|
994
|
-
error instanceof Error ? console.error("订阅失败==>" + topic, error) : (this.topics = ArrayUtil.union(this.topics, topic), console.log("订阅成功==>" + topic));
|
|
995
|
-
}) : this.addEventListener(EventType.MQTT_CONNECT, (res) => {
|
|
996
|
-
this.client.subscribe(topic, { qos: 1 }, (error, e) => {
|
|
997
|
-
error instanceof Error ? console.error("订阅失败==>" + topic, error) : (this.topics = ArrayUtil.union(this.topics, topic), console.log("订阅成功==>" + topic));
|
|
998
|
-
});
|
|
999
|
-
});
|
|
1000
|
-
return this;
|
|
1001
|
-
}
|
|
1002
|
-
unsubscribe(topic) {
|
|
1003
|
-
this.client.unsubscribe(topic, { qos: 1 }, (error, res) => {
|
|
1004
|
-
if (error instanceof Error) {
|
|
1005
|
-
console.error(`取消订阅失败==>${topic}`, error);
|
|
1006
|
-
} else {
|
|
1007
|
-
this.topics = ArrayUtil.difference(this.topics, topic);
|
|
1008
|
-
console.log(`取消订阅成功==>${topic}`);
|
|
1009
|
-
}
|
|
1010
|
-
});
|
|
1011
|
-
return this;
|
|
1012
|
-
}
|
|
1013
|
-
unsubscribeAll() {
|
|
1014
|
-
this.unsubscribe(this.topics);
|
|
1015
|
-
}
|
|
1016
|
-
unconnect() {
|
|
1017
|
-
this.client.end();
|
|
1018
|
-
this.client = null;
|
|
1019
|
-
this.dispatchEvent({ type: EventType.MQTT_CLOSE, message: null });
|
|
1020
|
-
console.log("断开mqtt成功==>" + this.url);
|
|
1021
613
|
}
|
|
1022
614
|
};
|
|
1023
|
-
|
|
1024
|
-
* Creates an instance of MqttClient.
|
|
1025
|
-
* @param {*} config mqtt实例参数
|
|
1026
|
-
*/
|
|
1027
|
-
__publicField(_MqttClient, "defaultContext", {
|
|
1028
|
-
MQTT_USERNAME: "iRVMS-WEB",
|
|
1029
|
-
MQTT_PASSWORD: "novasky888",
|
|
1030
|
-
MQTT_TIMEOUTM: 2e4
|
|
1031
|
-
});
|
|
1032
|
-
let MqttClient = _MqttClient;
|
|
1033
|
-
const _Storage = class _Storage {
|
|
615
|
+
const AjaxUtil = {
|
|
1034
616
|
/**
|
|
1035
|
-
*
|
|
1036
|
-
*
|
|
1037
|
-
* @param
|
|
1038
|
-
* @param value 值,默认为null
|
|
1039
|
-
* @param options 存储选项,可选参数
|
|
1040
|
-
* @param options.expires 过期时间,单位为毫秒,默认为null
|
|
1041
|
-
*/
|
|
1042
|
-
static set(key, value = null, options = {}) {
|
|
1043
|
-
var query_key = this._getPrefixedKey(key, options);
|
|
1044
|
-
try {
|
|
1045
|
-
const { expires } = options;
|
|
1046
|
-
const data = { data: value };
|
|
1047
|
-
if (expires) {
|
|
1048
|
-
data.expires = expires;
|
|
1049
|
-
}
|
|
1050
|
-
localStorage.setItem(query_key, JSON.stringify(data));
|
|
1051
|
-
} catch (e) {
|
|
1052
|
-
if (console) console.warn(`Storage didn't successfully save the '{"${key}": "${value}"}' pair, because the localStorage is full.`);
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
/**
|
|
1056
|
-
* 从localStorage中获取指定key的存储值
|
|
1057
|
-
*
|
|
1058
|
-
* @param key 存储键名
|
|
1059
|
-
* @param missing 当获取不到指定key的存储值时返回的默认值
|
|
1060
|
-
* @param options 其他配置选项
|
|
1061
|
-
* @returns 返回指定key的存储值,若获取不到则返回missing参数指定的默认值
|
|
1062
|
-
*/
|
|
1063
|
-
static get(key, missing, options) {
|
|
1064
|
-
var query_key = this._getPrefixedKey(key, options), value;
|
|
1065
|
-
try {
|
|
1066
|
-
value = JSON.parse(localStorage.getItem(query_key) || "");
|
|
1067
|
-
} catch (e) {
|
|
1068
|
-
if (localStorage[query_key]) {
|
|
1069
|
-
value = { data: localStorage.getItem(query_key) };
|
|
1070
|
-
} else {
|
|
1071
|
-
value = null;
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
if (!value) {
|
|
1075
|
-
return missing;
|
|
1076
|
-
} else if (typeof value === "object" && typeof value.data !== "undefined") {
|
|
1077
|
-
const expires = value.expires;
|
|
1078
|
-
if (expires && Date.now() > expires) {
|
|
1079
|
-
return missing;
|
|
1080
|
-
}
|
|
1081
|
-
return value.data;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
static keys() {
|
|
1085
|
-
const keys = [];
|
|
1086
|
-
var allKeys = Object.keys(localStorage);
|
|
1087
|
-
if (_Storage.prefix.length === 0) {
|
|
1088
|
-
return allKeys;
|
|
1089
|
-
}
|
|
1090
|
-
allKeys.forEach(function(key) {
|
|
1091
|
-
if (key.indexOf(_Storage.prefix) !== -1) {
|
|
1092
|
-
keys.push(key.replace(_Storage.prefix, ""));
|
|
1093
|
-
}
|
|
1094
|
-
});
|
|
1095
|
-
return keys;
|
|
1096
|
-
}
|
|
1097
|
-
static getAll(includeKeys) {
|
|
1098
|
-
var keys = _Storage.keys();
|
|
1099
|
-
if (includeKeys) {
|
|
1100
|
-
const result = [];
|
|
1101
|
-
keys.forEach((key) => {
|
|
1102
|
-
if (includeKeys.includes(key)) {
|
|
1103
|
-
const tempObj = {};
|
|
1104
|
-
tempObj[key] = _Storage.get(key, null, null);
|
|
1105
|
-
result.push(tempObj);
|
|
1106
|
-
}
|
|
1107
|
-
});
|
|
1108
|
-
return result;
|
|
1109
|
-
}
|
|
1110
|
-
return keys.map((key) => _Storage.get(key, null, null));
|
|
1111
|
-
}
|
|
1112
|
-
static remove(key, options) {
|
|
1113
|
-
var queryKey = this._getPrefixedKey(key, options);
|
|
1114
|
-
localStorage.removeItem(queryKey);
|
|
1115
|
-
}
|
|
1116
|
-
static clear(options) {
|
|
1117
|
-
if (_Storage.prefix.length) {
|
|
1118
|
-
this.keys().forEach((key) => {
|
|
1119
|
-
localStorage.removeItem(this._getPrefixedKey(key, options));
|
|
1120
|
-
});
|
|
1121
|
-
} else {
|
|
1122
|
-
localStorage.clear();
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
};
|
|
1126
|
-
__publicField(_Storage, "prefix", "");
|
|
1127
|
-
__publicField(_Storage, "_getPrefixedKey", function(key, options) {
|
|
1128
|
-
options = options || {};
|
|
1129
|
-
if (options.noPrefix) {
|
|
1130
|
-
return key;
|
|
1131
|
-
} else {
|
|
1132
|
-
return _Storage.prefix + key;
|
|
1133
|
-
}
|
|
1134
|
-
});
|
|
1135
|
-
let Storage = _Storage;
|
|
1136
|
-
const ImageUtil = {
|
|
1137
|
-
emptyImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
|
|
1138
|
-
/**
|
|
1139
|
-
*
|
|
1140
|
-
* @param image image,类型可以是HTMLCanvasElement、ImageData
|
|
1141
|
-
* @returns
|
|
1142
|
-
*/
|
|
1143
|
-
getURL(image) {
|
|
1144
|
-
let _canvas;
|
|
1145
|
-
if (/^data:/i.test(image.src)) {
|
|
1146
|
-
return image.src;
|
|
1147
|
-
}
|
|
1148
|
-
if (typeof HTMLCanvasElement === "undefined") {
|
|
1149
|
-
return image.src;
|
|
1150
|
-
}
|
|
1151
|
-
let canvas;
|
|
1152
|
-
if (image instanceof HTMLCanvasElement) {
|
|
1153
|
-
canvas = image;
|
|
1154
|
-
} else {
|
|
1155
|
-
if (_canvas === void 0) _canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
|
1156
|
-
_canvas.width = image.width;
|
|
1157
|
-
_canvas.height = image.height;
|
|
1158
|
-
const context = _canvas.getContext("2d");
|
|
1159
|
-
if (context) {
|
|
1160
|
-
if (image instanceof ImageData) {
|
|
1161
|
-
context.putImageData(image, 0, 0);
|
|
1162
|
-
} else {
|
|
1163
|
-
context.drawImage(image, 0, 0, image.width, image.height);
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
canvas = _canvas;
|
|
1167
|
-
}
|
|
1168
|
-
if (canvas.width > 2048 || canvas.height > 2048) {
|
|
1169
|
-
console.warn("ImageUtil.getDataURL: Image converted to jpg for performance reasons", image);
|
|
1170
|
-
return canvas.toDataURL("image/jpeg", 0.6);
|
|
1171
|
-
} else {
|
|
1172
|
-
return canvas.toDataURL("image/png");
|
|
1173
|
-
}
|
|
1174
|
-
},
|
|
1175
|
-
/**
|
|
1176
|
-
* 将图片的URL转换为Base64编码
|
|
1177
|
-
*
|
|
1178
|
-
* @param url 图片的URL地址
|
|
1179
|
-
* @param width 图片的宽度,默认为图片原始宽度
|
|
1180
|
-
* @param height 图片的高度,默认为图片原始高度
|
|
1181
|
-
* @returns 返回Promise对象,解析后得到包含Base64编码数据的对象
|
|
1182
|
-
*/
|
|
1183
|
-
getBase64(url) {
|
|
1184
|
-
return new Promise((resolve, reject) => {
|
|
1185
|
-
let image = new Image();
|
|
1186
|
-
image.setAttribute("crossOrigin", "Anonymous");
|
|
1187
|
-
image.src = url;
|
|
1188
|
-
image.onload = () => {
|
|
1189
|
-
let dataURL = this.getURL(image);
|
|
1190
|
-
resolve(dataURL);
|
|
1191
|
-
};
|
|
1192
|
-
image.onerror = reject;
|
|
1193
|
-
});
|
|
1194
|
-
},
|
|
1195
|
-
/**
|
|
1196
|
-
* 解析base64编码
|
|
1197
|
-
*
|
|
1198
|
-
* @param base64 base64编码字符串
|
|
1199
|
-
* @returns 返回一个对象,包含type(类型)、ext(扩展名)和data(数据)字段,如果解析失败则返回null
|
|
1200
|
-
*/
|
|
1201
|
-
parseBase64(base64) {
|
|
1202
|
-
let re = new RegExp("data:(?<type>.*?);base64,(?<data>.*)");
|
|
1203
|
-
let res = re.exec(base64);
|
|
1204
|
-
if (res && res.groups) {
|
|
1205
|
-
return {
|
|
1206
|
-
type: res.groups.type,
|
|
1207
|
-
ext: res.groups.type.split("/").slice(-1)[0],
|
|
1208
|
-
data: res.groups.data
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
return null;
|
|
1212
|
-
},
|
|
1213
|
-
/**
|
|
1214
|
-
* 复制图片到剪贴板
|
|
1215
|
-
*
|
|
1216
|
-
* @param url 图片的URL地址
|
|
1217
|
-
* @returns 无返回值
|
|
1218
|
-
* @throws 如果解析base64数据失败,则抛出异常
|
|
1219
|
-
*/
|
|
1220
|
-
async copyImage(url) {
|
|
1221
|
-
try {
|
|
1222
|
-
const base64Result = await this.getBase64(url);
|
|
1223
|
-
const parsedBase64 = this.parseBase64(base64Result.dataURL);
|
|
1224
|
-
if (!parsedBase64) {
|
|
1225
|
-
throw new Error("Failed to parse base64 data.");
|
|
1226
|
-
}
|
|
1227
|
-
let type = parsedBase64.type;
|
|
1228
|
-
let bytes = atob(parsedBase64.data);
|
|
1229
|
-
let ab = new ArrayBuffer(bytes.length);
|
|
1230
|
-
let ua = new Uint8Array(ab);
|
|
1231
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
1232
|
-
ua[i] = bytes.charCodeAt(i);
|
|
1233
|
-
}
|
|
1234
|
-
let blob = new Blob([ab], { type });
|
|
1235
|
-
await navigator.clipboard.write([new ClipboardItem({ [type]: blob })]);
|
|
1236
|
-
} catch (error) {
|
|
1237
|
-
console.error("Failed to copy image to clipboard:", error);
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
};
|
|
1241
|
-
const AjaxUtil = {
|
|
1242
|
-
/**
|
|
1243
|
-
* Get JSON data by jsonp
|
|
1244
|
-
* @param url - resource url
|
|
1245
|
-
* @param callback - callback function when completed
|
|
617
|
+
* Get JSON data by jsonp
|
|
618
|
+
* @param url - resource url
|
|
619
|
+
* @param callback - callback function when completed
|
|
1246
620
|
*/
|
|
1247
621
|
jsonp(url, callback) {
|
|
1248
|
-
const name = "_jsonp_" +
|
|
622
|
+
const name = "_jsonp_" + CommUtils.guid();
|
|
1249
623
|
const head = document.getElementsByTagName("head")[0];
|
|
1250
624
|
if (url.includes("?")) {
|
|
1251
625
|
url += "&callback=" + name;
|
|
@@ -1284,7 +658,7 @@ const AjaxUtil = {
|
|
|
1284
658
|
* );
|
|
1285
659
|
*/
|
|
1286
660
|
get(url, options, cb) {
|
|
1287
|
-
if (
|
|
661
|
+
if (CommUtils.isFunction(options)) {
|
|
1288
662
|
const t = cb;
|
|
1289
663
|
cb = options;
|
|
1290
664
|
options = t;
|
|
@@ -1416,7 +790,7 @@ const AjaxUtil = {
|
|
|
1416
790
|
* );
|
|
1417
791
|
*/
|
|
1418
792
|
getArrayBuffer(url, options, cb) {
|
|
1419
|
-
if (
|
|
793
|
+
if (CommUtils.isFunction(options)) {
|
|
1420
794
|
const t = cb;
|
|
1421
795
|
cb = options;
|
|
1422
796
|
options = t;
|
|
@@ -1469,7 +843,7 @@ const AjaxUtil = {
|
|
|
1469
843
|
* );
|
|
1470
844
|
*/
|
|
1471
845
|
getJSON(url, options, cb) {
|
|
1472
|
-
if (
|
|
846
|
+
if (CommUtils.isFunction(options)) {
|
|
1473
847
|
const t = cb;
|
|
1474
848
|
cb = options;
|
|
1475
849
|
options = t;
|
|
@@ -1486,6 +860,48 @@ const AjaxUtil = {
|
|
|
1486
860
|
return this.get(url, options, callback);
|
|
1487
861
|
}
|
|
1488
862
|
};
|
|
863
|
+
const MathUtils = {
|
|
864
|
+
DEG2RAD: Math.PI / 180,
|
|
865
|
+
RAD2DEG: 180 / Math.PI,
|
|
866
|
+
randInt(low, high) {
|
|
867
|
+
return low + Math.floor(Math.random() * (high - low + 1));
|
|
868
|
+
},
|
|
869
|
+
randFloat(low, high) {
|
|
870
|
+
return low + Math.random() * (high - low);
|
|
871
|
+
},
|
|
872
|
+
/**
|
|
873
|
+
* 角度转弧度
|
|
874
|
+
*
|
|
875
|
+
* @param {*} degrees
|
|
876
|
+
* @returns {*}
|
|
877
|
+
*/
|
|
878
|
+
deg2Rad(degrees) {
|
|
879
|
+
return degrees * this.DEG2RAD;
|
|
880
|
+
},
|
|
881
|
+
/**
|
|
882
|
+
* 弧度转角度
|
|
883
|
+
*
|
|
884
|
+
* @param {*} radians
|
|
885
|
+
* @returns {*}
|
|
886
|
+
*/
|
|
887
|
+
rad2Deg(radians) {
|
|
888
|
+
return radians * this.RAD2DEG;
|
|
889
|
+
},
|
|
890
|
+
round(value, n = 2) {
|
|
891
|
+
return Math.round(value * Math.pow(10, n)) / Math.pow(10, n);
|
|
892
|
+
},
|
|
893
|
+
/**
|
|
894
|
+
* 将数值限制在指定范围内
|
|
895
|
+
*
|
|
896
|
+
* @param val 需要限制的数值
|
|
897
|
+
* @param min 最小值
|
|
898
|
+
* @param max 最大值
|
|
899
|
+
* @returns 返回限制后的数值
|
|
900
|
+
*/
|
|
901
|
+
clamp(val, min, max) {
|
|
902
|
+
return Math.max(min, Math.min(max, val));
|
|
903
|
+
}
|
|
904
|
+
};
|
|
1489
905
|
const GeoUtil = {
|
|
1490
906
|
toRadian: Math.PI / 180,
|
|
1491
907
|
R: 6371393,
|
|
@@ -1853,7 +1269,7 @@ const StringUtil = {
|
|
|
1853
1269
|
*/
|
|
1854
1270
|
tag(strArray, ...args) {
|
|
1855
1271
|
args = args.map((val) => {
|
|
1856
|
-
switch (
|
|
1272
|
+
switch (CommUtils.getDataType(val)) {
|
|
1857
1273
|
case "Object":
|
|
1858
1274
|
return val || "{}";
|
|
1859
1275
|
case "Array":
|
|
@@ -1896,127 +1312,22 @@ const StringUtil = {
|
|
|
1896
1312
|
return str;
|
|
1897
1313
|
}
|
|
1898
1314
|
};
|
|
1899
|
-
const
|
|
1900
|
-
|
|
1901
|
-
let r = Math.floor(Math.random() * 256).toString(16);
|
|
1902
|
-
let g = Math.floor(Math.random() * 256).toString(16);
|
|
1903
|
-
let b = Math.floor(Math.random() * 256).toString(16);
|
|
1904
|
-
r = r.length === 1 ? "0" + r : r;
|
|
1905
|
-
g = g.length === 1 ? "0" + g : g;
|
|
1906
|
-
b = b.length === 1 ? "0" + b : b;
|
|
1907
|
-
return "#" + r + g + b;
|
|
1908
|
-
},
|
|
1315
|
+
const TYPES = ["Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon"];
|
|
1316
|
+
const GeoJsonUtil = {
|
|
1909
1317
|
/**
|
|
1910
|
-
*
|
|
1318
|
+
* 获取GeoJSON要素的几何类型
|
|
1911
1319
|
*
|
|
1912
|
-
* @param
|
|
1913
|
-
* @returns
|
|
1320
|
+
* @param feature GeoJSONFeature 类型的要素
|
|
1321
|
+
* @returns 返回要素的几何类型,如果要素没有几何属性则返回 null
|
|
1914
1322
|
*/
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
return hex;
|
|
1323
|
+
getGeoJsonType(feature) {
|
|
1324
|
+
return feature.geometry ? feature.geometry.type : null;
|
|
1918
1325
|
},
|
|
1919
1326
|
/**
|
|
1920
|
-
*
|
|
1327
|
+
* 判断给定的GeoJSON要素是否为有效的GeoJSON格式
|
|
1921
1328
|
*
|
|
1922
|
-
* @param
|
|
1923
|
-
* @returns
|
|
1924
|
-
*/
|
|
1925
|
-
rgbToRgba(rgbValue) {
|
|
1926
|
-
const rgb = /rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g.exec(rgbValue);
|
|
1927
|
-
return rgb ? `rgba(${rgb[1]}, 1)` : rgbValue;
|
|
1928
|
-
},
|
|
1929
|
-
/**
|
|
1930
|
-
* 将十六进制颜色值转换为rgba格式的颜色值
|
|
1931
|
-
*
|
|
1932
|
-
* @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
|
|
1933
|
-
* @returns 返回rgba格式的颜色值,格式为rgba(r,g,b,1)
|
|
1934
|
-
*/
|
|
1935
|
-
hexToRgba(hexValue) {
|
|
1936
|
-
const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
|
1937
|
-
const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
|
|
1938
|
-
const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
|
|
1939
|
-
const rgb = rgx.exec(hex);
|
|
1940
|
-
if (!rgb) {
|
|
1941
|
-
return hexValue;
|
|
1942
|
-
}
|
|
1943
|
-
const r = parseInt(rgb[1], 16);
|
|
1944
|
-
const g = parseInt(rgb[2], 16);
|
|
1945
|
-
const b = parseInt(rgb[3], 16);
|
|
1946
|
-
return `rgba(${r},${g},${b},1)`;
|
|
1947
|
-
},
|
|
1948
|
-
/**
|
|
1949
|
-
* 将 HSL 颜色值转换为 RGBA 颜色值
|
|
1950
|
-
*
|
|
1951
|
-
* @param hslValue HSL 颜色值字符串,格式为 "hsl(h, s%, l%)" 或 "hsla(h, s%, l%, a)",其中 h 为色相,s 为饱和度,l 为亮度,a 为透明度(可选)。
|
|
1952
|
-
* @returns 转换后的 RGBA 颜色值字符串,格式为 "rgba(r, g, b, a)",其中 r、g、b 为红绿蓝分量,a 为透明度。若输入为空或无效,则返回 null。
|
|
1953
|
-
*/
|
|
1954
|
-
hslToRgba(hslValue) {
|
|
1955
|
-
if (!hslValue) {
|
|
1956
|
-
return null;
|
|
1957
|
-
}
|
|
1958
|
-
const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
|
|
1959
|
-
if (!hsl) {
|
|
1960
|
-
return null;
|
|
1961
|
-
}
|
|
1962
|
-
const h = parseInt(hsl[1], 10) / 360;
|
|
1963
|
-
const s = parseInt(hsl[2], 10) / 100;
|
|
1964
|
-
const l = parseInt(hsl[3], 10) / 100;
|
|
1965
|
-
const a = hsl[4] ? parseFloat(hsl[4]) : 1;
|
|
1966
|
-
function hue2rgb(p, q, t) {
|
|
1967
|
-
if (t < 0) t += 1;
|
|
1968
|
-
if (t > 1) t -= 1;
|
|
1969
|
-
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
1970
|
-
if (t < 1 / 2) return q;
|
|
1971
|
-
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
1972
|
-
return p;
|
|
1973
|
-
}
|
|
1974
|
-
let r, g, b;
|
|
1975
|
-
if (s === 0) {
|
|
1976
|
-
r = g = b = l;
|
|
1977
|
-
} else {
|
|
1978
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
1979
|
-
const p = 2 * l - q;
|
|
1980
|
-
r = hue2rgb(p, q, h + 1 / 3);
|
|
1981
|
-
g = hue2rgb(p, q, h);
|
|
1982
|
-
b = hue2rgb(p, q, h - 1 / 3);
|
|
1983
|
-
}
|
|
1984
|
-
return `rgba(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)},${a})`;
|
|
1985
|
-
},
|
|
1986
|
-
isHex(a) {
|
|
1987
|
-
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
|
|
1988
|
-
},
|
|
1989
|
-
isRgb(a) {
|
|
1990
|
-
return /^rgb/.test(a);
|
|
1991
|
-
},
|
|
1992
|
-
isHsl(a) {
|
|
1993
|
-
return /^hsl/.test(a);
|
|
1994
|
-
},
|
|
1995
|
-
isColor(a) {
|
|
1996
|
-
return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
|
|
1997
|
-
},
|
|
1998
|
-
colorToRgb(val) {
|
|
1999
|
-
if (this.isRgb(val)) return this.rgbToRgba(val);
|
|
2000
|
-
if (this.isHex(val)) return this.hexToRgba(val);
|
|
2001
|
-
if (this.isHsl(val)) return this.hslToRgba(val);
|
|
2002
|
-
}
|
|
2003
|
-
};
|
|
2004
|
-
const TYPES = ["Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon"];
|
|
2005
|
-
const GeoJsonUtil = {
|
|
2006
|
-
/**
|
|
2007
|
-
* 获取GeoJSON要素的几何类型
|
|
2008
|
-
*
|
|
2009
|
-
* @param feature GeoJSONFeature 类型的要素
|
|
2010
|
-
* @returns 返回要素的几何类型,如果要素没有几何属性则返回 null
|
|
2011
|
-
*/
|
|
2012
|
-
getGeoJsonType(feature) {
|
|
2013
|
-
return feature.geometry ? feature.geometry.type : null;
|
|
2014
|
-
},
|
|
2015
|
-
/**
|
|
2016
|
-
* 判断给定的GeoJSON要素是否为有效的GeoJSON格式
|
|
2017
|
-
*
|
|
2018
|
-
* @param feature 要判断的GeoJSON要素
|
|
2019
|
-
* @returns 如果为有效的GeoJSON格式则返回true,否则返回false
|
|
1329
|
+
* @param feature 要判断的GeoJSON要素
|
|
1330
|
+
* @returns 如果为有效的GeoJSON格式则返回true,否则返回false
|
|
2020
1331
|
*/
|
|
2021
1332
|
isGeoJson(feature) {
|
|
2022
1333
|
const type = this.getGeoJsonType(feature);
|
|
@@ -2245,42 +1556,42 @@ const GeoJsonUtil = {
|
|
|
2245
1556
|
const AssertUtil = {
|
|
2246
1557
|
assertEmpty(...arg) {
|
|
2247
1558
|
arg.forEach((a) => {
|
|
2248
|
-
if (
|
|
1559
|
+
if (CommUtils.isEmpty(a)) {
|
|
2249
1560
|
throw Error(ErrorType.PARAMETER_ERROR_LACK + " -> " + a);
|
|
2250
1561
|
}
|
|
2251
1562
|
});
|
|
2252
1563
|
},
|
|
2253
1564
|
assertNumber(...arg) {
|
|
2254
1565
|
arg.forEach((a) => {
|
|
2255
|
-
if (!
|
|
1566
|
+
if (!CommUtils.isNumber(a)) {
|
|
2256
1567
|
throw Error(ErrorType.PARAMETER_ERROR_NUMBER + " -> " + a);
|
|
2257
1568
|
}
|
|
2258
1569
|
});
|
|
2259
1570
|
},
|
|
2260
1571
|
assertArray(...arg) {
|
|
2261
1572
|
arg.forEach((a) => {
|
|
2262
|
-
if (!
|
|
1573
|
+
if (!CommUtils.isArray(a)) {
|
|
2263
1574
|
throw Error(ErrorType.PARAMETER_ERROR_ARRAY + " -> " + a);
|
|
2264
1575
|
}
|
|
2265
1576
|
});
|
|
2266
1577
|
},
|
|
2267
1578
|
assertFunction(...arg) {
|
|
2268
1579
|
arg.forEach((a) => {
|
|
2269
|
-
if (!
|
|
1580
|
+
if (!CommUtils.isFunction(a)) {
|
|
2270
1581
|
throw Error(ErrorType.PARAMETER_ERROR_FUNCTION + " -> " + a);
|
|
2271
1582
|
}
|
|
2272
1583
|
});
|
|
2273
1584
|
},
|
|
2274
1585
|
assertObject(...arg) {
|
|
2275
1586
|
arg.forEach((a) => {
|
|
2276
|
-
if (!
|
|
1587
|
+
if (!CommUtils.isObject(a)) {
|
|
2277
1588
|
throw Error(ErrorType.PARAMETER_ERROR_OBJECT + " -> " + a);
|
|
2278
1589
|
}
|
|
2279
1590
|
});
|
|
2280
1591
|
},
|
|
2281
1592
|
assertColor(...arg) {
|
|
2282
1593
|
arg.forEach((a) => {
|
|
2283
|
-
if (!
|
|
1594
|
+
if (!Color.isColor(a)) {
|
|
2284
1595
|
throw Error(ErrorType.DATA_ERROR_COLOR + " -> " + a);
|
|
2285
1596
|
}
|
|
2286
1597
|
});
|
|
@@ -2377,50 +1688,217 @@ const AssertUtil = {
|
|
|
2377
1688
|
}
|
|
2378
1689
|
}
|
|
2379
1690
|
};
|
|
2380
|
-
const
|
|
1691
|
+
const myArray = Object.create(Array);
|
|
1692
|
+
myArray.groupBy = function(f) {
|
|
1693
|
+
var groups = {};
|
|
1694
|
+
this.forEach(function(o) {
|
|
1695
|
+
var group = JSON.stringify(f(o));
|
|
1696
|
+
groups[group] = groups[group] || [];
|
|
1697
|
+
groups[group].push(o);
|
|
1698
|
+
});
|
|
1699
|
+
return Object.keys(groups).map((group) => groups[group]);
|
|
1700
|
+
};
|
|
1701
|
+
myArray.distinct = function(f = (d) => d) {
|
|
1702
|
+
const arr = [];
|
|
1703
|
+
const obj = {};
|
|
1704
|
+
this.forEach((item) => {
|
|
1705
|
+
const val = f(item);
|
|
1706
|
+
const key = String(val);
|
|
1707
|
+
if (!obj[key]) {
|
|
1708
|
+
obj[key] = true;
|
|
1709
|
+
arr.push(item);
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1712
|
+
return arr;
|
|
1713
|
+
};
|
|
1714
|
+
myArray.prototype.max = function() {
|
|
1715
|
+
return Math.max.apply({}, this);
|
|
1716
|
+
};
|
|
1717
|
+
myArray.prototype.min = function() {
|
|
1718
|
+
return Math.min.apply({}, this);
|
|
1719
|
+
};
|
|
1720
|
+
myArray.sum = function() {
|
|
1721
|
+
return this.length > 0 ? this.reduce((prev = 0, curr = 0) => prev + curr) : 0;
|
|
1722
|
+
};
|
|
1723
|
+
myArray.avg = function() {
|
|
1724
|
+
return this.length ? this.sum() / this.length : 0;
|
|
1725
|
+
};
|
|
1726
|
+
myArray.desc = function(f = (d) => d) {
|
|
1727
|
+
return this.sort((n1, n2) => f(n2) - f(n1));
|
|
1728
|
+
};
|
|
1729
|
+
myArray.asc = function(f = (d) => d) {
|
|
1730
|
+
return this.sort((n1, n2) => f(n1) - f(n2));
|
|
1731
|
+
};
|
|
1732
|
+
myArray.random = function() {
|
|
1733
|
+
return this[Math.floor(Math.random() * this.length)];
|
|
1734
|
+
};
|
|
1735
|
+
myArray.remove = function(obj) {
|
|
1736
|
+
const i = this.indexOf(obj);
|
|
1737
|
+
if (i > -1) {
|
|
1738
|
+
this.splice(i, 1);
|
|
1739
|
+
}
|
|
1740
|
+
return this;
|
|
1741
|
+
};
|
|
1742
|
+
const ArrayUtil = {
|
|
2381
1743
|
/**
|
|
2382
|
-
*
|
|
1744
|
+
* 创建指定长度的数组,并返回其索引数组
|
|
2383
1745
|
*
|
|
2384
|
-
* @
|
|
1746
|
+
* @param length 数组长度
|
|
1747
|
+
* @returns 索引数组
|
|
2385
1748
|
*/
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
1749
|
+
create(length) {
|
|
1750
|
+
return [...new Array(length).keys()];
|
|
1751
|
+
},
|
|
1752
|
+
/**
|
|
1753
|
+
* 合并多个数组,并去重
|
|
1754
|
+
*
|
|
1755
|
+
* @param args 需要合并的数组
|
|
1756
|
+
* @returns 合并后的去重数组
|
|
1757
|
+
*/
|
|
1758
|
+
union(...args) {
|
|
1759
|
+
let res = [];
|
|
1760
|
+
args.forEach((arg) => {
|
|
1761
|
+
if (Array.isArray(arg)) {
|
|
1762
|
+
res = res.concat(arg.filter((v) => !res.includes(v)));
|
|
1763
|
+
}
|
|
1764
|
+
});
|
|
1765
|
+
return res;
|
|
1766
|
+
},
|
|
1767
|
+
/**
|
|
1768
|
+
* 求多个数组的交集
|
|
1769
|
+
*
|
|
1770
|
+
* @param args 多个需要求交集的数组
|
|
1771
|
+
* @returns 返回多个数组的交集数组
|
|
1772
|
+
*/
|
|
1773
|
+
intersection(...args) {
|
|
1774
|
+
let res = args[0] || [];
|
|
1775
|
+
args.forEach((arg) => {
|
|
1776
|
+
if (Array.isArray(arg)) {
|
|
1777
|
+
res = res.filter((v) => arg.includes(v));
|
|
1778
|
+
}
|
|
1779
|
+
});
|
|
1780
|
+
return res;
|
|
2400
1781
|
},
|
|
2401
1782
|
/**
|
|
2402
|
-
*
|
|
1783
|
+
* 将多个数组拼接为一个数组,并去除其中的空值。
|
|
2403
1784
|
*
|
|
2404
|
-
* @
|
|
1785
|
+
* @param args 需要拼接的数组列表。
|
|
1786
|
+
* @returns 拼接并去空后的数组。
|
|
2405
1787
|
*/
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
const windows = navigator.userAgent.indexOf("Windows", 0) != -1 ? 1 : 0;
|
|
2409
|
-
const mac = navigator.userAgent.indexOf("mac", 0) != -1 ? 1 : 0;
|
|
2410
|
-
const linux = navigator.userAgent.indexOf("Linux", 0) != -1 ? 1 : 0;
|
|
2411
|
-
const unix = navigator.userAgent.indexOf("X11", 0) != -1 ? 1 : 0;
|
|
2412
|
-
if (windows) os_type = "MS Windows";
|
|
2413
|
-
else if (mac) os_type = "Apple mac";
|
|
2414
|
-
else if (linux) os_type = "Linux";
|
|
2415
|
-
else if (unix) os_type = "Unix";
|
|
2416
|
-
return os_type;
|
|
1788
|
+
unionAll(...args) {
|
|
1789
|
+
return [...args].flat().filter((d) => !!d);
|
|
2417
1790
|
},
|
|
1791
|
+
/**
|
|
1792
|
+
* 求差集
|
|
1793
|
+
*
|
|
1794
|
+
* @param args 任意个集合
|
|
1795
|
+
* @returns 返回差集结果
|
|
1796
|
+
*/
|
|
1797
|
+
difference(...args) {
|
|
1798
|
+
if (args.length === 0) return [];
|
|
1799
|
+
return this.union(...args).filter((d) => !this.intersection(...args).includes(d));
|
|
1800
|
+
}
|
|
1801
|
+
};
|
|
1802
|
+
class BrowserUtil {
|
|
1803
|
+
/**
|
|
1804
|
+
* 获取系统类型
|
|
1805
|
+
*
|
|
1806
|
+
* @returns 返回一个包含系统类型和版本的对象
|
|
1807
|
+
*/
|
|
1808
|
+
static getSystem() {
|
|
1809
|
+
var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
1810
|
+
const userAgent = this.userAgent || ((_a2 = this.navigator) == null ? void 0 : _a2.userAgent);
|
|
1811
|
+
let type = "", version = "";
|
|
1812
|
+
if (userAgent.includes("Android") || userAgent.includes("Adr")) {
|
|
1813
|
+
type = "Android";
|
|
1814
|
+
version = ((_b = userAgent.match(/Android ([\d.]+);/)) == null ? void 0 : _b[1]) || "";
|
|
1815
|
+
} else if (userAgent.includes("CrOS")) {
|
|
1816
|
+
type = "Chromium OS";
|
|
1817
|
+
version = ((_c = userAgent.match(/MSIE ([\d.]+)/)) == null ? void 0 : _c[1]) || ((_d = userAgent.match(/rv:([\d.]+)/)) == null ? void 0 : _d[1]) || "";
|
|
1818
|
+
} else if (userAgent.includes("Linux") || userAgent.includes("X11")) {
|
|
1819
|
+
type = "Linux";
|
|
1820
|
+
version = ((_e = userAgent.match(/Linux ([\d.]+)/)) == null ? void 0 : _e[1]) || "";
|
|
1821
|
+
} else if (userAgent.includes("Ubuntu")) {
|
|
1822
|
+
type = "Ubuntu";
|
|
1823
|
+
version = ((_f = userAgent.match(/Ubuntu ([\d.]+)/)) == null ? void 0 : _f[1]) || "";
|
|
1824
|
+
} else if (userAgent.includes("Windows")) {
|
|
1825
|
+
let v = ((_g = userAgent.match(/^Mozilla\/\d.0 \(Windows NT ([\d.]+)[;)].*$/)) == null ? void 0 : _g[1]) || "";
|
|
1826
|
+
let hash = {
|
|
1827
|
+
"10.0": "10",
|
|
1828
|
+
"6.4": "10 Technical Preview",
|
|
1829
|
+
"6.3": "8.1",
|
|
1830
|
+
"6.2": "8",
|
|
1831
|
+
"6.1": "7",
|
|
1832
|
+
"6.0": "Vista",
|
|
1833
|
+
"5.2": "XP 64-bit",
|
|
1834
|
+
"5.1": "XP",
|
|
1835
|
+
"5.01": "2000 SP1",
|
|
1836
|
+
"5.0": "2000",
|
|
1837
|
+
"4.0": "NT",
|
|
1838
|
+
"4.90": "ME"
|
|
1839
|
+
};
|
|
1840
|
+
type = "Windows";
|
|
1841
|
+
version = v in hash ? hash[v] : v;
|
|
1842
|
+
} else if (userAgent.includes("like Mac OS X")) {
|
|
1843
|
+
type = "IOS";
|
|
1844
|
+
version = ((_h = userAgent.match(/OS ([\d_]+) like/)) == null ? void 0 : _h[1].replace(/_/g, ".")) || "";
|
|
1845
|
+
} else if (userAgent.includes("Macintosh")) {
|
|
1846
|
+
type = "macOS";
|
|
1847
|
+
version = ((_i = userAgent.match(/Mac OS X -?([\d_]+)/)) == null ? void 0 : _i[1].replace(/_/g, ".")) || "";
|
|
1848
|
+
}
|
|
1849
|
+
return { type, version };
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1852
|
+
* 获取浏览器类型信息
|
|
1853
|
+
*
|
|
1854
|
+
* @returns 浏览器类型信息,包含浏览器类型和版本号
|
|
1855
|
+
*/
|
|
1856
|
+
static getExplorer() {
|
|
1857
|
+
var _a2;
|
|
1858
|
+
const userAgent = this.userAgent || ((_a2 = this.navigator) == null ? void 0 : _a2.userAgent);
|
|
1859
|
+
let type = "", version = "";
|
|
1860
|
+
if (/MSIE|Trident/.test(userAgent)) {
|
|
1861
|
+
let matches = /MSIE\s(\d+\.\d+)/.exec(userAgent) || /rv:(\d+\.\d+)/.exec(userAgent);
|
|
1862
|
+
if (matches) {
|
|
1863
|
+
type = "IE";
|
|
1864
|
+
version = matches[1];
|
|
1865
|
+
}
|
|
1866
|
+
} else if (/Edge/.test(userAgent)) {
|
|
1867
|
+
let matches = /Edge\/(\d+\.\d+)/.exec(userAgent);
|
|
1868
|
+
if (matches) {
|
|
1869
|
+
type = "Edge";
|
|
1870
|
+
version = matches[1];
|
|
1871
|
+
}
|
|
1872
|
+
} else if (/Chrome/.test(userAgent) && /Google Inc/.test(this.navigator.vendor)) {
|
|
1873
|
+
let matches = /Chrome\/(\d+\.\d+)/.exec(userAgent);
|
|
1874
|
+
if (matches) {
|
|
1875
|
+
type = "Chrome";
|
|
1876
|
+
version = matches[1];
|
|
1877
|
+
}
|
|
1878
|
+
} else if (/Firefox/.test(userAgent)) {
|
|
1879
|
+
let matches = /Firefox\/(\d+\.\d+)/.exec(userAgent);
|
|
1880
|
+
if (matches) {
|
|
1881
|
+
type = "Firefox";
|
|
1882
|
+
version = matches[1];
|
|
1883
|
+
}
|
|
1884
|
+
} else if (/Safari/.test(userAgent) && /Apple Computer/.test(this.navigator.vendor)) {
|
|
1885
|
+
let matches = /Version\/(\d+\.\d+)([^S]*)(Safari)/.exec(userAgent);
|
|
1886
|
+
if (matches) {
|
|
1887
|
+
type = "Safari";
|
|
1888
|
+
version = matches[1];
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
return {
|
|
1892
|
+
type,
|
|
1893
|
+
version
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
2418
1896
|
/**
|
|
2419
1897
|
* 切换全屏状态
|
|
2420
1898
|
*
|
|
2421
1899
|
* @param status 是否全屏
|
|
2422
1900
|
*/
|
|
2423
|
-
switchFullScreen(status) {
|
|
1901
|
+
static switchFullScreen(status) {
|
|
2424
1902
|
if (status) {
|
|
2425
1903
|
const element = document.documentElement;
|
|
2426
1904
|
if (element.requestFullscreen) {
|
|
@@ -2443,41 +1921,196 @@ const BrowserUtil = {
|
|
|
2443
1921
|
document.webkitExitFullscreen();
|
|
2444
1922
|
}
|
|
2445
1923
|
}
|
|
2446
|
-
}
|
|
1924
|
+
}
|
|
2447
1925
|
/**
|
|
2448
|
-
*
|
|
1926
|
+
* 判断当前浏览器是否支持WebGL
|
|
2449
1927
|
*
|
|
2450
|
-
* @returns
|
|
1928
|
+
* @returns 如果支持WebGL则返回true,否则返回false
|
|
2451
1929
|
*/
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
const appElement = document.getElementById("app");
|
|
2456
|
-
if (!appElement) return;
|
|
2457
|
-
const appStyle = appElement.style;
|
|
2458
|
-
const realRatio = baseWidth / baseHeight;
|
|
2459
|
-
const designRatio = 16 / 9;
|
|
2460
|
-
let scaleRate = baseWidth / 1920;
|
|
2461
|
-
if (realRatio > designRatio) {
|
|
2462
|
-
scaleRate = baseHeight / 1080;
|
|
1930
|
+
static isSupportWebGL() {
|
|
1931
|
+
if (!(this == null ? void 0 : this.document)) {
|
|
1932
|
+
return false;
|
|
2463
1933
|
}
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
}
|
|
1934
|
+
const $canvas = this.document.createElement("canvas");
|
|
1935
|
+
const gl = $canvas.getContext("webgl") || $canvas.getContext("experimental-webgl");
|
|
1936
|
+
return gl && gl instanceof WebGLRenderingContext;
|
|
1937
|
+
}
|
|
2468
1938
|
/**
|
|
2469
|
-
* 获取
|
|
1939
|
+
* 获取GPU信息
|
|
2470
1940
|
*
|
|
2471
|
-
* @returns
|
|
1941
|
+
* @returns 返回包含GPU类型和型号的对象
|
|
2472
1942
|
*/
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
if (
|
|
2477
|
-
|
|
1943
|
+
static getGPU() {
|
|
1944
|
+
let type = "";
|
|
1945
|
+
let model = "";
|
|
1946
|
+
if (this == null ? void 0 : this.document) {
|
|
1947
|
+
let $canvas = this.document.createElement("canvas");
|
|
1948
|
+
let webgl = $canvas.getContext("webgl") || $canvas.getContext("experimental-webgl");
|
|
1949
|
+
if (webgl instanceof WebGLRenderingContext) {
|
|
1950
|
+
let debugInfo = webgl.getExtension("WEBGL_debug_renderer_info");
|
|
1951
|
+
if (debugInfo) {
|
|
1952
|
+
let gpu_str = webgl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
|
|
1953
|
+
type = (gpu_str.match(/ANGLE \((.+?),/) || [])[1] || "";
|
|
1954
|
+
model = (gpu_str.match(/, (.+?) (\(|vs_)/) || [])[1] || "";
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
2478
1957
|
}
|
|
1958
|
+
return {
|
|
1959
|
+
type,
|
|
1960
|
+
model
|
|
1961
|
+
};
|
|
2479
1962
|
}
|
|
2480
|
-
|
|
1963
|
+
/**
|
|
1964
|
+
* 获取当前浏览器设置的语言
|
|
1965
|
+
*
|
|
1966
|
+
* @returns 返回浏览器设置的语言,格式为 "语言_地区",例如 "zh_CN"。如果获取失败,则返回 "Unknown"。
|
|
1967
|
+
*/
|
|
1968
|
+
static getLanguage() {
|
|
1969
|
+
var _a2, _b;
|
|
1970
|
+
let g = ((_a2 = this.navigator) == null ? void 0 : _a2.language) || ((_b = this.navigator) == null ? void 0 : _b.userLanguage);
|
|
1971
|
+
if (typeof g !== "string") return "";
|
|
1972
|
+
let arr = g.split("-");
|
|
1973
|
+
if (arr[1]) {
|
|
1974
|
+
arr[1] = arr[1].toUpperCase();
|
|
1975
|
+
}
|
|
1976
|
+
let language = arr.join("_");
|
|
1977
|
+
return language;
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* 获取当前环境的时区。
|
|
1981
|
+
*
|
|
1982
|
+
* @returns 返回当前环境的时区,若获取失败则返回 undefined。
|
|
1983
|
+
*/
|
|
1984
|
+
static getTimeZone() {
|
|
1985
|
+
var _a2, _b;
|
|
1986
|
+
return (_b = (_a2 = Intl == null ? void 0 : Intl.DateTimeFormat()) == null ? void 0 : _a2.resolvedOptions()) == null ? void 0 : _b.timeZone;
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* 获取屏幕帧率
|
|
1990
|
+
*
|
|
1991
|
+
* @returns 返回一个Promise,resolve为计算得到的屏幕帧率
|
|
1992
|
+
* eg: const fps = await BrowserUtil.getScreenFPS()
|
|
1993
|
+
*/
|
|
1994
|
+
static async getScreenFPS() {
|
|
1995
|
+
return new Promise(function(resolve) {
|
|
1996
|
+
let lastTime = 0;
|
|
1997
|
+
let count = 1;
|
|
1998
|
+
let list = [];
|
|
1999
|
+
let tick = function(timestamp) {
|
|
2000
|
+
if (lastTime > 0) {
|
|
2001
|
+
if (count < 12) {
|
|
2002
|
+
list.push(timestamp - lastTime);
|
|
2003
|
+
lastTime = timestamp;
|
|
2004
|
+
count++;
|
|
2005
|
+
requestAnimationFrame(tick);
|
|
2006
|
+
} else {
|
|
2007
|
+
list.sort();
|
|
2008
|
+
list = list.slice(1, 11);
|
|
2009
|
+
let sum = list.reduce((a, b) => a + b);
|
|
2010
|
+
const fps = Math.round(1e4 / sum / 10) * 10;
|
|
2011
|
+
resolve(fps);
|
|
2012
|
+
}
|
|
2013
|
+
} else {
|
|
2014
|
+
lastTime = timestamp;
|
|
2015
|
+
requestAnimationFrame(tick);
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
requestAnimationFrame(tick);
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* 获取IP地址
|
|
2023
|
+
*
|
|
2024
|
+
* @returns 返回一个Promise,当成功获取到IP地址时resolve,返回IP地址字符串;当获取失败时reject,返回undefined
|
|
2025
|
+
*/
|
|
2026
|
+
static async getIPAddress() {
|
|
2027
|
+
const reg = {
|
|
2028
|
+
IPv4: /\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/,
|
|
2029
|
+
IPv6: /\b(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}\b/i
|
|
2030
|
+
};
|
|
2031
|
+
let RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
|
|
2032
|
+
const ipSet = /* @__PURE__ */ new Set();
|
|
2033
|
+
const onicecandidate = (ice) => {
|
|
2034
|
+
var _a2;
|
|
2035
|
+
const candidate = (_a2 = ice == null ? void 0 : ice.candidate) == null ? void 0 : _a2.candidate;
|
|
2036
|
+
if (candidate) {
|
|
2037
|
+
for (const regex of [reg["IPv4"], reg["IPv6"]]) {
|
|
2038
|
+
const match = candidate.match(regex);
|
|
2039
|
+
if (match) {
|
|
2040
|
+
ipSet.add(match[0]);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
};
|
|
2045
|
+
return new Promise(function(resolve, reject) {
|
|
2046
|
+
const conn = new RTCPeerConnection({
|
|
2047
|
+
iceServers: [
|
|
2048
|
+
{
|
|
2049
|
+
urls: "stun:stun.l.google.com:19302"
|
|
2050
|
+
},
|
|
2051
|
+
{
|
|
2052
|
+
urls: "stun:stun.services.mozilla.com"
|
|
2053
|
+
}
|
|
2054
|
+
]
|
|
2055
|
+
});
|
|
2056
|
+
conn.addEventListener("icecandidate", onicecandidate);
|
|
2057
|
+
conn.createDataChannel("");
|
|
2058
|
+
conn.createOffer().then((offer) => conn.setLocalDescription(offer), reject);
|
|
2059
|
+
let count = 20;
|
|
2060
|
+
let hander;
|
|
2061
|
+
let closeConnect = function() {
|
|
2062
|
+
try {
|
|
2063
|
+
conn.removeEventListener("icecandidate", onicecandidate);
|
|
2064
|
+
conn.close();
|
|
2065
|
+
} catch {
|
|
2066
|
+
}
|
|
2067
|
+
hander && clearInterval(hander);
|
|
2068
|
+
};
|
|
2069
|
+
hander = setInterval(function() {
|
|
2070
|
+
let ips = [...ipSet];
|
|
2071
|
+
if (ips.length) {
|
|
2072
|
+
closeConnect();
|
|
2073
|
+
resolve(ips[0]);
|
|
2074
|
+
} else if (count) {
|
|
2075
|
+
count--;
|
|
2076
|
+
} else {
|
|
2077
|
+
closeConnect();
|
|
2078
|
+
resolve("");
|
|
2079
|
+
}
|
|
2080
|
+
}, 100);
|
|
2081
|
+
});
|
|
2082
|
+
}
|
|
2083
|
+
/**
|
|
2084
|
+
* 获取网络状态信息
|
|
2085
|
+
*
|
|
2086
|
+
* @returns 返回包含网络状态信息的对象,包含以下属性:
|
|
2087
|
+
* - network: 网络类型,可能的值为 'wifi'、'4g' 等
|
|
2088
|
+
* - isOnline: 是否在线,为 true 或 false
|
|
2089
|
+
* - ip: 当前设备的 IP 地址
|
|
2090
|
+
*/
|
|
2091
|
+
static async getNetwork() {
|
|
2092
|
+
var _a2, _b;
|
|
2093
|
+
let network = "unknown";
|
|
2094
|
+
let connection = (_a2 = this.navigator) == null ? void 0 : _a2.connection;
|
|
2095
|
+
if (connection) {
|
|
2096
|
+
network = connection.type || connection.effectiveType;
|
|
2097
|
+
if (network == "2" || network == "unknown") {
|
|
2098
|
+
network = "wifi";
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
let isOnline = ((_b = this.navigator) == null ? void 0 : _b.onLine) || false;
|
|
2102
|
+
let ip = await this.getIPAddress();
|
|
2103
|
+
return {
|
|
2104
|
+
network,
|
|
2105
|
+
isOnline,
|
|
2106
|
+
ip
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
__publicField(BrowserUtil, "document", window == null ? void 0 : window.document);
|
|
2111
|
+
__publicField(BrowserUtil, "navigator", window == null ? void 0 : window.navigator);
|
|
2112
|
+
__publicField(BrowserUtil, "userAgent", (_a = window == null ? void 0 : window.navigator) == null ? void 0 : _a.userAgent);
|
|
2113
|
+
__publicField(BrowserUtil, "screen", window == null ? void 0 : window.screen);
|
|
2481
2114
|
const CoordsUtil = {
|
|
2482
2115
|
PI: 3.141592653589793,
|
|
2483
2116
|
XPI: 3.141592653589793 * 3e3 / 180,
|
|
@@ -2607,18 +2240,26 @@ const CoordsUtil = {
|
|
|
2607
2240
|
return ret;
|
|
2608
2241
|
},
|
|
2609
2242
|
/**
|
|
2610
|
-
*
|
|
2243
|
+
* 生成一个介于两个坐标之间的随机坐标
|
|
2611
2244
|
*
|
|
2612
|
-
* @param
|
|
2613
|
-
* @param
|
|
2614
|
-
* @returns
|
|
2245
|
+
* @param start 起始坐标,包含x和y属性
|
|
2246
|
+
* @param end 结束坐标,包含x和y属性
|
|
2247
|
+
* @returns 返回一个包含x和y属性的随机坐标
|
|
2615
2248
|
*/
|
|
2616
2249
|
random({ x: minX, y: minY }, { x: maxX, y: maxY }) {
|
|
2617
2250
|
return {
|
|
2618
|
-
|
|
2619
|
-
|
|
2251
|
+
x: Math.random() * (maxX - minX) + minX,
|
|
2252
|
+
y: Math.random() * (maxY - minY) + minY
|
|
2620
2253
|
};
|
|
2621
2254
|
},
|
|
2255
|
+
/**
|
|
2256
|
+
* 对坐标数组进行解构并应用函数处理
|
|
2257
|
+
*
|
|
2258
|
+
* @param arr 待解构的数组
|
|
2259
|
+
* @param fn 处理函数
|
|
2260
|
+
* @param context 函数执行上下文,可选
|
|
2261
|
+
* @returns 处理后的数组
|
|
2262
|
+
*/
|
|
2622
2263
|
deCompose(arr, fn, context) {
|
|
2623
2264
|
if (!Array.isArray(arr)) {
|
|
2624
2265
|
return context ? fn.call(context, arr) : fn(arr);
|
|
@@ -2627,7 +2268,7 @@ const CoordsUtil = {
|
|
|
2627
2268
|
let p, pp;
|
|
2628
2269
|
for (let i = 0, len = arr.length; i < len; i++) {
|
|
2629
2270
|
p = arr[i];
|
|
2630
|
-
if (
|
|
2271
|
+
if (CommUtils.isNil(p)) {
|
|
2631
2272
|
result.push(null);
|
|
2632
2273
|
continue;
|
|
2633
2274
|
}
|
|
@@ -2808,10 +2449,10 @@ const DomUtil = {
|
|
|
2808
2449
|
* @returns 元素的样式值,如果获取不到则返回 null
|
|
2809
2450
|
*/
|
|
2810
2451
|
getStyle(el, style) {
|
|
2811
|
-
var
|
|
2452
|
+
var _a2;
|
|
2812
2453
|
let value = el.style[style];
|
|
2813
2454
|
if (!value || value === "auto") {
|
|
2814
|
-
const css = (
|
|
2455
|
+
const css = (_a2 = document.defaultView) == null ? void 0 : _a2.getComputedStyle(el, null);
|
|
2815
2456
|
value = css ? css[style] : null;
|
|
2816
2457
|
if (value === "auto") value = null;
|
|
2817
2458
|
}
|
|
@@ -2897,8 +2538,8 @@ const DomUtil = {
|
|
|
2897
2538
|
* @returns 返回一个布尔值,表示元素是否包含指定类名
|
|
2898
2539
|
*/
|
|
2899
2540
|
hasClass(el, name) {
|
|
2900
|
-
var
|
|
2901
|
-
if ((
|
|
2541
|
+
var _a2;
|
|
2542
|
+
if ((_a2 = el.classList) == null ? void 0 : _a2.contains(name)) {
|
|
2902
2543
|
return true;
|
|
2903
2544
|
}
|
|
2904
2545
|
const className = this.getClass(el);
|
|
@@ -3199,6 +2840,661 @@ const UrlUtil = {
|
|
|
3199
2840
|
return obj;
|
|
3200
2841
|
}
|
|
3201
2842
|
};
|
|
2843
|
+
class Color {
|
|
2844
|
+
constructor(r, g, b, a) {
|
|
2845
|
+
__publicField(this, "_r");
|
|
2846
|
+
__publicField(this, "_g");
|
|
2847
|
+
__publicField(this, "_b");
|
|
2848
|
+
__publicField(this, "_alpha");
|
|
2849
|
+
this._validateColorChannel(r);
|
|
2850
|
+
this._validateColorChannel(g);
|
|
2851
|
+
this._validateColorChannel(b);
|
|
2852
|
+
this._r = r;
|
|
2853
|
+
this._g = g;
|
|
2854
|
+
this._b = b;
|
|
2855
|
+
this._alpha = MathUtils.clamp(a || 1, 0, 1);
|
|
2856
|
+
}
|
|
2857
|
+
_validateColorChannel(channel) {
|
|
2858
|
+
if (channel < 0 || channel > 255) {
|
|
2859
|
+
throw new Error("Color channel must be between 0 and 255.");
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
get rgba() {
|
|
2863
|
+
return { r: this._r, g: this._g, b: this._b, a: this._alpha };
|
|
2864
|
+
}
|
|
2865
|
+
get hex() {
|
|
2866
|
+
return Color.rgb2hex(this._r, this._g, this._b, this._alpha);
|
|
2867
|
+
}
|
|
2868
|
+
setAlpha(a) {
|
|
2869
|
+
this._alpha = MathUtils.clamp(a, 0, 1);
|
|
2870
|
+
return this;
|
|
2871
|
+
}
|
|
2872
|
+
// 设置颜色的RGB值
|
|
2873
|
+
setRgb(r, g, b) {
|
|
2874
|
+
this._validateColorChannel(r);
|
|
2875
|
+
this._validateColorChannel(g);
|
|
2876
|
+
this._validateColorChannel(b);
|
|
2877
|
+
this._r = r;
|
|
2878
|
+
this._g = g;
|
|
2879
|
+
this._b = b;
|
|
2880
|
+
return this;
|
|
2881
|
+
}
|
|
2882
|
+
/**
|
|
2883
|
+
* 从RGBA字符串创建Color对象
|
|
2884
|
+
*
|
|
2885
|
+
* @param rgbaValue RGBA颜色值字符串,格式为"rgba(r,g,b,a)"或"rgb(r,g,b)"
|
|
2886
|
+
* @returns 返回Color对象
|
|
2887
|
+
* @throws 如果rgbaValue不是有效的RGBA颜色值,则抛出错误
|
|
2888
|
+
*/
|
|
2889
|
+
static fromRgba(rgbaValue) {
|
|
2890
|
+
const rgbaMatch = rgbaValue.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([\d.]+))?\s*\)$/);
|
|
2891
|
+
if (!rgbaMatch) throw new Error("Invalid RGBA color value");
|
|
2892
|
+
const r = parseInt(rgbaMatch[1], 10);
|
|
2893
|
+
const g = parseInt(rgbaMatch[2], 10);
|
|
2894
|
+
const b = parseInt(rgbaMatch[3], 10);
|
|
2895
|
+
const a = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1;
|
|
2896
|
+
return new Color(r, g, b, a);
|
|
2897
|
+
}
|
|
2898
|
+
/**
|
|
2899
|
+
* 将十六进制颜色值转换为颜色对象
|
|
2900
|
+
*
|
|
2901
|
+
* @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
|
|
2902
|
+
* @returns 返回颜色对象
|
|
2903
|
+
*/
|
|
2904
|
+
static fromHex(hexValue, a = 1) {
|
|
2905
|
+
const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
|
2906
|
+
const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
|
|
2907
|
+
const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
|
|
2908
|
+
const rgb = rgx.exec(hex);
|
|
2909
|
+
if (!rgb) {
|
|
2910
|
+
throw new Error("Invalid HEX color value");
|
|
2911
|
+
}
|
|
2912
|
+
const r = parseInt(rgb[1], 16);
|
|
2913
|
+
const g = parseInt(rgb[2], 16);
|
|
2914
|
+
const b = parseInt(rgb[3], 16);
|
|
2915
|
+
return new Color(r, g, b, a);
|
|
2916
|
+
}
|
|
2917
|
+
/**
|
|
2918
|
+
* 从 HSL 字符串创建颜色对象
|
|
2919
|
+
*
|
|
2920
|
+
* @param hsl HSL 字符串,格式为 hsl(h, s%, l%) 或 hsla(h, s%, l%, a)
|
|
2921
|
+
* @returns 返回颜色对象,如果 hsl 字符串无效则返回 null
|
|
2922
|
+
*/
|
|
2923
|
+
static fromHsl(hslValue) {
|
|
2924
|
+
const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
|
|
2925
|
+
if (!hsl) {
|
|
2926
|
+
throw new Error("Invalid HSL color value");
|
|
2927
|
+
}
|
|
2928
|
+
const h = parseInt(hsl[1], 10) / 360;
|
|
2929
|
+
const s = parseInt(hsl[2], 10) / 100;
|
|
2930
|
+
const l = parseInt(hsl[3], 10) / 100;
|
|
2931
|
+
const a = hsl[4] ? parseFloat(hsl[4]) : 1;
|
|
2932
|
+
function hue2rgb(p, q, t) {
|
|
2933
|
+
if (t < 0) t += 1;
|
|
2934
|
+
if (t > 1) t -= 1;
|
|
2935
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
2936
|
+
if (t < 1 / 2) return q;
|
|
2937
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
2938
|
+
return p;
|
|
2939
|
+
}
|
|
2940
|
+
let r, g, b;
|
|
2941
|
+
if (s === 0) {
|
|
2942
|
+
r = g = b = l;
|
|
2943
|
+
} else {
|
|
2944
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
2945
|
+
const p = 2 * l - q;
|
|
2946
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
2947
|
+
g = hue2rgb(p, q, h);
|
|
2948
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
2949
|
+
}
|
|
2950
|
+
return new Color(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
|
|
2951
|
+
}
|
|
2952
|
+
/**
|
|
2953
|
+
* 从字符串中创建颜色对象
|
|
2954
|
+
*
|
|
2955
|
+
* @param str 字符串类型的颜色值,支持rgba、hex、hsl格式
|
|
2956
|
+
* @returns 返回创建的颜色对象
|
|
2957
|
+
* @throws 当颜色值无效时,抛出错误
|
|
2958
|
+
*/
|
|
2959
|
+
static from(str) {
|
|
2960
|
+
if (this.isRgb(str)) {
|
|
2961
|
+
return this.fromRgba(str);
|
|
2962
|
+
} else if (this.isHex(str)) {
|
|
2963
|
+
return this.fromHex(str);
|
|
2964
|
+
} else if (this.isHsl(str)) {
|
|
2965
|
+
return this.fromHsl(str);
|
|
2966
|
+
} else {
|
|
2967
|
+
throw new Error("Invalid color value");
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
/**
|
|
2971
|
+
* 将RGB颜色值转换为十六进制颜色值
|
|
2972
|
+
*
|
|
2973
|
+
* @param r 红色分量值,取值范围0-255
|
|
2974
|
+
* @param g 绿色分量值,取值范围0-255
|
|
2975
|
+
* @param b 蓝色分量值,取值范围0-255
|
|
2976
|
+
* @param a 可选参数,透明度分量值,取值范围0-1
|
|
2977
|
+
* @returns 十六进制颜色值,格式为#RRGGBB或#RRGGBBAA
|
|
2978
|
+
*/
|
|
2979
|
+
static rgb2hex(r, g, b, a) {
|
|
2980
|
+
var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
|
2981
|
+
if (a !== void 0) {
|
|
2982
|
+
const alpha = Math.round(a * 255).toString(16).padStart(2, "0");
|
|
2983
|
+
return hex + alpha;
|
|
2984
|
+
}
|
|
2985
|
+
return hex;
|
|
2986
|
+
}
|
|
2987
|
+
static isHex(a) {
|
|
2988
|
+
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
|
|
2989
|
+
}
|
|
2990
|
+
static isRgb(a) {
|
|
2991
|
+
return /^rgba?\s*\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*[\d.]+)?\s*\)$/.test(a);
|
|
2992
|
+
}
|
|
2993
|
+
static isHsl(a) {
|
|
2994
|
+
return /^(hsl|hsla)\(\d+,\s*[\d.]+%,\s*[\d.]+%(,\s*[\d.]+)?\)$/.test(a);
|
|
2995
|
+
}
|
|
2996
|
+
static isColor(a) {
|
|
2997
|
+
return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
|
|
2998
|
+
}
|
|
2999
|
+
static random() {
|
|
3000
|
+
let r = Math.floor(Math.random() * 256);
|
|
3001
|
+
let g = Math.floor(Math.random() * 256);
|
|
3002
|
+
let b = Math.floor(Math.random() * 256);
|
|
3003
|
+
let a = Math.random();
|
|
3004
|
+
return new Color(r, g, b, a);
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
class CanvasDrawer {
|
|
3008
|
+
constructor(el) {
|
|
3009
|
+
__publicField(this, "context", null);
|
|
3010
|
+
if (typeof el === "string") {
|
|
3011
|
+
el = document.querySelector("#" + el);
|
|
3012
|
+
if (!el) {
|
|
3013
|
+
throw new Error("Element not found");
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
if (el instanceof HTMLElement) {
|
|
3017
|
+
const canvas = el;
|
|
3018
|
+
if (canvas.getContext) {
|
|
3019
|
+
this.context = canvas.getContext("2d");
|
|
3020
|
+
} else {
|
|
3021
|
+
throw new Error("getContext is not available on this element");
|
|
3022
|
+
}
|
|
3023
|
+
} else {
|
|
3024
|
+
throw new Error("Element is not an HTMLElement");
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
/**
|
|
3028
|
+
* 绘制线条
|
|
3029
|
+
*
|
|
3030
|
+
* @param start 起始坐标点
|
|
3031
|
+
* @param end 终止坐标点
|
|
3032
|
+
* @param options 绘制选项,包括线条宽度和颜色
|
|
3033
|
+
* @throws 当画布上下文不存在时抛出错误
|
|
3034
|
+
*/
|
|
3035
|
+
drawLine({ x: startX, y: startY }, { x: endX, y: endY }, options = {}) {
|
|
3036
|
+
if (!this.context) {
|
|
3037
|
+
throw new Error("Canvas context is null or undefined");
|
|
3038
|
+
}
|
|
3039
|
+
this.context.beginPath();
|
|
3040
|
+
const width = options.width || 1;
|
|
3041
|
+
const color = options.color || "#000";
|
|
3042
|
+
this.context.lineWidth = width;
|
|
3043
|
+
this.context.strokeStyle = color;
|
|
3044
|
+
this.context.moveTo(startX, startY);
|
|
3045
|
+
this.context.lineTo(endX, endY);
|
|
3046
|
+
this.context.stroke();
|
|
3047
|
+
}
|
|
3048
|
+
/**
|
|
3049
|
+
* 绘制圆弧
|
|
3050
|
+
*
|
|
3051
|
+
* @param x 圆心x坐标
|
|
3052
|
+
* @param y 圆心y坐标
|
|
3053
|
+
* @param radius 半径
|
|
3054
|
+
* @param startAngle 起始角度(度)
|
|
3055
|
+
* @param endAngle 结束角度(度)
|
|
3056
|
+
* @param anticlockwise 是否逆时针绘制
|
|
3057
|
+
* @param isFill 是否填充
|
|
3058
|
+
* @param bgColor 背景颜色
|
|
3059
|
+
* @throws 当Canvas context为null或undefined时抛出错误
|
|
3060
|
+
*/
|
|
3061
|
+
drawArc({ x, y }, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
|
|
3062
|
+
if (!this.context) {
|
|
3063
|
+
throw new Error("Canvas context is null or undefined");
|
|
3064
|
+
}
|
|
3065
|
+
if (isFill) {
|
|
3066
|
+
this.context.fillStyle = bgColor;
|
|
3067
|
+
this.context.beginPath();
|
|
3068
|
+
this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
|
|
3069
|
+
this.context.fill();
|
|
3070
|
+
} else {
|
|
3071
|
+
this.context.strokeStyle = bgColor;
|
|
3072
|
+
this.context.beginPath();
|
|
3073
|
+
this.context.arc(x, y, radius, MathUtils.deg2Rad(startAngle), MathUtils.deg2Rad(endAngle), anticlockwise);
|
|
3074
|
+
this.context.stroke();
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
static createCanvas(width = 1, height = 1) {
|
|
3078
|
+
const canvas = document.createElement("canvas");
|
|
3079
|
+
if (width) {
|
|
3080
|
+
canvas.width = width;
|
|
3081
|
+
}
|
|
3082
|
+
if (height) {
|
|
3083
|
+
canvas.height = height;
|
|
3084
|
+
}
|
|
3085
|
+
return canvas;
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
class EventDispatcher {
|
|
3089
|
+
constructor() {
|
|
3090
|
+
__publicField(this, "_listeners");
|
|
3091
|
+
__publicField(this, "_mutex", {});
|
|
3092
|
+
__publicField(this, "_context");
|
|
3093
|
+
}
|
|
3094
|
+
addEventListener(type, listener, context, mutexStatus) {
|
|
3095
|
+
if (this._listeners === void 0) this._listeners = {};
|
|
3096
|
+
this._context = context;
|
|
3097
|
+
const mutex = this._mutex;
|
|
3098
|
+
const listeners = this._listeners;
|
|
3099
|
+
if (listeners[type] === void 0) {
|
|
3100
|
+
listeners[type] = [];
|
|
3101
|
+
}
|
|
3102
|
+
if (listeners[type].indexOf(listener) === -1) {
|
|
3103
|
+
if (mutexStatus) {
|
|
3104
|
+
mutex[type] = listener;
|
|
3105
|
+
}
|
|
3106
|
+
listeners[type].push(listener);
|
|
3107
|
+
}
|
|
3108
|
+
return this;
|
|
3109
|
+
}
|
|
3110
|
+
hasEventListener(type, listener) {
|
|
3111
|
+
if (this._listeners === null || this._listeners === void 0) return false;
|
|
3112
|
+
const listeners = this._listeners;
|
|
3113
|
+
return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
|
|
3114
|
+
}
|
|
3115
|
+
removeEventListener(type, listener) {
|
|
3116
|
+
if (this._listeners === void 0) return;
|
|
3117
|
+
const listeners = this._listeners;
|
|
3118
|
+
const listenerArray = listeners[type];
|
|
3119
|
+
if (this._mutex[type] === listener) {
|
|
3120
|
+
this._mutex[type] = null;
|
|
3121
|
+
}
|
|
3122
|
+
if (listenerArray !== void 0) {
|
|
3123
|
+
const index = listenerArray.map((d) => d.toString()).indexOf(listener.toString());
|
|
3124
|
+
if (index !== -1) {
|
|
3125
|
+
listenerArray.splice(index, 1);
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
dispatchEvent(event) {
|
|
3130
|
+
if (this._listeners === void 0) return;
|
|
3131
|
+
const listeners = this._listeners;
|
|
3132
|
+
const listenerArray = listeners[event.type];
|
|
3133
|
+
if (listenerArray !== void 0) {
|
|
3134
|
+
event.target = this;
|
|
3135
|
+
const array = listenerArray.slice(0);
|
|
3136
|
+
if (this._mutex[event.type] !== void 0) {
|
|
3137
|
+
const find = array.find((item) => item === this._mutex[event.type]);
|
|
3138
|
+
if (find) {
|
|
3139
|
+
find.call(this._context || this, event);
|
|
3140
|
+
return;
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
for (let i = 0, l = array.length; i < l; i++) {
|
|
3144
|
+
const item = array[i];
|
|
3145
|
+
if (typeof item === "function") {
|
|
3146
|
+
item.call(this._context || this, event);
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
removeAllListener() {
|
|
3152
|
+
this._mutex = {};
|
|
3153
|
+
for (const key in this._listeners) {
|
|
3154
|
+
this._listeners[key] = [];
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
class HashMap extends Map {
|
|
3159
|
+
isEmpty() {
|
|
3160
|
+
return this.size === 0;
|
|
3161
|
+
}
|
|
3162
|
+
_values() {
|
|
3163
|
+
return Array.from(this.values());
|
|
3164
|
+
}
|
|
3165
|
+
_keys() {
|
|
3166
|
+
return Array.from(this.keys());
|
|
3167
|
+
}
|
|
3168
|
+
_entries() {
|
|
3169
|
+
return Array.from(this.entries());
|
|
3170
|
+
}
|
|
3171
|
+
/**
|
|
3172
|
+
* 从键值对数组创建一个HashMap对象
|
|
3173
|
+
*
|
|
3174
|
+
* @param array 键值对数组,默认为空数组
|
|
3175
|
+
* @returns 返回一个新的HashMap对象
|
|
3176
|
+
*/
|
|
3177
|
+
static fromEntries(array = []) {
|
|
3178
|
+
const hashMap = new HashMap();
|
|
3179
|
+
array.forEach((element) => {
|
|
3180
|
+
if (Array.isArray(element) && element.length === 2) {
|
|
3181
|
+
hashMap.set(element[0], element[1]);
|
|
3182
|
+
}
|
|
3183
|
+
});
|
|
3184
|
+
return hashMap;
|
|
3185
|
+
}
|
|
3186
|
+
/**
|
|
3187
|
+
* 从JSON字符串创建HashMap实例
|
|
3188
|
+
*
|
|
3189
|
+
* @param str JSON字符串
|
|
3190
|
+
* @returns HashMap实例
|
|
3191
|
+
*/
|
|
3192
|
+
static fromJson(str) {
|
|
3193
|
+
const json = ObjectUtil.parse(str);
|
|
3194
|
+
return new HashMap(Object.entries(json));
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
class WebSocketClient extends EventDispatcher {
|
|
3198
|
+
constructor(url = "ws://127.0.0.1:10088") {
|
|
3199
|
+
super();
|
|
3200
|
+
__publicField(this, "maxCheckTimes", 10);
|
|
3201
|
+
__publicField(this, "url");
|
|
3202
|
+
__publicField(this, "checkTimes", 0);
|
|
3203
|
+
__publicField(this, "connectStatus", false);
|
|
3204
|
+
__publicField(this, "client", null);
|
|
3205
|
+
this.maxCheckTimes = 10;
|
|
3206
|
+
this.url = url;
|
|
3207
|
+
this.checkTimes = 0;
|
|
3208
|
+
this.connect();
|
|
3209
|
+
this.connCheckStatus(this.maxCheckTimes);
|
|
3210
|
+
}
|
|
3211
|
+
connect() {
|
|
3212
|
+
this.disconnect();
|
|
3213
|
+
if (this.url) {
|
|
3214
|
+
try {
|
|
3215
|
+
console.info("创建ws连接>>>" + this.url);
|
|
3216
|
+
this.client = new WebSocket(this.url);
|
|
3217
|
+
if (this.client) {
|
|
3218
|
+
const self = this;
|
|
3219
|
+
this.client.onopen = function(message) {
|
|
3220
|
+
self.dispatchEvent({
|
|
3221
|
+
type: EventType.WEB_SOCKET_CONNECT,
|
|
3222
|
+
message
|
|
3223
|
+
});
|
|
3224
|
+
};
|
|
3225
|
+
this.client.onmessage = function(message) {
|
|
3226
|
+
self.connectStatus = true;
|
|
3227
|
+
self.dispatchEvent({
|
|
3228
|
+
type: EventType.WEB_SOCKET_MESSAGE,
|
|
3229
|
+
message
|
|
3230
|
+
});
|
|
3231
|
+
};
|
|
3232
|
+
this.client.onclose = function(message) {
|
|
3233
|
+
self.dispatchEvent({
|
|
3234
|
+
type: EventType.WEB_SOCKET_CLOSE,
|
|
3235
|
+
message
|
|
3236
|
+
});
|
|
3237
|
+
};
|
|
3238
|
+
if (this.checkTimes === this.maxCheckTimes) {
|
|
3239
|
+
this.client.onerror = function(message) {
|
|
3240
|
+
self.dispatchEvent({
|
|
3241
|
+
type: EventType.WEB_SOCKET_ERROR,
|
|
3242
|
+
message
|
|
3243
|
+
});
|
|
3244
|
+
};
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
} catch (ex) {
|
|
3248
|
+
console.error("创建ws连接失败" + this.url + ":" + ex);
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
disconnect() {
|
|
3253
|
+
if (this.client) {
|
|
3254
|
+
try {
|
|
3255
|
+
console.log("ws断开连接" + this.url);
|
|
3256
|
+
this.client.close();
|
|
3257
|
+
this.client = null;
|
|
3258
|
+
} catch (ex) {
|
|
3259
|
+
this.client = null;
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
connCheckStatus(times) {
|
|
3264
|
+
if (this.checkTimes > times) return;
|
|
3265
|
+
setTimeout(() => {
|
|
3266
|
+
this.checkTimes++;
|
|
3267
|
+
if (this.client && this.client.readyState !== 0 && this.client.readyState !== 1) {
|
|
3268
|
+
this.connect();
|
|
3269
|
+
}
|
|
3270
|
+
this.connCheckStatus(times);
|
|
3271
|
+
}, 2e3);
|
|
3272
|
+
}
|
|
3273
|
+
send(message) {
|
|
3274
|
+
if (this.client && this.client.readyState === 1) {
|
|
3275
|
+
this.client.send(message);
|
|
3276
|
+
return true;
|
|
3277
|
+
}
|
|
3278
|
+
console.error(this.url + "消息发送失败:" + message);
|
|
3279
|
+
return false;
|
|
3280
|
+
}
|
|
3281
|
+
heartbeat() {
|
|
3282
|
+
setTimeout(() => {
|
|
3283
|
+
if (this.client && this.client.readyState === 1) {
|
|
3284
|
+
this.send("HeartBeat");
|
|
3285
|
+
}
|
|
3286
|
+
console.log("HeartBeat," + this.url);
|
|
3287
|
+
setTimeout(this.heartbeat, 3e4);
|
|
3288
|
+
}, 1e3);
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
const _MqttClient = class _MqttClient extends EventDispatcher {
|
|
3292
|
+
constructor(url = `ws://${window.document.domain}:20007/mqtt`, config = {}) {
|
|
3293
|
+
super();
|
|
3294
|
+
__publicField(this, "state");
|
|
3295
|
+
__publicField(this, "url");
|
|
3296
|
+
__publicField(this, "context");
|
|
3297
|
+
__publicField(this, "options");
|
|
3298
|
+
__publicField(this, "client");
|
|
3299
|
+
__publicField(this, "topics");
|
|
3300
|
+
this.context = CommUtils.extend(_MqttClient.defaultContext, config);
|
|
3301
|
+
this.options = {
|
|
3302
|
+
connectTimeout: this.context.MQTT_TIMEOUTM,
|
|
3303
|
+
clientId: CommUtils.guid(),
|
|
3304
|
+
username: this.context.MQTT_USERNAME,
|
|
3305
|
+
password: this.context.MQTT_PASSWORD,
|
|
3306
|
+
clean: true
|
|
3307
|
+
};
|
|
3308
|
+
this.url = url;
|
|
3309
|
+
this.client = connect(this.url, this.options);
|
|
3310
|
+
this._onConnect();
|
|
3311
|
+
this._onMessage();
|
|
3312
|
+
this.state = 0;
|
|
3313
|
+
this.topics = [];
|
|
3314
|
+
}
|
|
3315
|
+
_onConnect() {
|
|
3316
|
+
this.client.on("connect", () => {
|
|
3317
|
+
this.state = 1;
|
|
3318
|
+
console.log("链接mqtt成功==>" + this.url);
|
|
3319
|
+
this.dispatchEvent({ type: EventType.MQTT_CONNECT, message: this });
|
|
3320
|
+
});
|
|
3321
|
+
this.client.on("error", (err) => {
|
|
3322
|
+
console.log("链接mqtt报错", err);
|
|
3323
|
+
this.state = -1;
|
|
3324
|
+
this.dispatchEvent({ type: EventType.MQTT_ERROR, message: this });
|
|
3325
|
+
this.client.end();
|
|
3326
|
+
this.client.reconnect();
|
|
3327
|
+
});
|
|
3328
|
+
}
|
|
3329
|
+
_onMessage() {
|
|
3330
|
+
this.client.on("message", (topic, message) => {
|
|
3331
|
+
let dataString = message;
|
|
3332
|
+
let data = "";
|
|
3333
|
+
if (message instanceof Uint8Array) {
|
|
3334
|
+
dataString = message.toString();
|
|
3335
|
+
}
|
|
3336
|
+
try {
|
|
3337
|
+
data = ObjectUtil.parse(dataString);
|
|
3338
|
+
} catch (error) {
|
|
3339
|
+
throw new Error(ErrorType.JSON_PARSE_ERROR);
|
|
3340
|
+
}
|
|
3341
|
+
this.dispatchEvent({
|
|
3342
|
+
type: EventType.MQTT_MESSAGE,
|
|
3343
|
+
message: { topic, data }
|
|
3344
|
+
});
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3347
|
+
sendMsg(topic, msg) {
|
|
3348
|
+
if (!this.client.connected) {
|
|
3349
|
+
console.error("客户端未连接");
|
|
3350
|
+
return;
|
|
3351
|
+
}
|
|
3352
|
+
this.client.publish(topic, msg, { qos: 1, retain: true });
|
|
3353
|
+
}
|
|
3354
|
+
subscribe(topic) {
|
|
3355
|
+
this.state === 1 ? this.client.subscribe(topic, { qos: 1 }, (error, e) => {
|
|
3356
|
+
error instanceof Error ? console.error("订阅失败==>" + topic, error) : (this.topics = ArrayUtil.union(this.topics, topic), console.log("订阅成功==>" + topic));
|
|
3357
|
+
}) : this.addEventListener(EventType.MQTT_CONNECT, (res) => {
|
|
3358
|
+
this.client.subscribe(topic, { qos: 1 }, (error, e) => {
|
|
3359
|
+
error instanceof Error ? console.error("订阅失败==>" + topic, error) : (this.topics = ArrayUtil.union(this.topics, topic), console.log("订阅成功==>" + topic));
|
|
3360
|
+
});
|
|
3361
|
+
});
|
|
3362
|
+
return this;
|
|
3363
|
+
}
|
|
3364
|
+
unsubscribe(topic) {
|
|
3365
|
+
this.client.unsubscribe(topic, { qos: 1 }, (error, res) => {
|
|
3366
|
+
if (error instanceof Error) {
|
|
3367
|
+
console.error(`取消订阅失败==>${topic}`, error);
|
|
3368
|
+
} else {
|
|
3369
|
+
this.topics = ArrayUtil.difference(this.topics, topic);
|
|
3370
|
+
console.log(`取消订阅成功==>${topic}`);
|
|
3371
|
+
}
|
|
3372
|
+
});
|
|
3373
|
+
return this;
|
|
3374
|
+
}
|
|
3375
|
+
unsubscribeAll() {
|
|
3376
|
+
this.unsubscribe(this.topics);
|
|
3377
|
+
}
|
|
3378
|
+
unconnect() {
|
|
3379
|
+
this.client.end();
|
|
3380
|
+
this.client = null;
|
|
3381
|
+
this.dispatchEvent({ type: EventType.MQTT_CLOSE, message: null });
|
|
3382
|
+
console.log("断开mqtt成功==>" + this.url);
|
|
3383
|
+
}
|
|
3384
|
+
};
|
|
3385
|
+
/**
|
|
3386
|
+
* Creates an instance of MqttClient.
|
|
3387
|
+
* @param {*} config mqtt实例参数
|
|
3388
|
+
*/
|
|
3389
|
+
__publicField(_MqttClient, "defaultContext", {
|
|
3390
|
+
MQTT_USERNAME: "iRVMS-WEB",
|
|
3391
|
+
MQTT_PASSWORD: "novasky888",
|
|
3392
|
+
MQTT_TIMEOUTM: 2e4
|
|
3393
|
+
});
|
|
3394
|
+
let MqttClient = _MqttClient;
|
|
3395
|
+
const _Storage = class _Storage {
|
|
3396
|
+
/**
|
|
3397
|
+
* 将键值对存储到localStorage中
|
|
3398
|
+
*
|
|
3399
|
+
* @param key 键名
|
|
3400
|
+
* @param value 值,默认为null
|
|
3401
|
+
* @param options 存储选项,可选参数
|
|
3402
|
+
* @param options.expires 过期时间,单位为毫秒,默认为null
|
|
3403
|
+
*/
|
|
3404
|
+
static set(key, value = null, options = {}) {
|
|
3405
|
+
var query_key = this._getPrefixedKey(key, options);
|
|
3406
|
+
try {
|
|
3407
|
+
const { expires } = options;
|
|
3408
|
+
const data = { data: value };
|
|
3409
|
+
if (expires) {
|
|
3410
|
+
data.expires = expires;
|
|
3411
|
+
}
|
|
3412
|
+
localStorage.setItem(query_key, JSON.stringify(data));
|
|
3413
|
+
} catch (e) {
|
|
3414
|
+
if (console) console.warn(`Storage didn't successfully save the '{"${key}": "${value}"}' pair, because the localStorage is full.`);
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
/**
|
|
3418
|
+
* 从localStorage中获取指定key的存储值
|
|
3419
|
+
*
|
|
3420
|
+
* @param key 存储键名
|
|
3421
|
+
* @param missing 当获取不到指定key的存储值时返回的默认值
|
|
3422
|
+
* @param options 其他配置选项
|
|
3423
|
+
* @returns 返回指定key的存储值,若获取不到则返回missing参数指定的默认值
|
|
3424
|
+
*/
|
|
3425
|
+
static get(key, missing, options) {
|
|
3426
|
+
var query_key = this._getPrefixedKey(key, options), value;
|
|
3427
|
+
try {
|
|
3428
|
+
value = JSON.parse(localStorage.getItem(query_key) || "");
|
|
3429
|
+
} catch (e) {
|
|
3430
|
+
if (localStorage[query_key]) {
|
|
3431
|
+
value = { data: localStorage.getItem(query_key) };
|
|
3432
|
+
} else {
|
|
3433
|
+
value = null;
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
if (!value) {
|
|
3437
|
+
return missing;
|
|
3438
|
+
} else if (typeof value === "object" && typeof value.data !== "undefined") {
|
|
3439
|
+
const expires = value.expires;
|
|
3440
|
+
if (expires && Date.now() > expires) {
|
|
3441
|
+
return missing;
|
|
3442
|
+
}
|
|
3443
|
+
return value.data;
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
static keys() {
|
|
3447
|
+
const keys = [];
|
|
3448
|
+
var allKeys = Object.keys(localStorage);
|
|
3449
|
+
if (_Storage.prefix.length === 0) {
|
|
3450
|
+
return allKeys;
|
|
3451
|
+
}
|
|
3452
|
+
allKeys.forEach(function(key) {
|
|
3453
|
+
if (key.indexOf(_Storage.prefix) !== -1) {
|
|
3454
|
+
keys.push(key.replace(_Storage.prefix, ""));
|
|
3455
|
+
}
|
|
3456
|
+
});
|
|
3457
|
+
return keys;
|
|
3458
|
+
}
|
|
3459
|
+
static getAll(includeKeys) {
|
|
3460
|
+
var keys = _Storage.keys();
|
|
3461
|
+
if (includeKeys) {
|
|
3462
|
+
const result = [];
|
|
3463
|
+
keys.forEach((key) => {
|
|
3464
|
+
if (includeKeys.includes(key)) {
|
|
3465
|
+
const tempObj = {};
|
|
3466
|
+
tempObj[key] = _Storage.get(key, null, null);
|
|
3467
|
+
result.push(tempObj);
|
|
3468
|
+
}
|
|
3469
|
+
});
|
|
3470
|
+
return result;
|
|
3471
|
+
}
|
|
3472
|
+
return keys.map((key) => _Storage.get(key, null, null));
|
|
3473
|
+
}
|
|
3474
|
+
static remove(key, options) {
|
|
3475
|
+
var queryKey = this._getPrefixedKey(key, options);
|
|
3476
|
+
localStorage.removeItem(queryKey);
|
|
3477
|
+
}
|
|
3478
|
+
static clear(options) {
|
|
3479
|
+
if (_Storage.prefix.length) {
|
|
3480
|
+
this.keys().forEach((key) => {
|
|
3481
|
+
localStorage.removeItem(this._getPrefixedKey(key, options));
|
|
3482
|
+
});
|
|
3483
|
+
} else {
|
|
3484
|
+
localStorage.clear();
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
};
|
|
3488
|
+
__publicField(_Storage, "prefix", "");
|
|
3489
|
+
__publicField(_Storage, "_getPrefixedKey", function(key, options) {
|
|
3490
|
+
options = options || {};
|
|
3491
|
+
if (options.noPrefix) {
|
|
3492
|
+
return key;
|
|
3493
|
+
} else {
|
|
3494
|
+
return _Storage.prefix + key;
|
|
3495
|
+
}
|
|
3496
|
+
});
|
|
3497
|
+
let Storage = _Storage;
|
|
3202
3498
|
export {
|
|
3203
3499
|
AjaxUtil,
|
|
3204
3500
|
ArrayUtil,
|
|
@@ -3206,7 +3502,7 @@ export {
|
|
|
3206
3502
|
AudioPlayer,
|
|
3207
3503
|
BrowserUtil,
|
|
3208
3504
|
CanvasDrawer,
|
|
3209
|
-
|
|
3505
|
+
Color,
|
|
3210
3506
|
Cookie,
|
|
3211
3507
|
CoordsUtil,
|
|
3212
3508
|
DateUtil,
|
|
@@ -3231,6 +3527,6 @@ export {
|
|
|
3231
3527
|
Storage,
|
|
3232
3528
|
StringUtil,
|
|
3233
3529
|
UrlUtil,
|
|
3234
|
-
|
|
3530
|
+
CommUtils as Util,
|
|
3235
3531
|
WebSocketClient
|
|
3236
3532
|
};
|