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