gis-common 4.4.11 → 5.0.2
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/MqttClient.d.ts +1 -0
- package/dist/core/Storage.d.ts +3 -3
- package/dist/gis-common.es.js +1641 -1597
- package/dist/gis-common.umd.js +1 -1
- package/dist/utils/AjaxUtil.d.ts +1 -1
- package/dist/utils/BrowserUtil.d.ts +9 -0
- package/dist/utils/CommUtil.d.ts +3 -18
- package/dist/utils/DomUtil.d.ts +1 -1
- package/dist/utils/ObjectUtil.d.ts +24 -1
- package/dist/utils/UrlUtil.d.ts +1 -3
- package/dist/utils/ValidateUtil.d.ts +6 -0
- package/package.json +41 -41
package/dist/gis-common.es.js
CHANGED
|
@@ -137,32 +137,64 @@ class AudioPlayer {
|
|
|
137
137
|
this.audio.muted = val;
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
140
|
+
const ExceptionUtil = {
|
|
141
|
+
getException(type, message) {
|
|
142
|
+
const _Exception = function() {
|
|
143
|
+
Error.call(this, message);
|
|
144
|
+
this.name = type;
|
|
145
|
+
this.message = message;
|
|
146
|
+
this.stack = new Error().stack;
|
|
147
|
+
};
|
|
148
|
+
if (Error) _Exception.__proto__ = Error;
|
|
149
|
+
_Exception.prototype = Object.create(Error && Error.prototype);
|
|
150
|
+
_Exception.prototype.constructor = _Exception;
|
|
151
|
+
return _Exception;
|
|
152
|
+
},
|
|
153
|
+
throwException(msg) {
|
|
154
|
+
const _Exception = this.getException("Exception", msg);
|
|
155
|
+
throw new _Exception(msg);
|
|
156
|
+
},
|
|
157
|
+
throwColorException(msg) {
|
|
158
|
+
const _Exception = this.getException("ColorException", ErrorType.DATA_ERROR_COLOR + " -> " + (msg || ""));
|
|
159
|
+
throw new _Exception(msg);
|
|
160
|
+
},
|
|
161
|
+
throwCoordinateException(msg) {
|
|
162
|
+
const _Exception = this.getException("CoordinateException", ErrorType.DATA_ERROR_COORDINATE + " -> " + (msg || ""));
|
|
163
|
+
throw new _Exception(msg);
|
|
164
|
+
},
|
|
165
|
+
throwGeoJsonException(msg) {
|
|
166
|
+
const _Exception = this.getException("GeoJsonException", ErrorType.DATA_ERROR_GEOJSON + " -> " + (msg || ""));
|
|
167
|
+
throw new _Exception(msg);
|
|
168
|
+
},
|
|
169
|
+
throwEmptyException(msg) {
|
|
170
|
+
const _Exception = this.getException("EmptyException", ErrorType.PARAMETER_ERROR_LACK + " -> " + (msg || ""));
|
|
171
|
+
throw new _Exception(msg);
|
|
172
|
+
},
|
|
173
|
+
throwIntegerException(msg) {
|
|
174
|
+
const _Exception = this.getException("IntegerException", ErrorType.PARAMETER_ERROR_INTEGER + " -> " + (msg || ""));
|
|
175
|
+
throw new _Exception(msg);
|
|
176
|
+
},
|
|
177
|
+
throwNumberException(msg) {
|
|
178
|
+
const _Exception = this.getException("NumberException", ErrorType.PARAMETER_ERROR_NUMBER + " -> " + (msg || ""));
|
|
179
|
+
throw new _Exception(msg);
|
|
180
|
+
},
|
|
181
|
+
throwArrayException(msg) {
|
|
182
|
+
const _Exception = this.getException("ArrayException", ErrorType.PARAMETER_ERROR_ARRAY + " -> " + (msg || ""));
|
|
183
|
+
throw new _Exception(msg);
|
|
184
|
+
},
|
|
185
|
+
throwFunctionException(msg) {
|
|
186
|
+
const _Exception = this.getException("FunctionException", ErrorType.PARAMETER_ERROR_FUNCTION + " -> " + (msg || ""));
|
|
187
|
+
throw new _Exception(msg);
|
|
188
|
+
},
|
|
189
|
+
throwProcessException(msg) {
|
|
190
|
+
const _Exception = this.getException("ProcessException", ErrorType.PROCESS_FAIL + " -> " + (msg || ""));
|
|
191
|
+
throw new _Exception(msg);
|
|
192
|
+
},
|
|
193
|
+
throwNetworkException(msg) {
|
|
194
|
+
const _Exception = this.getException("NetworkException", ErrorType.REQUEST_ERROR_TIMEOUT + " -> " + (msg || ""));
|
|
195
|
+
throw new _Exception(msg);
|
|
164
196
|
}
|
|
165
|
-
}
|
|
197
|
+
};
|
|
166
198
|
const Util = {
|
|
167
199
|
/**
|
|
168
200
|
* 获取数据类型
|
|
@@ -271,25 +303,6 @@ const Util = {
|
|
|
271
303
|
}
|
|
272
304
|
return res;
|
|
273
305
|
},
|
|
274
|
-
/**
|
|
275
|
-
* 将一个或多个对象的所有可枚举属性复制到目标对象。
|
|
276
|
-
*
|
|
277
|
-
* @param dest 目标对象,用于接收复制的属性。
|
|
278
|
-
* @param args 一个或多个源对象,用于提供要复制的属性。
|
|
279
|
-
* @returns 返回目标对象,包含所有复制的属性。
|
|
280
|
-
*/
|
|
281
|
-
extend(dest, ...args) {
|
|
282
|
-
let i, j, len, src;
|
|
283
|
-
for (j = 0, len = args.length; j < len; j++) {
|
|
284
|
-
src = args[j];
|
|
285
|
-
for (i in src) {
|
|
286
|
-
if (src.hasOwnProperty(i)) {
|
|
287
|
-
dest[i] = src[i];
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
return dest;
|
|
292
|
-
},
|
|
293
306
|
/**
|
|
294
307
|
* 将扁平化数组转换为树形结构数组
|
|
295
308
|
*
|
|
@@ -368,7 +381,7 @@ const Util = {
|
|
|
368
381
|
css.rel = "stylesheet";
|
|
369
382
|
css.type = "text/css";
|
|
370
383
|
css.onerror = function() {
|
|
371
|
-
|
|
384
|
+
ExceptionUtil.throwException(`Style loading failed for URL: ${url}`);
|
|
372
385
|
};
|
|
373
386
|
document.head.appendChild(css);
|
|
374
387
|
});
|
|
@@ -386,7 +399,7 @@ const Util = {
|
|
|
386
399
|
return str.replace(templateRe, (match, key) => {
|
|
387
400
|
const value = data[key];
|
|
388
401
|
if (value === void 0) {
|
|
389
|
-
|
|
402
|
+
ExceptionUtil.throwException(`${ErrorType.DATA_ERROR_JSON}: ${match}`);
|
|
390
403
|
} else if (typeof value === "function") {
|
|
391
404
|
return value(data);
|
|
392
405
|
} else {
|
|
@@ -405,28 +418,6 @@ const Util = {
|
|
|
405
418
|
Object.keys(data).filter((d) => !this.isEmpty(data[d])).map((i) => [i, data[i]])
|
|
406
419
|
);
|
|
407
420
|
},
|
|
408
|
-
deepAssign(target, ...sources) {
|
|
409
|
-
if (typeof target !== "object" || target === null) {
|
|
410
|
-
target = {};
|
|
411
|
-
}
|
|
412
|
-
for (const source of sources) {
|
|
413
|
-
if (typeof source === "object" && source !== null) {
|
|
414
|
-
for (const key in source) {
|
|
415
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
416
|
-
if (typeof source[key] === "object" && source[key] !== null) {
|
|
417
|
-
if (!target[key]) {
|
|
418
|
-
target[key] = Array.isArray(source[key]) ? [] : {};
|
|
419
|
-
}
|
|
420
|
-
this.deepAssign(target[key], source[key]);
|
|
421
|
-
} else {
|
|
422
|
-
target[key] = source[key];
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
return target;
|
|
429
|
-
},
|
|
430
421
|
/**
|
|
431
422
|
* 复制文本到剪贴板
|
|
432
423
|
*
|
|
@@ -500,705 +491,95 @@ const Util = {
|
|
|
500
491
|
return numC < numT;
|
|
501
492
|
}
|
|
502
493
|
};
|
|
503
|
-
const
|
|
504
|
-
DEG2RAD: Math.PI / 180,
|
|
505
|
-
RAD2DEG: 180 / Math.PI,
|
|
506
|
-
randInt(low, high) {
|
|
507
|
-
return low + Math.floor(Math.random() * (high - low + 1));
|
|
508
|
-
},
|
|
509
|
-
randFloat(low, high) {
|
|
510
|
-
return low + Math.random() * (high - low);
|
|
511
|
-
},
|
|
494
|
+
const ObjectUtil = {
|
|
512
495
|
/**
|
|
513
|
-
*
|
|
496
|
+
* 浅合并对象,并返回合并后的对象。
|
|
514
497
|
*
|
|
515
|
-
* @param
|
|
516
|
-
* @
|
|
498
|
+
* @param dest 目标对象,用于接收复制的属性。
|
|
499
|
+
* @param args 一个或多个源对象,用于提供要复制的属性。
|
|
500
|
+
* @returns 返回目标对象,包含所有复制的属性。
|
|
517
501
|
*/
|
|
518
|
-
|
|
519
|
-
|
|
502
|
+
assign(dest, ...args) {
|
|
503
|
+
let i, j, len, src;
|
|
504
|
+
if (Util.isEmpty(dest)) {
|
|
505
|
+
dest = {};
|
|
506
|
+
}
|
|
507
|
+
for (j = 0, len = args.length; j < len; j++) {
|
|
508
|
+
src = args[j];
|
|
509
|
+
for (i in src) {
|
|
510
|
+
if (src.hasOwnProperty(i)) {
|
|
511
|
+
dest[i] = src[i];
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return dest;
|
|
520
516
|
},
|
|
521
517
|
/**
|
|
522
|
-
*
|
|
518
|
+
* 深度合并对象,并返回合并后的对象
|
|
523
519
|
*
|
|
524
|
-
* @param
|
|
525
|
-
* @
|
|
520
|
+
* @param target 目标对象
|
|
521
|
+
* @param sources 待合并的对象列表
|
|
522
|
+
* @returns 合并后的对象
|
|
526
523
|
*/
|
|
527
|
-
|
|
528
|
-
|
|
524
|
+
deepAssign(target, ...sources) {
|
|
525
|
+
const visited = /* @__PURE__ */ new WeakMap();
|
|
526
|
+
const _deepAssign = (target2, source) => {
|
|
527
|
+
if (typeof source !== "object" || source === null) {
|
|
528
|
+
return source;
|
|
529
|
+
}
|
|
530
|
+
if (visited.has(source)) {
|
|
531
|
+
return visited.get(source);
|
|
532
|
+
}
|
|
533
|
+
let output;
|
|
534
|
+
if (Array.isArray(source)) {
|
|
535
|
+
output = Array.isArray(target2) ? target2 : [];
|
|
536
|
+
} else {
|
|
537
|
+
output = typeof target2 === "object" && target2 !== null && !Array.isArray(target2) ? target2 : {};
|
|
538
|
+
}
|
|
539
|
+
visited.set(source, output);
|
|
540
|
+
for (const key in source) {
|
|
541
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
542
|
+
output[key] = _deepAssign(output[key], source[key]);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return output;
|
|
546
|
+
};
|
|
547
|
+
let result = Object(target);
|
|
548
|
+
for (const source of sources) {
|
|
549
|
+
if (typeof source === "object" && source !== null) {
|
|
550
|
+
result = _deepAssign(result, source);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return result;
|
|
529
554
|
},
|
|
530
|
-
|
|
531
|
-
return
|
|
555
|
+
clone(obj) {
|
|
556
|
+
return JSON.parse(JSON.stringify(obj));
|
|
532
557
|
},
|
|
533
558
|
/**
|
|
534
|
-
*
|
|
559
|
+
* 深拷贝一个对象到另一个对象
|
|
535
560
|
*
|
|
536
|
-
* @param
|
|
537
|
-
* @param
|
|
538
|
-
* @
|
|
539
|
-
* @returns 返回限制后的数值
|
|
561
|
+
* @param destObj 目标对象,深拷贝后的对象将存储在该对象中
|
|
562
|
+
* @param srcObj 源对象,深拷贝的来源对象
|
|
563
|
+
* @returns 深拷贝后的目标对象
|
|
540
564
|
*/
|
|
541
|
-
|
|
542
|
-
|
|
565
|
+
deepClone(destObj, srcObj) {
|
|
566
|
+
this.assign(destObj, srcObj);
|
|
567
|
+
Object.setPrototypeOf(destObj.__proto__, srcObj.__proto__);
|
|
568
|
+
return destObj;
|
|
569
|
+
},
|
|
570
|
+
isEqual(a, b) {
|
|
571
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
572
|
+
},
|
|
573
|
+
parse(str) {
|
|
574
|
+
if (Util.isEmpty(str)) return {};
|
|
575
|
+
if (Util.isObject(str)) return str;
|
|
576
|
+
try {
|
|
577
|
+
return JSON.parse(str);
|
|
578
|
+
} catch {
|
|
579
|
+
ExceptionUtil.throwException(ErrorType.DATA_ERROR_JSON + " -> " + str);
|
|
580
|
+
}
|
|
543
581
|
}
|
|
544
582
|
};
|
|
545
|
-
const ColorName = {
|
|
546
|
-
aliceblue: [240, 248, 255],
|
|
547
|
-
antiquewhite: [250, 235, 215],
|
|
548
|
-
aqua: [0, 255, 255],
|
|
549
|
-
aquamarine: [127, 255, 212],
|
|
550
|
-
azure: [240, 255, 255],
|
|
551
|
-
beige: [245, 245, 220],
|
|
552
|
-
bisque: [255, 228, 196],
|
|
553
|
-
black: [0, 0, 0],
|
|
554
|
-
blanchedalmond: [255, 235, 205],
|
|
555
|
-
blue: [0, 0, 255],
|
|
556
|
-
blueviolet: [138, 43, 226],
|
|
557
|
-
brown: [165, 42, 42],
|
|
558
|
-
burlywood: [222, 184, 135],
|
|
559
|
-
cadetblue: [95, 158, 160],
|
|
560
|
-
chartreuse: [127, 255, 0],
|
|
561
|
-
chocolate: [210, 105, 30],
|
|
562
|
-
coral: [255, 127, 80],
|
|
563
|
-
cornflowerblue: [100, 149, 237],
|
|
564
|
-
cornsilk: [255, 248, 220],
|
|
565
|
-
crimson: [220, 20, 60],
|
|
566
|
-
cyan: [0, 255, 255],
|
|
567
|
-
darkblue: [0, 0, 139],
|
|
568
|
-
darkcyan: [0, 139, 139],
|
|
569
|
-
darkgoldenrod: [184, 134, 11],
|
|
570
|
-
darkgray: [169, 169, 169],
|
|
571
|
-
darkgreen: [0, 100, 0],
|
|
572
|
-
darkgrey: [169, 169, 169],
|
|
573
|
-
darkkhaki: [189, 183, 107],
|
|
574
|
-
darkmagenta: [139, 0, 139],
|
|
575
|
-
darkolivegreen: [85, 107, 47],
|
|
576
|
-
darkorange: [255, 140, 0],
|
|
577
|
-
darkorchid: [153, 50, 204],
|
|
578
|
-
darkred: [139, 0, 0],
|
|
579
|
-
darksalmon: [233, 150, 122],
|
|
580
|
-
darkseagreen: [143, 188, 143],
|
|
581
|
-
darkslateblue: [72, 61, 139],
|
|
582
|
-
darkslategray: [47, 79, 79],
|
|
583
|
-
darkslategrey: [47, 79, 79],
|
|
584
|
-
darkturquoise: [0, 206, 209],
|
|
585
|
-
darkviolet: [148, 0, 211],
|
|
586
|
-
deeppink: [255, 20, 147],
|
|
587
|
-
deepskyblue: [0, 191, 255],
|
|
588
|
-
dimgray: [105, 105, 105],
|
|
589
|
-
dimgrey: [105, 105, 105],
|
|
590
|
-
dodgerblue: [30, 144, 255],
|
|
591
|
-
firebrick: [178, 34, 34],
|
|
592
|
-
floralwhite: [255, 250, 240],
|
|
593
|
-
forestgreen: [34, 139, 34],
|
|
594
|
-
fuchsia: [255, 0, 255],
|
|
595
|
-
gainsboro: [220, 220, 220],
|
|
596
|
-
ghostwhite: [248, 248, 255],
|
|
597
|
-
gold: [255, 215, 0],
|
|
598
|
-
goldenrod: [218, 165, 32],
|
|
599
|
-
gray: [128, 128, 128],
|
|
600
|
-
green: [0, 128, 0],
|
|
601
|
-
greenyellow: [173, 255, 47],
|
|
602
|
-
grey: [128, 128, 128],
|
|
603
|
-
honeydew: [240, 255, 240],
|
|
604
|
-
hotpink: [255, 105, 180],
|
|
605
|
-
indianred: [205, 92, 92],
|
|
606
|
-
indigo: [75, 0, 130],
|
|
607
|
-
ivory: [255, 255, 240],
|
|
608
|
-
khaki: [240, 230, 140],
|
|
609
|
-
lavender: [230, 230, 250],
|
|
610
|
-
lavenderblush: [255, 240, 245],
|
|
611
|
-
lawngreen: [124, 252, 0],
|
|
612
|
-
lemonchiffon: [255, 250, 205],
|
|
613
|
-
lightblue: [173, 216, 230],
|
|
614
|
-
lightcoral: [240, 128, 128],
|
|
615
|
-
lightcyan: [224, 255, 255],
|
|
616
|
-
lightgoldenrodyellow: [250, 250, 210],
|
|
617
|
-
lightgray: [211, 211, 211],
|
|
618
|
-
lightgreen: [144, 238, 144],
|
|
619
|
-
lightgrey: [211, 211, 211],
|
|
620
|
-
lightpink: [255, 182, 193],
|
|
621
|
-
lightsalmon: [255, 160, 122],
|
|
622
|
-
lightseagreen: [32, 178, 170],
|
|
623
|
-
lightskyblue: [135, 206, 250],
|
|
624
|
-
lightslategray: [119, 136, 153],
|
|
625
|
-
lightslategrey: [119, 136, 153],
|
|
626
|
-
lightsteelblue: [176, 196, 222],
|
|
627
|
-
lightyellow: [255, 255, 224],
|
|
628
|
-
lime: [0, 255, 0],
|
|
629
|
-
limegreen: [50, 205, 50],
|
|
630
|
-
linen: [250, 240, 230],
|
|
631
|
-
magenta: [255, 0, 255],
|
|
632
|
-
maroon: [128, 0, 0],
|
|
633
|
-
mediumaquamarine: [102, 205, 170],
|
|
634
|
-
mediumblue: [0, 0, 205],
|
|
635
|
-
mediumorchid: [186, 85, 211],
|
|
636
|
-
mediumpurple: [147, 112, 219],
|
|
637
|
-
mediumseagreen: [60, 179, 113],
|
|
638
|
-
mediumslateblue: [123, 104, 238],
|
|
639
|
-
mediumspringgreen: [0, 250, 154],
|
|
640
|
-
mediumturquoise: [72, 209, 204],
|
|
641
|
-
mediumvioletred: [199, 21, 133],
|
|
642
|
-
midnightblue: [25, 25, 112],
|
|
643
|
-
mintcream: [245, 255, 250],
|
|
644
|
-
mistyrose: [255, 228, 225],
|
|
645
|
-
moccasin: [255, 228, 181],
|
|
646
|
-
navajowhite: [255, 222, 173],
|
|
647
|
-
navy: [0, 0, 128],
|
|
648
|
-
oldlace: [253, 245, 230],
|
|
649
|
-
olive: [128, 128, 0],
|
|
650
|
-
olivedrab: [107, 142, 35],
|
|
651
|
-
orange: [255, 165, 0],
|
|
652
|
-
orangered: [255, 69, 0],
|
|
653
|
-
orchid: [218, 112, 214],
|
|
654
|
-
palegoldenrod: [238, 232, 170],
|
|
655
|
-
palegreen: [152, 251, 152],
|
|
656
|
-
paleturquoise: [175, 238, 238],
|
|
657
|
-
palevioletred: [219, 112, 147],
|
|
658
|
-
papayawhip: [255, 239, 213],
|
|
659
|
-
peachpuff: [255, 218, 185],
|
|
660
|
-
peru: [205, 133, 63],
|
|
661
|
-
pink: [255, 192, 203],
|
|
662
|
-
plum: [221, 160, 221],
|
|
663
|
-
powderblue: [176, 224, 230],
|
|
664
|
-
purple: [128, 0, 128],
|
|
665
|
-
rebeccapurple: [102, 51, 153],
|
|
666
|
-
red: [255, 0, 0],
|
|
667
|
-
rosybrown: [188, 143, 143],
|
|
668
|
-
royalblue: [65, 105, 225],
|
|
669
|
-
saddlebrown: [139, 69, 19],
|
|
670
|
-
salmon: [250, 128, 114],
|
|
671
|
-
sandybrown: [244, 164, 96],
|
|
672
|
-
seagreen: [46, 139, 87],
|
|
673
|
-
seashell: [255, 245, 238],
|
|
674
|
-
sienna: [160, 82, 45],
|
|
675
|
-
silver: [192, 192, 192],
|
|
676
|
-
skyblue: [135, 206, 235],
|
|
677
|
-
slateblue: [106, 90, 205],
|
|
678
|
-
slategray: [112, 128, 144],
|
|
679
|
-
slategrey: [112, 128, 144],
|
|
680
|
-
snow: [255, 250, 250],
|
|
681
|
-
springgreen: [0, 255, 127],
|
|
682
|
-
steelblue: [70, 130, 180],
|
|
683
|
-
tan: [210, 180, 140],
|
|
684
|
-
teal: [0, 128, 128],
|
|
685
|
-
thistle: [216, 191, 216],
|
|
686
|
-
tomato: [255, 99, 71],
|
|
687
|
-
turquoise: [64, 224, 208],
|
|
688
|
-
violet: [238, 130, 238],
|
|
689
|
-
wheat: [245, 222, 179],
|
|
690
|
-
white: [255, 255, 255],
|
|
691
|
-
whitesmoke: [245, 245, 245],
|
|
692
|
-
yellow: [255, 255, 0],
|
|
693
|
-
yellowgreen: [154, 205, 50]
|
|
694
|
-
};
|
|
695
|
-
class Color {
|
|
696
|
-
constructor(r, g, b, a) {
|
|
697
|
-
__publicField(this, "_r");
|
|
698
|
-
__publicField(this, "_g");
|
|
699
|
-
__publicField(this, "_b");
|
|
700
|
-
__publicField(this, "_alpha");
|
|
701
|
-
this._validateColorChannel(r);
|
|
702
|
-
this._validateColorChannel(g);
|
|
703
|
-
this._validateColorChannel(b);
|
|
704
|
-
this._r = r;
|
|
705
|
-
this._g = g;
|
|
706
|
-
this._b = b;
|
|
707
|
-
this._alpha = MathUtil.clamp(a || 1, 0, 1);
|
|
708
|
-
}
|
|
709
|
-
_validateColorChannel(channel) {
|
|
710
|
-
if (channel < 0 || channel > 255) {
|
|
711
|
-
throw new Error("Color channel must be between 0 and 255.");
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
toString() {
|
|
715
|
-
return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
|
|
716
|
-
}
|
|
717
|
-
toJson() {
|
|
718
|
-
return { r: this._r, g: this._g, b: this._b, a: this._alpha };
|
|
719
|
-
}
|
|
720
|
-
get rgba() {
|
|
721
|
-
return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
|
|
722
|
-
}
|
|
723
|
-
get hex() {
|
|
724
|
-
return Color.rgb2hex(this._r, this._g, this._b, this._alpha);
|
|
725
|
-
}
|
|
726
|
-
setAlpha(a) {
|
|
727
|
-
this._alpha = MathUtil.clamp(a, 0, 1);
|
|
728
|
-
return this;
|
|
729
|
-
}
|
|
730
|
-
// 设置颜色的RGB值
|
|
731
|
-
setRgb(r, g, b) {
|
|
732
|
-
this._validateColorChannel(r);
|
|
733
|
-
this._validateColorChannel(g);
|
|
734
|
-
this._validateColorChannel(b);
|
|
735
|
-
this._r = r;
|
|
736
|
-
this._g = g;
|
|
737
|
-
this._b = b;
|
|
738
|
-
return this;
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* 从RGBA字符串创建Color对象
|
|
742
|
-
*
|
|
743
|
-
* @param rgbaValue RGBA颜色值字符串,格式为"rgba(r,g,b,a)"或"rgb(r,g,b)"
|
|
744
|
-
* @returns 返回Color对象
|
|
745
|
-
* @throws 如果rgbaValue不是有效的RGBA颜色值,则抛出错误
|
|
746
|
-
*/
|
|
747
|
-
static fromRgba(rgbaValue) {
|
|
748
|
-
const rgbaMatch = rgbaValue.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([\d.]+))?\s*\)$/);
|
|
749
|
-
if (!rgbaMatch) throw new Error("Invalid RGBA color value");
|
|
750
|
-
const r = parseInt(rgbaMatch[1], 10);
|
|
751
|
-
const g = parseInt(rgbaMatch[2], 10);
|
|
752
|
-
const b = parseInt(rgbaMatch[3], 10);
|
|
753
|
-
const a = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1;
|
|
754
|
-
return new Color(r, g, b, a);
|
|
755
|
-
}
|
|
756
|
-
/**
|
|
757
|
-
* 将十六进制颜色值转换为颜色对象
|
|
758
|
-
*
|
|
759
|
-
* @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
|
|
760
|
-
* @returns 返回颜色对象
|
|
761
|
-
*/
|
|
762
|
-
static fromHex(hexValue, a = 1) {
|
|
763
|
-
const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
|
764
|
-
const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
|
|
765
|
-
const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
|
|
766
|
-
const rgb = rgx.exec(hex);
|
|
767
|
-
if (!rgb) {
|
|
768
|
-
throw new Error("Invalid HEX color value");
|
|
769
|
-
}
|
|
770
|
-
const r = parseInt(rgb[1], 16);
|
|
771
|
-
const g = parseInt(rgb[2], 16);
|
|
772
|
-
const b = parseInt(rgb[3], 16);
|
|
773
|
-
return new Color(r, g, b, a);
|
|
774
|
-
}
|
|
775
|
-
/**
|
|
776
|
-
* 从 HSL 字符串创建颜色对象
|
|
777
|
-
*
|
|
778
|
-
* @param hsl HSL 字符串,格式为 hsl(h, s%, l%) 或 hsla(h, s%, l%, a)
|
|
779
|
-
* @returns 返回颜色对象,如果 hsl 字符串无效则返回 null
|
|
780
|
-
*/
|
|
781
|
-
static fromHsl(hslValue) {
|
|
782
|
-
const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
|
|
783
|
-
if (!hsl) {
|
|
784
|
-
throw new Error("Invalid HSL color value");
|
|
785
|
-
}
|
|
786
|
-
const h = parseInt(hsl[1], 10) / 360;
|
|
787
|
-
const s = parseInt(hsl[2], 10) / 100;
|
|
788
|
-
const l = parseInt(hsl[3], 10) / 100;
|
|
789
|
-
const a = hsl[4] ? parseFloat(hsl[4]) : 1;
|
|
790
|
-
function hue2rgb(p, q, t) {
|
|
791
|
-
if (t < 0) t += 1;
|
|
792
|
-
if (t > 1) t -= 1;
|
|
793
|
-
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
794
|
-
if (t < 1 / 2) return q;
|
|
795
|
-
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
796
|
-
return p;
|
|
797
|
-
}
|
|
798
|
-
let r, g, b;
|
|
799
|
-
if (s === 0) {
|
|
800
|
-
r = g = b = l;
|
|
801
|
-
} else {
|
|
802
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
803
|
-
const p = 2 * l - q;
|
|
804
|
-
r = hue2rgb(p, q, h + 1 / 3);
|
|
805
|
-
g = hue2rgb(p, q, h);
|
|
806
|
-
b = hue2rgb(p, q, h - 1 / 3);
|
|
807
|
-
}
|
|
808
|
-
return new Color(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
|
|
809
|
-
}
|
|
810
|
-
static fromName(str) {
|
|
811
|
-
const rgba = ColorName[str];
|
|
812
|
-
if (!rgba) {
|
|
813
|
-
throw new Error(`Invalid color name: ${str}`);
|
|
814
|
-
}
|
|
815
|
-
return new Color(rgba[0], rgba[1], rgba[2], rgba.length > 3 ? rgba[3] : 1);
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* 从字符串中创建颜色对象
|
|
819
|
-
*
|
|
820
|
-
* @param str 字符串类型的颜色值,支持rgba、hex、hsl格式
|
|
821
|
-
* @returns 返回创建的颜色对象
|
|
822
|
-
* @throws 当颜色值无效时,抛出错误
|
|
823
|
-
*/
|
|
824
|
-
static from(str) {
|
|
825
|
-
if (this.isRgb(str)) {
|
|
826
|
-
return this.fromRgba(str);
|
|
827
|
-
} else if (this.isHex(str)) {
|
|
828
|
-
return this.fromHex(str);
|
|
829
|
-
} else if (this.isHsl(str)) {
|
|
830
|
-
return this.fromHsl(str);
|
|
831
|
-
} else if (Object.keys(ColorName).map((key) => key.toString()).includes(str)) {
|
|
832
|
-
return this.fromName(str);
|
|
833
|
-
} else {
|
|
834
|
-
throw new Error("Invalid color value");
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* 将RGB颜色值转换为十六进制颜色值
|
|
839
|
-
*
|
|
840
|
-
* @param r 红色分量值,取值范围0-255
|
|
841
|
-
* @param g 绿色分量值,取值范围0-255
|
|
842
|
-
* @param b 蓝色分量值,取值范围0-255
|
|
843
|
-
* @param a 可选参数,透明度分量值,取值范围0-1
|
|
844
|
-
* @returns 十六进制颜色值,格式为#RRGGBB或#RRGGBBAA
|
|
845
|
-
*/
|
|
846
|
-
static rgb2hex(r, g, b, a) {
|
|
847
|
-
var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
|
848
|
-
if (a !== void 0) {
|
|
849
|
-
const alpha = Math.round(a * 255).toString(16).padStart(2, "0");
|
|
850
|
-
return hex + alpha;
|
|
851
|
-
}
|
|
852
|
-
return hex;
|
|
853
|
-
}
|
|
854
|
-
static isHex(a) {
|
|
855
|
-
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
|
|
856
|
-
}
|
|
857
|
-
static isRgb(a) {
|
|
858
|
-
return /^rgba?\s*\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*[\d.]+)?\s*\)$/.test(a);
|
|
859
|
-
}
|
|
860
|
-
static isHsl(a) {
|
|
861
|
-
return /^(hsl|hsla)\(\d+,\s*[\d.]+%,\s*[\d.]+%(,\s*[\d.]+)?\)$/.test(a);
|
|
862
|
-
}
|
|
863
|
-
static isColor(a) {
|
|
864
|
-
return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
|
|
865
|
-
}
|
|
866
|
-
static random() {
|
|
867
|
-
let r = Math.floor(Math.random() * 256);
|
|
868
|
-
let g = Math.floor(Math.random() * 256);
|
|
869
|
-
let b = Math.floor(Math.random() * 256);
|
|
870
|
-
let a = Math.random();
|
|
871
|
-
return new Color(r, g, b, a);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
class CanvasDrawer {
|
|
875
|
-
constructor(el) {
|
|
876
|
-
__publicField(this, "ctx");
|
|
877
|
-
if (typeof el === "string") {
|
|
878
|
-
el = document.querySelector("#" + el);
|
|
879
|
-
if (!el) {
|
|
880
|
-
throw new Error("Element not found");
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
if (el instanceof HTMLElement) {
|
|
884
|
-
const canvas = el;
|
|
885
|
-
if (canvas.getContext) {
|
|
886
|
-
this.ctx = canvas.getContext("2d");
|
|
887
|
-
} else {
|
|
888
|
-
throw new Error("getContext is not available on this element");
|
|
889
|
-
}
|
|
890
|
-
} else {
|
|
891
|
-
throw new Error("Element is not an HTMLElement");
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
/**
|
|
895
|
-
* 绘制线条
|
|
896
|
-
*
|
|
897
|
-
* @param start 起始坐标点
|
|
898
|
-
* @param end 终止坐标点
|
|
899
|
-
* @param options 绘制选项,包括线条宽度和颜色
|
|
900
|
-
* @throws 当画布上下文不存在时抛出错误
|
|
901
|
-
*/
|
|
902
|
-
drawLine({ x: startX, y: startY }, { x: endX, y: endY }, options = {}) {
|
|
903
|
-
this.ctx.beginPath();
|
|
904
|
-
const width = options.width || 1;
|
|
905
|
-
const color = options.color || "#000";
|
|
906
|
-
this.ctx.lineWidth = width;
|
|
907
|
-
this.ctx.strokeStyle = color;
|
|
908
|
-
this.ctx.moveTo(startX, startY);
|
|
909
|
-
this.ctx.lineTo(endX, endY);
|
|
910
|
-
this.ctx.stroke();
|
|
911
|
-
}
|
|
912
|
-
/**
|
|
913
|
-
* 绘制圆弧
|
|
914
|
-
*
|
|
915
|
-
* @param x 圆心x坐标
|
|
916
|
-
* @param y 圆心y坐标
|
|
917
|
-
* @param radius 半径
|
|
918
|
-
* @param startAngle 起始角度(度)
|
|
919
|
-
* @param endAngle 结束角度(度)
|
|
920
|
-
* @param anticlockwise 是否逆时针绘制
|
|
921
|
-
* @param isFill 是否填充
|
|
922
|
-
* @param bgColor 背景颜色
|
|
923
|
-
* @throws 当Canvas context为null或undefined时抛出错误
|
|
924
|
-
*/
|
|
925
|
-
drawArc({ x, y }, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
|
|
926
|
-
if (isFill) {
|
|
927
|
-
this.ctx.fillStyle = bgColor;
|
|
928
|
-
this.ctx.beginPath();
|
|
929
|
-
this.ctx.arc(x, y, radius, MathUtil.deg2Rad(startAngle), MathUtil.deg2Rad(endAngle), anticlockwise);
|
|
930
|
-
this.ctx.fill();
|
|
931
|
-
} else {
|
|
932
|
-
this.ctx.strokeStyle = bgColor;
|
|
933
|
-
this.ctx.beginPath();
|
|
934
|
-
this.ctx.arc(x, y, radius, MathUtil.deg2Rad(startAngle), MathUtil.deg2Rad(endAngle), anticlockwise);
|
|
935
|
-
this.ctx.stroke();
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
drawImage({ x, y }, src) {
|
|
939
|
-
const img = new Image();
|
|
940
|
-
img.src = src;
|
|
941
|
-
img.onload = () => {
|
|
942
|
-
const width = img.width;
|
|
943
|
-
const height = img.height;
|
|
944
|
-
if (!this.ctx) {
|
|
945
|
-
throw new Error("Canvas context is null");
|
|
946
|
-
}
|
|
947
|
-
this.ctx.drawImage(img, x, y, -width / 2, -height / 2);
|
|
948
|
-
};
|
|
949
|
-
}
|
|
950
|
-
drawText({ x, y }, text, options) {
|
|
951
|
-
this.ctx.font = options.font || "10px sans-serif";
|
|
952
|
-
this.ctx.strokeStyle = options.color || "#000";
|
|
953
|
-
this.ctx.lineWidth = 2;
|
|
954
|
-
this.ctx.strokeText(text, x, y);
|
|
955
|
-
}
|
|
956
|
-
static createCanvas(width = 1, height = 1) {
|
|
957
|
-
const canvas = document.createElement("canvas");
|
|
958
|
-
if (width) {
|
|
959
|
-
canvas.width = width;
|
|
960
|
-
}
|
|
961
|
-
if (height) {
|
|
962
|
-
canvas.height = height;
|
|
963
|
-
}
|
|
964
|
-
return canvas;
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
class EventDispatcher {
|
|
968
|
-
constructor() {
|
|
969
|
-
__publicField(this, "_listeners", {});
|
|
970
|
-
__publicField(this, "_mutex", {});
|
|
971
|
-
__publicField(this, "_context");
|
|
972
|
-
}
|
|
973
|
-
addEventListener(type, listener, context, mutexStatus) {
|
|
974
|
-
this._context = context;
|
|
975
|
-
const mutex = this._mutex;
|
|
976
|
-
const listeners = this._listeners;
|
|
977
|
-
if (listeners[type] === void 0) {
|
|
978
|
-
listeners[type] = [];
|
|
979
|
-
}
|
|
980
|
-
if (listeners[type].indexOf(listener) === -1) {
|
|
981
|
-
if (mutexStatus) {
|
|
982
|
-
mutex[type] = listener;
|
|
983
|
-
}
|
|
984
|
-
listeners[type].push(listener);
|
|
985
|
-
}
|
|
986
|
-
return this;
|
|
987
|
-
}
|
|
988
|
-
hasEventListener(type, listener) {
|
|
989
|
-
if (this._listeners === null || this._listeners === void 0) return false;
|
|
990
|
-
const listeners = this._listeners;
|
|
991
|
-
return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
|
|
992
|
-
}
|
|
993
|
-
removeEventListener(type, listener) {
|
|
994
|
-
if (this._listeners === void 0) return;
|
|
995
|
-
const listeners = this._listeners;
|
|
996
|
-
const listenerArray = listeners[type];
|
|
997
|
-
if (this._mutex[type] === listener) {
|
|
998
|
-
this._mutex[type] = null;
|
|
999
|
-
}
|
|
1000
|
-
if (listenerArray !== void 0) {
|
|
1001
|
-
const index = listenerArray.map((d) => d.toString()).indexOf(listener.toString());
|
|
1002
|
-
if (index !== -1) {
|
|
1003
|
-
listenerArray.splice(index, 1);
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
dispatchEvent(event) {
|
|
1008
|
-
if (this._listeners === void 0) return;
|
|
1009
|
-
const listeners = this._listeners;
|
|
1010
|
-
const listenerArray = listeners[event.type];
|
|
1011
|
-
if (listenerArray !== void 0) {
|
|
1012
|
-
event.target = this;
|
|
1013
|
-
const array = listenerArray.slice(0);
|
|
1014
|
-
if (this._mutex[event.type] !== void 0) {
|
|
1015
|
-
const find = array.find((item) => item === this._mutex[event.type]);
|
|
1016
|
-
if (find) {
|
|
1017
|
-
find.call(this._context || this, event);
|
|
1018
|
-
return;
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
for (let i = 0, l = array.length; i < l; i++) {
|
|
1022
|
-
const item = array[i];
|
|
1023
|
-
if (typeof item === "function") {
|
|
1024
|
-
item.call(this._context || this, event);
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
removeAllListener() {
|
|
1030
|
-
this._mutex = {};
|
|
1031
|
-
for (const key in this._listeners) {
|
|
1032
|
-
this._listeners[key] = [];
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
on(type, listener, context, mutexStatus) {
|
|
1036
|
-
return this.addEventListener.call(this, type, listener, context, mutexStatus);
|
|
1037
|
-
}
|
|
1038
|
-
off(type, listener) {
|
|
1039
|
-
return this.removeEventListener.call(this, type, listener);
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
const ObjectUtil = {
|
|
1043
|
-
clone(obj) {
|
|
1044
|
-
return JSON.parse(JSON.stringify(obj));
|
|
1045
|
-
},
|
|
1046
|
-
deepClone(a) {
|
|
1047
|
-
function F() {
|
|
1048
|
-
}
|
|
1049
|
-
F.prototype = a;
|
|
1050
|
-
return new F();
|
|
1051
|
-
},
|
|
1052
|
-
isEqual(a, b) {
|
|
1053
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
1054
|
-
},
|
|
1055
|
-
parse(str) {
|
|
1056
|
-
if (Util.isEmpty(str)) return {};
|
|
1057
|
-
if (Util.isObject(str)) return str;
|
|
1058
|
-
try {
|
|
1059
|
-
return JSON.parse(str);
|
|
1060
|
-
} catch {
|
|
1061
|
-
throw new Error(ErrorType.DATA_ERROR_JSON + " -> " + str);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
};
|
|
1065
|
-
class HashMap extends Map {
|
|
1066
|
-
isEmpty() {
|
|
1067
|
-
return this.size === 0;
|
|
1068
|
-
}
|
|
1069
|
-
_values() {
|
|
1070
|
-
return Array.from(this.values());
|
|
1071
|
-
}
|
|
1072
|
-
_keys() {
|
|
1073
|
-
return Array.from(this.keys());
|
|
1074
|
-
}
|
|
1075
|
-
_entries() {
|
|
1076
|
-
return Array.from(this.entries());
|
|
1077
|
-
}
|
|
1078
|
-
/**
|
|
1079
|
-
* 从键值对数组创建一个HashMap对象
|
|
1080
|
-
*
|
|
1081
|
-
* @param array 键值对数组,默认为空数组
|
|
1082
|
-
* @returns 返回一个新的HashMap对象
|
|
1083
|
-
*/
|
|
1084
|
-
static fromEntries(array = []) {
|
|
1085
|
-
const hashMap = new HashMap();
|
|
1086
|
-
array.forEach((element) => {
|
|
1087
|
-
if (Array.isArray(element) && element.length === 2) {
|
|
1088
|
-
hashMap.set(element[0], element[1]);
|
|
1089
|
-
}
|
|
1090
|
-
});
|
|
1091
|
-
return hashMap;
|
|
1092
|
-
}
|
|
1093
|
-
/**
|
|
1094
|
-
* 从JSON字符串创建HashMap实例
|
|
1095
|
-
*
|
|
1096
|
-
* @param str JSON字符串
|
|
1097
|
-
* @returns HashMap实例
|
|
1098
|
-
*/
|
|
1099
|
-
static fromJson(str) {
|
|
1100
|
-
const json = ObjectUtil.parse(str);
|
|
1101
|
-
return new HashMap(Object.entries(json));
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
class WebSocketClient extends EventDispatcher {
|
|
1105
|
-
constructor(url = "ws://127.0.0.1:10088") {
|
|
1106
|
-
super();
|
|
1107
|
-
__publicField(this, "maxCheckTimes", 10);
|
|
1108
|
-
__publicField(this, "url");
|
|
1109
|
-
__publicField(this, "checkTimes", 0);
|
|
1110
|
-
__publicField(this, "connectStatus", false);
|
|
1111
|
-
__publicField(this, "client", null);
|
|
1112
|
-
this.maxCheckTimes = 10;
|
|
1113
|
-
this.url = url;
|
|
1114
|
-
this.checkTimes = 0;
|
|
1115
|
-
this.connect();
|
|
1116
|
-
this.connCheckStatus(this.maxCheckTimes);
|
|
1117
|
-
}
|
|
1118
|
-
connect() {
|
|
1119
|
-
this.disconnect();
|
|
1120
|
-
if (this.url) {
|
|
1121
|
-
try {
|
|
1122
|
-
const self = this;
|
|
1123
|
-
console.info("创建ws连接>>>" + this.url);
|
|
1124
|
-
this.client = new WebSocket(this.url);
|
|
1125
|
-
if (this.client) {
|
|
1126
|
-
this.client.onopen = function(message) {
|
|
1127
|
-
self.dispatchEvent({
|
|
1128
|
-
type: EventType.WEB_SOCKET_CONNECT,
|
|
1129
|
-
message
|
|
1130
|
-
});
|
|
1131
|
-
};
|
|
1132
|
-
this.client.onmessage = function(message) {
|
|
1133
|
-
self.connectStatus = true;
|
|
1134
|
-
self.dispatchEvent({
|
|
1135
|
-
type: EventType.WEB_SOCKET_MESSAGE,
|
|
1136
|
-
message
|
|
1137
|
-
});
|
|
1138
|
-
};
|
|
1139
|
-
this.client.onclose = function(message) {
|
|
1140
|
-
self.dispatchEvent({
|
|
1141
|
-
type: EventType.WEB_SOCKET_CLOSE,
|
|
1142
|
-
message
|
|
1143
|
-
});
|
|
1144
|
-
};
|
|
1145
|
-
if (this.checkTimes === this.maxCheckTimes) {
|
|
1146
|
-
this.client.onerror = function(message) {
|
|
1147
|
-
self.dispatchEvent({
|
|
1148
|
-
type: EventType.WEB_SOCKET_ERROR,
|
|
1149
|
-
message
|
|
1150
|
-
});
|
|
1151
|
-
};
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
} catch (ex) {
|
|
1155
|
-
console.error("创建ws连接失败" + this.url + ":" + ex);
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
disconnect() {
|
|
1160
|
-
if (this.client) {
|
|
1161
|
-
try {
|
|
1162
|
-
console.log("ws断开连接" + this.url);
|
|
1163
|
-
this.client.close();
|
|
1164
|
-
this.client = null;
|
|
1165
|
-
} catch (ex) {
|
|
1166
|
-
this.client = null;
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
connCheckStatus(times) {
|
|
1171
|
-
if (this.checkTimes > times) return;
|
|
1172
|
-
setTimeout(() => {
|
|
1173
|
-
this.checkTimes++;
|
|
1174
|
-
if (this.state !== WebSocket.CONNECTING && this.state !== WebSocket.OPEN) {
|
|
1175
|
-
this.connect();
|
|
1176
|
-
}
|
|
1177
|
-
this.connCheckStatus(times);
|
|
1178
|
-
}, 2e3);
|
|
1179
|
-
}
|
|
1180
|
-
send(message) {
|
|
1181
|
-
if (this.client && this.state === WebSocket.OPEN) {
|
|
1182
|
-
this.client.send(message);
|
|
1183
|
-
return true;
|
|
1184
|
-
}
|
|
1185
|
-
console.error(this.url + "消息发送失败:" + message);
|
|
1186
|
-
return this;
|
|
1187
|
-
}
|
|
1188
|
-
heartbeat() {
|
|
1189
|
-
setTimeout(() => {
|
|
1190
|
-
if (this.state === WebSocket.OPEN) {
|
|
1191
|
-
this.send("HeartBeat");
|
|
1192
|
-
}
|
|
1193
|
-
console.log("HeartBeat," + this.url);
|
|
1194
|
-
setTimeout(this.heartbeat, 3e4);
|
|
1195
|
-
}, 1e3);
|
|
1196
|
-
}
|
|
1197
|
-
get state() {
|
|
1198
|
-
var _a2;
|
|
1199
|
-
return (_a2 = this.client) == null ? void 0 : _a2.readyState;
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
583
|
const ImageUtil = {
|
|
1203
584
|
emptyImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
|
|
1204
585
|
createCircle(radius, options = {}) {
|
|
@@ -1291,7 +672,7 @@ const ImageUtil = {
|
|
|
1291
672
|
data: res.groups.data
|
|
1292
673
|
};
|
|
1293
674
|
}
|
|
1294
|
-
|
|
675
|
+
ExceptionUtil.throwException("parseBase64: Invalid base64 string");
|
|
1295
676
|
},
|
|
1296
677
|
/**
|
|
1297
678
|
* 复制图片到剪贴板
|
|
@@ -1317,7 +698,7 @@ const ImageUtil = {
|
|
|
1317
698
|
let blob = new Blob([ab], { type });
|
|
1318
699
|
await navigator.clipboard.write([new ClipboardItem({ [type]: blob })]);
|
|
1319
700
|
} catch (error) {
|
|
1320
|
-
|
|
701
|
+
ExceptionUtil.throwException("Failed to copy image to clipboard:");
|
|
1321
702
|
}
|
|
1322
703
|
}
|
|
1323
704
|
};
|
|
@@ -1551,7 +932,7 @@ const AjaxUtil = {
|
|
|
1551
932
|
* }
|
|
1552
933
|
* );
|
|
1553
934
|
*/
|
|
1554
|
-
getJSON(url, options, cb) {
|
|
935
|
+
getJSON(url, options = {}, cb) {
|
|
1555
936
|
if (Util.isFunction(options)) {
|
|
1556
937
|
const t = cb;
|
|
1557
938
|
cb = options;
|
|
@@ -1563,290 +944,661 @@ const AjaxUtil = {
|
|
|
1563
944
|
cb(err, data);
|
|
1564
945
|
}
|
|
1565
946
|
};
|
|
1566
|
-
if (options && options["jsonp"]) {
|
|
1567
|
-
return this.jsonp(url, callback);
|
|
1568
|
-
}
|
|
1569
|
-
return this.get(url, options, callback);
|
|
947
|
+
if (options && options["jsonp"]) {
|
|
948
|
+
return this.jsonp(url, callback);
|
|
949
|
+
}
|
|
950
|
+
return this.get(url, options, callback);
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
const MathUtil = {
|
|
954
|
+
DEG2RAD: Math.PI / 180,
|
|
955
|
+
RAD2DEG: 180 / Math.PI,
|
|
956
|
+
randInt(low, high) {
|
|
957
|
+
return low + Math.floor(Math.random() * (high - low + 1));
|
|
958
|
+
},
|
|
959
|
+
randFloat(low, high) {
|
|
960
|
+
return low + Math.random() * (high - low);
|
|
961
|
+
},
|
|
962
|
+
/**
|
|
963
|
+
* 角度转弧度
|
|
964
|
+
*
|
|
965
|
+
* @param {*} degrees
|
|
966
|
+
* @returns {*}
|
|
967
|
+
*/
|
|
968
|
+
deg2Rad(degrees) {
|
|
969
|
+
return degrees * this.DEG2RAD;
|
|
970
|
+
},
|
|
971
|
+
/**
|
|
972
|
+
* 弧度转角度
|
|
973
|
+
*
|
|
974
|
+
* @param {*} radians
|
|
975
|
+
* @returns {*}
|
|
976
|
+
*/
|
|
977
|
+
rad2Deg(radians) {
|
|
978
|
+
return radians * this.RAD2DEG;
|
|
979
|
+
},
|
|
980
|
+
round(value, n = 2) {
|
|
981
|
+
return Util.isNumber(value) ? Math.round(Number(value) * Math.pow(10, n)) / Math.pow(10, n) : 0;
|
|
982
|
+
},
|
|
983
|
+
/**
|
|
984
|
+
* 将数值限制在指定范围内
|
|
985
|
+
*
|
|
986
|
+
* @param val 需要限制的数值
|
|
987
|
+
* @param min 最小值
|
|
988
|
+
* @param max 最大值
|
|
989
|
+
* @returns 返回限制后的数值
|
|
990
|
+
*/
|
|
991
|
+
clamp(val, min, max) {
|
|
992
|
+
return Math.max(min, Math.min(max, val));
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
class GeoUtil {
|
|
996
|
+
// 地球赤道半径
|
|
997
|
+
/**
|
|
998
|
+
* 判断给定的经纬度是否合法
|
|
999
|
+
*
|
|
1000
|
+
* @param lng 经度值
|
|
1001
|
+
* @param lat 纬度值
|
|
1002
|
+
* @returns 如果经纬度合法,返回true;否则返回false
|
|
1003
|
+
*/
|
|
1004
|
+
static isLnglat(lng, lat) {
|
|
1005
|
+
return Util.isNumber(lng) && Util.isNumber(lat) && !!(+lat >= -90 && +lat <= 90 && +lng >= -180 && +lng <= 180);
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* 计算两哥平面坐标点间的距离
|
|
1009
|
+
*
|
|
1010
|
+
* @param p1 坐标点1,包含x和y属性
|
|
1011
|
+
* @param p2 坐标点2,包含x和y属性
|
|
1012
|
+
* @returns 返回两点间的欧几里得距离
|
|
1013
|
+
*/
|
|
1014
|
+
static distance(p1, p2) {
|
|
1015
|
+
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* 计算两个经纬度点之间的距离
|
|
1019
|
+
*
|
|
1020
|
+
* @param A 经纬度点A,包含lng(经度)和lat(纬度)两个属性
|
|
1021
|
+
* @param B 经纬度点B,包含lng(经度)和lat(纬度)两个属性
|
|
1022
|
+
* @returns 返回两点之间的距离,单位为米
|
|
1023
|
+
*/
|
|
1024
|
+
static distanceByPoints(A, B) {
|
|
1025
|
+
const { lng: lngA, lat: latA } = A;
|
|
1026
|
+
const { lng: lngB, lat: latB } = B;
|
|
1027
|
+
const earthR = 6371e3;
|
|
1028
|
+
const x = Math.cos(latA * Math.PI / 180) * Math.cos(latB * Math.PI / 180) * Math.cos((lngA - lngB) * Math.PI / 180);
|
|
1029
|
+
const y = Math.sin(latA * Math.PI / 180) * Math.sin(latB * Math.PI / 180);
|
|
1030
|
+
let s = x + y;
|
|
1031
|
+
if (s > 1) s = 1;
|
|
1032
|
+
if (s < -1) s = -1;
|
|
1033
|
+
const alpha = Math.acos(s);
|
|
1034
|
+
const distance = alpha * earthR;
|
|
1035
|
+
return distance;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* 格式化经纬度为度分秒格式
|
|
1039
|
+
*
|
|
1040
|
+
* @param lng 经度
|
|
1041
|
+
* @param lat 纬度
|
|
1042
|
+
* @returns 返回格式化后的经纬度字符串,格式为:经度度分秒,纬度度分秒
|
|
1043
|
+
*/
|
|
1044
|
+
static formatLnglat(lng, lat) {
|
|
1045
|
+
let res = "";
|
|
1046
|
+
function formatDegreeToDMS(valueInDegrees) {
|
|
1047
|
+
const degree = Math.floor(valueInDegrees);
|
|
1048
|
+
const minutes = Math.floor((valueInDegrees - degree) * 60);
|
|
1049
|
+
const seconds = (valueInDegrees - degree) * 3600 - minutes * 60;
|
|
1050
|
+
return `${degree}°${minutes}′${seconds.toFixed(2)}″`;
|
|
1051
|
+
}
|
|
1052
|
+
if (this.isLnglat(lng, lat)) {
|
|
1053
|
+
res = formatDegreeToDMS(lng) + "," + formatDegreeToDMS(lat);
|
|
1054
|
+
} else if (!isNaN(lng)) {
|
|
1055
|
+
res = formatDegreeToDMS(lng);
|
|
1056
|
+
} else if (!isNaN(lat)) {
|
|
1057
|
+
res = formatDegreeToDMS(lat);
|
|
1058
|
+
}
|
|
1059
|
+
return res;
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* 将经纬度字符串转换为度
|
|
1063
|
+
*
|
|
1064
|
+
* @param lng 经度字符串
|
|
1065
|
+
* @param lat 纬度字符串
|
|
1066
|
+
* @returns 转换后的经纬度对象
|
|
1067
|
+
*/
|
|
1068
|
+
static transformLnglat(lng, lat) {
|
|
1069
|
+
function dms2deg(dmsString) {
|
|
1070
|
+
const isNegative = /[sw]/i.test(dmsString);
|
|
1071
|
+
let factor = isNegative ? -1 : 1;
|
|
1072
|
+
const numericParts = dmsString.match(/[\d.]+/g) || [];
|
|
1073
|
+
let degrees = 0;
|
|
1074
|
+
for (let i = 0; i < numericParts.length; i++) {
|
|
1075
|
+
degrees += parseFloat(numericParts[i]) / factor;
|
|
1076
|
+
factor *= 60;
|
|
1077
|
+
}
|
|
1078
|
+
return degrees;
|
|
1079
|
+
}
|
|
1080
|
+
if (lng && lat) {
|
|
1081
|
+
return {
|
|
1082
|
+
lng: dms2deg(lng),
|
|
1083
|
+
lat: dms2deg(lat)
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* 射线法判断点是否在多边形内
|
|
1089
|
+
*
|
|
1090
|
+
* @param p 点对象,包含x和y属性
|
|
1091
|
+
* @param poly 多边形顶点数组,可以是字符串数组或对象数组
|
|
1092
|
+
* @returns 返回字符串,表示点相对于多边形的位置:'in'表示在多边形内,'out'表示在多边形外,'on'表示在多边形上
|
|
1093
|
+
*/
|
|
1094
|
+
static rayCasting(p, poly) {
|
|
1095
|
+
var px = p.x, py = p.y, flag = false;
|
|
1096
|
+
for (var i = 0, l = poly.length, j = l - 1; i < l; j = i, i++) {
|
|
1097
|
+
var sx = poly[i].x, sy = poly[i].y, tx = poly[j].x, ty = poly[j].y;
|
|
1098
|
+
if (sx === px && sy === py || tx === px && ty === py) {
|
|
1099
|
+
return "on";
|
|
1100
|
+
}
|
|
1101
|
+
if (sy < py && ty >= py || sy >= py && ty < py) {
|
|
1102
|
+
var x = sx + (py - sy) * (tx - sx) / (ty - sy);
|
|
1103
|
+
if (x === px) {
|
|
1104
|
+
return "on";
|
|
1105
|
+
}
|
|
1106
|
+
if (x > px) {
|
|
1107
|
+
flag = !flag;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return flag ? "in" : "out";
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* 旋转点
|
|
1115
|
+
*
|
|
1116
|
+
* @param p1 旋转前点坐标
|
|
1117
|
+
* @param p2 旋转中心坐标
|
|
1118
|
+
* @param θ 旋转角度(顺时针旋转为正)
|
|
1119
|
+
* @returns 旋转后点坐标
|
|
1120
|
+
*/
|
|
1121
|
+
static rotatePoint(p1, p2, θ) {
|
|
1122
|
+
const x = (p1.x - p2.x) * Math.cos(Math.PI / 180 * -θ) - (p1.y - p2.y) * Math.sin(Math.PI / 180 * -θ) + p2.x;
|
|
1123
|
+
const y = (p1.x - p2.x) * Math.sin(Math.PI / 180 * -θ) + (p1.y - p2.y) * Math.cos(Math.PI / 180 * -θ) + p2.y;
|
|
1124
|
+
return { x, y };
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* 根据两个平面坐标点计算方位角和距离
|
|
1128
|
+
*
|
|
1129
|
+
* @param p1 第一个点的坐标对象
|
|
1130
|
+
* @param p2 第二个点的坐标对象
|
|
1131
|
+
* @returns 返回一个对象,包含angle和distance属性,分别表示两点之间的角度(以度为单位,取值范围为0~359)和距离
|
|
1132
|
+
*/
|
|
1133
|
+
static calcBearAndDis(p1, p2) {
|
|
1134
|
+
const { x: x1, y: y1 } = p1;
|
|
1135
|
+
const { x: x2, y: y2 } = p2;
|
|
1136
|
+
const dx = x2 - x1;
|
|
1137
|
+
const dy = y2 - y1;
|
|
1138
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
1139
|
+
const angleInRadians = Math.atan2(dy, dx);
|
|
1140
|
+
const angle = (angleInRadians * (180 / Math.PI) + 360 + 90) % 360;
|
|
1141
|
+
return { angle, distance };
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* 根据两个经纬度点计算方位角和距离
|
|
1145
|
+
*
|
|
1146
|
+
* @param latlng1 第一个经纬度点
|
|
1147
|
+
* @param latlng2 第二个经纬度点
|
|
1148
|
+
* @returns 包含方位角和距离的对象
|
|
1149
|
+
*/
|
|
1150
|
+
static calcBearAndDisByPoints(latlng1, latlng2) {
|
|
1151
|
+
var f1 = latlng1.lat * 1, l1 = latlng1.lng * 1, f2 = latlng2.lat * 1, l2 = latlng2.lng * 1;
|
|
1152
|
+
var y = Math.sin((l2 - l1) * this.toRadian) * Math.cos(f2 * this.toRadian);
|
|
1153
|
+
var x = Math.cos(f1 * this.toRadian) * Math.sin(f2 * this.toRadian) - Math.sin(f1 * this.toRadian) * Math.cos(f2 * this.toRadian) * Math.cos((l2 - l1) * this.toRadian);
|
|
1154
|
+
var angle = Math.atan2(y, x) * (180 / Math.PI);
|
|
1155
|
+
var deltaF = (f2 - f1) * this.toRadian;
|
|
1156
|
+
var deltaL = (l2 - l1) * this.toRadian;
|
|
1157
|
+
var a = Math.sin(deltaF / 2) * Math.sin(deltaF / 2) + Math.cos(f1 * this.toRadian) * Math.cos(f2 * this.toRadian) * Math.sin(deltaL / 2) * Math.sin(deltaL / 2);
|
|
1158
|
+
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
1159
|
+
var distance = this.R * c;
|
|
1160
|
+
return {
|
|
1161
|
+
angle,
|
|
1162
|
+
distance
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* 计算点P到线段P1P2的最短距离
|
|
1167
|
+
*
|
|
1168
|
+
* @param p 点P的坐标
|
|
1169
|
+
* @param p1 线段起点P1的坐标
|
|
1170
|
+
* @param p2 线段终点P2的坐标
|
|
1171
|
+
* @returns 点P到线段P1P2的最短距离
|
|
1172
|
+
*/
|
|
1173
|
+
static distanceToSegment(p, p1, p2) {
|
|
1174
|
+
const x = p.x, y = p.y, x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y;
|
|
1175
|
+
const cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
|
|
1176
|
+
if (cross <= 0) {
|
|
1177
|
+
return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
|
|
1178
|
+
}
|
|
1179
|
+
const d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
|
|
1180
|
+
if (cross >= d2) {
|
|
1181
|
+
return Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
|
|
1182
|
+
}
|
|
1183
|
+
const r = cross / d2;
|
|
1184
|
+
const px = x1 + (x2 - x1) * r;
|
|
1185
|
+
const py = y1 + (y2 - y1) * r;
|
|
1186
|
+
return Math.sqrt((x - px) * (x - px) + (y - py) * (y - py));
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* 根据给定的经纬度、角度和距离计算新的经纬度点
|
|
1190
|
+
*
|
|
1191
|
+
* @param latlng 给定的经纬度点,类型为LngLat
|
|
1192
|
+
* @param angle 角度值,单位为度,表示从当前点出发的方向
|
|
1193
|
+
* @param distance 距离值,单位为米,表示从当前点出发的距离
|
|
1194
|
+
* @returns 返回计算后的新经纬度点,类型为{lat: number, lng: number}
|
|
1195
|
+
*/
|
|
1196
|
+
static calcPointByBearAndDis(latlng, angle, distance) {
|
|
1197
|
+
const sLat = MathUtil.deg2Rad(latlng.lat * 1);
|
|
1198
|
+
const sLng = MathUtil.deg2Rad(latlng.lng * 1);
|
|
1199
|
+
const d = distance / this.R;
|
|
1200
|
+
angle = MathUtil.deg2Rad(angle);
|
|
1201
|
+
const lat = Math.asin(Math.sin(sLat) * Math.cos(d) + Math.cos(sLat) * Math.sin(d) * Math.cos(angle));
|
|
1202
|
+
const lon = sLng + Math.atan2(Math.sin(angle) * Math.sin(d) * Math.cos(sLat), Math.cos(d) - Math.sin(sLat) * Math.sin(lat));
|
|
1203
|
+
return {
|
|
1204
|
+
lat: MathUtil.rad2Deg(lat),
|
|
1205
|
+
lng: MathUtil.rad2Deg(lon)
|
|
1206
|
+
};
|
|
1570
1207
|
}
|
|
1571
|
-
};
|
|
1572
|
-
class GeoUtil {
|
|
1573
|
-
// 地球赤道半径
|
|
1574
1208
|
/**
|
|
1575
|
-
*
|
|
1209
|
+
* 将墨卡托坐标转换为经纬度坐标
|
|
1210
|
+
*
|
|
1211
|
+
* @param x 墨卡托坐标的x值
|
|
1212
|
+
* @param y 墨卡托坐标的y值
|
|
1213
|
+
* @returns 返回包含转换后的经度lng和纬度lat的对象
|
|
1214
|
+
*/
|
|
1215
|
+
static mercatorTolonlat(x, y) {
|
|
1216
|
+
const earthRadius = this.R_EQU;
|
|
1217
|
+
const lng = x / earthRadius * (180 / Math.PI);
|
|
1218
|
+
const lat = Math.atan(Math.exp(y / earthRadius)) * (180 / Math.PI) * 2 - 90;
|
|
1219
|
+
return { lng, lat };
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* 将经纬度坐标转换为墨卡托坐标
|
|
1576
1223
|
*
|
|
1577
1224
|
* @param lng 经度值
|
|
1578
1225
|
* @param lat 纬度值
|
|
1579
|
-
* @returns
|
|
1226
|
+
* @returns 墨卡托坐标对象,包含x和y属性
|
|
1580
1227
|
*/
|
|
1581
|
-
static
|
|
1582
|
-
|
|
1228
|
+
static lonlatToMercator(lng, lat) {
|
|
1229
|
+
var earthRad = this.R_EQU;
|
|
1230
|
+
const x = lng * Math.PI / 180 * earthRad;
|
|
1231
|
+
var a = lat * Math.PI / 180;
|
|
1232
|
+
const y = earthRad / 2 * Math.log((1 + Math.sin(a)) / (1 - Math.sin(a)));
|
|
1233
|
+
return { x, y };
|
|
1583
1234
|
}
|
|
1584
1235
|
/**
|
|
1585
|
-
*
|
|
1236
|
+
* 根据百分比获取坐标
|
|
1586
1237
|
*
|
|
1587
|
-
* @param
|
|
1588
|
-
* @param
|
|
1589
|
-
* @
|
|
1238
|
+
* @param start 起点坐标
|
|
1239
|
+
* @param end 终点坐标
|
|
1240
|
+
* @param percent 百分比,取值范围0-1
|
|
1241
|
+
* @returns 返回插值后的坐标
|
|
1590
1242
|
*/
|
|
1591
|
-
static
|
|
1592
|
-
|
|
1243
|
+
static interpolate({ x: x1, y: y1, z: z1 = 0 }, { x: x2, y: y2, z: z2 = 0 }, percent) {
|
|
1244
|
+
const dx = x2 - x1, dy = y2 - y1, dz = z2 - z1;
|
|
1245
|
+
return { x: x1 + dx * percent, y: y1 + dy * percent, z: z1 + dz * percent };
|
|
1593
1246
|
}
|
|
1594
1247
|
/**
|
|
1595
|
-
*
|
|
1248
|
+
* 计算三角形面积
|
|
1596
1249
|
*
|
|
1597
|
-
* @param
|
|
1598
|
-
* @param
|
|
1599
|
-
* @
|
|
1250
|
+
* @param a 第一个点的坐标
|
|
1251
|
+
* @param b 第二个点的坐标
|
|
1252
|
+
* @param c 第三个点的坐标
|
|
1253
|
+
* @returns 返回三角形的面积
|
|
1600
1254
|
*/
|
|
1601
|
-
static
|
|
1602
|
-
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1612
|
-
return
|
|
1255
|
+
static getTraingleArea(a, b, c) {
|
|
1256
|
+
let area = 0;
|
|
1257
|
+
const side = [];
|
|
1258
|
+
side[0] = Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2) + Math.pow((a.z || 0) - (b.z || 0), 2));
|
|
1259
|
+
side[1] = Math.sqrt(Math.pow(a.x - c.x, 2) + Math.pow(a.y - c.y, 2) + Math.pow((a.z || 0) - (c.z || 0), 2));
|
|
1260
|
+
side[2] = Math.sqrt(Math.pow(c.x - b.x, 2) + Math.pow(c.y - b.y, 2) + Math.pow((c.z || 0) - (b.z || 0), 2));
|
|
1261
|
+
if (side[0] + side[1] <= side[2] || side[0] + side[2] <= side[1] || side[1] + side[2] <= side[0]) {
|
|
1262
|
+
return area;
|
|
1263
|
+
}
|
|
1264
|
+
const p = (side[0] + side[1] + side[2]) / 2;
|
|
1265
|
+
area = Math.sqrt(p * (p - side[0]) * (p - side[1]) * (p - side[2]));
|
|
1266
|
+
return area;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
__publicField(GeoUtil, "toRadian", Math.PI / 180);
|
|
1270
|
+
__publicField(GeoUtil, "R", 6371393);
|
|
1271
|
+
// 地球平均半径
|
|
1272
|
+
__publicField(GeoUtil, "R_EQU", 6378137);
|
|
1273
|
+
const ColorName = {
|
|
1274
|
+
aliceblue: [240, 248, 255],
|
|
1275
|
+
antiquewhite: [250, 235, 215],
|
|
1276
|
+
aqua: [0, 255, 255],
|
|
1277
|
+
aquamarine: [127, 255, 212],
|
|
1278
|
+
azure: [240, 255, 255],
|
|
1279
|
+
beige: [245, 245, 220],
|
|
1280
|
+
bisque: [255, 228, 196],
|
|
1281
|
+
black: [0, 0, 0],
|
|
1282
|
+
blanchedalmond: [255, 235, 205],
|
|
1283
|
+
blue: [0, 0, 255],
|
|
1284
|
+
blueviolet: [138, 43, 226],
|
|
1285
|
+
brown: [165, 42, 42],
|
|
1286
|
+
burlywood: [222, 184, 135],
|
|
1287
|
+
cadetblue: [95, 158, 160],
|
|
1288
|
+
chartreuse: [127, 255, 0],
|
|
1289
|
+
chocolate: [210, 105, 30],
|
|
1290
|
+
coral: [255, 127, 80],
|
|
1291
|
+
cornflowerblue: [100, 149, 237],
|
|
1292
|
+
cornsilk: [255, 248, 220],
|
|
1293
|
+
crimson: [220, 20, 60],
|
|
1294
|
+
cyan: [0, 255, 255],
|
|
1295
|
+
darkblue: [0, 0, 139],
|
|
1296
|
+
darkcyan: [0, 139, 139],
|
|
1297
|
+
darkgoldenrod: [184, 134, 11],
|
|
1298
|
+
darkgray: [169, 169, 169],
|
|
1299
|
+
darkgreen: [0, 100, 0],
|
|
1300
|
+
darkgrey: [169, 169, 169],
|
|
1301
|
+
darkkhaki: [189, 183, 107],
|
|
1302
|
+
darkmagenta: [139, 0, 139],
|
|
1303
|
+
darkolivegreen: [85, 107, 47],
|
|
1304
|
+
darkorange: [255, 140, 0],
|
|
1305
|
+
darkorchid: [153, 50, 204],
|
|
1306
|
+
darkred: [139, 0, 0],
|
|
1307
|
+
darksalmon: [233, 150, 122],
|
|
1308
|
+
darkseagreen: [143, 188, 143],
|
|
1309
|
+
darkslateblue: [72, 61, 139],
|
|
1310
|
+
darkslategray: [47, 79, 79],
|
|
1311
|
+
darkslategrey: [47, 79, 79],
|
|
1312
|
+
darkturquoise: [0, 206, 209],
|
|
1313
|
+
darkviolet: [148, 0, 211],
|
|
1314
|
+
deeppink: [255, 20, 147],
|
|
1315
|
+
deepskyblue: [0, 191, 255],
|
|
1316
|
+
dimgray: [105, 105, 105],
|
|
1317
|
+
dimgrey: [105, 105, 105],
|
|
1318
|
+
dodgerblue: [30, 144, 255],
|
|
1319
|
+
firebrick: [178, 34, 34],
|
|
1320
|
+
floralwhite: [255, 250, 240],
|
|
1321
|
+
forestgreen: [34, 139, 34],
|
|
1322
|
+
fuchsia: [255, 0, 255],
|
|
1323
|
+
gainsboro: [220, 220, 220],
|
|
1324
|
+
ghostwhite: [248, 248, 255],
|
|
1325
|
+
gold: [255, 215, 0],
|
|
1326
|
+
goldenrod: [218, 165, 32],
|
|
1327
|
+
gray: [128, 128, 128],
|
|
1328
|
+
green: [0, 128, 0],
|
|
1329
|
+
greenyellow: [173, 255, 47],
|
|
1330
|
+
grey: [128, 128, 128],
|
|
1331
|
+
honeydew: [240, 255, 240],
|
|
1332
|
+
hotpink: [255, 105, 180],
|
|
1333
|
+
indianred: [205, 92, 92],
|
|
1334
|
+
indigo: [75, 0, 130],
|
|
1335
|
+
ivory: [255, 255, 240],
|
|
1336
|
+
khaki: [240, 230, 140],
|
|
1337
|
+
lavender: [230, 230, 250],
|
|
1338
|
+
lavenderblush: [255, 240, 245],
|
|
1339
|
+
lawngreen: [124, 252, 0],
|
|
1340
|
+
lemonchiffon: [255, 250, 205],
|
|
1341
|
+
lightblue: [173, 216, 230],
|
|
1342
|
+
lightcoral: [240, 128, 128],
|
|
1343
|
+
lightcyan: [224, 255, 255],
|
|
1344
|
+
lightgoldenrodyellow: [250, 250, 210],
|
|
1345
|
+
lightgray: [211, 211, 211],
|
|
1346
|
+
lightgreen: [144, 238, 144],
|
|
1347
|
+
lightgrey: [211, 211, 211],
|
|
1348
|
+
lightpink: [255, 182, 193],
|
|
1349
|
+
lightsalmon: [255, 160, 122],
|
|
1350
|
+
lightseagreen: [32, 178, 170],
|
|
1351
|
+
lightskyblue: [135, 206, 250],
|
|
1352
|
+
lightslategray: [119, 136, 153],
|
|
1353
|
+
lightslategrey: [119, 136, 153],
|
|
1354
|
+
lightsteelblue: [176, 196, 222],
|
|
1355
|
+
lightyellow: [255, 255, 224],
|
|
1356
|
+
lime: [0, 255, 0],
|
|
1357
|
+
limegreen: [50, 205, 50],
|
|
1358
|
+
linen: [250, 240, 230],
|
|
1359
|
+
magenta: [255, 0, 255],
|
|
1360
|
+
maroon: [128, 0, 0],
|
|
1361
|
+
mediumaquamarine: [102, 205, 170],
|
|
1362
|
+
mediumblue: [0, 0, 205],
|
|
1363
|
+
mediumorchid: [186, 85, 211],
|
|
1364
|
+
mediumpurple: [147, 112, 219],
|
|
1365
|
+
mediumseagreen: [60, 179, 113],
|
|
1366
|
+
mediumslateblue: [123, 104, 238],
|
|
1367
|
+
mediumspringgreen: [0, 250, 154],
|
|
1368
|
+
mediumturquoise: [72, 209, 204],
|
|
1369
|
+
mediumvioletred: [199, 21, 133],
|
|
1370
|
+
midnightblue: [25, 25, 112],
|
|
1371
|
+
mintcream: [245, 255, 250],
|
|
1372
|
+
mistyrose: [255, 228, 225],
|
|
1373
|
+
moccasin: [255, 228, 181],
|
|
1374
|
+
navajowhite: [255, 222, 173],
|
|
1375
|
+
navy: [0, 0, 128],
|
|
1376
|
+
oldlace: [253, 245, 230],
|
|
1377
|
+
olive: [128, 128, 0],
|
|
1378
|
+
olivedrab: [107, 142, 35],
|
|
1379
|
+
orange: [255, 165, 0],
|
|
1380
|
+
orangered: [255, 69, 0],
|
|
1381
|
+
orchid: [218, 112, 214],
|
|
1382
|
+
palegoldenrod: [238, 232, 170],
|
|
1383
|
+
palegreen: [152, 251, 152],
|
|
1384
|
+
paleturquoise: [175, 238, 238],
|
|
1385
|
+
palevioletred: [219, 112, 147],
|
|
1386
|
+
papayawhip: [255, 239, 213],
|
|
1387
|
+
peachpuff: [255, 218, 185],
|
|
1388
|
+
peru: [205, 133, 63],
|
|
1389
|
+
pink: [255, 192, 203],
|
|
1390
|
+
plum: [221, 160, 221],
|
|
1391
|
+
powderblue: [176, 224, 230],
|
|
1392
|
+
purple: [128, 0, 128],
|
|
1393
|
+
rebeccapurple: [102, 51, 153],
|
|
1394
|
+
red: [255, 0, 0],
|
|
1395
|
+
rosybrown: [188, 143, 143],
|
|
1396
|
+
royalblue: [65, 105, 225],
|
|
1397
|
+
saddlebrown: [139, 69, 19],
|
|
1398
|
+
salmon: [250, 128, 114],
|
|
1399
|
+
sandybrown: [244, 164, 96],
|
|
1400
|
+
seagreen: [46, 139, 87],
|
|
1401
|
+
seashell: [255, 245, 238],
|
|
1402
|
+
sienna: [160, 82, 45],
|
|
1403
|
+
silver: [192, 192, 192],
|
|
1404
|
+
skyblue: [135, 206, 235],
|
|
1405
|
+
slateblue: [106, 90, 205],
|
|
1406
|
+
slategray: [112, 128, 144],
|
|
1407
|
+
slategrey: [112, 128, 144],
|
|
1408
|
+
snow: [255, 250, 250],
|
|
1409
|
+
springgreen: [0, 255, 127],
|
|
1410
|
+
steelblue: [70, 130, 180],
|
|
1411
|
+
tan: [210, 180, 140],
|
|
1412
|
+
teal: [0, 128, 128],
|
|
1413
|
+
thistle: [216, 191, 216],
|
|
1414
|
+
tomato: [255, 99, 71],
|
|
1415
|
+
turquoise: [64, 224, 208],
|
|
1416
|
+
violet: [238, 130, 238],
|
|
1417
|
+
wheat: [245, 222, 179],
|
|
1418
|
+
white: [255, 255, 255],
|
|
1419
|
+
whitesmoke: [245, 245, 245],
|
|
1420
|
+
yellow: [255, 255, 0],
|
|
1421
|
+
yellowgreen: [154, 205, 50]
|
|
1422
|
+
};
|
|
1423
|
+
class Color {
|
|
1424
|
+
constructor(r, g, b, a) {
|
|
1425
|
+
__publicField(this, "_r");
|
|
1426
|
+
__publicField(this, "_g");
|
|
1427
|
+
__publicField(this, "_b");
|
|
1428
|
+
__publicField(this, "_alpha");
|
|
1429
|
+
this._validateColorChannel(r);
|
|
1430
|
+
this._validateColorChannel(g);
|
|
1431
|
+
this._validateColorChannel(b);
|
|
1432
|
+
this._r = r;
|
|
1433
|
+
this._g = g;
|
|
1434
|
+
this._b = b;
|
|
1435
|
+
this._alpha = MathUtil.clamp(a || 1, 0, 1);
|
|
1613
1436
|
}
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
* @param lng 经度
|
|
1618
|
-
* @param lat 纬度
|
|
1619
|
-
* @returns 返回格式化后的经纬度字符串,格式为:经度度分秒,纬度度分秒
|
|
1620
|
-
*/
|
|
1621
|
-
static formatLnglat(lng, lat) {
|
|
1622
|
-
let res = "";
|
|
1623
|
-
function formatDegreeToDMS(valueInDegrees) {
|
|
1624
|
-
const degree = Math.floor(valueInDegrees);
|
|
1625
|
-
const minutes = Math.floor((valueInDegrees - degree) * 60);
|
|
1626
|
-
const seconds = (valueInDegrees - degree) * 3600 - minutes * 60;
|
|
1627
|
-
return `${degree}°${minutes}′${seconds.toFixed(2)}″`;
|
|
1628
|
-
}
|
|
1629
|
-
if (this.isLnglat(lng, lat)) {
|
|
1630
|
-
res = formatDegreeToDMS(lng) + "," + formatDegreeToDMS(lat);
|
|
1631
|
-
} else if (!isNaN(lng)) {
|
|
1632
|
-
res = formatDegreeToDMS(lng);
|
|
1633
|
-
} else if (!isNaN(lat)) {
|
|
1634
|
-
res = formatDegreeToDMS(lat);
|
|
1437
|
+
_validateColorChannel(channel) {
|
|
1438
|
+
if (channel < 0 || channel > 255) {
|
|
1439
|
+
ExceptionUtil.throwException("Color channel must be between 0 and 255.");
|
|
1635
1440
|
}
|
|
1636
|
-
return res;
|
|
1637
1441
|
}
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
*
|
|
1641
|
-
* @param lng 经度字符串
|
|
1642
|
-
* @param lat 纬度字符串
|
|
1643
|
-
* @returns 转换后的经纬度对象
|
|
1644
|
-
*/
|
|
1645
|
-
static transformLnglat(lng, lat) {
|
|
1646
|
-
function dms2deg(dmsString) {
|
|
1647
|
-
const isNegative = /[sw]/i.test(dmsString);
|
|
1648
|
-
let factor = isNegative ? -1 : 1;
|
|
1649
|
-
const numericParts = dmsString.match(/[\d.]+/g) || [];
|
|
1650
|
-
let degrees = 0;
|
|
1651
|
-
for (let i = 0; i < numericParts.length; i++) {
|
|
1652
|
-
degrees += parseFloat(numericParts[i]) / factor;
|
|
1653
|
-
factor *= 60;
|
|
1654
|
-
}
|
|
1655
|
-
return degrees;
|
|
1656
|
-
}
|
|
1657
|
-
if (lng && lat) {
|
|
1658
|
-
return {
|
|
1659
|
-
lng: dms2deg(lng),
|
|
1660
|
-
lat: dms2deg(lat)
|
|
1661
|
-
};
|
|
1662
|
-
}
|
|
1442
|
+
toString() {
|
|
1443
|
+
return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
|
|
1663
1444
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
*
|
|
1667
|
-
* @param p 点对象,包含x和y属性
|
|
1668
|
-
* @param poly 多边形顶点数组,可以是字符串数组或对象数组
|
|
1669
|
-
* @returns 返回字符串,表示点相对于多边形的位置:'in'表示在多边形内,'out'表示在多边形外,'on'表示在多边形上
|
|
1670
|
-
*/
|
|
1671
|
-
static rayCasting(p, poly) {
|
|
1672
|
-
var px = p.x, py = p.y, flag = false;
|
|
1673
|
-
for (var i = 0, l = poly.length, j = l - 1; i < l; j = i, i++) {
|
|
1674
|
-
var sx = poly[i].x, sy = poly[i].y, tx = poly[j].x, ty = poly[j].y;
|
|
1675
|
-
if (sx === px && sy === py || tx === px && ty === py) {
|
|
1676
|
-
return "on";
|
|
1677
|
-
}
|
|
1678
|
-
if (sy < py && ty >= py || sy >= py && ty < py) {
|
|
1679
|
-
var x = sx + (py - sy) * (tx - sx) / (ty - sy);
|
|
1680
|
-
if (x === px) {
|
|
1681
|
-
return "on";
|
|
1682
|
-
}
|
|
1683
|
-
if (x > px) {
|
|
1684
|
-
flag = !flag;
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
return flag ? "in" : "out";
|
|
1445
|
+
toJson() {
|
|
1446
|
+
return { r: this._r, g: this._g, b: this._b, a: this._alpha };
|
|
1689
1447
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
*
|
|
1693
|
-
* @param p1 旋转前点坐标
|
|
1694
|
-
* @param p2 旋转中心坐标
|
|
1695
|
-
* @param θ 旋转角度(顺时针旋转为正)
|
|
1696
|
-
* @returns 旋转后点坐标
|
|
1697
|
-
*/
|
|
1698
|
-
static rotatePoint(p1, p2, θ) {
|
|
1699
|
-
const x = (p1.x - p2.x) * Math.cos(Math.PI / 180 * -θ) - (p1.y - p2.y) * Math.sin(Math.PI / 180 * -θ) + p2.x;
|
|
1700
|
-
const y = (p1.x - p2.x) * Math.sin(Math.PI / 180 * -θ) + (p1.y - p2.y) * Math.cos(Math.PI / 180 * -θ) + p2.y;
|
|
1701
|
-
return { x, y };
|
|
1448
|
+
get rgba() {
|
|
1449
|
+
return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
|
|
1702
1450
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
*
|
|
1706
|
-
* @param p1 第一个点的坐标对象
|
|
1707
|
-
* @param p2 第二个点的坐标对象
|
|
1708
|
-
* @returns 返回一个对象,包含angle和distance属性,分别表示两点之间的角度(以度为单位,取值范围为0~359)和距离
|
|
1709
|
-
*/
|
|
1710
|
-
static calcBearAndDis(p1, p2) {
|
|
1711
|
-
const { x: x1, y: y1 } = p1;
|
|
1712
|
-
const { x: x2, y: y2 } = p2;
|
|
1713
|
-
const dx = x2 - x1;
|
|
1714
|
-
const dy = y2 - y1;
|
|
1715
|
-
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
1716
|
-
const angleInRadians = Math.atan2(dy, dx);
|
|
1717
|
-
const angle = (angleInRadians * (180 / Math.PI) + 360 + 90) % 360;
|
|
1718
|
-
return { angle, distance };
|
|
1451
|
+
get hex() {
|
|
1452
|
+
return Color.rgb2hex(this._r, this._g, this._b, this._alpha);
|
|
1719
1453
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
* @param latlng1 第一个经纬度点
|
|
1724
|
-
* @param latlng2 第二个经纬度点
|
|
1725
|
-
* @returns 包含方位角和距离的对象
|
|
1726
|
-
*/
|
|
1727
|
-
static calcBearAndDisByPoints(latlng1, latlng2) {
|
|
1728
|
-
var f1 = latlng1.lat * 1, l1 = latlng1.lng * 1, f2 = latlng2.lat * 1, l2 = latlng2.lng * 1;
|
|
1729
|
-
var y = Math.sin((l2 - l1) * this.toRadian) * Math.cos(f2 * this.toRadian);
|
|
1730
|
-
var x = Math.cos(f1 * this.toRadian) * Math.sin(f2 * this.toRadian) - Math.sin(f1 * this.toRadian) * Math.cos(f2 * this.toRadian) * Math.cos((l2 - l1) * this.toRadian);
|
|
1731
|
-
var angle = Math.atan2(y, x) * (180 / Math.PI);
|
|
1732
|
-
var deltaF = (f2 - f1) * this.toRadian;
|
|
1733
|
-
var deltaL = (l2 - l1) * this.toRadian;
|
|
1734
|
-
var a = Math.sin(deltaF / 2) * Math.sin(deltaF / 2) + Math.cos(f1 * this.toRadian) * Math.cos(f2 * this.toRadian) * Math.sin(deltaL / 2) * Math.sin(deltaL / 2);
|
|
1735
|
-
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
1736
|
-
var distance = this.R * c;
|
|
1737
|
-
return {
|
|
1738
|
-
angle,
|
|
1739
|
-
distance
|
|
1740
|
-
};
|
|
1454
|
+
setAlpha(a) {
|
|
1455
|
+
this._alpha = MathUtil.clamp(a, 0, 1);
|
|
1456
|
+
return this;
|
|
1741
1457
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
const x = p.x, y = p.y, x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y;
|
|
1752
|
-
const cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
|
|
1753
|
-
if (cross <= 0) {
|
|
1754
|
-
return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
|
|
1755
|
-
}
|
|
1756
|
-
const d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
|
|
1757
|
-
if (cross >= d2) {
|
|
1758
|
-
return Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
|
|
1759
|
-
}
|
|
1760
|
-
const r = cross / d2;
|
|
1761
|
-
const px = x1 + (x2 - x1) * r;
|
|
1762
|
-
const py = y1 + (y2 - y1) * r;
|
|
1763
|
-
return Math.sqrt((x - px) * (x - px) + (y - py) * (y - py));
|
|
1458
|
+
// 设置颜色的RGB值
|
|
1459
|
+
setRgb(r, g, b) {
|
|
1460
|
+
this._validateColorChannel(r);
|
|
1461
|
+
this._validateColorChannel(g);
|
|
1462
|
+
this._validateColorChannel(b);
|
|
1463
|
+
this._r = r;
|
|
1464
|
+
this._g = g;
|
|
1465
|
+
this._b = b;
|
|
1466
|
+
return this;
|
|
1764
1467
|
}
|
|
1765
1468
|
/**
|
|
1766
|
-
*
|
|
1469
|
+
* 从RGBA字符串创建Color对象
|
|
1767
1470
|
*
|
|
1768
|
-
* @param
|
|
1769
|
-
* @
|
|
1770
|
-
* @
|
|
1771
|
-
* @returns 返回计算后的新经纬度点,类型为{lat: number, lng: number}
|
|
1471
|
+
* @param rgbaValue RGBA颜色值字符串,格式为"rgba(r,g,b,a)"或"rgb(r,g,b)"
|
|
1472
|
+
* @returns 返回Color对象
|
|
1473
|
+
* @throws 如果rgbaValue不是有效的RGBA颜色值,则抛出错误
|
|
1772
1474
|
*/
|
|
1773
|
-
static
|
|
1774
|
-
const
|
|
1775
|
-
|
|
1776
|
-
const
|
|
1777
|
-
|
|
1778
|
-
const
|
|
1779
|
-
const
|
|
1780
|
-
return
|
|
1781
|
-
lat: MathUtil.rad2Deg(lat),
|
|
1782
|
-
lng: MathUtil.rad2Deg(lon)
|
|
1783
|
-
};
|
|
1475
|
+
static fromRgba(rgbaValue) {
|
|
1476
|
+
const rgbaMatch = rgbaValue.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([\d.]+))?\s*\)$/);
|
|
1477
|
+
if (!rgbaMatch) throw new Error("Invalid RGBA color value");
|
|
1478
|
+
const r = parseInt(rgbaMatch[1], 10);
|
|
1479
|
+
const g = parseInt(rgbaMatch[2], 10);
|
|
1480
|
+
const b = parseInt(rgbaMatch[3], 10);
|
|
1481
|
+
const a = rgbaMatch[5] ? parseFloat(rgbaMatch[5]) : 1;
|
|
1482
|
+
return new Color(r, g, b, a);
|
|
1784
1483
|
}
|
|
1785
1484
|
/**
|
|
1786
|
-
*
|
|
1485
|
+
* 将十六进制颜色值转换为颜色对象
|
|
1787
1486
|
*
|
|
1788
|
-
* @param
|
|
1789
|
-
* @
|
|
1790
|
-
* @returns 返回包含转换后的经度lng和纬度lat的对象
|
|
1487
|
+
* @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
|
|
1488
|
+
* @returns 返回颜色对象
|
|
1791
1489
|
*/
|
|
1792
|
-
static
|
|
1793
|
-
const
|
|
1794
|
-
const
|
|
1795
|
-
const
|
|
1796
|
-
|
|
1490
|
+
static fromHex(hexValue, a = 1) {
|
|
1491
|
+
const rgxShort = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
|
1492
|
+
const hex = hexValue.replace(rgxShort, (m, r2, g2, b2) => r2 + r2 + g2 + g2 + b2 + b2);
|
|
1493
|
+
const rgx = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
|
|
1494
|
+
const rgb = rgx.exec(hex);
|
|
1495
|
+
if (!rgb) {
|
|
1496
|
+
throw new Error("Invalid HEX color value");
|
|
1497
|
+
}
|
|
1498
|
+
const r = parseInt(rgb[1], 16);
|
|
1499
|
+
const g = parseInt(rgb[2], 16);
|
|
1500
|
+
const b = parseInt(rgb[3], 16);
|
|
1501
|
+
return new Color(r, g, b, a);
|
|
1797
1502
|
}
|
|
1798
1503
|
/**
|
|
1799
|
-
*
|
|
1504
|
+
* 从 HSL 字符串创建颜色对象
|
|
1800
1505
|
*
|
|
1801
|
-
* @param
|
|
1802
|
-
* @
|
|
1803
|
-
* @returns 墨卡托坐标对象,包含x和y属性
|
|
1506
|
+
* @param hsl HSL 字符串,格式为 hsl(h, s%, l%) 或 hsla(h, s%, l%, a)
|
|
1507
|
+
* @returns 返回颜色对象,如果 hsl 字符串无效则返回 null
|
|
1804
1508
|
*/
|
|
1805
|
-
static
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1509
|
+
static fromHsl(hslValue) {
|
|
1510
|
+
const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue) || /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g.exec(hslValue);
|
|
1511
|
+
if (!hsl) {
|
|
1512
|
+
throw new Error("Invalid HSL color value");
|
|
1513
|
+
}
|
|
1514
|
+
const h = parseInt(hsl[1], 10) / 360;
|
|
1515
|
+
const s = parseInt(hsl[2], 10) / 100;
|
|
1516
|
+
const l = parseInt(hsl[3], 10) / 100;
|
|
1517
|
+
const a = hsl[4] ? parseFloat(hsl[4]) : 1;
|
|
1518
|
+
function hue2rgb(p, q, t) {
|
|
1519
|
+
if (t < 0) t += 1;
|
|
1520
|
+
if (t > 1) t -= 1;
|
|
1521
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
1522
|
+
if (t < 1 / 2) return q;
|
|
1523
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
1524
|
+
return p;
|
|
1525
|
+
}
|
|
1526
|
+
let r, g, b;
|
|
1527
|
+
if (s === 0) {
|
|
1528
|
+
r = g = b = l;
|
|
1529
|
+
} else {
|
|
1530
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
1531
|
+
const p = 2 * l - q;
|
|
1532
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
1533
|
+
g = hue2rgb(p, q, h);
|
|
1534
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
1535
|
+
}
|
|
1536
|
+
return new Color(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
|
|
1537
|
+
}
|
|
1538
|
+
static fromName(str) {
|
|
1539
|
+
const rgba = ColorName[str];
|
|
1540
|
+
if (!rgba) {
|
|
1541
|
+
ExceptionUtil.throwException(`Invalid color name: ${str}`);
|
|
1542
|
+
}
|
|
1543
|
+
return new Color(rgba[0], rgba[1], rgba[2], rgba.length > 3 ? rgba[3] : 1);
|
|
1811
1544
|
}
|
|
1812
1545
|
/**
|
|
1813
|
-
*
|
|
1546
|
+
* 从字符串中创建颜色对象
|
|
1814
1547
|
*
|
|
1815
|
-
* @param
|
|
1816
|
-
* @
|
|
1817
|
-
* @
|
|
1818
|
-
* @returns 返回插值后的坐标
|
|
1548
|
+
* @param str 字符串类型的颜色值,支持rgba、hex、hsl格式
|
|
1549
|
+
* @returns 返回创建的颜色对象
|
|
1550
|
+
* @throws 当颜色值无效时,抛出错误
|
|
1819
1551
|
*/
|
|
1820
|
-
static
|
|
1821
|
-
|
|
1822
|
-
|
|
1552
|
+
static from(str) {
|
|
1553
|
+
if (this.isRgb(str)) {
|
|
1554
|
+
return this.fromRgba(str);
|
|
1555
|
+
} else if (this.isHex(str)) {
|
|
1556
|
+
return this.fromHex(str);
|
|
1557
|
+
} else if (this.isHsl(str)) {
|
|
1558
|
+
return this.fromHsl(str);
|
|
1559
|
+
} else if (Object.keys(ColorName).map((key) => key.toString()).includes(str)) {
|
|
1560
|
+
return this.fromName(str);
|
|
1561
|
+
} else {
|
|
1562
|
+
return ExceptionUtil.throwException("Invalid color value");
|
|
1563
|
+
}
|
|
1823
1564
|
}
|
|
1824
1565
|
/**
|
|
1825
|
-
*
|
|
1566
|
+
* 将RGB颜色值转换为十六进制颜色值
|
|
1826
1567
|
*
|
|
1827
|
-
* @param
|
|
1828
|
-
* @param
|
|
1829
|
-
* @param
|
|
1830
|
-
* @
|
|
1568
|
+
* @param r 红色分量值,取值范围0-255
|
|
1569
|
+
* @param g 绿色分量值,取值范围0-255
|
|
1570
|
+
* @param b 蓝色分量值,取值范围0-255
|
|
1571
|
+
* @param a 可选参数,透明度分量值,取值范围0-1
|
|
1572
|
+
* @returns 十六进制颜色值,格式为#RRGGBB或#RRGGBBAA
|
|
1831
1573
|
*/
|
|
1832
|
-
static
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
side[2] = Math.sqrt(Math.pow(c.x - b.x, 2) + Math.pow(c.y - b.y, 2) + Math.pow((c.z || 0) - (b.z || 0), 2));
|
|
1838
|
-
if (side[0] + side[1] <= side[2] || side[0] + side[2] <= side[1] || side[1] + side[2] <= side[0]) {
|
|
1839
|
-
return area;
|
|
1574
|
+
static rgb2hex(r, g, b, a) {
|
|
1575
|
+
var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
|
1576
|
+
if (a !== void 0) {
|
|
1577
|
+
const alpha = Math.round(a * 255).toString(16).padStart(2, "0");
|
|
1578
|
+
return hex + alpha;
|
|
1840
1579
|
}
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1580
|
+
return hex;
|
|
1581
|
+
}
|
|
1582
|
+
static isHex(a) {
|
|
1583
|
+
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a);
|
|
1584
|
+
}
|
|
1585
|
+
static isRgb(a) {
|
|
1586
|
+
return /^rgba?\s*\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*[\d.]+)?\s*\)$/.test(a);
|
|
1587
|
+
}
|
|
1588
|
+
static isHsl(a) {
|
|
1589
|
+
return /^(hsl|hsla)\(\d+,\s*[\d.]+%,\s*[\d.]+%(,\s*[\d.]+)?\)$/.test(a);
|
|
1590
|
+
}
|
|
1591
|
+
static isColor(a) {
|
|
1592
|
+
return this.isHex(a) || this.isRgb(a) || this.isHsl(a);
|
|
1593
|
+
}
|
|
1594
|
+
static random() {
|
|
1595
|
+
let r = Math.floor(Math.random() * 256);
|
|
1596
|
+
let g = Math.floor(Math.random() * 256);
|
|
1597
|
+
let b = Math.floor(Math.random() * 256);
|
|
1598
|
+
let a = Math.random();
|
|
1599
|
+
return new Color(r, g, b, a);
|
|
1844
1600
|
}
|
|
1845
1601
|
}
|
|
1846
|
-
__publicField(GeoUtil, "toRadian", Math.PI / 180);
|
|
1847
|
-
__publicField(GeoUtil, "R", 6371393);
|
|
1848
|
-
// 地球平均半径
|
|
1849
|
-
__publicField(GeoUtil, "R_EQU", 6378137);
|
|
1850
1602
|
const TYPES = ["Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon"];
|
|
1851
1603
|
const GeoJsonUtil = {
|
|
1852
1604
|
/**
|
|
@@ -2088,64 +1840,6 @@ const GeoJsonUtil = {
|
|
|
2088
1840
|
};
|
|
2089
1841
|
}
|
|
2090
1842
|
};
|
|
2091
|
-
const ExceptionUtil = {
|
|
2092
|
-
getException(type, message) {
|
|
2093
|
-
const _Exception = function() {
|
|
2094
|
-
Error.call(this, message);
|
|
2095
|
-
this.name = type;
|
|
2096
|
-
this.message = message;
|
|
2097
|
-
this.stack = new Error().stack;
|
|
2098
|
-
};
|
|
2099
|
-
if (Error) _Exception.__proto__ = Error;
|
|
2100
|
-
_Exception.prototype = Object.create(Error && Error.prototype);
|
|
2101
|
-
_Exception.prototype.constructor = _Exception;
|
|
2102
|
-
return _Exception;
|
|
2103
|
-
},
|
|
2104
|
-
throwException(msg) {
|
|
2105
|
-
const _Exception = this.getException("Exception", msg);
|
|
2106
|
-
throw new _Exception(msg);
|
|
2107
|
-
},
|
|
2108
|
-
throwColorException(msg) {
|
|
2109
|
-
const _Exception = this.getException("ColorException", ErrorType.DATA_ERROR_COLOR + " -> " + (msg || ""));
|
|
2110
|
-
throw new _Exception(msg);
|
|
2111
|
-
},
|
|
2112
|
-
throwCoordinateException(msg) {
|
|
2113
|
-
const _Exception = this.getException("CoordinateException", ErrorType.DATA_ERROR_COORDINATE + " -> " + (msg || ""));
|
|
2114
|
-
throw new _Exception(msg);
|
|
2115
|
-
},
|
|
2116
|
-
throwGeoJsonException(msg) {
|
|
2117
|
-
const _Exception = this.getException("GeoJsonException", ErrorType.DATA_ERROR_GEOJSON + " -> " + (msg || ""));
|
|
2118
|
-
throw new _Exception(msg);
|
|
2119
|
-
},
|
|
2120
|
-
throwEmptyException(msg) {
|
|
2121
|
-
const _Exception = this.getException("EmptyException", ErrorType.PARAMETER_ERROR_LACK + " -> " + (msg || ""));
|
|
2122
|
-
throw new _Exception(msg);
|
|
2123
|
-
},
|
|
2124
|
-
throwIntegerException(msg) {
|
|
2125
|
-
const _Exception = this.getException("IntegerException", ErrorType.PARAMETER_ERROR_INTEGER + " -> " + (msg || ""));
|
|
2126
|
-
throw new _Exception(msg);
|
|
2127
|
-
},
|
|
2128
|
-
throwNumberException(msg) {
|
|
2129
|
-
const _Exception = this.getException("NumberException", ErrorType.PARAMETER_ERROR_NUMBER + " -> " + (msg || ""));
|
|
2130
|
-
throw new _Exception(msg);
|
|
2131
|
-
},
|
|
2132
|
-
throwArrayException(msg) {
|
|
2133
|
-
const _Exception = this.getException("ArrayException", ErrorType.PARAMETER_ERROR_ARRAY + " -> " + (msg || ""));
|
|
2134
|
-
throw new _Exception(msg);
|
|
2135
|
-
},
|
|
2136
|
-
throwFunctionException(msg) {
|
|
2137
|
-
const _Exception = this.getException("FunctionException", ErrorType.PARAMETER_ERROR_FUNCTION + " -> " + (msg || ""));
|
|
2138
|
-
throw new _Exception(msg);
|
|
2139
|
-
},
|
|
2140
|
-
throwProcessException(msg) {
|
|
2141
|
-
const _Exception = this.getException("ProcessException", ErrorType.PROCESS_FAIL + " -> " + (msg || ""));
|
|
2142
|
-
throw new _Exception(msg);
|
|
2143
|
-
},
|
|
2144
|
-
throwNetworkException(msg) {
|
|
2145
|
-
const _Exception = this.getException("NetworkException", ErrorType.REQUEST_ERROR_TIMEOUT + " -> " + (msg || ""));
|
|
2146
|
-
throw new _Exception(msg);
|
|
2147
|
-
}
|
|
2148
|
-
};
|
|
2149
1843
|
const AssertUtil = {
|
|
2150
1844
|
assertEmpty(...arg) {
|
|
2151
1845
|
arg.forEach((a) => {
|
|
@@ -2489,18 +2183,30 @@ class BrowserUtil {
|
|
|
2489
2183
|
* @returns 返回包含GPU类型和型号的对象
|
|
2490
2184
|
*/
|
|
2491
2185
|
static getGPU() {
|
|
2492
|
-
let type = "";
|
|
2493
|
-
let model = "";
|
|
2186
|
+
let type = "unknown";
|
|
2187
|
+
let model = "unknown";
|
|
2494
2188
|
if (this == null ? void 0 : this.document) {
|
|
2495
2189
|
let $canvas = this.document.createElement("canvas");
|
|
2496
|
-
let
|
|
2497
|
-
if (
|
|
2498
|
-
let debugInfo =
|
|
2190
|
+
let gl = $canvas.getContext("webgl") || $canvas.getContext("experimental-webgl");
|
|
2191
|
+
if (gl instanceof WebGLRenderingContext) {
|
|
2192
|
+
let debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
|
|
2499
2193
|
if (debugInfo) {
|
|
2500
|
-
let gpu_str =
|
|
2194
|
+
let gpu_str = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
|
|
2501
2195
|
type = (gpu_str.match(/ANGLE \((.+?),/) || [])[1] || "";
|
|
2502
2196
|
model = (gpu_str.match(/, (.+?) (\(|vs_)/) || [])[1] || "";
|
|
2503
2197
|
}
|
|
2198
|
+
return {
|
|
2199
|
+
type,
|
|
2200
|
+
model,
|
|
2201
|
+
spec: {
|
|
2202
|
+
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
|
|
2203
|
+
// 最大纹理尺寸 16384/4k
|
|
2204
|
+
maxRenderBufferSize: gl.getParameter(gl.MAX_RENDERBUFFER_SIZE),
|
|
2205
|
+
// 最大渲染缓冲尺寸 16384/4k
|
|
2206
|
+
maxTextureUnits: gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)
|
|
2207
|
+
// 纹理单元数量 32
|
|
2208
|
+
}
|
|
2209
|
+
};
|
|
2504
2210
|
}
|
|
2505
2211
|
}
|
|
2506
2212
|
return {
|
|
@@ -3036,650 +2742,986 @@ const DomUtil = {
|
|
|
3036
2742
|
if (container) {
|
|
3037
2743
|
container.appendChild(el);
|
|
3038
2744
|
}
|
|
3039
|
-
return el;
|
|
2745
|
+
return el;
|
|
2746
|
+
},
|
|
2747
|
+
/**
|
|
2748
|
+
* 从父节点中移除指定元素。
|
|
2749
|
+
*
|
|
2750
|
+
* @param el 要移除的元素对象,必须包含parentNode属性。
|
|
2751
|
+
*/
|
|
2752
|
+
remove(el) {
|
|
2753
|
+
const parent = el.parentNode;
|
|
2754
|
+
if (parent) {
|
|
2755
|
+
parent.removeChild(el);
|
|
2756
|
+
}
|
|
2757
|
+
},
|
|
2758
|
+
/**
|
|
2759
|
+
* 清空给定元素的子节点
|
|
2760
|
+
*
|
|
2761
|
+
* @param el 要清空子节点的元素,包含firstChild和removeChild属性
|
|
2762
|
+
*/
|
|
2763
|
+
empty(el) {
|
|
2764
|
+
while (el.firstChild) {
|
|
2765
|
+
el.removeChild(el.firstChild);
|
|
2766
|
+
}
|
|
2767
|
+
},
|
|
2768
|
+
/**
|
|
2769
|
+
* 将元素移到父节点的最前面
|
|
2770
|
+
*
|
|
2771
|
+
* @param el 要移动的元素,需要包含 parentNode 属性
|
|
2772
|
+
*/
|
|
2773
|
+
toFront(el) {
|
|
2774
|
+
const parent = el.parentNode;
|
|
2775
|
+
if (parent && parent.lastChild !== el) {
|
|
2776
|
+
parent.appendChild(el);
|
|
2777
|
+
}
|
|
2778
|
+
},
|
|
2779
|
+
/**
|
|
2780
|
+
* 将元素移动到其父节点的最前面
|
|
2781
|
+
*
|
|
2782
|
+
* @param el 要移动的元素,需要包含parentNode属性
|
|
2783
|
+
*/
|
|
2784
|
+
toBack(el) {
|
|
2785
|
+
const parent = el.parentNode;
|
|
2786
|
+
if (parent && parent.firstChild !== el) {
|
|
2787
|
+
parent.insertBefore(el, parent.firstChild);
|
|
2788
|
+
}
|
|
2789
|
+
},
|
|
2790
|
+
/**
|
|
2791
|
+
* 获取元素的类名
|
|
2792
|
+
*
|
|
2793
|
+
* @param el 包含对应元素和类名的对象
|
|
2794
|
+
* @param el.correspondingElement 对应的元素
|
|
2795
|
+
* @param el.className 类名对象
|
|
2796
|
+
* @param el.className.baseVal 类名字符串
|
|
2797
|
+
* @returns 返回元素的类名字符串
|
|
2798
|
+
*/
|
|
2799
|
+
getClass(el) {
|
|
2800
|
+
const shadowElement = (el == null ? void 0 : el.host) || el;
|
|
2801
|
+
return shadowElement.className.toString();
|
|
2802
|
+
},
|
|
2803
|
+
/**
|
|
2804
|
+
* 判断元素是否包含指定类名
|
|
2805
|
+
*
|
|
2806
|
+
* @param el 元素对象,包含classList属性,classList属性包含contains方法
|
|
2807
|
+
* @param name 要判断的类名
|
|
2808
|
+
* @returns 返回一个布尔值,表示元素是否包含指定类名
|
|
2809
|
+
*/
|
|
2810
|
+
hasClass(el, name) {
|
|
2811
|
+
var _a2;
|
|
2812
|
+
if ((_a2 = el.classList) == null ? void 0 : _a2.contains(name)) {
|
|
2813
|
+
return true;
|
|
2814
|
+
}
|
|
2815
|
+
const className = this.getClass(el);
|
|
2816
|
+
return className.length > 0 && new RegExp(`(^|\\s)${name}(\\s|$)`).test(className);
|
|
2817
|
+
},
|
|
2818
|
+
/**
|
|
2819
|
+
* 给指定的 HTML 元素添加类名
|
|
2820
|
+
*
|
|
2821
|
+
* @param el 要添加类名的 HTML 元素
|
|
2822
|
+
* @param name 要添加的类名,多个类名之间用空格分隔
|
|
2823
|
+
*/
|
|
2824
|
+
addClass(el, name) {
|
|
2825
|
+
if (el.classList !== void 0) {
|
|
2826
|
+
const classes = splitWords(name);
|
|
2827
|
+
for (let i = 0, len = classes.length; i < len; i++) {
|
|
2828
|
+
el.classList.add(classes[i]);
|
|
2829
|
+
}
|
|
2830
|
+
} else if (!this.hasClass(el, name)) {
|
|
2831
|
+
const className = this.getClass(el);
|
|
2832
|
+
this.setClass(el, (className ? className + " " : "") + name);
|
|
2833
|
+
}
|
|
2834
|
+
},
|
|
2835
|
+
/**
|
|
2836
|
+
* 从元素中移除指定类名
|
|
2837
|
+
*
|
|
2838
|
+
* @param el 要移除类名的元素
|
|
2839
|
+
* @param name 要移除的类名,多个类名用空格分隔
|
|
2840
|
+
*/
|
|
2841
|
+
removeClass(el, name) {
|
|
2842
|
+
if (el.classList !== void 0) {
|
|
2843
|
+
const classes = splitWords(name);
|
|
2844
|
+
classes.forEach((className) => el.classList.remove(className));
|
|
2845
|
+
} else {
|
|
2846
|
+
this.setClass(el, (" " + this.getClass(el) + " ").replace(" " + name + " ", " ").trim());
|
|
2847
|
+
}
|
|
2848
|
+
},
|
|
2849
|
+
/**
|
|
2850
|
+
* 设置元素的 CSS 类名
|
|
2851
|
+
*
|
|
2852
|
+
* @param el HTML 或 SVG 元素
|
|
2853
|
+
* @param name 要设置的类名,多个类名之间用空格分隔
|
|
2854
|
+
*/
|
|
2855
|
+
setClass(el, name) {
|
|
2856
|
+
if ("classList" in el) {
|
|
2857
|
+
el.classList.value = "";
|
|
2858
|
+
name.split(" ").forEach((className) => el.classList.add(className));
|
|
2859
|
+
}
|
|
3040
2860
|
},
|
|
3041
2861
|
/**
|
|
3042
|
-
*
|
|
2862
|
+
* 从字符串中解析XML文档,并返回根节点
|
|
3043
2863
|
*
|
|
3044
|
-
* @param
|
|
2864
|
+
* @param str 要解析的XML字符串
|
|
2865
|
+
* @returns 解析后的XML文档的根节点
|
|
3045
2866
|
*/
|
|
3046
|
-
|
|
3047
|
-
const
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
2867
|
+
parseFromString(str) {
|
|
2868
|
+
const parser = new DOMParser();
|
|
2869
|
+
const doc = parser.parseFromString(str, "text/xml");
|
|
2870
|
+
return doc.children[0];
|
|
2871
|
+
}
|
|
2872
|
+
};
|
|
2873
|
+
const FileUtil = {
|
|
3052
2874
|
/**
|
|
3053
|
-
*
|
|
2875
|
+
* 将Base64编码的字符串转换为Blob对象
|
|
3054
2876
|
*
|
|
3055
|
-
* @param
|
|
2877
|
+
* @param data Base64编码的字符串
|
|
2878
|
+
* @returns 转换后的Blob对象
|
|
3056
2879
|
*/
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
2880
|
+
convertBase64ToBlob(data) {
|
|
2881
|
+
const mimeString = data.split(",")[0].split(":")[1].split(";")[0];
|
|
2882
|
+
const byteCharacters = atob(data.split(",")[1]);
|
|
2883
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
2884
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
2885
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
3060
2886
|
}
|
|
2887
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
2888
|
+
const blob = new Blob([byteArray], { type: mimeString });
|
|
2889
|
+
return blob;
|
|
3061
2890
|
},
|
|
3062
2891
|
/**
|
|
3063
|
-
*
|
|
2892
|
+
* 将base64字符串转换为文件对象
|
|
3064
2893
|
*
|
|
3065
|
-
* @param
|
|
2894
|
+
* @param dataurl 包含base64字符串的数据URL
|
|
2895
|
+
* @param filename 文件的名称
|
|
2896
|
+
* @returns 返回文件对象
|
|
3066
2897
|
*/
|
|
3067
|
-
|
|
3068
|
-
const
|
|
3069
|
-
|
|
3070
|
-
|
|
2898
|
+
convertBase64ToFile(dataurl, filename) {
|
|
2899
|
+
const arr = dataurl.split(",");
|
|
2900
|
+
const mimeMatch = arr[0].match(/:(.*?);/);
|
|
2901
|
+
const mime = mimeMatch ? mimeMatch[1] : "image/png";
|
|
2902
|
+
const bstr = atob(arr[1]);
|
|
2903
|
+
const u8arr = new Uint8Array(bstr.length);
|
|
2904
|
+
for (let i = 0; i < bstr.length; i++) {
|
|
2905
|
+
u8arr[i] = bstr.charCodeAt(i);
|
|
3071
2906
|
}
|
|
2907
|
+
const file = new File([u8arr], filename, { type: mime });
|
|
2908
|
+
return file;
|
|
3072
2909
|
},
|
|
3073
2910
|
/**
|
|
3074
|
-
*
|
|
2911
|
+
* 从文件下载数据
|
|
3075
2912
|
*
|
|
3076
|
-
* @param
|
|
2913
|
+
* @param data 要下载的数据,可以是字符串数组、BlobPart 或 MediaSource
|
|
2914
|
+
* @param saveName 下载后文件的保存名称
|
|
3077
2915
|
*/
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
2916
|
+
downloadFromFile(data, saveName) {
|
|
2917
|
+
if (typeof data == "object") {
|
|
2918
|
+
if (data instanceof Blob) {
|
|
2919
|
+
data = URL.createObjectURL(data);
|
|
2920
|
+
} else {
|
|
2921
|
+
const str = JSON.stringify(data);
|
|
2922
|
+
const blob = new Blob([str], { type: "text/json" });
|
|
2923
|
+
data = window.URL.createObjectURL(blob);
|
|
2924
|
+
}
|
|
2925
|
+
} else if (typeof data == "string" && data.indexOf("http") === -1) {
|
|
2926
|
+
const blob = new Blob([data], { type: "text/json" });
|
|
2927
|
+
data = window.URL.createObjectURL(blob);
|
|
3082
2928
|
}
|
|
2929
|
+
var link = document.createElement("a");
|
|
2930
|
+
link.href = data;
|
|
2931
|
+
link.download = saveName || "";
|
|
2932
|
+
link.click();
|
|
2933
|
+
window.URL.revokeObjectURL(link.href);
|
|
3083
2934
|
},
|
|
3084
2935
|
/**
|
|
3085
|
-
*
|
|
3086
|
-
*
|
|
3087
|
-
* @param
|
|
3088
|
-
* @
|
|
3089
|
-
* @param el.className 类名对象
|
|
3090
|
-
* @param el.className.baseVal 类名字符串
|
|
3091
|
-
* @returns 返回元素的类名字符串
|
|
2936
|
+
* 获取远程文件并读取
|
|
2937
|
+
* @param url 远程文件地址
|
|
2938
|
+
* @param fileName 文件名
|
|
2939
|
+
* @returns Promise<ArrayBuffer>
|
|
3092
2940
|
*/
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
2941
|
+
readFileFromUrl(url, fileName) {
|
|
2942
|
+
return new Promise((resolve, reject) => {
|
|
2943
|
+
const xhr = new XMLHttpRequest();
|
|
2944
|
+
xhr.open("GET", url, true);
|
|
2945
|
+
xhr.responseType = "blob";
|
|
2946
|
+
const suffix = fileName.split(".");
|
|
2947
|
+
try {
|
|
2948
|
+
xhr.onload = function() {
|
|
2949
|
+
if (this.status >= 200 && this.status < 300) {
|
|
2950
|
+
const blob = xhr.response;
|
|
2951
|
+
const objectURL = URL.createObjectURL(blob);
|
|
2952
|
+
const file = new File([blob], fileName, {
|
|
2953
|
+
type: suffix[1],
|
|
2954
|
+
lastModified: (/* @__PURE__ */ new Date()).getTime()
|
|
2955
|
+
});
|
|
2956
|
+
const reader = new FileReader();
|
|
2957
|
+
reader.readAsArrayBuffer(file);
|
|
2958
|
+
reader.onload = function(e) {
|
|
2959
|
+
var _a2;
|
|
2960
|
+
URL.revokeObjectURL(objectURL);
|
|
2961
|
+
resolve((_a2 = e.target) == null ? void 0 : _a2.result);
|
|
2962
|
+
};
|
|
2963
|
+
reader.onerror = function(error) {
|
|
2964
|
+
reject(error);
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
};
|
|
2968
|
+
xhr.onerror = function(error) {
|
|
2969
|
+
reject(error);
|
|
2970
|
+
};
|
|
2971
|
+
} catch (error) {
|
|
2972
|
+
reject(error);
|
|
2973
|
+
}
|
|
2974
|
+
xhr.send();
|
|
2975
|
+
});
|
|
2976
|
+
}
|
|
2977
|
+
};
|
|
2978
|
+
class MessageUtil {
|
|
2979
|
+
static resetWarned() {
|
|
2980
|
+
this.warned = {};
|
|
2981
|
+
}
|
|
2982
|
+
static changeVoice() {
|
|
2983
|
+
this.isMute = !!Number(!this.isMute);
|
|
2984
|
+
localStorage.setItem("mute", Number(this.isMute).toString());
|
|
2985
|
+
}
|
|
2986
|
+
static _call(method, message, options) {
|
|
2987
|
+
if (!this.warned[message]) {
|
|
2988
|
+
method(message, options);
|
|
2989
|
+
this.warned[message] = true;
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
3097
2992
|
/**
|
|
3098
|
-
*
|
|
2993
|
+
* 播放消息提示音和文字朗读(语音需要有交互)
|
|
3099
2994
|
*
|
|
3100
|
-
* @param
|
|
3101
|
-
* @param
|
|
3102
|
-
* @
|
|
2995
|
+
* @param type 消息类型
|
|
2996
|
+
* @param message 消息内容
|
|
2997
|
+
* @param options 配置选项,可选参数,包括语言、音量、语速和音高
|
|
2998
|
+
* @returns 无返回值
|
|
3103
2999
|
*/
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3000
|
+
static msg(type, message, options = {}) {
|
|
3001
|
+
if (this.isMute) return;
|
|
3002
|
+
const typename = Util.decodeDict(type, "success", "恭喜", "error", "发生错误", "warning", "警告", "info", "友情提示") + ":";
|
|
3003
|
+
this.speechSynthesisUtterance.text = typename + message;
|
|
3004
|
+
this.speechSynthesisUtterance.lang = options.lang || "zh-CN";
|
|
3005
|
+
this.speechSynthesisUtterance.volume = options.volume || 1;
|
|
3006
|
+
this.speechSynthesisUtterance.rate = options.rate || 1;
|
|
3007
|
+
this.speechSynthesisUtterance.pitch = options.pitch || 1;
|
|
3008
|
+
this.speechSynthesis.speak(this.speechSynthesisUtterance);
|
|
3009
|
+
}
|
|
3010
|
+
static stop(e) {
|
|
3011
|
+
this.speechSynthesisUtterance.text = e;
|
|
3012
|
+
this.speechSynthesis.cancel();
|
|
3013
|
+
}
|
|
3014
|
+
static warning(message, options = {}) {
|
|
3015
|
+
console.warn(`Warning: %c${message}`, "color:#E6A23C;font-weight: bold;");
|
|
3016
|
+
this.msg("warning", message, options);
|
|
3017
|
+
}
|
|
3018
|
+
static warningOnce(message, options = {}) {
|
|
3019
|
+
this._call(this.warning.bind(this), message, options);
|
|
3020
|
+
}
|
|
3021
|
+
static info(message, options = {}) {
|
|
3022
|
+
if (process.env.NODE_ENV === "development" && console !== void 0) {
|
|
3023
|
+
console.info(`Info: %c${message}`, "color:#909399;font-weight: bold;");
|
|
3108
3024
|
}
|
|
3109
|
-
|
|
3110
|
-
|
|
3025
|
+
this.msg("info", message, options);
|
|
3026
|
+
}
|
|
3027
|
+
static infoOnce(message, options = {}) {
|
|
3028
|
+
this._call(this.info.bind(this), message, options);
|
|
3029
|
+
}
|
|
3030
|
+
static error(message, options = {}) {
|
|
3031
|
+
console.error(`Error: %c${message}`, "color:#F56C6C;font-weight: bold;");
|
|
3032
|
+
this.msg("error", message, options);
|
|
3033
|
+
}
|
|
3034
|
+
static errorOnce(message, options = {}) {
|
|
3035
|
+
this._call(this.error.bind(this), message, options);
|
|
3036
|
+
}
|
|
3037
|
+
static success(message, options = {}) {
|
|
3038
|
+
if (process.env.NODE_ENV === "development" && console !== void 0) {
|
|
3039
|
+
console.log(`Success: %c${message}`, "color:#67C23A;font-weight: bold;");
|
|
3040
|
+
}
|
|
3041
|
+
this.msg("success", message, options);
|
|
3042
|
+
}
|
|
3043
|
+
static successOnce(message, options = {}) {
|
|
3044
|
+
this._call(this.success.bind(this), message, options);
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
__publicField(MessageUtil, "warned", {});
|
|
3048
|
+
__publicField(MessageUtil, "isMute", !!Number(localStorage.getItem("mute")) || false);
|
|
3049
|
+
__publicField(MessageUtil, "speechSynthesis", window.speechSynthesis);
|
|
3050
|
+
__publicField(MessageUtil, "speechSynthesisUtterance", new SpeechSynthesisUtterance());
|
|
3051
|
+
const OptimizeUtil = {
|
|
3052
|
+
/**
|
|
3053
|
+
* 防抖函数,在指定的等待时间内,如果连续触发事件,则只在最后一次触发后执行函数。适用于像搜索输入框这种需要用户停止输入后才调用的场景
|
|
3054
|
+
*
|
|
3055
|
+
* @param func 需要防抖的函数。
|
|
3056
|
+
* @param wait 等待时间,单位毫秒。
|
|
3057
|
+
* @param immediate 是否立即执行函数,默认为true。
|
|
3058
|
+
* @returns 返回防抖后的函数。
|
|
3059
|
+
*/
|
|
3060
|
+
debounce(func, wait, immediate = true) {
|
|
3061
|
+
let timeout = null;
|
|
3062
|
+
let args;
|
|
3063
|
+
let timestamp;
|
|
3064
|
+
let result;
|
|
3065
|
+
const later = () => {
|
|
3066
|
+
const last = Date.now() - timestamp;
|
|
3067
|
+
if (last < wait && last > 0) {
|
|
3068
|
+
timeout = setTimeout(later, wait - last);
|
|
3069
|
+
} else {
|
|
3070
|
+
timeout = null;
|
|
3071
|
+
if (!immediate) {
|
|
3072
|
+
result = func.apply(this, args);
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
};
|
|
3076
|
+
return (...args2) => {
|
|
3077
|
+
timestamp = Date.now();
|
|
3078
|
+
const callNow = immediate && !timeout;
|
|
3079
|
+
if (!timeout) {
|
|
3080
|
+
timeout = setTimeout(later, wait);
|
|
3081
|
+
}
|
|
3082
|
+
if (callNow) {
|
|
3083
|
+
result = func.apply(this, args2);
|
|
3084
|
+
if (!timeout) {
|
|
3085
|
+
args2 = null;
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
return result;
|
|
3089
|
+
};
|
|
3111
3090
|
},
|
|
3112
3091
|
/**
|
|
3113
|
-
*
|
|
3092
|
+
* 节流函数,适用于像滚动事件、窗口resize事件这种需要限制调用频率的场景
|
|
3114
3093
|
*
|
|
3115
|
-
* @param
|
|
3116
|
-
* @param
|
|
3094
|
+
* @param func 需要节流的函数
|
|
3095
|
+
* @param wait 节流间隔,单位为毫秒
|
|
3096
|
+
* @param type 节流类型,1表示时间戳方式,2表示定时器方式
|
|
3097
|
+
* @returns 返回一个新的函数,该函数在节流控制下执行传入的函数
|
|
3117
3098
|
*/
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3099
|
+
throttle(func, wait, type = 1) {
|
|
3100
|
+
let previous = 0;
|
|
3101
|
+
let timeout = null;
|
|
3102
|
+
return (...args) => {
|
|
3103
|
+
if (type === 1) {
|
|
3104
|
+
const now = Date.now();
|
|
3105
|
+
if (now - previous >= wait) {
|
|
3106
|
+
func.apply(this, args);
|
|
3107
|
+
previous = now;
|
|
3108
|
+
}
|
|
3109
|
+
} else if (type === 2) {
|
|
3110
|
+
if (!timeout) {
|
|
3111
|
+
timeout = setTimeout(() => {
|
|
3112
|
+
timeout = null;
|
|
3113
|
+
func.apply(this, args);
|
|
3114
|
+
}, wait);
|
|
3115
|
+
}
|
|
3123
3116
|
}
|
|
3124
|
-
}
|
|
3125
|
-
const className = this.getClass(el);
|
|
3126
|
-
this.setClass(el, (className ? className + " " : "") + name);
|
|
3127
|
-
}
|
|
3117
|
+
};
|
|
3128
3118
|
},
|
|
3129
3119
|
/**
|
|
3130
|
-
*
|
|
3120
|
+
* 缓存函数,将传入的函数fn的计算结果缓存,提高重复计算的效率
|
|
3131
3121
|
*
|
|
3132
|
-
* @param
|
|
3133
|
-
* @
|
|
3122
|
+
* @param fn 传入待缓存的函数
|
|
3123
|
+
* @returns 返回缓存后的函数
|
|
3134
3124
|
*/
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3125
|
+
memoize(fn) {
|
|
3126
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3127
|
+
return (...args) => {
|
|
3128
|
+
const argsString = JSON.stringify(args);
|
|
3129
|
+
if (cache.has(argsString)) {
|
|
3130
|
+
return cache.get(argsString);
|
|
3131
|
+
} else {
|
|
3132
|
+
const result = fn.apply(this, args);
|
|
3133
|
+
cache.set(argsString, result);
|
|
3134
|
+
return result;
|
|
3135
|
+
}
|
|
3136
|
+
};
|
|
3142
3137
|
},
|
|
3143
3138
|
/**
|
|
3144
|
-
*
|
|
3139
|
+
* 递归调用函数,以一定的频率和持续时间执行。
|
|
3145
3140
|
*
|
|
3146
|
-
* @param
|
|
3147
|
-
* @param
|
|
3141
|
+
* @param fun 要递归调用的函数。
|
|
3142
|
+
* @param frequency 每次调用函数之间的时间间隔,单位为毫秒,默认为500毫秒。
|
|
3143
|
+
* @param duration 函数递归调用的总时长,单位为毫秒,默认为5000毫秒。
|
|
3148
3144
|
*/
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3145
|
+
recurve(fun, frequency = 500, duration = 5e3) {
|
|
3146
|
+
let timer = 0;
|
|
3147
|
+
setTimeout(() => {
|
|
3148
|
+
timer++;
|
|
3149
|
+
if (timer < Math.floor(duration / frequency)) {
|
|
3150
|
+
fun.call(this);
|
|
3151
|
+
setTimeout(this.recurve.bind(this, fun, frequency, duration), frequency);
|
|
3152
|
+
}
|
|
3153
|
+
}, frequency);
|
|
3154
3154
|
},
|
|
3155
3155
|
/**
|
|
3156
|
-
*
|
|
3156
|
+
* 确保函数只被调用一次
|
|
3157
3157
|
*
|
|
3158
|
-
* @param
|
|
3159
|
-
* @returns
|
|
3158
|
+
* @param func 要被调用的函数
|
|
3159
|
+
* @returns 返回一个新的函数,该函数在被首次调用时会执行传入的函数,之后再次调用将不再执行
|
|
3160
3160
|
*/
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3161
|
+
once(func) {
|
|
3162
|
+
let called = false;
|
|
3163
|
+
return function(...args) {
|
|
3164
|
+
if (!called) {
|
|
3165
|
+
called = true;
|
|
3166
|
+
return func(...args);
|
|
3167
|
+
}
|
|
3168
|
+
};
|
|
3165
3169
|
}
|
|
3166
3170
|
};
|
|
3167
|
-
const
|
|
3171
|
+
const StringUtil = {
|
|
3168
3172
|
/**
|
|
3169
|
-
*
|
|
3173
|
+
* 转换字符串大小写
|
|
3170
3174
|
*
|
|
3171
|
-
* @param
|
|
3172
|
-
* @
|
|
3175
|
+
* @param str 待转换的字符串
|
|
3176
|
+
* @param type 转换类型,可选值为 0-4,默认为 4
|
|
3177
|
+
* 0:字母大小写反转
|
|
3178
|
+
* 1:首字母大写,其余小写
|
|
3179
|
+
* 2:首字母小写,其余大写
|
|
3180
|
+
* 3:全部大写
|
|
3181
|
+
* 4:全部小写
|
|
3182
|
+
* @returns 转换后的字符串
|
|
3173
3183
|
*/
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3184
|
+
changeCase(str, type) {
|
|
3185
|
+
type = type || CaseType.LOWER_CASE;
|
|
3186
|
+
switch (type) {
|
|
3187
|
+
case CaseType.REVERSE_CASE:
|
|
3188
|
+
return str.split("").map(function(word) {
|
|
3189
|
+
if (/[a-z]/.test(word)) {
|
|
3190
|
+
return word.toUpperCase();
|
|
3191
|
+
} else {
|
|
3192
|
+
return word.toLowerCase();
|
|
3193
|
+
}
|
|
3194
|
+
}).join("");
|
|
3195
|
+
case CaseType.UPPER_CAMEL_CASE:
|
|
3196
|
+
return str.replace(/\b\w+\b/g, function(word) {
|
|
3197
|
+
return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
|
|
3198
|
+
});
|
|
3199
|
+
case CaseType.LOWER_CAMEL_CASE:
|
|
3200
|
+
return str.replace(/\b\w+\b/g, function(word) {
|
|
3201
|
+
return word.substring(0, 1).toLowerCase() + word.substring(1).toUpperCase();
|
|
3202
|
+
});
|
|
3203
|
+
case CaseType.UPPER_CASE:
|
|
3204
|
+
return str.toUpperCase();
|
|
3205
|
+
case CaseType.LOWER_CASE:
|
|
3206
|
+
return str.toLowerCase();
|
|
3207
|
+
default:
|
|
3208
|
+
return str;
|
|
3180
3209
|
}
|
|
3181
|
-
const byteArray = new Uint8Array(byteNumbers);
|
|
3182
|
-
const blob = new Blob([byteArray], { type: mimeString });
|
|
3183
|
-
return blob;
|
|
3184
3210
|
},
|
|
3185
3211
|
/**
|
|
3186
|
-
*
|
|
3212
|
+
* 计算字符串的字节长度
|
|
3187
3213
|
*
|
|
3188
|
-
* @param
|
|
3189
|
-
* @
|
|
3190
|
-
* @returns 返回文件对象
|
|
3214
|
+
* @param str 需要计算字节长度的字符串
|
|
3215
|
+
* @returns 返回字符串的字节长度
|
|
3191
3216
|
*/
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
const mimeMatch = arr[0].match(/:(.*?);/);
|
|
3195
|
-
const mime = mimeMatch ? mimeMatch[1] : "image/png";
|
|
3196
|
-
const bstr = atob(arr[1]);
|
|
3197
|
-
const u8arr = new Uint8Array(bstr.length);
|
|
3198
|
-
for (let i = 0; i < bstr.length; i++) {
|
|
3199
|
-
u8arr[i] = bstr.charCodeAt(i);
|
|
3200
|
-
}
|
|
3201
|
-
const file = new File([u8arr], filename, { type: mime });
|
|
3202
|
-
return file;
|
|
3217
|
+
getByteLength(str) {
|
|
3218
|
+
return str.replace(/[\u0391-\uFFE5]/g, "aa").length;
|
|
3203
3219
|
},
|
|
3204
3220
|
/**
|
|
3205
|
-
*
|
|
3221
|
+
* 截取字符串中指定字节长度的子串
|
|
3206
3222
|
*
|
|
3207
|
-
* @param
|
|
3208
|
-
* @param
|
|
3223
|
+
* @param str 字符串对象,包含replace、length和substring方法
|
|
3224
|
+
* @param start 截取起始位置
|
|
3225
|
+
* @param n 截取字节长度
|
|
3226
|
+
* @returns 返回截取后的子串
|
|
3209
3227
|
*/
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3228
|
+
subStringByte(str, start, n) {
|
|
3229
|
+
var r = /[^\x00-\xff]/g;
|
|
3230
|
+
if (str.replace(r, "mm").length <= n) {
|
|
3231
|
+
return str;
|
|
3232
|
+
}
|
|
3233
|
+
var m = Math.floor(n / 2);
|
|
3234
|
+
for (var i = m; i < str.length; i++) {
|
|
3235
|
+
let sub = str.substring(start, i);
|
|
3236
|
+
if (sub.replace(r, "mm").length >= n) {
|
|
3237
|
+
return sub;
|
|
3218
3238
|
}
|
|
3219
|
-
} else if (typeof data == "string" && data.indexOf("http") === -1) {
|
|
3220
|
-
const blob = new Blob([data], { type: "text/json" });
|
|
3221
|
-
data = window.URL.createObjectURL(blob);
|
|
3222
3239
|
}
|
|
3223
|
-
|
|
3224
|
-
link.href = data;
|
|
3225
|
-
link.download = saveName || "";
|
|
3226
|
-
link.click();
|
|
3227
|
-
window.URL.revokeObjectURL(link.href);
|
|
3240
|
+
return str;
|
|
3228
3241
|
},
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3242
|
+
string2Bytes(str) {
|
|
3243
|
+
const bytes = [];
|
|
3244
|
+
let c;
|
|
3245
|
+
const len = str.length;
|
|
3246
|
+
for (let i = 0; i < len; i++) {
|
|
3247
|
+
c = str.charCodeAt(i);
|
|
3248
|
+
if (c >= 65536 && c <= 1114111) {
|
|
3249
|
+
bytes.push(c >> 18 & 7 | 240);
|
|
3250
|
+
bytes.push(c >> 12 & 63 | 128);
|
|
3251
|
+
bytes.push(c >> 6 & 63 | 128);
|
|
3252
|
+
bytes.push(c & 63 | 128);
|
|
3253
|
+
} else if (c >= 2048 && c <= 65535) {
|
|
3254
|
+
bytes.push(c >> 12 & 15 | 224);
|
|
3255
|
+
bytes.push(c >> 6 & 63 | 128);
|
|
3256
|
+
bytes.push(c & 63 | 128);
|
|
3257
|
+
} else if (c >= 128 && c <= 2047) {
|
|
3258
|
+
bytes.push(c >> 6 & 31 | 192);
|
|
3259
|
+
bytes.push(c & 63 | 128);
|
|
3260
|
+
} else {
|
|
3261
|
+
bytes.push(c & 255);
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
return new Uint8Array(bytes);
|
|
3265
|
+
},
|
|
3266
|
+
bytes2String(uint8arr) {
|
|
3267
|
+
if (typeof uint8arr === "string") {
|
|
3268
|
+
return uint8arr;
|
|
3269
|
+
}
|
|
3270
|
+
let str = "";
|
|
3271
|
+
const _arr = uint8arr;
|
|
3272
|
+
for (let i = 0; i < _arr.length; i++) {
|
|
3273
|
+
const one = _arr[i].toString(2), v = one.match(/^1+?(?=0)/);
|
|
3274
|
+
if (v && one.length == 8) {
|
|
3275
|
+
const bytesLength = v[0].length;
|
|
3276
|
+
let store = _arr[i].toString(2).slice(7 - bytesLength);
|
|
3277
|
+
for (let st = 1; st < bytesLength; st++) {
|
|
3278
|
+
store += _arr[st + i].toString(2).slice(2);
|
|
3279
|
+
}
|
|
3280
|
+
str += String.fromCharCode(parseInt(store, 2));
|
|
3281
|
+
i += bytesLength - 1;
|
|
3282
|
+
} else {
|
|
3283
|
+
str += String.fromCharCode(_arr[i]);
|
|
3267
3284
|
}
|
|
3268
|
-
xhr.send();
|
|
3269
|
-
});
|
|
3270
|
-
}
|
|
3271
|
-
};
|
|
3272
|
-
class MessageUtil {
|
|
3273
|
-
static resetWarned() {
|
|
3274
|
-
this.warned = {};
|
|
3275
|
-
}
|
|
3276
|
-
static changeVoice() {
|
|
3277
|
-
this.isMute = !!Number(!this.isMute);
|
|
3278
|
-
localStorage.setItem("mute", Number(this.isMute).toString());
|
|
3279
|
-
}
|
|
3280
|
-
static _call(method, message, options) {
|
|
3281
|
-
if (!this.warned[message]) {
|
|
3282
|
-
method(message, options);
|
|
3283
|
-
this.warned[message] = true;
|
|
3284
3285
|
}
|
|
3286
|
+
return str;
|
|
3285
3287
|
}
|
|
3288
|
+
};
|
|
3289
|
+
const UrlUtil = {
|
|
3286
3290
|
/**
|
|
3287
|
-
*
|
|
3291
|
+
* 将json对象转换为查询字符串
|
|
3288
3292
|
*
|
|
3289
|
-
* @param
|
|
3290
|
-
* @
|
|
3291
|
-
* @param options 配置选项,可选参数,包括语言、音量、语速和音高
|
|
3292
|
-
* @returns 无返回值
|
|
3293
|
+
* @param json 待转换的json对象
|
|
3294
|
+
* @returns 转换后的查询字符串
|
|
3293
3295
|
*/
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
this.speechSynthesis.speak(this.speechSynthesisUtterance);
|
|
3303
|
-
}
|
|
3304
|
-
static stop(e) {
|
|
3305
|
-
this.speechSynthesisUtterance.text = e;
|
|
3306
|
-
this.speechSynthesis.cancel();
|
|
3307
|
-
}
|
|
3308
|
-
static warning(message, options = {}) {
|
|
3309
|
-
if (process.env.NODE_ENV === "development" && console !== void 0) {
|
|
3310
|
-
console.warn(`Warning: %c${message}`, "color:#E6A23C;font-weight: bold;");
|
|
3311
|
-
}
|
|
3312
|
-
this.msg("warning", message, options);
|
|
3313
|
-
}
|
|
3314
|
-
static warningOnce(message, options = {}) {
|
|
3315
|
-
this._call(this.warning.bind(this), message, options);
|
|
3316
|
-
}
|
|
3317
|
-
static info(message, options = {}) {
|
|
3318
|
-
if (process.env.NODE_ENV === "development" && console !== void 0) {
|
|
3319
|
-
console.info(`Info: %c${message}`, "color:#909399;font-weight: bold;");
|
|
3320
|
-
}
|
|
3321
|
-
this.msg("info", message, options);
|
|
3322
|
-
}
|
|
3323
|
-
static infoOnce(message, options = {}) {
|
|
3324
|
-
this._call(this.info.bind(this), message, options);
|
|
3325
|
-
}
|
|
3326
|
-
static error(message, options = {}) {
|
|
3327
|
-
if (process.env.NODE_ENV === "development" && console !== void 0) {
|
|
3328
|
-
console.error(`Error: %c${message}`, "color:#F56C6C;font-weight: bold;");
|
|
3329
|
-
}
|
|
3330
|
-
this.msg("error", message, options);
|
|
3331
|
-
}
|
|
3332
|
-
static errorOnce(message, options = {}) {
|
|
3333
|
-
this._call(this.error.bind(this), message, options);
|
|
3334
|
-
}
|
|
3335
|
-
static success(message, options = {}) {
|
|
3336
|
-
if (process.env.NODE_ENV === "development" && console !== void 0) {
|
|
3337
|
-
console.log(`Success: %c${message}`, "color:#67C23A;font-weight: bold;");
|
|
3296
|
+
json2Query(json) {
|
|
3297
|
+
var tempArr = [];
|
|
3298
|
+
for (var i in json) {
|
|
3299
|
+
if (json.hasOwnProperty(i)) {
|
|
3300
|
+
var key = i;
|
|
3301
|
+
var value = json[i];
|
|
3302
|
+
tempArr.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
|
|
3303
|
+
}
|
|
3338
3304
|
}
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
this._call(this.success.bind(this), message, options);
|
|
3343
|
-
}
|
|
3344
|
-
}
|
|
3345
|
-
__publicField(MessageUtil, "warned", {});
|
|
3346
|
-
__publicField(MessageUtil, "isMute", !!Number(localStorage.getItem("mute")) || false);
|
|
3347
|
-
__publicField(MessageUtil, "speechSynthesis", window.speechSynthesis);
|
|
3348
|
-
__publicField(MessageUtil, "speechSynthesisUtterance", new SpeechSynthesisUtterance());
|
|
3349
|
-
const OptimizeUtil = {
|
|
3305
|
+
var urlParamsStr = tempArr.join("&");
|
|
3306
|
+
return urlParamsStr;
|
|
3307
|
+
},
|
|
3350
3308
|
/**
|
|
3351
|
-
*
|
|
3309
|
+
* 从 URL 中解析出查询参数和哈希参数,并以对象的形式返回
|
|
3352
3310
|
*
|
|
3353
|
-
* @param
|
|
3354
|
-
* @param
|
|
3355
|
-
* @
|
|
3356
|
-
* @returns 返回防抖后的函数。
|
|
3311
|
+
* @param href 需要解析的 URL 链接,默认为当前窗口的 URL
|
|
3312
|
+
* @param needDecode 是否需要解码参数值,默认为 true
|
|
3313
|
+
* @returns 返回一个包含解析后参数的对象,其中键为参数名,值为参数值
|
|
3357
3314
|
*/
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
let
|
|
3363
|
-
|
|
3364
|
-
const
|
|
3365
|
-
if (
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
timestamp = Date.now();
|
|
3376
|
-
const callNow = immediate && !timeout;
|
|
3377
|
-
if (!timeout) {
|
|
3378
|
-
timeout = setTimeout(later, wait);
|
|
3379
|
-
}
|
|
3380
|
-
if (callNow) {
|
|
3381
|
-
result = func.apply(this, args2);
|
|
3382
|
-
if (!timeout) {
|
|
3383
|
-
args2 = null;
|
|
3315
|
+
query2Json(href = window.location.href, needDecode = true) {
|
|
3316
|
+
const reg = /([^&=]+)=([\w\W]*?)(&|$|#)/g;
|
|
3317
|
+
const { search, hash } = new URL(href);
|
|
3318
|
+
const args = [search, hash];
|
|
3319
|
+
let obj = {};
|
|
3320
|
+
for (let i = 0; i < args.length; i++) {
|
|
3321
|
+
const str = args[i];
|
|
3322
|
+
if (str) {
|
|
3323
|
+
const s = str.replace(/#|\//g, "");
|
|
3324
|
+
const arr = s.split("?");
|
|
3325
|
+
if (arr.length > 1) {
|
|
3326
|
+
for (let j = 1; j < arr.length; j++) {
|
|
3327
|
+
let res;
|
|
3328
|
+
while (res = reg.exec(arr[j])) {
|
|
3329
|
+
obj[res[1]] = needDecode ? decodeURIComponent(res[2]) : res[2];
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3384
3332
|
}
|
|
3385
3333
|
}
|
|
3386
|
-
|
|
3387
|
-
|
|
3334
|
+
}
|
|
3335
|
+
return obj;
|
|
3336
|
+
}
|
|
3337
|
+
};
|
|
3338
|
+
const ValidateUtil = {
|
|
3339
|
+
isUrl(v) {
|
|
3340
|
+
return /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/.test(
|
|
3341
|
+
v
|
|
3342
|
+
);
|
|
3388
3343
|
},
|
|
3389
3344
|
/**
|
|
3390
|
-
* 节流函数,适用于像滚动事件、窗口resize事件这种需要限制调用频率的场景
|
|
3391
3345
|
*
|
|
3392
|
-
* @param
|
|
3393
|
-
* @
|
|
3394
|
-
* @param type 节流类型,1表示时间戳方式,2表示定时器方式
|
|
3395
|
-
* @returns 返回一个新的函数,该函数在节流控制下执行传入的函数
|
|
3346
|
+
* @param path 路径字符串
|
|
3347
|
+
* @returns 是否为外链
|
|
3396
3348
|
*/
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3349
|
+
isExternal(path) {
|
|
3350
|
+
const reg = /^(https?:|mailto:|tel:)/;
|
|
3351
|
+
return reg.test(path);
|
|
3352
|
+
},
|
|
3353
|
+
isPhone(v) {
|
|
3354
|
+
return /^1[3|4|5|6|7|8|9][0-9]{9}$/.test(v);
|
|
3355
|
+
},
|
|
3356
|
+
isTel(v) {
|
|
3357
|
+
return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(v);
|
|
3358
|
+
},
|
|
3359
|
+
/**
|
|
3360
|
+
* 判断是否是强密码,至少包含一个大写字母、一个小写字母、一个数字的组合、长度8-20位
|
|
3361
|
+
*
|
|
3362
|
+
* @param v 待检测的密码字符串
|
|
3363
|
+
* @returns 如果是是强密码,则返回 true;否则返回 false
|
|
3364
|
+
*/
|
|
3365
|
+
isStrongPwd(v) {
|
|
3366
|
+
return /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,20}$/.test(v);
|
|
3367
|
+
},
|
|
3368
|
+
isEmail(v) {
|
|
3369
|
+
return /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
|
|
3370
|
+
v
|
|
3371
|
+
);
|
|
3372
|
+
},
|
|
3373
|
+
isIP(v) {
|
|
3374
|
+
return /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/.test(
|
|
3375
|
+
v
|
|
3376
|
+
);
|
|
3377
|
+
},
|
|
3378
|
+
isEnglish(v) {
|
|
3379
|
+
return /^[a-zA-Z]+$/.test(v);
|
|
3380
|
+
},
|
|
3381
|
+
isChinese(v) {
|
|
3382
|
+
return /^[\u4E00-\u9FA5]+$/.test(v);
|
|
3383
|
+
},
|
|
3384
|
+
isHTML(v) {
|
|
3385
|
+
return /<("[^"]*"|'[^']*'|[^'">])*>/.test(v);
|
|
3386
|
+
},
|
|
3387
|
+
isXML(v) {
|
|
3388
|
+
return /^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$/.test(v);
|
|
3389
|
+
},
|
|
3390
|
+
isIDCard(v) {
|
|
3391
|
+
return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(v);
|
|
3392
|
+
}
|
|
3393
|
+
};
|
|
3394
|
+
class Cookie {
|
|
3395
|
+
static set(name, value, days = 30) {
|
|
3396
|
+
if (typeof name !== "string" || typeof days !== "number") {
|
|
3397
|
+
ExceptionUtil.throwException("Invalid arguments");
|
|
3398
|
+
}
|
|
3399
|
+
const exp = /* @__PURE__ */ new Date();
|
|
3400
|
+
exp.setTime(exp.getTime() + days * 24 * 60 * 60 * 1e3);
|
|
3401
|
+
document.cookie = `${name}=${encodeURIComponent(value)};expires=${exp.toUTCString()}`;
|
|
3402
|
+
}
|
|
3403
|
+
static remove(name) {
|
|
3404
|
+
var exp = /* @__PURE__ */ new Date();
|
|
3405
|
+
exp.setTime(exp.getTime() - 1);
|
|
3406
|
+
var cval = this.get(name);
|
|
3407
|
+
if (cval != null) {
|
|
3408
|
+
document.cookie = name + "=" + cval + ";expires=" + exp.toUTCString();
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
static get(name) {
|
|
3412
|
+
var arr = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)"));
|
|
3413
|
+
if (arr != null) {
|
|
3414
|
+
return arr[2];
|
|
3415
|
+
} else {
|
|
3416
|
+
return "";
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
class CanvasDrawer {
|
|
3421
|
+
constructor(el) {
|
|
3422
|
+
__publicField(this, "ctx");
|
|
3423
|
+
if (typeof el === "string") {
|
|
3424
|
+
el = document.querySelector("#" + el);
|
|
3425
|
+
if (!el) {
|
|
3426
|
+
throw new Error("Element not found");
|
|
3414
3427
|
}
|
|
3415
|
-
}
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
* @param fn 传入待缓存的函数
|
|
3421
|
-
* @returns 返回缓存后的函数
|
|
3422
|
-
*/
|
|
3423
|
-
memoize(fn) {
|
|
3424
|
-
const cache = /* @__PURE__ */ new Map();
|
|
3425
|
-
return (...args) => {
|
|
3426
|
-
const argsString = JSON.stringify(args);
|
|
3427
|
-
if (cache.has(argsString)) {
|
|
3428
|
-
return cache.get(argsString);
|
|
3428
|
+
}
|
|
3429
|
+
if (el instanceof HTMLElement) {
|
|
3430
|
+
const canvas = el;
|
|
3431
|
+
if (canvas.getContext) {
|
|
3432
|
+
this.ctx = canvas.getContext("2d");
|
|
3429
3433
|
} else {
|
|
3430
|
-
|
|
3431
|
-
cache.set(argsString, result);
|
|
3432
|
-
return result;
|
|
3434
|
+
throw new Error("getContext is not available on this element");
|
|
3433
3435
|
}
|
|
3434
|
-
}
|
|
3435
|
-
|
|
3436
|
+
} else {
|
|
3437
|
+
throw new Error("Element is not an HTMLElement");
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3436
3440
|
/**
|
|
3437
|
-
*
|
|
3441
|
+
* 绘制线条
|
|
3438
3442
|
*
|
|
3439
|
-
* @param
|
|
3440
|
-
* @param
|
|
3441
|
-
* @param
|
|
3443
|
+
* @param start 起始坐标点
|
|
3444
|
+
* @param end 终止坐标点
|
|
3445
|
+
* @param options 绘制选项,包括线条宽度和颜色
|
|
3446
|
+
* @throws 当画布上下文不存在时抛出错误
|
|
3442
3447
|
*/
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
}
|
|
3448
|
+
drawLine({ x: startX, y: startY }, { x: endX, y: endY }, options = {}) {
|
|
3449
|
+
this.ctx.beginPath();
|
|
3450
|
+
const width = options.width || 1;
|
|
3451
|
+
const color = options.color || "#000";
|
|
3452
|
+
this.ctx.lineWidth = width;
|
|
3453
|
+
this.ctx.strokeStyle = color;
|
|
3454
|
+
this.ctx.moveTo(startX, startY);
|
|
3455
|
+
this.ctx.lineTo(endX, endY);
|
|
3456
|
+
this.ctx.stroke();
|
|
3457
|
+
}
|
|
3453
3458
|
/**
|
|
3454
|
-
*
|
|
3459
|
+
* 绘制圆弧
|
|
3455
3460
|
*
|
|
3456
|
-
* @param
|
|
3457
|
-
* @
|
|
3461
|
+
* @param x 圆心x坐标
|
|
3462
|
+
* @param y 圆心y坐标
|
|
3463
|
+
* @param radius 半径
|
|
3464
|
+
* @param startAngle 起始角度(度)
|
|
3465
|
+
* @param endAngle 结束角度(度)
|
|
3466
|
+
* @param anticlockwise 是否逆时针绘制
|
|
3467
|
+
* @param isFill 是否填充
|
|
3468
|
+
* @param bgColor 背景颜色
|
|
3469
|
+
* @throws 当Canvas context为null或undefined时抛出错误
|
|
3458
3470
|
*/
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3471
|
+
drawArc({ x, y }, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
|
|
3472
|
+
if (isFill) {
|
|
3473
|
+
this.ctx.fillStyle = bgColor;
|
|
3474
|
+
this.ctx.beginPath();
|
|
3475
|
+
this.ctx.arc(x, y, radius, MathUtil.deg2Rad(startAngle), MathUtil.deg2Rad(endAngle), anticlockwise);
|
|
3476
|
+
this.ctx.fill();
|
|
3477
|
+
} else {
|
|
3478
|
+
this.ctx.strokeStyle = bgColor;
|
|
3479
|
+
this.ctx.beginPath();
|
|
3480
|
+
this.ctx.arc(x, y, radius, MathUtil.deg2Rad(startAngle), MathUtil.deg2Rad(endAngle), anticlockwise);
|
|
3481
|
+
this.ctx.stroke();
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
drawImage({ x, y }, src) {
|
|
3485
|
+
const img = new Image();
|
|
3486
|
+
img.src = src;
|
|
3487
|
+
img.onload = () => {
|
|
3488
|
+
const width = img.width;
|
|
3489
|
+
const height = img.height;
|
|
3490
|
+
if (!this.ctx) {
|
|
3491
|
+
throw new Error("Canvas context is null");
|
|
3465
3492
|
}
|
|
3493
|
+
this.ctx.drawImage(img, x, y, -width / 2, -height / 2);
|
|
3466
3494
|
};
|
|
3467
3495
|
}
|
|
3468
|
-
}
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
* 3:全部大写
|
|
3479
|
-
* 4:全部小写
|
|
3480
|
-
* @returns 转换后的字符串
|
|
3481
|
-
*/
|
|
3482
|
-
changeCase(str, type) {
|
|
3483
|
-
type = type || CaseType.LOWER_CASE;
|
|
3484
|
-
switch (type) {
|
|
3485
|
-
case CaseType.REVERSE_CASE:
|
|
3486
|
-
return str.split("").map(function(word) {
|
|
3487
|
-
if (/[a-z]/.test(word)) {
|
|
3488
|
-
return word.toUpperCase();
|
|
3489
|
-
} else {
|
|
3490
|
-
return word.toLowerCase();
|
|
3491
|
-
}
|
|
3492
|
-
}).join("");
|
|
3493
|
-
case CaseType.UPPER_CAMEL_CASE:
|
|
3494
|
-
return str.replace(/\b\w+\b/g, function(word) {
|
|
3495
|
-
return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
|
|
3496
|
-
});
|
|
3497
|
-
case CaseType.LOWER_CAMEL_CASE:
|
|
3498
|
-
return str.replace(/\b\w+\b/g, function(word) {
|
|
3499
|
-
return word.substring(0, 1).toLowerCase() + word.substring(1).toUpperCase();
|
|
3500
|
-
});
|
|
3501
|
-
case CaseType.UPPER_CASE:
|
|
3502
|
-
return str.toUpperCase();
|
|
3503
|
-
case CaseType.LOWER_CASE:
|
|
3504
|
-
return str.toLowerCase();
|
|
3505
|
-
default:
|
|
3506
|
-
return str;
|
|
3496
|
+
drawText({ x, y }, text, options) {
|
|
3497
|
+
this.ctx.font = options.font || "10px sans-serif";
|
|
3498
|
+
this.ctx.strokeStyle = options.color || "#000";
|
|
3499
|
+
this.ctx.lineWidth = 2;
|
|
3500
|
+
this.ctx.strokeText(text, x, y);
|
|
3501
|
+
}
|
|
3502
|
+
static createCanvas(width = 1, height = 1) {
|
|
3503
|
+
const canvas = document.createElement("canvas");
|
|
3504
|
+
if (width) {
|
|
3505
|
+
canvas.width = width;
|
|
3507
3506
|
}
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
* 计算字符串的字节长度
|
|
3511
|
-
*
|
|
3512
|
-
* @param str 需要计算字节长度的字符串
|
|
3513
|
-
* @returns 返回字符串的字节长度
|
|
3514
|
-
*/
|
|
3515
|
-
getByteLength(str) {
|
|
3516
|
-
return str.replace(/[\u0391-\uFFE5]/g, "aa").length;
|
|
3517
|
-
},
|
|
3518
|
-
/**
|
|
3519
|
-
* 截取字符串中指定字节长度的子串
|
|
3520
|
-
*
|
|
3521
|
-
* @param str 字符串对象,包含replace、length和substring方法
|
|
3522
|
-
* @param start 截取起始位置
|
|
3523
|
-
* @param n 截取字节长度
|
|
3524
|
-
* @returns 返回截取后的子串
|
|
3525
|
-
*/
|
|
3526
|
-
subStringByte(str, start, n) {
|
|
3527
|
-
var r = /[^\x00-\xff]/g;
|
|
3528
|
-
if (str.replace(r, "mm").length <= n) {
|
|
3529
|
-
return str;
|
|
3507
|
+
if (height) {
|
|
3508
|
+
canvas.height = height;
|
|
3530
3509
|
}
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3510
|
+
return canvas;
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
class EventDispatcher {
|
|
3514
|
+
constructor() {
|
|
3515
|
+
__publicField(this, "_listeners", {});
|
|
3516
|
+
__publicField(this, "_mutex", {});
|
|
3517
|
+
__publicField(this, "_context");
|
|
3518
|
+
}
|
|
3519
|
+
addEventListener(type, listener, context, mutexStatus) {
|
|
3520
|
+
this._context = context;
|
|
3521
|
+
const mutex = this._mutex;
|
|
3522
|
+
const listeners = this._listeners;
|
|
3523
|
+
if (listeners[type] === void 0) {
|
|
3524
|
+
listeners[type] = [];
|
|
3537
3525
|
}
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
const bytes = [];
|
|
3542
|
-
let c;
|
|
3543
|
-
const len = str.length;
|
|
3544
|
-
for (let i = 0; i < len; i++) {
|
|
3545
|
-
c = str.charCodeAt(i);
|
|
3546
|
-
if (c >= 65536 && c <= 1114111) {
|
|
3547
|
-
bytes.push(c >> 18 & 7 | 240);
|
|
3548
|
-
bytes.push(c >> 12 & 63 | 128);
|
|
3549
|
-
bytes.push(c >> 6 & 63 | 128);
|
|
3550
|
-
bytes.push(c & 63 | 128);
|
|
3551
|
-
} else if (c >= 2048 && c <= 65535) {
|
|
3552
|
-
bytes.push(c >> 12 & 15 | 224);
|
|
3553
|
-
bytes.push(c >> 6 & 63 | 128);
|
|
3554
|
-
bytes.push(c & 63 | 128);
|
|
3555
|
-
} else if (c >= 128 && c <= 2047) {
|
|
3556
|
-
bytes.push(c >> 6 & 31 | 192);
|
|
3557
|
-
bytes.push(c & 63 | 128);
|
|
3558
|
-
} else {
|
|
3559
|
-
bytes.push(c & 255);
|
|
3526
|
+
if (listeners[type].indexOf(listener) === -1) {
|
|
3527
|
+
if (mutexStatus) {
|
|
3528
|
+
mutex[type] = listener;
|
|
3560
3529
|
}
|
|
3530
|
+
listeners[type].push(listener);
|
|
3561
3531
|
}
|
|
3562
|
-
return
|
|
3563
|
-
}
|
|
3564
|
-
|
|
3565
|
-
if (
|
|
3566
|
-
|
|
3532
|
+
return this;
|
|
3533
|
+
}
|
|
3534
|
+
hasEventListener(type, listener) {
|
|
3535
|
+
if (this._listeners === null || this._listeners === void 0) return false;
|
|
3536
|
+
const listeners = this._listeners;
|
|
3537
|
+
return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
|
|
3538
|
+
}
|
|
3539
|
+
removeEventListener(type, listener) {
|
|
3540
|
+
if (this._listeners === void 0) return;
|
|
3541
|
+
const listeners = this._listeners;
|
|
3542
|
+
const listenerArray = listeners[type];
|
|
3543
|
+
if (this._mutex[type] === listener) {
|
|
3544
|
+
this._mutex[type] = null;
|
|
3545
|
+
}
|
|
3546
|
+
if (listenerArray !== void 0) {
|
|
3547
|
+
const index = listenerArray.map((d) => d.toString()).indexOf(listener.toString());
|
|
3548
|
+
if (index !== -1) {
|
|
3549
|
+
listenerArray.splice(index, 1);
|
|
3550
|
+
}
|
|
3567
3551
|
}
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3552
|
+
}
|
|
3553
|
+
dispatchEvent(event) {
|
|
3554
|
+
if (this._listeners === void 0) return;
|
|
3555
|
+
const listeners = this._listeners;
|
|
3556
|
+
const listenerArray = listeners[event.type];
|
|
3557
|
+
if (listenerArray !== void 0) {
|
|
3558
|
+
event.target = this;
|
|
3559
|
+
const array = listenerArray.slice(0);
|
|
3560
|
+
if (this._mutex[event.type] !== void 0) {
|
|
3561
|
+
const find = array.find((item) => item === this._mutex[event.type]);
|
|
3562
|
+
if (find) {
|
|
3563
|
+
find.call(this._context || this, event);
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
for (let i = 0, l = array.length; i < l; i++) {
|
|
3568
|
+
const item = array[i];
|
|
3569
|
+
if (typeof item === "function") {
|
|
3570
|
+
item.call(this._context || this, event);
|
|
3577
3571
|
}
|
|
3578
|
-
str += String.fromCharCode(parseInt(store, 2));
|
|
3579
|
-
i += bytesLength - 1;
|
|
3580
|
-
} else {
|
|
3581
|
-
str += String.fromCharCode(_arr[i]);
|
|
3582
3572
|
}
|
|
3583
3573
|
}
|
|
3584
|
-
return str;
|
|
3585
3574
|
}
|
|
3586
|
-
|
|
3587
|
-
|
|
3575
|
+
removeAllListener() {
|
|
3576
|
+
this._mutex = {};
|
|
3577
|
+
for (const key in this._listeners) {
|
|
3578
|
+
this._listeners[key] = [];
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
on(type, listener, context, mutexStatus) {
|
|
3582
|
+
return this.addEventListener.call(this, type, listener, context, mutexStatus);
|
|
3583
|
+
}
|
|
3584
|
+
off(type, listener) {
|
|
3585
|
+
return this.removeEventListener.call(this, type, listener);
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
class HashMap extends Map {
|
|
3589
|
+
isEmpty() {
|
|
3590
|
+
return this.size === 0;
|
|
3591
|
+
}
|
|
3592
|
+
_values() {
|
|
3593
|
+
return Array.from(this.values());
|
|
3594
|
+
}
|
|
3595
|
+
_keys() {
|
|
3596
|
+
return Array.from(this.keys());
|
|
3597
|
+
}
|
|
3598
|
+
_entries() {
|
|
3599
|
+
return Array.from(this.entries());
|
|
3600
|
+
}
|
|
3588
3601
|
/**
|
|
3589
|
-
*
|
|
3602
|
+
* 从键值对数组创建一个HashMap对象
|
|
3590
3603
|
*
|
|
3591
|
-
* @param
|
|
3592
|
-
* @returns
|
|
3604
|
+
* @param array 键值对数组,默认为空数组
|
|
3605
|
+
* @returns 返回一个新的HashMap对象
|
|
3593
3606
|
*/
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
if (
|
|
3598
|
-
|
|
3599
|
-
var value = json[i];
|
|
3600
|
-
tempArr.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
|
|
3607
|
+
static fromEntries(array = []) {
|
|
3608
|
+
const hashMap = new HashMap();
|
|
3609
|
+
array.forEach((element) => {
|
|
3610
|
+
if (Array.isArray(element) && element.length === 2) {
|
|
3611
|
+
hashMap.set(element[0], element[1]);
|
|
3601
3612
|
}
|
|
3602
|
-
}
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
},
|
|
3613
|
+
});
|
|
3614
|
+
return hashMap;
|
|
3615
|
+
}
|
|
3606
3616
|
/**
|
|
3607
|
-
* 从
|
|
3617
|
+
* 从JSON字符串创建HashMap实例
|
|
3608
3618
|
*
|
|
3609
|
-
* @param
|
|
3610
|
-
* @
|
|
3611
|
-
* @returns 返回一个包含解析后参数的对象,其中键为参数名,值为参数值
|
|
3619
|
+
* @param str JSON字符串
|
|
3620
|
+
* @returns HashMap实例
|
|
3612
3621
|
*/
|
|
3613
|
-
|
|
3614
|
-
const
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3622
|
+
static fromJson(str) {
|
|
3623
|
+
const json = ObjectUtil.parse(str);
|
|
3624
|
+
return new HashMap(Object.entries(json));
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
class WebSocketClient extends EventDispatcher {
|
|
3628
|
+
constructor(url = "ws://127.0.0.1:10088") {
|
|
3629
|
+
super();
|
|
3630
|
+
__publicField(this, "maxCheckTimes", 10);
|
|
3631
|
+
__publicField(this, "url");
|
|
3632
|
+
__publicField(this, "checkTimes", 0);
|
|
3633
|
+
__publicField(this, "connectStatus", false);
|
|
3634
|
+
__publicField(this, "client", null);
|
|
3635
|
+
this.maxCheckTimes = 10;
|
|
3636
|
+
this.url = url;
|
|
3637
|
+
this.checkTimes = 0;
|
|
3638
|
+
this.connect();
|
|
3639
|
+
this.connCheckStatus(this.maxCheckTimes);
|
|
3640
|
+
}
|
|
3641
|
+
connect() {
|
|
3642
|
+
this.disconnect();
|
|
3643
|
+
if (this.url) {
|
|
3644
|
+
try {
|
|
3645
|
+
const self = this;
|
|
3646
|
+
console.info("创建ws连接>>>" + this.url);
|
|
3647
|
+
this.client = new WebSocket(this.url);
|
|
3648
|
+
if (this.client) {
|
|
3649
|
+
this.client.onopen = function(message) {
|
|
3650
|
+
self.dispatchEvent({
|
|
3651
|
+
type: EventType.WEB_SOCKET_CONNECT,
|
|
3652
|
+
message
|
|
3653
|
+
});
|
|
3654
|
+
};
|
|
3655
|
+
this.client.onmessage = function(message) {
|
|
3656
|
+
self.connectStatus = true;
|
|
3657
|
+
self.dispatchEvent({
|
|
3658
|
+
type: EventType.WEB_SOCKET_MESSAGE,
|
|
3659
|
+
message
|
|
3660
|
+
});
|
|
3661
|
+
};
|
|
3662
|
+
this.client.onclose = function(message) {
|
|
3663
|
+
self.dispatchEvent({
|
|
3664
|
+
type: EventType.WEB_SOCKET_CLOSE,
|
|
3665
|
+
message
|
|
3666
|
+
});
|
|
3667
|
+
};
|
|
3668
|
+
if (this.checkTimes === this.maxCheckTimes) {
|
|
3669
|
+
this.client.onerror = function(message) {
|
|
3670
|
+
self.dispatchEvent({
|
|
3671
|
+
type: EventType.WEB_SOCKET_ERROR,
|
|
3672
|
+
message
|
|
3673
|
+
});
|
|
3674
|
+
};
|
|
3629
3675
|
}
|
|
3630
3676
|
}
|
|
3677
|
+
} catch (ex) {
|
|
3678
|
+
console.error("创建ws连接失败" + this.url + ":" + ex);
|
|
3631
3679
|
}
|
|
3632
3680
|
}
|
|
3633
|
-
return obj;
|
|
3634
3681
|
}
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
isTel(v) {
|
|
3646
|
-
return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(v);
|
|
3647
|
-
},
|
|
3648
|
-
/**
|
|
3649
|
-
* 判断是否是强密码,至少包含一个大写字母、一个小写字母、一个数字的组合、长度8-20位
|
|
3650
|
-
*
|
|
3651
|
-
* @param v 待检测的密码字符串
|
|
3652
|
-
* @returns 如果是是强密码,则返回 true;否则返回 false
|
|
3653
|
-
*/
|
|
3654
|
-
isStrongPwd(v) {
|
|
3655
|
-
return /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,20}$/.test(v);
|
|
3656
|
-
},
|
|
3657
|
-
isEmail(v) {
|
|
3658
|
-
return /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
|
|
3659
|
-
v
|
|
3660
|
-
);
|
|
3661
|
-
},
|
|
3662
|
-
isIP(v) {
|
|
3663
|
-
return /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/.test(
|
|
3664
|
-
v
|
|
3665
|
-
);
|
|
3666
|
-
},
|
|
3667
|
-
isEnglish(v) {
|
|
3668
|
-
return /^[a-zA-Z]+$/.test(v);
|
|
3669
|
-
},
|
|
3670
|
-
isChinese(v) {
|
|
3671
|
-
return /^[\u4E00-\u9FA5]+$/.test(v);
|
|
3672
|
-
},
|
|
3673
|
-
isHTML(v) {
|
|
3674
|
-
return /<("[^"]*"|'[^']*'|[^'">])*>/.test(v);
|
|
3675
|
-
},
|
|
3676
|
-
isXML(v) {
|
|
3677
|
-
return /^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$/.test(v);
|
|
3678
|
-
},
|
|
3679
|
-
isIDCard(v) {
|
|
3680
|
-
return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(v);
|
|
3682
|
+
disconnect() {
|
|
3683
|
+
if (this.client) {
|
|
3684
|
+
try {
|
|
3685
|
+
console.log("ws断开连接" + this.url);
|
|
3686
|
+
this.client.close();
|
|
3687
|
+
this.client = null;
|
|
3688
|
+
} catch (ex) {
|
|
3689
|
+
this.client = null;
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3681
3692
|
}
|
|
3682
|
-
|
|
3693
|
+
connCheckStatus(times) {
|
|
3694
|
+
if (this.checkTimes > times) return;
|
|
3695
|
+
setTimeout(() => {
|
|
3696
|
+
this.checkTimes++;
|
|
3697
|
+
if (this.state !== WebSocket.CONNECTING && this.state !== WebSocket.OPEN) {
|
|
3698
|
+
this.connect();
|
|
3699
|
+
}
|
|
3700
|
+
this.connCheckStatus(times);
|
|
3701
|
+
}, 2e3);
|
|
3702
|
+
}
|
|
3703
|
+
send(message) {
|
|
3704
|
+
if (this.client && this.state === WebSocket.OPEN) {
|
|
3705
|
+
this.client.send(message);
|
|
3706
|
+
return true;
|
|
3707
|
+
}
|
|
3708
|
+
console.error(this.url + "消息发送失败:" + message);
|
|
3709
|
+
return this;
|
|
3710
|
+
}
|
|
3711
|
+
heartbeat() {
|
|
3712
|
+
setTimeout(() => {
|
|
3713
|
+
if (this.state === WebSocket.OPEN) {
|
|
3714
|
+
this.send("HeartBeat");
|
|
3715
|
+
}
|
|
3716
|
+
console.log("HeartBeat," + this.url);
|
|
3717
|
+
setTimeout(this.heartbeat, 3e4);
|
|
3718
|
+
}, 1e3);
|
|
3719
|
+
}
|
|
3720
|
+
get state() {
|
|
3721
|
+
var _a2;
|
|
3722
|
+
return (_a2 = this.client) == null ? void 0 : _a2.readyState;
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3683
3725
|
const _MqttClient = class _MqttClient extends EventDispatcher {
|
|
3684
3726
|
constructor(url = `ws://${window.document.domain}:20007/mqtt`, config = {}) {
|
|
3685
3727
|
super();
|
|
@@ -3690,13 +3732,14 @@ const _MqttClient = class _MqttClient extends EventDispatcher {
|
|
|
3690
3732
|
__publicField(this, "client");
|
|
3691
3733
|
__publicField(this, "topics");
|
|
3692
3734
|
__publicField(this, "_timer");
|
|
3693
|
-
this.context =
|
|
3735
|
+
this.context = ObjectUtil.assign(_MqttClient.defaultContext, config);
|
|
3694
3736
|
this.options = {
|
|
3695
3737
|
connectTimeout: this.context.MQTT_TIMEOUTM,
|
|
3696
3738
|
clientId: Util.guid(),
|
|
3697
3739
|
username: this.context.MQTT_USERNAME,
|
|
3698
3740
|
password: this.context.MQTT_PASSWORD,
|
|
3699
|
-
clean: true
|
|
3741
|
+
clean: true,
|
|
3742
|
+
rejectUnauthorized: this.context.REJECT_UNAUTHORIZED
|
|
3700
3743
|
};
|
|
3701
3744
|
this.url = url;
|
|
3702
3745
|
this.client = connect(this.url, this.options);
|
|
@@ -3796,7 +3839,8 @@ const _MqttClient = class _MqttClient extends EventDispatcher {
|
|
|
3796
3839
|
*/
|
|
3797
3840
|
__publicField(_MqttClient, "defaultContext", {
|
|
3798
3841
|
MQTT_TIMEOUTM: 2e3,
|
|
3799
|
-
MQTT_MAX_RETRY: 3
|
|
3842
|
+
MQTT_MAX_RETRY: 3,
|
|
3843
|
+
REJECT_UNAUTHORIZED: true
|
|
3800
3844
|
});
|
|
3801
3845
|
let MqttClient = _MqttClient;
|
|
3802
3846
|
const _Storage = class _Storage {
|
|
@@ -4081,7 +4125,7 @@ class WebGL {
|
|
|
4081
4125
|
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
|
4082
4126
|
if (!compiled) {
|
|
4083
4127
|
var error = gl.getShaderInfoLog(shader);
|
|
4084
|
-
|
|
4128
|
+
ExceptionUtil.throwException("Failed to compile shader: " + error);
|
|
4085
4129
|
gl.deleteShader(shader);
|
|
4086
4130
|
return null;
|
|
4087
4131
|
}
|