gis-common 5.0.1 → 5.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -137,32 +137,64 @@ class AudioPlayer {
137
137
  this.audio.muted = val;
138
138
  }
139
139
  }
140
- class Cookie {
141
- static set(name, value, days = 30) {
142
- if (typeof name !== "string" || typeof days !== "number") {
143
- throw new Error("Invalid arguments");
144
- }
145
- const exp = /* @__PURE__ */ new Date();
146
- exp.setTime(exp.getTime() + days * 24 * 60 * 60 * 1e3);
147
- document.cookie = `${name}=${encodeURIComponent(value)};expires=${exp.toUTCString()}`;
148
- }
149
- static remove(name) {
150
- var exp = /* @__PURE__ */ new Date();
151
- exp.setTime(exp.getTime() - 1);
152
- var cval = this.get(name);
153
- if (cval != null) {
154
- document.cookie = name + "=" + cval + ";expires=" + exp.toUTCString();
155
- }
156
- }
157
- static get(name) {
158
- var arr = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)"));
159
- if (arr != null) {
160
- return arr[2];
161
- } else {
162
- return "";
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
- console.error(`Style loading failed for URL: ${url}`);
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
- throw new Error(`${ErrorType.DATA_ERROR_JSON}: ${match}`);
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 MathUtil = {
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 {*} degrees
475
- * @returns {*}
498
+ * @param dest 目标对象,用于接收复制的属性。
499
+ * @param args 一个或多个源对象,用于提供要复制的属性。
500
+ * @returns 返回目标对象,包含所有复制的属性。
476
501
  */
477
- deg2Rad(degrees) {
478
- return degrees * this.DEG2RAD;
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 {*} radians
484
- * @returns {*}
520
+ * @param target 目标对象
521
+ * @param sources 待合并的对象列表
522
+ * @returns 合并后的对象
485
523
  */
486
- rad2Deg(radians) {
487
- return radians * this.RAD2DEG;
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
- round(value, n = 2) {
490
- return Util.isNumber(value) ? Math.round(Number(value) * Math.pow(10, n)) / Math.pow(10, n) : 0;
555
+ clone(obj) {
556
+ return JSON.parse(JSON.stringify(obj));
491
557
  },
492
558
  /**
493
- * 将数值限制在指定范围内
559
+ * 深拷贝一个对象到另一个对象
494
560
  *
495
- * @param val 需要限制的数值
496
- * @param min 最小值
497
- * @param max 最大值
498
- * @returns 返回限制后的数值
561
+ * @param destObj 目标对象,深拷贝后的对象将存储在该对象中
562
+ * @param srcObj 源对象,深拷贝的来源对象
563
+ * @returns 深拷贝后的目标对象
499
564
  */
500
- clamp(val, min, max) {
501
- return Math.max(min, Math.min(max, val));
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
- return null;
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
- console.error("Failed to copy image to clipboard:", error);
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;
@@ -1562,290 +944,661 @@ const AjaxUtil = {
1562
944
  cb(err, data);
1563
945
  }
1564
946
  };
1565
- if (options && options["jsonp"]) {
1566
- return this.jsonp(url, callback);
1567
- }
1568
- return this.get(url, options, callback);
947
+ if (options && options["jsonp"]) {
948
+ return this.jsonp(url, callback);
949
+ }
950
+ return this.get(url, options, callback);
951
+ }
952
+ };
953
+ const MathUtil = {
954
+ DEG2RAD: Math.PI / 180,
955
+ RAD2DEG: 180 / Math.PI,
956
+ randInt(low, high) {
957
+ return low + Math.floor(Math.random() * (high - low + 1));
958
+ },
959
+ randFloat(low, high) {
960
+ return low + Math.random() * (high - low);
961
+ },
962
+ /**
963
+ * 角度转弧度
964
+ *
965
+ * @param {*} degrees
966
+ * @returns {*}
967
+ */
968
+ deg2Rad(degrees) {
969
+ return degrees * this.DEG2RAD;
970
+ },
971
+ /**
972
+ * 弧度转角度
973
+ *
974
+ * @param {*} radians
975
+ * @returns {*}
976
+ */
977
+ rad2Deg(radians) {
978
+ return radians * this.RAD2DEG;
979
+ },
980
+ round(value, n = 2) {
981
+ return Util.isNumber(value) ? Math.round(Number(value) * Math.pow(10, n)) / Math.pow(10, n) : 0;
982
+ },
983
+ /**
984
+ * 将数值限制在指定范围内
985
+ *
986
+ * @param val 需要限制的数值
987
+ * @param min 最小值
988
+ * @param max 最大值
989
+ * @returns 返回限制后的数值
990
+ */
991
+ clamp(val, min, max) {
992
+ return Math.max(min, Math.min(max, val));
993
+ }
994
+ };
995
+ class GeoUtil {
996
+ // 地球赤道半径
997
+ /**
998
+ * 判断给定的经纬度是否合法
999
+ *
1000
+ * @param lng 经度值
1001
+ * @param lat 纬度值
1002
+ * @returns 如果经纬度合法,返回true;否则返回false
1003
+ */
1004
+ static isLnglat(lng, lat) {
1005
+ return Util.isNumber(lng) && Util.isNumber(lat) && !!(+lat >= -90 && +lat <= 90 && +lng >= -180 && +lng <= 180);
1006
+ }
1007
+ /**
1008
+ * 计算两哥平面坐标点间的距离
1009
+ *
1010
+ * @param p1 坐标点1,包含x和y属性
1011
+ * @param p2 坐标点2,包含x和y属性
1012
+ * @returns 返回两点间的欧几里得距离
1013
+ */
1014
+ static distance(p1, p2) {
1015
+ return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
1016
+ }
1017
+ /**
1018
+ * 计算两个经纬度点之间的距离
1019
+ *
1020
+ * @param A 经纬度点A,包含lng(经度)和lat(纬度)两个属性
1021
+ * @param B 经纬度点B,包含lng(经度)和lat(纬度)两个属性
1022
+ * @returns 返回两点之间的距离,单位为米
1023
+ */
1024
+ static distanceByPoints(A, B) {
1025
+ const { lng: lngA, lat: latA } = A;
1026
+ const { lng: lngB, lat: latB } = B;
1027
+ const earthR = 6371e3;
1028
+ const x = Math.cos(latA * Math.PI / 180) * Math.cos(latB * Math.PI / 180) * Math.cos((lngA - lngB) * Math.PI / 180);
1029
+ const y = Math.sin(latA * Math.PI / 180) * Math.sin(latB * Math.PI / 180);
1030
+ let s = x + y;
1031
+ if (s > 1) s = 1;
1032
+ if (s < -1) s = -1;
1033
+ const alpha = Math.acos(s);
1034
+ const distance = alpha * earthR;
1035
+ return distance;
1036
+ }
1037
+ /**
1038
+ * 格式化经纬度为度分秒格式
1039
+ *
1040
+ * @param lng 经度
1041
+ * @param lat 纬度
1042
+ * @returns 返回格式化后的经纬度字符串,格式为:经度度分秒,纬度度分秒
1043
+ */
1044
+ static formatLnglat(lng, lat) {
1045
+ let res = "";
1046
+ function formatDegreeToDMS(valueInDegrees) {
1047
+ const degree = Math.floor(valueInDegrees);
1048
+ const minutes = Math.floor((valueInDegrees - degree) * 60);
1049
+ const seconds = (valueInDegrees - degree) * 3600 - minutes * 60;
1050
+ return `${degree}°${minutes}′${seconds.toFixed(2)}″`;
1051
+ }
1052
+ if (this.isLnglat(lng, lat)) {
1053
+ res = formatDegreeToDMS(lng) + "," + formatDegreeToDMS(lat);
1054
+ } else if (!isNaN(lng)) {
1055
+ res = formatDegreeToDMS(lng);
1056
+ } else if (!isNaN(lat)) {
1057
+ res = formatDegreeToDMS(lat);
1058
+ }
1059
+ return res;
1060
+ }
1061
+ /**
1062
+ * 将经纬度字符串转换为度
1063
+ *
1064
+ * @param lng 经度字符串
1065
+ * @param lat 纬度字符串
1066
+ * @returns 转换后的经纬度对象
1067
+ */
1068
+ static transformLnglat(lng, lat) {
1069
+ function dms2deg(dmsString) {
1070
+ const isNegative = /[sw]/i.test(dmsString);
1071
+ let factor = isNegative ? -1 : 1;
1072
+ const numericParts = dmsString.match(/[\d.]+/g) || [];
1073
+ let degrees = 0;
1074
+ for (let i = 0; i < numericParts.length; i++) {
1075
+ degrees += parseFloat(numericParts[i]) / factor;
1076
+ factor *= 60;
1077
+ }
1078
+ return degrees;
1079
+ }
1080
+ if (lng && lat) {
1081
+ return {
1082
+ lng: dms2deg(lng),
1083
+ lat: dms2deg(lat)
1084
+ };
1085
+ }
1086
+ }
1087
+ /**
1088
+ * 射线法判断点是否在多边形内
1089
+ *
1090
+ * @param p 点对象,包含x和y属性
1091
+ * @param poly 多边形顶点数组,可以是字符串数组或对象数组
1092
+ * @returns 返回字符串,表示点相对于多边形的位置:'in'表示在多边形内,'out'表示在多边形外,'on'表示在多边形上
1093
+ */
1094
+ static rayCasting(p, poly) {
1095
+ var px = p.x, py = p.y, flag = false;
1096
+ for (var i = 0, l = poly.length, j = l - 1; i < l; j = i, i++) {
1097
+ var sx = poly[i].x, sy = poly[i].y, tx = poly[j].x, ty = poly[j].y;
1098
+ if (sx === px && sy === py || tx === px && ty === py) {
1099
+ return "on";
1100
+ }
1101
+ if (sy < py && ty >= py || sy >= py && ty < py) {
1102
+ var x = sx + (py - sy) * (tx - sx) / (ty - sy);
1103
+ if (x === px) {
1104
+ return "on";
1105
+ }
1106
+ if (x > px) {
1107
+ flag = !flag;
1108
+ }
1109
+ }
1110
+ }
1111
+ return flag ? "in" : "out";
1112
+ }
1113
+ /**
1114
+ * 旋转点
1115
+ *
1116
+ * @param p1 旋转前点坐标
1117
+ * @param p2 旋转中心坐标
1118
+ * @param θ 旋转角度(顺时针旋转为正)
1119
+ * @returns 旋转后点坐标
1120
+ */
1121
+ static rotatePoint(p1, p2, θ) {
1122
+ const x = (p1.x - p2.x) * Math.cos(Math.PI / 180 * -θ) - (p1.y - p2.y) * Math.sin(Math.PI / 180 * -θ) + p2.x;
1123
+ const y = (p1.x - p2.x) * Math.sin(Math.PI / 180 * -θ) + (p1.y - p2.y) * Math.cos(Math.PI / 180 * -θ) + p2.y;
1124
+ return { x, y };
1125
+ }
1126
+ /**
1127
+ * 根据两个平面坐标点计算方位角和距离
1128
+ *
1129
+ * @param p1 第一个点的坐标对象
1130
+ * @param p2 第二个点的坐标对象
1131
+ * @returns 返回一个对象,包含angle和distance属性,分别表示两点之间的角度(以度为单位,取值范围为0~359)和距离
1132
+ */
1133
+ static calcBearAndDis(p1, p2) {
1134
+ const { x: x1, y: y1 } = p1;
1135
+ const { x: x2, y: y2 } = p2;
1136
+ const dx = x2 - x1;
1137
+ const dy = y2 - y1;
1138
+ const distance = Math.sqrt(dx * dx + dy * dy);
1139
+ const angleInRadians = Math.atan2(dy, dx);
1140
+ const angle = (angleInRadians * (180 / Math.PI) + 360 + 90) % 360;
1141
+ return { angle, distance };
1142
+ }
1143
+ /**
1144
+ * 根据两个经纬度点计算方位角和距离
1145
+ *
1146
+ * @param latlng1 第一个经纬度点
1147
+ * @param latlng2 第二个经纬度点
1148
+ * @returns 包含方位角和距离的对象
1149
+ */
1150
+ static calcBearAndDisByPoints(latlng1, latlng2) {
1151
+ var f1 = latlng1.lat * 1, l1 = latlng1.lng * 1, f2 = latlng2.lat * 1, l2 = latlng2.lng * 1;
1152
+ var y = Math.sin((l2 - l1) * this.toRadian) * Math.cos(f2 * this.toRadian);
1153
+ var x = Math.cos(f1 * this.toRadian) * Math.sin(f2 * this.toRadian) - Math.sin(f1 * this.toRadian) * Math.cos(f2 * this.toRadian) * Math.cos((l2 - l1) * this.toRadian);
1154
+ var angle = Math.atan2(y, x) * (180 / Math.PI);
1155
+ var deltaF = (f2 - f1) * this.toRadian;
1156
+ var deltaL = (l2 - l1) * this.toRadian;
1157
+ var a = Math.sin(deltaF / 2) * Math.sin(deltaF / 2) + Math.cos(f1 * this.toRadian) * Math.cos(f2 * this.toRadian) * Math.sin(deltaL / 2) * Math.sin(deltaL / 2);
1158
+ var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1159
+ var distance = this.R * c;
1160
+ return {
1161
+ angle,
1162
+ distance
1163
+ };
1164
+ }
1165
+ /**
1166
+ * 计算点P到线段P1P2的最短距离
1167
+ *
1168
+ * @param p 点P的坐标
1169
+ * @param p1 线段起点P1的坐标
1170
+ * @param p2 线段终点P2的坐标
1171
+ * @returns 点P到线段P1P2的最短距离
1172
+ */
1173
+ static distanceToSegment(p, p1, p2) {
1174
+ const x = p.x, y = p.y, x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y;
1175
+ const cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
1176
+ if (cross <= 0) {
1177
+ return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
1178
+ }
1179
+ const d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
1180
+ if (cross >= d2) {
1181
+ return Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
1182
+ }
1183
+ const r = cross / d2;
1184
+ const px = x1 + (x2 - x1) * r;
1185
+ const py = y1 + (y2 - y1) * r;
1186
+ return Math.sqrt((x - px) * (x - px) + (y - py) * (y - py));
1187
+ }
1188
+ /**
1189
+ * 根据给定的经纬度、角度和距离计算新的经纬度点
1190
+ *
1191
+ * @param latlng 给定的经纬度点,类型为LngLat
1192
+ * @param angle 角度值,单位为度,表示从当前点出发的方向
1193
+ * @param distance 距离值,单位为米,表示从当前点出发的距离
1194
+ * @returns 返回计算后的新经纬度点,类型为{lat: number, lng: number}
1195
+ */
1196
+ static calcPointByBearAndDis(latlng, angle, distance) {
1197
+ const sLat = MathUtil.deg2Rad(latlng.lat * 1);
1198
+ const sLng = MathUtil.deg2Rad(latlng.lng * 1);
1199
+ const d = distance / this.R;
1200
+ angle = MathUtil.deg2Rad(angle);
1201
+ const lat = Math.asin(Math.sin(sLat) * Math.cos(d) + Math.cos(sLat) * Math.sin(d) * Math.cos(angle));
1202
+ const lon = sLng + Math.atan2(Math.sin(angle) * Math.sin(d) * Math.cos(sLat), Math.cos(d) - Math.sin(sLat) * Math.sin(lat));
1203
+ return {
1204
+ lat: MathUtil.rad2Deg(lat),
1205
+ lng: MathUtil.rad2Deg(lon)
1206
+ };
1569
1207
  }
1570
- };
1571
- class GeoUtil {
1572
- // 地球赤道半径
1573
1208
  /**
1574
- * 判断给定的经纬度是否合法
1209
+ * 将墨卡托坐标转换为经纬度坐标
1210
+ *
1211
+ * @param x 墨卡托坐标的x值
1212
+ * @param y 墨卡托坐标的y值
1213
+ * @returns 返回包含转换后的经度lng和纬度lat的对象
1214
+ */
1215
+ static mercatorTolonlat(x, y) {
1216
+ const earthRadius = this.R_EQU;
1217
+ const lng = x / earthRadius * (180 / Math.PI);
1218
+ const lat = Math.atan(Math.exp(y / earthRadius)) * (180 / Math.PI) * 2 - 90;
1219
+ return { lng, lat };
1220
+ }
1221
+ /**
1222
+ * 将经纬度坐标转换为墨卡托坐标
1575
1223
  *
1576
1224
  * @param lng 经度值
1577
1225
  * @param lat 纬度值
1578
- * @returns 如果经纬度合法,返回true;否则返回false
1226
+ * @returns 墨卡托坐标对象,包含x和y属性
1579
1227
  */
1580
- static isLnglat(lng, lat) {
1581
- return Util.isNumber(lng) && Util.isNumber(lat) && !!(+lat >= -90 && +lat <= 90 && +lng >= -180 && +lng <= 180);
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 };
1582
1234
  }
1583
1235
  /**
1584
- * 计算两哥平面坐标点间的距离
1236
+ * 根据百分比获取坐标
1585
1237
  *
1586
- * @param p1 坐标点1,包含x和y属性
1587
- * @param p2 坐标点2,包含x和y属性
1588
- * @returns 返回两点间的欧几里得距离
1238
+ * @param start 起点坐标
1239
+ * @param end 终点坐标
1240
+ * @param percent 百分比,取值范围0-1
1241
+ * @returns 返回插值后的坐标
1589
1242
  */
1590
- static distance(p1, p2) {
1591
- return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
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 };
1592
1246
  }
1593
1247
  /**
1594
- * 计算两个经纬度点之间的距离
1248
+ * 计算三角形面积
1595
1249
  *
1596
- * @param A 经纬度点A,包含lng(经度)和lat(纬度)两个属性
1597
- * @param B 经纬度点B,包含lng(经度)和lat(纬度)两个属性
1598
- * @returns 返回两点之间的距离,单位为米
1250
+ * @param a 第一个点的坐标
1251
+ * @param b 第二个点的坐标
1252
+ * @param c 第三个点的坐标
1253
+ * @returns 返回三角形的面积
1599
1254
  */
1600
- static distanceByPoints(A, B) {
1601
- const { lng: lngA, lat: latA } = A;
1602
- const { lng: lngB, lat: latB } = B;
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;
1255
+ static getTraingleArea(a, b, c) {
1256
+ let area = 0;
1257
+ const side = [];
1258
+ side[0] = Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2) + Math.pow((a.z || 0) - (b.z || 0), 2));
1259
+ side[1] = Math.sqrt(Math.pow(a.x - c.x, 2) + Math.pow(a.y - c.y, 2) + Math.pow((a.z || 0) - (c.z || 0), 2));
1260
+ side[2] = Math.sqrt(Math.pow(c.x - b.x, 2) + Math.pow(c.y - b.y, 2) + Math.pow((c.z || 0) - (b.z || 0), 2));
1261
+ if (side[0] + side[1] <= side[2] || side[0] + side[2] <= side[1] || side[1] + side[2] <= side[0]) {
1262
+ return area;
1263
+ }
1264
+ const p = (side[0] + side[1] + side[2]) / 2;
1265
+ area = Math.sqrt(p * (p - side[0]) * (p - side[1]) * (p - side[2]));
1266
+ return area;
1267
+ }
1268
+ }
1269
+ __publicField(GeoUtil, "toRadian", Math.PI / 180);
1270
+ __publicField(GeoUtil, "R", 6371393);
1271
+ // 地球平均半径
1272
+ __publicField(GeoUtil, "R_EQU", 6378137);
1273
+ const ColorName = {
1274
+ aliceblue: [240, 248, 255],
1275
+ antiquewhite: [250, 235, 215],
1276
+ aqua: [0, 255, 255],
1277
+ aquamarine: [127, 255, 212],
1278
+ azure: [240, 255, 255],
1279
+ beige: [245, 245, 220],
1280
+ bisque: [255, 228, 196],
1281
+ black: [0, 0, 0],
1282
+ blanchedalmond: [255, 235, 205],
1283
+ blue: [0, 0, 255],
1284
+ blueviolet: [138, 43, 226],
1285
+ brown: [165, 42, 42],
1286
+ burlywood: [222, 184, 135],
1287
+ cadetblue: [95, 158, 160],
1288
+ chartreuse: [127, 255, 0],
1289
+ chocolate: [210, 105, 30],
1290
+ coral: [255, 127, 80],
1291
+ cornflowerblue: [100, 149, 237],
1292
+ cornsilk: [255, 248, 220],
1293
+ crimson: [220, 20, 60],
1294
+ cyan: [0, 255, 255],
1295
+ darkblue: [0, 0, 139],
1296
+ darkcyan: [0, 139, 139],
1297
+ darkgoldenrod: [184, 134, 11],
1298
+ darkgray: [169, 169, 169],
1299
+ darkgreen: [0, 100, 0],
1300
+ darkgrey: [169, 169, 169],
1301
+ darkkhaki: [189, 183, 107],
1302
+ darkmagenta: [139, 0, 139],
1303
+ darkolivegreen: [85, 107, 47],
1304
+ darkorange: [255, 140, 0],
1305
+ darkorchid: [153, 50, 204],
1306
+ darkred: [139, 0, 0],
1307
+ darksalmon: [233, 150, 122],
1308
+ darkseagreen: [143, 188, 143],
1309
+ darkslateblue: [72, 61, 139],
1310
+ darkslategray: [47, 79, 79],
1311
+ darkslategrey: [47, 79, 79],
1312
+ darkturquoise: [0, 206, 209],
1313
+ darkviolet: [148, 0, 211],
1314
+ deeppink: [255, 20, 147],
1315
+ deepskyblue: [0, 191, 255],
1316
+ dimgray: [105, 105, 105],
1317
+ dimgrey: [105, 105, 105],
1318
+ dodgerblue: [30, 144, 255],
1319
+ firebrick: [178, 34, 34],
1320
+ floralwhite: [255, 250, 240],
1321
+ forestgreen: [34, 139, 34],
1322
+ fuchsia: [255, 0, 255],
1323
+ gainsboro: [220, 220, 220],
1324
+ ghostwhite: [248, 248, 255],
1325
+ gold: [255, 215, 0],
1326
+ goldenrod: [218, 165, 32],
1327
+ gray: [128, 128, 128],
1328
+ green: [0, 128, 0],
1329
+ greenyellow: [173, 255, 47],
1330
+ grey: [128, 128, 128],
1331
+ honeydew: [240, 255, 240],
1332
+ hotpink: [255, 105, 180],
1333
+ indianred: [205, 92, 92],
1334
+ indigo: [75, 0, 130],
1335
+ ivory: [255, 255, 240],
1336
+ khaki: [240, 230, 140],
1337
+ lavender: [230, 230, 250],
1338
+ lavenderblush: [255, 240, 245],
1339
+ lawngreen: [124, 252, 0],
1340
+ lemonchiffon: [255, 250, 205],
1341
+ lightblue: [173, 216, 230],
1342
+ lightcoral: [240, 128, 128],
1343
+ lightcyan: [224, 255, 255],
1344
+ lightgoldenrodyellow: [250, 250, 210],
1345
+ lightgray: [211, 211, 211],
1346
+ lightgreen: [144, 238, 144],
1347
+ lightgrey: [211, 211, 211],
1348
+ lightpink: [255, 182, 193],
1349
+ lightsalmon: [255, 160, 122],
1350
+ lightseagreen: [32, 178, 170],
1351
+ lightskyblue: [135, 206, 250],
1352
+ lightslategray: [119, 136, 153],
1353
+ lightslategrey: [119, 136, 153],
1354
+ lightsteelblue: [176, 196, 222],
1355
+ lightyellow: [255, 255, 224],
1356
+ lime: [0, 255, 0],
1357
+ limegreen: [50, 205, 50],
1358
+ linen: [250, 240, 230],
1359
+ magenta: [255, 0, 255],
1360
+ maroon: [128, 0, 0],
1361
+ mediumaquamarine: [102, 205, 170],
1362
+ mediumblue: [0, 0, 205],
1363
+ mediumorchid: [186, 85, 211],
1364
+ mediumpurple: [147, 112, 219],
1365
+ mediumseagreen: [60, 179, 113],
1366
+ mediumslateblue: [123, 104, 238],
1367
+ mediumspringgreen: [0, 250, 154],
1368
+ mediumturquoise: [72, 209, 204],
1369
+ mediumvioletred: [199, 21, 133],
1370
+ midnightblue: [25, 25, 112],
1371
+ mintcream: [245, 255, 250],
1372
+ mistyrose: [255, 228, 225],
1373
+ moccasin: [255, 228, 181],
1374
+ navajowhite: [255, 222, 173],
1375
+ navy: [0, 0, 128],
1376
+ oldlace: [253, 245, 230],
1377
+ olive: [128, 128, 0],
1378
+ olivedrab: [107, 142, 35],
1379
+ orange: [255, 165, 0],
1380
+ orangered: [255, 69, 0],
1381
+ orchid: [218, 112, 214],
1382
+ palegoldenrod: [238, 232, 170],
1383
+ palegreen: [152, 251, 152],
1384
+ paleturquoise: [175, 238, 238],
1385
+ palevioletred: [219, 112, 147],
1386
+ papayawhip: [255, 239, 213],
1387
+ peachpuff: [255, 218, 185],
1388
+ peru: [205, 133, 63],
1389
+ pink: [255, 192, 203],
1390
+ plum: [221, 160, 221],
1391
+ powderblue: [176, 224, 230],
1392
+ purple: [128, 0, 128],
1393
+ rebeccapurple: [102, 51, 153],
1394
+ red: [255, 0, 0],
1395
+ rosybrown: [188, 143, 143],
1396
+ royalblue: [65, 105, 225],
1397
+ saddlebrown: [139, 69, 19],
1398
+ salmon: [250, 128, 114],
1399
+ sandybrown: [244, 164, 96],
1400
+ seagreen: [46, 139, 87],
1401
+ seashell: [255, 245, 238],
1402
+ sienna: [160, 82, 45],
1403
+ silver: [192, 192, 192],
1404
+ skyblue: [135, 206, 235],
1405
+ slateblue: [106, 90, 205],
1406
+ slategray: [112, 128, 144],
1407
+ slategrey: [112, 128, 144],
1408
+ snow: [255, 250, 250],
1409
+ springgreen: [0, 255, 127],
1410
+ steelblue: [70, 130, 180],
1411
+ tan: [210, 180, 140],
1412
+ teal: [0, 128, 128],
1413
+ thistle: [216, 191, 216],
1414
+ tomato: [255, 99, 71],
1415
+ turquoise: [64, 224, 208],
1416
+ violet: [238, 130, 238],
1417
+ wheat: [245, 222, 179],
1418
+ white: [255, 255, 255],
1419
+ whitesmoke: [245, 245, 245],
1420
+ yellow: [255, 255, 0],
1421
+ yellowgreen: [154, 205, 50]
1422
+ };
1423
+ class Color {
1424
+ constructor(r, g, b, a) {
1425
+ __publicField(this, "_r");
1426
+ __publicField(this, "_g");
1427
+ __publicField(this, "_b");
1428
+ __publicField(this, "_alpha");
1429
+ this._validateColorChannel(r);
1430
+ this._validateColorChannel(g);
1431
+ this._validateColorChannel(b);
1432
+ this._r = r;
1433
+ this._g = g;
1434
+ this._b = b;
1435
+ this._alpha = MathUtil.clamp(a || 1, 0, 1);
1612
1436
  }
1613
- /**
1614
- * 格式化经纬度为度分秒格式
1615
- *
1616
- * @param lng 经度
1617
- * @param lat 纬度
1618
- * @returns 返回格式化后的经纬度字符串,格式为:经度度分秒,纬度度分秒
1619
- */
1620
- static formatLnglat(lng, lat) {
1621
- let res = "";
1622
- function formatDegreeToDMS(valueInDegrees) {
1623
- const degree = Math.floor(valueInDegrees);
1624
- const minutes = Math.floor((valueInDegrees - degree) * 60);
1625
- const seconds = (valueInDegrees - degree) * 3600 - minutes * 60;
1626
- return `${degree}°${minutes}′${seconds.toFixed(2)}″`;
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);
1437
+ _validateColorChannel(channel) {
1438
+ if (channel < 0 || channel > 255) {
1439
+ ExceptionUtil.throwException("Color channel must be between 0 and 255.");
1634
1440
  }
1635
- return res;
1636
1441
  }
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
- };
1661
- }
1442
+ toString() {
1443
+ return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
1662
1444
  }
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";
1445
+ toJson() {
1446
+ return { r: this._r, g: this._g, b: this._b, a: this._alpha };
1688
1447
  }
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 };
1448
+ get rgba() {
1449
+ return `rgba(${this._r}, ${this._g}, ${this._b}, ${this._alpha})`;
1701
1450
  }
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 };
1451
+ get hex() {
1452
+ return Color.rgb2hex(this._r, this._g, this._b, this._alpha);
1718
1453
  }
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
- };
1454
+ setAlpha(a) {
1455
+ this._alpha = MathUtil.clamp(a, 0, 1);
1456
+ return this;
1740
1457
  }
1741
- /**
1742
- * 计算点P到线段P1P2的最短距离
1743
- *
1744
- * @param p 点P的坐标
1745
- * @param p1 线段起点P1的坐标
1746
- * @param p2 线段终点P2的坐标
1747
- * @returns 点P到线段P1P2的最短距离
1748
- */
1749
- static distanceToSegment(p, p1, p2) {
1750
- const x = p.x, y = p.y, x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y;
1751
- const cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
1752
- if (cross <= 0) {
1753
- return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
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));
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 latlng 给定的经纬度点,类型为LngLat
1768
- * @param angle 角度值,单位为度,表示从当前点出发的方向
1769
- * @param distance 距离值,单位为米,表示从当前点出发的距离
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 calcPointByBearAndDis(latlng, angle, distance) {
1773
- const sLat = MathUtil.deg2Rad(latlng.lat * 1);
1774
- const sLng = MathUtil.deg2Rad(latlng.lng * 1);
1775
- const d = distance / this.R;
1776
- angle = MathUtil.deg2Rad(angle);
1777
- const lat = Math.asin(Math.sin(sLat) * Math.cos(d) + Math.cos(sLat) * Math.sin(d) * Math.cos(angle));
1778
- const lon = sLng + Math.atan2(Math.sin(angle) * Math.sin(d) * Math.cos(sLat), Math.cos(d) - Math.sin(sLat) * Math.sin(lat));
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 x 墨卡托坐标的x值
1788
- * @param y 墨卡托坐标的y值
1789
- * @returns 返回包含转换后的经度lng和纬度lat的对象
1487
+ * @param hexValue 十六进制颜色值,可带或不带#前缀,支持3位和6位表示
1488
+ * @returns 返回颜色对象
1790
1489
  */
1791
- static mercatorTolonlat(x, y) {
1792
- const earthRadius = this.R_EQU;
1793
- const lng = x / earthRadius * (180 / Math.PI);
1794
- const lat = Math.atan(Math.exp(y / earthRadius)) * (180 / Math.PI) * 2 - 90;
1795
- return { lng, lat };
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 lng 经度值
1801
- * @param lat 纬度值
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 lonlatToMercator(lng, lat) {
1805
- var earthRad = this.R_EQU;
1806
- const x = lng * Math.PI / 180 * earthRad;
1807
- var a = lat * Math.PI / 180;
1808
- const y = earthRad / 2 * Math.log((1 + Math.sin(a)) / (1 - Math.sin(a)));
1809
- return { x, y };
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 start 起点坐标
1815
- * @param end 终点坐标
1816
- * @param percent 百分比,取值范围0-1
1817
- * @returns 返回插值后的坐标
1548
+ * @param str 字符串类型的颜色值,支持rgba、hex、hsl格式
1549
+ * @returns 返回创建的颜色对象
1550
+ * @throws 当颜色值无效时,抛出错误
1818
1551
  */
1819
- static interpolate({ x: x1, y: y1, z: z1 = 0 }, { x: x2, y: y2, z: z2 = 0 }, percent) {
1820
- const dx = x2 - x1, dy = y2 - y1, dz = z2 - z1;
1821
- return { x: x1 + dx * percent, y: y1 + dy * percent, z: z1 + dz * percent };
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 a 第一个点的坐标
1827
- * @param b 第二个点的坐标
1828
- * @param c 第三个点的坐标
1829
- * @returns 返回三角形的面积
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 getTraingleArea(a, b, c) {
1832
- let area = 0;
1833
- const side = [];
1834
- 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));
1835
- 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));
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
- const p = (side[0] + side[1] + side[2]) / 2;
1841
- area = Math.sqrt(p * (p - side[0]) * (p - side[1]) * (p - side[2]));
1842
- return area;
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) => {
@@ -2488,18 +2183,30 @@ class BrowserUtil {
2488
2183
  * @returns 返回包含GPU类型和型号的对象
2489
2184
  */
2490
2185
  static getGPU() {
2491
- let type = "";
2492
- let model = "";
2186
+ let type = "unknown";
2187
+ let model = "unknown";
2493
2188
  if (this == null ? void 0 : this.document) {
2494
2189
  let $canvas = this.document.createElement("canvas");
2495
- let webgl = $canvas.getContext("webgl") || $canvas.getContext("experimental-webgl");
2496
- if (webgl instanceof WebGLRenderingContext) {
2497
- let debugInfo = webgl.getExtension("WEBGL_debug_renderer_info");
2190
+ let gl = $canvas.getContext("webgl") || $canvas.getContext("experimental-webgl");
2191
+ if (gl instanceof WebGLRenderingContext) {
2192
+ let debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
2498
2193
  if (debugInfo) {
2499
- let gpu_str = webgl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
2194
+ let gpu_str = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
2500
2195
  type = (gpu_str.match(/ANGLE \((.+?),/) || [])[1] || "";
2501
2196
  model = (gpu_str.match(/, (.+?) (\(|vs_)/) || [])[1] || "";
2502
2197
  }
2198
+ return {
2199
+ type,
2200
+ model,
2201
+ spec: {
2202
+ maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
2203
+ // 最大纹理尺寸 16384/4k
2204
+ maxRenderBufferSize: gl.getParameter(gl.MAX_RENDERBUFFER_SIZE),
2205
+ // 最大渲染缓冲尺寸 16384/4k
2206
+ maxTextureUnits: gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)
2207
+ // 纹理单元数量 32
2208
+ }
2209
+ };
2503
2210
  }
2504
2211
  }
2505
2212
  return {
@@ -3035,650 +2742,986 @@ const DomUtil = {
3035
2742
  if (container) {
3036
2743
  container.appendChild(el);
3037
2744
  }
3038
- return el;
2745
+ return el;
2746
+ },
2747
+ /**
2748
+ * 从父节点中移除指定元素。
2749
+ *
2750
+ * @param el 要移除的元素对象,必须包含parentNode属性。
2751
+ */
2752
+ remove(el) {
2753
+ const parent = el.parentNode;
2754
+ if (parent) {
2755
+ parent.removeChild(el);
2756
+ }
2757
+ },
2758
+ /**
2759
+ * 清空给定元素的子节点
2760
+ *
2761
+ * @param el 要清空子节点的元素,包含firstChild和removeChild属性
2762
+ */
2763
+ empty(el) {
2764
+ while (el.firstChild) {
2765
+ el.removeChild(el.firstChild);
2766
+ }
2767
+ },
2768
+ /**
2769
+ * 将元素移到父节点的最前面
2770
+ *
2771
+ * @param el 要移动的元素,需要包含 parentNode 属性
2772
+ */
2773
+ toFront(el) {
2774
+ const parent = el.parentNode;
2775
+ if (parent && parent.lastChild !== el) {
2776
+ parent.appendChild(el);
2777
+ }
2778
+ },
2779
+ /**
2780
+ * 将元素移动到其父节点的最前面
2781
+ *
2782
+ * @param el 要移动的元素,需要包含parentNode属性
2783
+ */
2784
+ toBack(el) {
2785
+ const parent = el.parentNode;
2786
+ if (parent && parent.firstChild !== el) {
2787
+ parent.insertBefore(el, parent.firstChild);
2788
+ }
2789
+ },
2790
+ /**
2791
+ * 获取元素的类名
2792
+ *
2793
+ * @param el 包含对应元素和类名的对象
2794
+ * @param el.correspondingElement 对应的元素
2795
+ * @param el.className 类名对象
2796
+ * @param el.className.baseVal 类名字符串
2797
+ * @returns 返回元素的类名字符串
2798
+ */
2799
+ getClass(el) {
2800
+ const shadowElement = (el == null ? void 0 : el.host) || el;
2801
+ return shadowElement.className.toString();
2802
+ },
2803
+ /**
2804
+ * 判断元素是否包含指定类名
2805
+ *
2806
+ * @param el 元素对象,包含classList属性,classList属性包含contains方法
2807
+ * @param name 要判断的类名
2808
+ * @returns 返回一个布尔值,表示元素是否包含指定类名
2809
+ */
2810
+ hasClass(el, name) {
2811
+ var _a2;
2812
+ if ((_a2 = el.classList) == null ? void 0 : _a2.contains(name)) {
2813
+ return true;
2814
+ }
2815
+ const className = this.getClass(el);
2816
+ return className.length > 0 && new RegExp(`(^|\\s)${name}(\\s|$)`).test(className);
2817
+ },
2818
+ /**
2819
+ * 给指定的 HTML 元素添加类名
2820
+ *
2821
+ * @param el 要添加类名的 HTML 元素
2822
+ * @param name 要添加的类名,多个类名之间用空格分隔
2823
+ */
2824
+ addClass(el, name) {
2825
+ if (el.classList !== void 0) {
2826
+ const classes = splitWords(name);
2827
+ for (let i = 0, len = classes.length; i < len; i++) {
2828
+ el.classList.add(classes[i]);
2829
+ }
2830
+ } else if (!this.hasClass(el, name)) {
2831
+ const className = this.getClass(el);
2832
+ this.setClass(el, (className ? className + " " : "") + name);
2833
+ }
2834
+ },
2835
+ /**
2836
+ * 从元素中移除指定类名
2837
+ *
2838
+ * @param el 要移除类名的元素
2839
+ * @param name 要移除的类名,多个类名用空格分隔
2840
+ */
2841
+ removeClass(el, name) {
2842
+ if (el.classList !== void 0) {
2843
+ const classes = splitWords(name);
2844
+ classes.forEach((className) => el.classList.remove(className));
2845
+ } else {
2846
+ this.setClass(el, (" " + this.getClass(el) + " ").replace(" " + name + " ", " ").trim());
2847
+ }
2848
+ },
2849
+ /**
2850
+ * 设置元素的 CSS 类名
2851
+ *
2852
+ * @param el HTML 或 SVG 元素
2853
+ * @param name 要设置的类名,多个类名之间用空格分隔
2854
+ */
2855
+ setClass(el, name) {
2856
+ if ("classList" in el) {
2857
+ el.classList.value = "";
2858
+ name.split(" ").forEach((className) => el.classList.add(className));
2859
+ }
3039
2860
  },
3040
2861
  /**
3041
- * 从父节点中移除指定元素。
2862
+ * 从字符串中解析XML文档,并返回根节点
3042
2863
  *
3043
- * @param el 要移除的元素对象,必须包含parentNode属性。
2864
+ * @param str 要解析的XML字符串
2865
+ * @returns 解析后的XML文档的根节点
3044
2866
  */
3045
- remove(el) {
3046
- const parent = el.parentNode;
3047
- if (parent) {
3048
- parent.removeChild(el);
3049
- }
3050
- },
2867
+ parseFromString(str) {
2868
+ const parser = new DOMParser();
2869
+ const doc = parser.parseFromString(str, "text/xml");
2870
+ return doc.children[0];
2871
+ }
2872
+ };
2873
+ const FileUtil = {
3051
2874
  /**
3052
- * 清空给定元素的子节点
2875
+ * 将Base64编码的字符串转换为Blob对象
3053
2876
  *
3054
- * @param el 要清空子节点的元素,包含firstChild和removeChild属性
2877
+ * @param data Base64编码的字符串
2878
+ * @returns 转换后的Blob对象
3055
2879
  */
3056
- empty(el) {
3057
- while (el.firstChild) {
3058
- el.removeChild(el.firstChild);
2880
+ convertBase64ToBlob(data) {
2881
+ const mimeString = data.split(",")[0].split(":")[1].split(";")[0];
2882
+ const byteCharacters = atob(data.split(",")[1]);
2883
+ const byteNumbers = new Array(byteCharacters.length);
2884
+ for (let i = 0; i < byteCharacters.length; i++) {
2885
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
3059
2886
  }
2887
+ const byteArray = new Uint8Array(byteNumbers);
2888
+ const blob = new Blob([byteArray], { type: mimeString });
2889
+ return blob;
3060
2890
  },
3061
2891
  /**
3062
- * 将元素移到父节点的最前面
2892
+ * 将base64字符串转换为文件对象
3063
2893
  *
3064
- * @param el 要移动的元素,需要包含 parentNode 属性
2894
+ * @param dataurl 包含base64字符串的数据URL
2895
+ * @param filename 文件的名称
2896
+ * @returns 返回文件对象
3065
2897
  */
3066
- toFront(el) {
3067
- const parent = el.parentNode;
3068
- if (parent && parent.lastChild !== el) {
3069
- parent.appendChild(el);
2898
+ convertBase64ToFile(dataurl, filename) {
2899
+ const arr = dataurl.split(",");
2900
+ const mimeMatch = arr[0].match(/:(.*?);/);
2901
+ const mime = mimeMatch ? mimeMatch[1] : "image/png";
2902
+ const bstr = atob(arr[1]);
2903
+ const u8arr = new Uint8Array(bstr.length);
2904
+ for (let i = 0; i < bstr.length; i++) {
2905
+ u8arr[i] = bstr.charCodeAt(i);
3070
2906
  }
2907
+ const file = new File([u8arr], filename, { type: mime });
2908
+ return file;
3071
2909
  },
3072
2910
  /**
3073
- * 将元素移动到其父节点的最前面
2911
+ * 从文件下载数据
3074
2912
  *
3075
- * @param el 要移动的元素,需要包含parentNode属性
2913
+ * @param data 要下载的数据,可以是字符串数组、BlobPart 或 MediaSource
2914
+ * @param saveName 下载后文件的保存名称
3076
2915
  */
3077
- toBack(el) {
3078
- const parent = el.parentNode;
3079
- if (parent && parent.firstChild !== el) {
3080
- parent.insertBefore(el, parent.firstChild);
2916
+ downloadFromFile(data, saveName) {
2917
+ if (typeof data == "object") {
2918
+ if (data instanceof Blob) {
2919
+ data = URL.createObjectURL(data);
2920
+ } else {
2921
+ const str = JSON.stringify(data);
2922
+ const blob = new Blob([str], { type: "text/json" });
2923
+ data = window.URL.createObjectURL(blob);
2924
+ }
2925
+ } else if (typeof data == "string" && data.indexOf("http") === -1) {
2926
+ const blob = new Blob([data], { type: "text/json" });
2927
+ data = window.URL.createObjectURL(blob);
3081
2928
  }
2929
+ var link = document.createElement("a");
2930
+ link.href = data;
2931
+ link.download = saveName || "";
2932
+ link.click();
2933
+ window.URL.revokeObjectURL(link.href);
3082
2934
  },
3083
2935
  /**
3084
- * 获取元素的类名
3085
- *
3086
- * @param el 包含对应元素和类名的对象
3087
- * @param el.correspondingElement 对应的元素
3088
- * @param el.className 类名对象
3089
- * @param el.className.baseVal 类名字符串
3090
- * @returns 返回元素的类名字符串
2936
+ * 获取远程文件并读取
2937
+ * @param url 远程文件地址
2938
+ * @param fileName 文件名
2939
+ * @returns Promise<ArrayBuffer>
3091
2940
  */
3092
- getClass(el) {
3093
- const shadowElement = (el == null ? void 0 : el.host) || el;
3094
- return shadowElement.className.toString();
3095
- },
2941
+ readFileFromUrl(url, fileName) {
2942
+ return new Promise((resolve, reject) => {
2943
+ const xhr = new XMLHttpRequest();
2944
+ xhr.open("GET", url, true);
2945
+ xhr.responseType = "blob";
2946
+ const suffix = fileName.split(".");
2947
+ try {
2948
+ xhr.onload = function() {
2949
+ if (this.status >= 200 && this.status < 300) {
2950
+ const blob = xhr.response;
2951
+ const objectURL = URL.createObjectURL(blob);
2952
+ const file = new File([blob], fileName, {
2953
+ type: suffix[1],
2954
+ lastModified: (/* @__PURE__ */ new Date()).getTime()
2955
+ });
2956
+ const reader = new FileReader();
2957
+ reader.readAsArrayBuffer(file);
2958
+ reader.onload = function(e) {
2959
+ var _a2;
2960
+ URL.revokeObjectURL(objectURL);
2961
+ resolve((_a2 = e.target) == null ? void 0 : _a2.result);
2962
+ };
2963
+ reader.onerror = function(error) {
2964
+ reject(error);
2965
+ };
2966
+ }
2967
+ };
2968
+ xhr.onerror = function(error) {
2969
+ reject(error);
2970
+ };
2971
+ } catch (error) {
2972
+ reject(error);
2973
+ }
2974
+ xhr.send();
2975
+ });
2976
+ }
2977
+ };
2978
+ class MessageUtil {
2979
+ static resetWarned() {
2980
+ this.warned = {};
2981
+ }
2982
+ static changeVoice() {
2983
+ this.isMute = !!Number(!this.isMute);
2984
+ localStorage.setItem("mute", Number(this.isMute).toString());
2985
+ }
2986
+ static _call(method, message, options) {
2987
+ if (!this.warned[message]) {
2988
+ method(message, options);
2989
+ this.warned[message] = true;
2990
+ }
2991
+ }
3096
2992
  /**
3097
- * 判断元素是否包含指定类名
2993
+ * 播放消息提示音和文字朗读(语音需要有交互)
3098
2994
  *
3099
- * @param el 元素对象,包含classList属性,classList属性包含contains方法
3100
- * @param name 要判断的类名
3101
- * @returns 返回一个布尔值,表示元素是否包含指定类名
2995
+ * @param type 消息类型
2996
+ * @param message 消息内容
2997
+ * @param options 配置选项,可选参数,包括语言、音量、语速和音高
2998
+ * @returns 无返回值
3102
2999
  */
3103
- hasClass(el, name) {
3104
- var _a2;
3105
- if ((_a2 = el.classList) == null ? void 0 : _a2.contains(name)) {
3106
- return true;
3000
+ static msg(type, message, options = {}) {
3001
+ if (this.isMute) return;
3002
+ const typename = Util.decodeDict(type, "success", "恭喜", "error", "发生错误", "warning", "警告", "info", "友情提示") + ":";
3003
+ this.speechSynthesisUtterance.text = typename + message;
3004
+ this.speechSynthesisUtterance.lang = options.lang || "zh-CN";
3005
+ this.speechSynthesisUtterance.volume = options.volume || 1;
3006
+ this.speechSynthesisUtterance.rate = options.rate || 1;
3007
+ this.speechSynthesisUtterance.pitch = options.pitch || 1;
3008
+ this.speechSynthesis.speak(this.speechSynthesisUtterance);
3009
+ }
3010
+ static stop(e) {
3011
+ this.speechSynthesisUtterance.text = e;
3012
+ this.speechSynthesis.cancel();
3013
+ }
3014
+ static warning(message, options = {}) {
3015
+ console.warn(`Warning: %c${message}`, "color:#E6A23C;font-weight: bold;");
3016
+ this.msg("warning", message, options);
3017
+ }
3018
+ static warningOnce(message, options = {}) {
3019
+ this._call(this.warning.bind(this), message, options);
3020
+ }
3021
+ static info(message, options = {}) {
3022
+ if (process.env.NODE_ENV === "development" && console !== void 0) {
3023
+ console.info(`Info: %c${message}`, "color:#909399;font-weight: bold;");
3107
3024
  }
3108
- const className = this.getClass(el);
3109
- return className.length > 0 && new RegExp(`(^|\\s)${name}(\\s|$)`).test(className);
3025
+ this.msg("info", message, options);
3026
+ }
3027
+ static infoOnce(message, options = {}) {
3028
+ this._call(this.info.bind(this), message, options);
3029
+ }
3030
+ static error(message, options = {}) {
3031
+ console.error(`Error: %c${message}`, "color:#F56C6C;font-weight: bold;");
3032
+ this.msg("error", message, options);
3033
+ }
3034
+ static errorOnce(message, options = {}) {
3035
+ this._call(this.error.bind(this), message, options);
3036
+ }
3037
+ static success(message, options = {}) {
3038
+ if (process.env.NODE_ENV === "development" && console !== void 0) {
3039
+ console.log(`Success: %c${message}`, "color:#67C23A;font-weight: bold;");
3040
+ }
3041
+ this.msg("success", message, options);
3042
+ }
3043
+ static successOnce(message, options = {}) {
3044
+ this._call(this.success.bind(this), message, options);
3045
+ }
3046
+ }
3047
+ __publicField(MessageUtil, "warned", {});
3048
+ __publicField(MessageUtil, "isMute", !!Number(localStorage.getItem("mute")) || false);
3049
+ __publicField(MessageUtil, "speechSynthesis", window.speechSynthesis);
3050
+ __publicField(MessageUtil, "speechSynthesisUtterance", new SpeechSynthesisUtterance());
3051
+ const OptimizeUtil = {
3052
+ /**
3053
+ * 防抖函数,在指定的等待时间内,如果连续触发事件,则只在最后一次触发后执行函数。适用于像搜索输入框这种需要用户停止输入后才调用的场景
3054
+ *
3055
+ * @param func 需要防抖的函数。
3056
+ * @param wait 等待时间,单位毫秒。
3057
+ * @param immediate 是否立即执行函数,默认为true。
3058
+ * @returns 返回防抖后的函数。
3059
+ */
3060
+ debounce(func, wait, immediate = true) {
3061
+ let timeout = null;
3062
+ let args;
3063
+ let timestamp;
3064
+ let result;
3065
+ const later = () => {
3066
+ const last = Date.now() - timestamp;
3067
+ if (last < wait && last > 0) {
3068
+ timeout = setTimeout(later, wait - last);
3069
+ } else {
3070
+ timeout = null;
3071
+ if (!immediate) {
3072
+ result = func.apply(this, args);
3073
+ }
3074
+ }
3075
+ };
3076
+ return (...args2) => {
3077
+ timestamp = Date.now();
3078
+ const callNow = immediate && !timeout;
3079
+ if (!timeout) {
3080
+ timeout = setTimeout(later, wait);
3081
+ }
3082
+ if (callNow) {
3083
+ result = func.apply(this, args2);
3084
+ if (!timeout) {
3085
+ args2 = null;
3086
+ }
3087
+ }
3088
+ return result;
3089
+ };
3110
3090
  },
3111
3091
  /**
3112
- * 给指定的 HTML 元素添加类名
3092
+ * 节流函数,适用于像滚动事件、窗口resize事件这种需要限制调用频率的场景
3113
3093
  *
3114
- * @param el 要添加类名的 HTML 元素
3115
- * @param name 要添加的类名,多个类名之间用空格分隔
3094
+ * @param func 需要节流的函数
3095
+ * @param wait 节流间隔,单位为毫秒
3096
+ * @param type 节流类型,1表示时间戳方式,2表示定时器方式
3097
+ * @returns 返回一个新的函数,该函数在节流控制下执行传入的函数
3116
3098
  */
3117
- addClass(el, name) {
3118
- if (el.classList !== void 0) {
3119
- const classes = splitWords(name);
3120
- for (let i = 0, len = classes.length; i < len; i++) {
3121
- el.classList.add(classes[i]);
3099
+ throttle(func, wait, type = 1) {
3100
+ let previous = 0;
3101
+ let timeout = null;
3102
+ return (...args) => {
3103
+ if (type === 1) {
3104
+ const now = Date.now();
3105
+ if (now - previous >= wait) {
3106
+ func.apply(this, args);
3107
+ previous = now;
3108
+ }
3109
+ } else if (type === 2) {
3110
+ if (!timeout) {
3111
+ timeout = setTimeout(() => {
3112
+ timeout = null;
3113
+ func.apply(this, args);
3114
+ }, wait);
3115
+ }
3122
3116
  }
3123
- } else if (!this.hasClass(el, name)) {
3124
- const className = this.getClass(el);
3125
- this.setClass(el, (className ? className + " " : "") + name);
3126
- }
3117
+ };
3127
3118
  },
3128
3119
  /**
3129
- * 从元素中移除指定类名
3120
+ * 缓存函数,将传入的函数fn的计算结果缓存,提高重复计算的效率
3130
3121
  *
3131
- * @param el 要移除类名的元素
3132
- * @param name 要移除的类名,多个类名用空格分隔
3122
+ * @param fn 传入待缓存的函数
3123
+ * @returns 返回缓存后的函数
3133
3124
  */
3134
- removeClass(el, name) {
3135
- if (el.classList !== void 0) {
3136
- const classes = splitWords(name);
3137
- classes.forEach((className) => el.classList.remove(className));
3138
- } else {
3139
- this.setClass(el, (" " + this.getClass(el) + " ").replace(" " + name + " ", " ").trim());
3140
- }
3125
+ memoize(fn) {
3126
+ const cache = /* @__PURE__ */ new Map();
3127
+ return (...args) => {
3128
+ const argsString = JSON.stringify(args);
3129
+ if (cache.has(argsString)) {
3130
+ return cache.get(argsString);
3131
+ } else {
3132
+ const result = fn.apply(this, args);
3133
+ cache.set(argsString, result);
3134
+ return result;
3135
+ }
3136
+ };
3141
3137
  },
3142
3138
  /**
3143
- * 设置元素的 CSS 类名
3139
+ * 递归调用函数,以一定的频率和持续时间执行。
3144
3140
  *
3145
- * @param el HTML 或 SVG 元素
3146
- * @param name 要设置的类名,多个类名之间用空格分隔
3141
+ * @param fun 要递归调用的函数。
3142
+ * @param frequency 每次调用函数之间的时间间隔,单位为毫秒,默认为500毫秒。
3143
+ * @param duration 函数递归调用的总时长,单位为毫秒,默认为5000毫秒。
3147
3144
  */
3148
- setClass(el, name) {
3149
- if ("classList" in el) {
3150
- el.classList.value = "";
3151
- name.split(" ").forEach((className) => el.classList.add(className));
3152
- }
3145
+ recurve(fun, frequency = 500, duration = 5e3) {
3146
+ let timer = 0;
3147
+ setTimeout(() => {
3148
+ timer++;
3149
+ if (timer < Math.floor(duration / frequency)) {
3150
+ fun.call(this);
3151
+ setTimeout(this.recurve.bind(this, fun, frequency, duration), frequency);
3152
+ }
3153
+ }, frequency);
3153
3154
  },
3154
3155
  /**
3155
- * 从字符串中解析XML文档,并返回根节点
3156
+ * 确保函数只被调用一次
3156
3157
  *
3157
- * @param str 要解析的XML字符串
3158
- * @returns 解析后的XML文档的根节点
3158
+ * @param func 要被调用的函数
3159
+ * @returns 返回一个新的函数,该函数在被首次调用时会执行传入的函数,之后再次调用将不再执行
3159
3160
  */
3160
- parseFromString(str) {
3161
- const parser = new DOMParser();
3162
- const doc = parser.parseFromString(str, "text/xml");
3163
- return doc.children[0];
3161
+ once(func) {
3162
+ let called = false;
3163
+ return function(...args) {
3164
+ if (!called) {
3165
+ called = true;
3166
+ return func(...args);
3167
+ }
3168
+ };
3164
3169
  }
3165
3170
  };
3166
- const FileUtil = {
3171
+ const StringUtil = {
3167
3172
  /**
3168
- * 将Base64编码的字符串转换为Blob对象
3173
+ * 转换字符串大小写
3169
3174
  *
3170
- * @param data Base64编码的字符串
3171
- * @returns 转换后的Blob对象
3175
+ * @param str 待转换的字符串
3176
+ * @param type 转换类型,可选值为 0-4,默认为 4
3177
+ * 0:字母大小写反转
3178
+ * 1:首字母大写,其余小写
3179
+ * 2:首字母小写,其余大写
3180
+ * 3:全部大写
3181
+ * 4:全部小写
3182
+ * @returns 转换后的字符串
3172
3183
  */
3173
- convertBase64ToBlob(data) {
3174
- const mimeString = data.split(",")[0].split(":")[1].split(";")[0];
3175
- const byteCharacters = atob(data.split(",")[1]);
3176
- const byteNumbers = new Array(byteCharacters.length);
3177
- for (let i = 0; i < byteCharacters.length; i++) {
3178
- byteNumbers[i] = byteCharacters.charCodeAt(i);
3184
+ changeCase(str, type) {
3185
+ type = type || CaseType.LOWER_CASE;
3186
+ switch (type) {
3187
+ case CaseType.REVERSE_CASE:
3188
+ return str.split("").map(function(word) {
3189
+ if (/[a-z]/.test(word)) {
3190
+ return word.toUpperCase();
3191
+ } else {
3192
+ return word.toLowerCase();
3193
+ }
3194
+ }).join("");
3195
+ case CaseType.UPPER_CAMEL_CASE:
3196
+ return str.replace(/\b\w+\b/g, function(word) {
3197
+ return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
3198
+ });
3199
+ case CaseType.LOWER_CAMEL_CASE:
3200
+ return str.replace(/\b\w+\b/g, function(word) {
3201
+ return word.substring(0, 1).toLowerCase() + word.substring(1).toUpperCase();
3202
+ });
3203
+ case CaseType.UPPER_CASE:
3204
+ return str.toUpperCase();
3205
+ case CaseType.LOWER_CASE:
3206
+ return str.toLowerCase();
3207
+ default:
3208
+ return str;
3179
3209
  }
3180
- const byteArray = new Uint8Array(byteNumbers);
3181
- const blob = new Blob([byteArray], { type: mimeString });
3182
- return blob;
3183
3210
  },
3184
3211
  /**
3185
- * 将base64字符串转换为文件对象
3212
+ * 计算字符串的字节长度
3186
3213
  *
3187
- * @param dataurl 包含base64字符串的数据URL
3188
- * @param filename 文件的名称
3189
- * @returns 返回文件对象
3214
+ * @param str 需要计算字节长度的字符串
3215
+ * @returns 返回字符串的字节长度
3190
3216
  */
3191
- convertBase64ToFile(dataurl, filename) {
3192
- const arr = dataurl.split(",");
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;
3217
+ getByteLength(str) {
3218
+ return str.replace(/[\u0391-\uFFE5]/g, "aa").length;
3202
3219
  },
3203
3220
  /**
3204
- * 从文件下载数据
3221
+ * 截取字符串中指定字节长度的子串
3205
3222
  *
3206
- * @param data 要下载的数据,可以是字符串数组、BlobPart 或 MediaSource
3207
- * @param saveName 下载后文件的保存名称
3223
+ * @param str 字符串对象,包含replace、length和substring方法
3224
+ * @param start 截取起始位置
3225
+ * @param n 截取字节长度
3226
+ * @returns 返回截取后的子串
3208
3227
  */
3209
- downloadFromFile(data, saveName) {
3210
- if (typeof data == "object") {
3211
- if (data instanceof Blob) {
3212
- data = URL.createObjectURL(data);
3213
- } else {
3214
- const str = JSON.stringify(data);
3215
- const blob = new Blob([str], { type: "text/json" });
3216
- data = window.URL.createObjectURL(blob);
3228
+ subStringByte(str, start, n) {
3229
+ var r = /[^\x00-\xff]/g;
3230
+ if (str.replace(r, "mm").length <= n) {
3231
+ return str;
3232
+ }
3233
+ var m = Math.floor(n / 2);
3234
+ for (var i = m; i < str.length; i++) {
3235
+ let sub = str.substring(start, i);
3236
+ if (sub.replace(r, "mm").length >= n) {
3237
+ return sub;
3217
3238
  }
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
3239
  }
3222
- var link = document.createElement("a");
3223
- link.href = data;
3224
- link.download = saveName || "";
3225
- link.click();
3226
- window.URL.revokeObjectURL(link.href);
3240
+ return str;
3227
3241
  },
3228
- /**
3229
- * 获取远程文件并读取
3230
- * @param url 远程文件地址
3231
- * @param fileName 文件名
3232
- * @returns Promise<ArrayBuffer>
3233
- */
3234
- readFileFromUrl(url, fileName) {
3235
- return new Promise((resolve, reject) => {
3236
- const xhr = new XMLHttpRequest();
3237
- xhr.open("GET", url, true);
3238
- xhr.responseType = "blob";
3239
- const suffix = fileName.split(".");
3240
- try {
3241
- xhr.onload = function() {
3242
- if (this.status >= 200 && this.status < 300) {
3243
- const blob = xhr.response;
3244
- const objectURL = URL.createObjectURL(blob);
3245
- const file = new File([blob], fileName, {
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);
3242
+ string2Bytes(str) {
3243
+ const bytes = [];
3244
+ let c;
3245
+ const len = str.length;
3246
+ for (let i = 0; i < len; i++) {
3247
+ c = str.charCodeAt(i);
3248
+ if (c >= 65536 && c <= 1114111) {
3249
+ bytes.push(c >> 18 & 7 | 240);
3250
+ bytes.push(c >> 12 & 63 | 128);
3251
+ bytes.push(c >> 6 & 63 | 128);
3252
+ bytes.push(c & 63 | 128);
3253
+ } else if (c >= 2048 && c <= 65535) {
3254
+ bytes.push(c >> 12 & 15 | 224);
3255
+ bytes.push(c >> 6 & 63 | 128);
3256
+ bytes.push(c & 63 | 128);
3257
+ } else if (c >= 128 && c <= 2047) {
3258
+ bytes.push(c >> 6 & 31 | 192);
3259
+ bytes.push(c & 63 | 128);
3260
+ } else {
3261
+ bytes.push(c & 255);
3262
+ }
3263
+ }
3264
+ return new Uint8Array(bytes);
3265
+ },
3266
+ bytes2String(uint8arr) {
3267
+ if (typeof uint8arr === "string") {
3268
+ return uint8arr;
3269
+ }
3270
+ let str = "";
3271
+ const _arr = uint8arr;
3272
+ for (let i = 0; i < _arr.length; i++) {
3273
+ const one = _arr[i].toString(2), v = one.match(/^1+?(?=0)/);
3274
+ if (v && one.length == 8) {
3275
+ const bytesLength = v[0].length;
3276
+ let store = _arr[i].toString(2).slice(7 - bytesLength);
3277
+ for (let st = 1; st < bytesLength; st++) {
3278
+ store += _arr[st + i].toString(2).slice(2);
3279
+ }
3280
+ str += String.fromCharCode(parseInt(store, 2));
3281
+ i += bytesLength - 1;
3282
+ } else {
3283
+ str += String.fromCharCode(_arr[i]);
3266
3284
  }
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
3285
  }
3286
+ return str;
3284
3287
  }
3288
+ };
3289
+ const UrlUtil = {
3285
3290
  /**
3286
- * 播放消息提示音和文字朗读(语音需要有交互)
3291
+ * 将json对象转换为查询字符串
3287
3292
  *
3288
- * @param type 消息类型
3289
- * @param message 消息内容
3290
- * @param options 配置选项,可选参数,包括语言、音量、语速和音高
3291
- * @returns 无返回值
3293
+ * @param json 待转换的json对象
3294
+ * @returns 转换后的查询字符串
3292
3295
  */
3293
- static msg(type, message, options = {}) {
3294
- if (this.isMute) return;
3295
- const typename = Util.decodeDict(type, "success", "恭喜", "error", "发生错误", "warning", "警告", "info", "友情提示") + ":";
3296
- this.speechSynthesisUtterance.text = typename + message;
3297
- this.speechSynthesisUtterance.lang = options.lang || "zh-CN";
3298
- this.speechSynthesisUtterance.volume = options.volume || 1;
3299
- this.speechSynthesisUtterance.rate = options.rate || 1;
3300
- this.speechSynthesisUtterance.pitch = options.pitch || 1;
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;");
3296
+ json2Query(json) {
3297
+ var tempArr = [];
3298
+ for (var i in json) {
3299
+ if (json.hasOwnProperty(i)) {
3300
+ var key = i;
3301
+ var value = json[i];
3302
+ tempArr.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
3303
+ }
3337
3304
  }
3338
- this.msg("success", message, options);
3339
- }
3340
- static successOnce(message, options = {}) {
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 = {
3305
+ var urlParamsStr = tempArr.join("&");
3306
+ return urlParamsStr;
3307
+ },
3349
3308
  /**
3350
- * 防抖函数,在指定的等待时间内,如果连续触发事件,则只在最后一次触发后执行函数。适用于像搜索输入框这种需要用户停止输入后才调用的场景
3309
+ * 从 URL 中解析出查询参数和哈希参数,并以对象的形式返回
3351
3310
  *
3352
- * @param func 需要防抖的函数。
3353
- * @param wait 等待时间,单位毫秒。
3354
- * @param immediate 是否立即执行函数,默认为true。
3355
- * @returns 返回防抖后的函数。
3311
+ * @param href 需要解析的 URL 链接,默认为当前窗口的 URL
3312
+ * @param needDecode 是否需要解码参数值,默认为 true
3313
+ * @returns 返回一个包含解析后参数的对象,其中键为参数名,值为参数值
3356
3314
  */
3357
- debounce(func, wait, immediate = true) {
3358
- let timeout = null;
3359
- let args;
3360
- let timestamp;
3361
- let result;
3362
- const later = () => {
3363
- const last = Date.now() - timestamp;
3364
- if (last < wait && last > 0) {
3365
- timeout = setTimeout(later, wait - last);
3366
- } else {
3367
- timeout = null;
3368
- if (!immediate) {
3369
- result = func.apply(this, args);
3370
- }
3371
- }
3372
- };
3373
- return (...args2) => {
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;
3315
+ query2Json(href = window.location.href, needDecode = true) {
3316
+ const reg = /([^&=]+)=([\w\W]*?)(&|$|#)/g;
3317
+ const { search, hash } = new URL(href);
3318
+ const args = [search, hash];
3319
+ let obj = {};
3320
+ for (let i = 0; i < args.length; i++) {
3321
+ const str = args[i];
3322
+ if (str) {
3323
+ const s = str.replace(/#|\//g, "");
3324
+ const arr = s.split("?");
3325
+ if (arr.length > 1) {
3326
+ for (let j = 1; j < arr.length; j++) {
3327
+ let res;
3328
+ while (res = reg.exec(arr[j])) {
3329
+ obj[res[1]] = needDecode ? decodeURIComponent(res[2]) : res[2];
3330
+ }
3331
+ }
3383
3332
  }
3384
3333
  }
3385
- return result;
3386
- };
3334
+ }
3335
+ return obj;
3336
+ }
3337
+ };
3338
+ const ValidateUtil = {
3339
+ isUrl(v) {
3340
+ return /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/.test(
3341
+ v
3342
+ );
3387
3343
  },
3388
3344
  /**
3389
- * 节流函数,适用于像滚动事件、窗口resize事件这种需要限制调用频率的场景
3390
3345
  *
3391
- * @param func 需要节流的函数
3392
- * @param wait 节流间隔,单位为毫秒
3393
- * @param type 节流类型,1表示时间戳方式,2表示定时器方式
3394
- * @returns 返回一个新的函数,该函数在节流控制下执行传入的函数
3346
+ * @param path 路径字符串
3347
+ * @returns 是否为外链
3395
3348
  */
3396
- throttle(func, wait, type = 1) {
3397
- let previous = 0;
3398
- let timeout = null;
3399
- return (...args) => {
3400
- if (type === 1) {
3401
- const now = Date.now();
3402
- if (now - previous >= wait) {
3403
- func.apply(this, args);
3404
- previous = now;
3405
- }
3406
- } else if (type === 2) {
3407
- if (!timeout) {
3408
- timeout = setTimeout(() => {
3409
- timeout = null;
3410
- func.apply(this, args);
3411
- }, wait);
3412
- }
3349
+ isExternal(path) {
3350
+ const reg = /^(https?:|mailto:|tel:)/;
3351
+ return reg.test(path);
3352
+ },
3353
+ isPhone(v) {
3354
+ return /^1[3|4|5|6|7|8|9][0-9]{9}$/.test(v);
3355
+ },
3356
+ isTel(v) {
3357
+ return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(v);
3358
+ },
3359
+ /**
3360
+ * 判断是否是强密码,至少包含一个大写字母、一个小写字母、一个数字的组合、长度8-20位
3361
+ *
3362
+ * @param v 待检测的密码字符串
3363
+ * @returns 如果是是强密码,则返回 true;否则返回 false
3364
+ */
3365
+ isStrongPwd(v) {
3366
+ return /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,20}$/.test(v);
3367
+ },
3368
+ isEmail(v) {
3369
+ return /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
3370
+ v
3371
+ );
3372
+ },
3373
+ isIP(v) {
3374
+ return /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/.test(
3375
+ v
3376
+ );
3377
+ },
3378
+ isEnglish(v) {
3379
+ return /^[a-zA-Z]+$/.test(v);
3380
+ },
3381
+ isChinese(v) {
3382
+ return /^[\u4E00-\u9FA5]+$/.test(v);
3383
+ },
3384
+ isHTML(v) {
3385
+ return /<("[^"]*"|'[^']*'|[^'">])*>/.test(v);
3386
+ },
3387
+ isXML(v) {
3388
+ return /^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$/.test(v);
3389
+ },
3390
+ isIDCard(v) {
3391
+ return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(v);
3392
+ }
3393
+ };
3394
+ class Cookie {
3395
+ static set(name, value, days = 30) {
3396
+ if (typeof name !== "string" || typeof days !== "number") {
3397
+ ExceptionUtil.throwException("Invalid arguments");
3398
+ }
3399
+ const exp = /* @__PURE__ */ new Date();
3400
+ exp.setTime(exp.getTime() + days * 24 * 60 * 60 * 1e3);
3401
+ document.cookie = `${name}=${encodeURIComponent(value)};expires=${exp.toUTCString()}`;
3402
+ }
3403
+ static remove(name) {
3404
+ var exp = /* @__PURE__ */ new Date();
3405
+ exp.setTime(exp.getTime() - 1);
3406
+ var cval = this.get(name);
3407
+ if (cval != null) {
3408
+ document.cookie = name + "=" + cval + ";expires=" + exp.toUTCString();
3409
+ }
3410
+ }
3411
+ static get(name) {
3412
+ var arr = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)"));
3413
+ if (arr != null) {
3414
+ return arr[2];
3415
+ } else {
3416
+ return "";
3417
+ }
3418
+ }
3419
+ }
3420
+ class CanvasDrawer {
3421
+ constructor(el) {
3422
+ __publicField(this, "ctx");
3423
+ if (typeof el === "string") {
3424
+ el = document.querySelector("#" + el);
3425
+ if (!el) {
3426
+ throw new Error("Element not found");
3413
3427
  }
3414
- };
3415
- },
3416
- /**
3417
- * 缓存函数,将传入的函数fn的计算结果缓存,提高重复计算的效率
3418
- *
3419
- * @param fn 传入待缓存的函数
3420
- * @returns 返回缓存后的函数
3421
- */
3422
- memoize(fn) {
3423
- const cache = /* @__PURE__ */ new Map();
3424
- return (...args) => {
3425
- const argsString = JSON.stringify(args);
3426
- if (cache.has(argsString)) {
3427
- return cache.get(argsString);
3428
+ }
3429
+ if (el instanceof HTMLElement) {
3430
+ const canvas = el;
3431
+ if (canvas.getContext) {
3432
+ this.ctx = canvas.getContext("2d");
3428
3433
  } else {
3429
- const result = fn.apply(this, args);
3430
- cache.set(argsString, result);
3431
- return result;
3434
+ throw new Error("getContext is not available on this element");
3432
3435
  }
3433
- };
3434
- },
3436
+ } else {
3437
+ throw new Error("Element is not an HTMLElement");
3438
+ }
3439
+ }
3435
3440
  /**
3436
- * 递归调用函数,以一定的频率和持续时间执行。
3441
+ * 绘制线条
3437
3442
  *
3438
- * @param fun 要递归调用的函数。
3439
- * @param frequency 每次调用函数之间的时间间隔,单位为毫秒,默认为500毫秒。
3440
- * @param duration 函数递归调用的总时长,单位为毫秒,默认为5000毫秒。
3443
+ * @param start 起始坐标点
3444
+ * @param end 终止坐标点
3445
+ * @param options 绘制选项,包括线条宽度和颜色
3446
+ * @throws 当画布上下文不存在时抛出错误
3441
3447
  */
3442
- recurve(fun, frequency = 500, duration = 5e3) {
3443
- let timer = 0;
3444
- setTimeout(() => {
3445
- timer++;
3446
- if (timer < Math.floor(duration / frequency)) {
3447
- fun.call(this);
3448
- setTimeout(this.recurve.bind(this, fun, frequency, duration), frequency);
3449
- }
3450
- }, frequency);
3451
- },
3448
+ drawLine({ x: startX, y: startY }, { x: endX, y: endY }, options = {}) {
3449
+ this.ctx.beginPath();
3450
+ const width = options.width || 1;
3451
+ const color = options.color || "#000";
3452
+ this.ctx.lineWidth = width;
3453
+ this.ctx.strokeStyle = color;
3454
+ this.ctx.moveTo(startX, startY);
3455
+ this.ctx.lineTo(endX, endY);
3456
+ this.ctx.stroke();
3457
+ }
3452
3458
  /**
3453
- * 确保函数只被调用一次
3459
+ * 绘制圆弧
3454
3460
  *
3455
- * @param func 要被调用的函数
3456
- * @returns 返回一个新的函数,该函数在被首次调用时会执行传入的函数,之后再次调用将不再执行
3461
+ * @param x 圆心x坐标
3462
+ * @param y 圆心y坐标
3463
+ * @param radius 半径
3464
+ * @param startAngle 起始角度(度)
3465
+ * @param endAngle 结束角度(度)
3466
+ * @param anticlockwise 是否逆时针绘制
3467
+ * @param isFill 是否填充
3468
+ * @param bgColor 背景颜色
3469
+ * @throws 当Canvas context为null或undefined时抛出错误
3457
3470
  */
3458
- once(func) {
3459
- let called = false;
3460
- return function(...args) {
3461
- if (!called) {
3462
- called = true;
3463
- return func(...args);
3471
+ drawArc({ x, y }, radius, startAngle, endAngle, anticlockwise, isFill, bgColor) {
3472
+ if (isFill) {
3473
+ this.ctx.fillStyle = bgColor;
3474
+ this.ctx.beginPath();
3475
+ this.ctx.arc(x, y, radius, MathUtil.deg2Rad(startAngle), MathUtil.deg2Rad(endAngle), anticlockwise);
3476
+ this.ctx.fill();
3477
+ } else {
3478
+ this.ctx.strokeStyle = bgColor;
3479
+ this.ctx.beginPath();
3480
+ this.ctx.arc(x, y, radius, MathUtil.deg2Rad(startAngle), MathUtil.deg2Rad(endAngle), anticlockwise);
3481
+ this.ctx.stroke();
3482
+ }
3483
+ }
3484
+ drawImage({ x, y }, src) {
3485
+ const img = new Image();
3486
+ img.src = src;
3487
+ img.onload = () => {
3488
+ const width = img.width;
3489
+ const height = img.height;
3490
+ if (!this.ctx) {
3491
+ throw new Error("Canvas context is null");
3464
3492
  }
3493
+ this.ctx.drawImage(img, x, y, -width / 2, -height / 2);
3465
3494
  };
3466
3495
  }
3467
- };
3468
- const StringUtil = {
3469
- /**
3470
- * 转换字符串大小写
3471
- *
3472
- * @param str 待转换的字符串
3473
- * @param type 转换类型,可选值为 0-4,默认为 4
3474
- * 0:字母大小写反转
3475
- * 1:首字母大写,其余小写
3476
- * 2:首字母小写,其余大写
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;
3496
+ drawText({ x, y }, text, options) {
3497
+ this.ctx.font = options.font || "10px sans-serif";
3498
+ this.ctx.strokeStyle = options.color || "#000";
3499
+ this.ctx.lineWidth = 2;
3500
+ this.ctx.strokeText(text, x, y);
3501
+ }
3502
+ static createCanvas(width = 1, height = 1) {
3503
+ const canvas = document.createElement("canvas");
3504
+ if (width) {
3505
+ canvas.width = width;
3506
3506
  }
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;
3507
+ if (height) {
3508
+ canvas.height = height;
3529
3509
  }
3530
- var m = Math.floor(n / 2);
3531
- for (var i = m; i < str.length; i++) {
3532
- let sub = str.substring(start, i);
3533
- if (sub.replace(r, "mm").length >= n) {
3534
- return sub;
3535
- }
3510
+ return canvas;
3511
+ }
3512
+ }
3513
+ class EventDispatcher {
3514
+ constructor() {
3515
+ __publicField(this, "_listeners", {});
3516
+ __publicField(this, "_mutex", {});
3517
+ __publicField(this, "_context");
3518
+ }
3519
+ addEventListener(type, listener, context, mutexStatus) {
3520
+ this._context = context;
3521
+ const mutex = this._mutex;
3522
+ const listeners = this._listeners;
3523
+ if (listeners[type] === void 0) {
3524
+ listeners[type] = [];
3536
3525
  }
3537
- return str;
3538
- },
3539
- string2Bytes(str) {
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);
3526
+ if (listeners[type].indexOf(listener) === -1) {
3527
+ if (mutexStatus) {
3528
+ mutex[type] = listener;
3559
3529
  }
3530
+ listeners[type].push(listener);
3560
3531
  }
3561
- return new Uint8Array(bytes);
3562
- },
3563
- bytes2String(uint8arr) {
3564
- if (typeof uint8arr === "string") {
3565
- return uint8arr;
3532
+ return this;
3533
+ }
3534
+ hasEventListener(type, listener) {
3535
+ if (this._listeners === null || this._listeners === void 0) return false;
3536
+ const listeners = this._listeners;
3537
+ return listeners[type] !== void 0 && listeners[type].indexOf(listener) !== -1;
3538
+ }
3539
+ removeEventListener(type, listener) {
3540
+ if (this._listeners === void 0) return;
3541
+ const listeners = this._listeners;
3542
+ const listenerArray = listeners[type];
3543
+ if (this._mutex[type] === listener) {
3544
+ this._mutex[type] = null;
3545
+ }
3546
+ if (listenerArray !== void 0) {
3547
+ const index = listenerArray.map((d) => d.toString()).indexOf(listener.toString());
3548
+ if (index !== -1) {
3549
+ listenerArray.splice(index, 1);
3550
+ }
3566
3551
  }
3567
- let str = "";
3568
- const _arr = uint8arr;
3569
- for (let i = 0; i < _arr.length; i++) {
3570
- const one = _arr[i].toString(2), v = one.match(/^1+?(?=0)/);
3571
- if (v && one.length == 8) {
3572
- const bytesLength = v[0].length;
3573
- let store = _arr[i].toString(2).slice(7 - bytesLength);
3574
- for (let st = 1; st < bytesLength; st++) {
3575
- store += _arr[st + i].toString(2).slice(2);
3552
+ }
3553
+ dispatchEvent(event) {
3554
+ if (this._listeners === void 0) return;
3555
+ const listeners = this._listeners;
3556
+ const listenerArray = listeners[event.type];
3557
+ if (listenerArray !== void 0) {
3558
+ event.target = this;
3559
+ const array = listenerArray.slice(0);
3560
+ if (this._mutex[event.type] !== void 0) {
3561
+ const find = array.find((item) => item === this._mutex[event.type]);
3562
+ if (find) {
3563
+ find.call(this._context || this, event);
3564
+ return;
3565
+ }
3566
+ }
3567
+ for (let i = 0, l = array.length; i < l; i++) {
3568
+ const item = array[i];
3569
+ if (typeof item === "function") {
3570
+ item.call(this._context || this, event);
3576
3571
  }
3577
- str += String.fromCharCode(parseInt(store, 2));
3578
- i += bytesLength - 1;
3579
- } else {
3580
- str += String.fromCharCode(_arr[i]);
3581
3572
  }
3582
3573
  }
3583
- return str;
3584
3574
  }
3585
- };
3586
- const UrlUtil = {
3575
+ removeAllListener() {
3576
+ this._mutex = {};
3577
+ for (const key in this._listeners) {
3578
+ this._listeners[key] = [];
3579
+ }
3580
+ }
3581
+ on(type, listener, context, mutexStatus) {
3582
+ return this.addEventListener.call(this, type, listener, context, mutexStatus);
3583
+ }
3584
+ off(type, listener) {
3585
+ return this.removeEventListener.call(this, type, listener);
3586
+ }
3587
+ }
3588
+ class HashMap extends Map {
3589
+ isEmpty() {
3590
+ return this.size === 0;
3591
+ }
3592
+ _values() {
3593
+ return Array.from(this.values());
3594
+ }
3595
+ _keys() {
3596
+ return Array.from(this.keys());
3597
+ }
3598
+ _entries() {
3599
+ return Array.from(this.entries());
3600
+ }
3587
3601
  /**
3588
- * 将json对象转换为查询字符串
3602
+ * 从键值对数组创建一个HashMap对象
3589
3603
  *
3590
- * @param json 待转换的json对象
3591
- * @returns 转换后的查询字符串
3604
+ * @param array 键值对数组,默认为空数组
3605
+ * @returns 返回一个新的HashMap对象
3592
3606
  */
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));
3607
+ static fromEntries(array = []) {
3608
+ const hashMap = new HashMap();
3609
+ array.forEach((element) => {
3610
+ if (Array.isArray(element) && element.length === 2) {
3611
+ hashMap.set(element[0], element[1]);
3600
3612
  }
3601
- }
3602
- var urlParamsStr = tempArr.join("&");
3603
- return urlParamsStr;
3604
- },
3613
+ });
3614
+ return hashMap;
3615
+ }
3605
3616
  /**
3606
- * 从 URL 中解析出查询参数和哈希参数,并以对象的形式返回
3617
+ * 从JSON字符串创建HashMap实例
3607
3618
  *
3608
- * @param href 需要解析的 URL 链接,默认为当前窗口的 URL
3609
- * @param needDecode 是否需要解码参数值,默认为 true
3610
- * @returns 返回一个包含解析后参数的对象,其中键为参数名,值为参数值
3619
+ * @param str JSON字符串
3620
+ * @returns HashMap实例
3611
3621
  */
3612
- query2Json(href = window.location.href, needDecode = true) {
3613
- const reg = /([^&=]+)=([\w\W]*?)(&|$|#)/g;
3614
- const { search, hash } = new URL(href);
3615
- const args = [search, hash];
3616
- let obj = {};
3617
- for (let i = 0; i < args.length; i++) {
3618
- const str = args[i];
3619
- if (str) {
3620
- const s = str.replace(/#|\//g, "");
3621
- const arr = s.split("?");
3622
- if (arr.length > 1) {
3623
- for (let j = 1; j < arr.length; j++) {
3624
- let res;
3625
- while (res = reg.exec(arr[j])) {
3626
- obj[res[1]] = needDecode ? decodeURIComponent(res[2]) : res[2];
3627
- }
3622
+ static fromJson(str) {
3623
+ const json = ObjectUtil.parse(str);
3624
+ return new HashMap(Object.entries(json));
3625
+ }
3626
+ }
3627
+ class WebSocketClient extends EventDispatcher {
3628
+ constructor(url = "ws://127.0.0.1:10088") {
3629
+ super();
3630
+ __publicField(this, "maxCheckTimes", 10);
3631
+ __publicField(this, "url");
3632
+ __publicField(this, "checkTimes", 0);
3633
+ __publicField(this, "connectStatus", false);
3634
+ __publicField(this, "client", null);
3635
+ this.maxCheckTimes = 10;
3636
+ this.url = url;
3637
+ this.checkTimes = 0;
3638
+ this.connect();
3639
+ this.connCheckStatus(this.maxCheckTimes);
3640
+ }
3641
+ connect() {
3642
+ this.disconnect();
3643
+ if (this.url) {
3644
+ try {
3645
+ const self = this;
3646
+ console.info("创建ws连接>>>" + this.url);
3647
+ this.client = new WebSocket(this.url);
3648
+ if (this.client) {
3649
+ this.client.onopen = function(message) {
3650
+ self.dispatchEvent({
3651
+ type: EventType.WEB_SOCKET_CONNECT,
3652
+ message
3653
+ });
3654
+ };
3655
+ this.client.onmessage = function(message) {
3656
+ self.connectStatus = true;
3657
+ self.dispatchEvent({
3658
+ type: EventType.WEB_SOCKET_MESSAGE,
3659
+ message
3660
+ });
3661
+ };
3662
+ this.client.onclose = function(message) {
3663
+ self.dispatchEvent({
3664
+ type: EventType.WEB_SOCKET_CLOSE,
3665
+ message
3666
+ });
3667
+ };
3668
+ if (this.checkTimes === this.maxCheckTimes) {
3669
+ this.client.onerror = function(message) {
3670
+ self.dispatchEvent({
3671
+ type: EventType.WEB_SOCKET_ERROR,
3672
+ message
3673
+ });
3674
+ };
3628
3675
  }
3629
3676
  }
3677
+ } catch (ex) {
3678
+ console.error("创建ws连接失败" + this.url + ":" + ex);
3630
3679
  }
3631
3680
  }
3632
- return obj;
3633
3681
  }
3634
- };
3635
- const ValidateUtil = {
3636
- isUrl(v) {
3637
- 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(
3638
- v
3639
- );
3640
- },
3641
- isPhone(v) {
3642
- return /^1[3|4|5|6|7|8|9][0-9]{9}$/.test(v);
3643
- },
3644
- isTel(v) {
3645
- return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(v);
3646
- },
3647
- /**
3648
- * 判断是否是强密码,至少包含一个大写字母、一个小写字母、一个数字的组合、长度8-20位
3649
- *
3650
- * @param v 待检测的密码字符串
3651
- * @returns 如果是是强密码,则返回 true;否则返回 false
3652
- */
3653
- isStrongPwd(v) {
3654
- return /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,20}$/.test(v);
3655
- },
3656
- isEmail(v) {
3657
- 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(
3658
- v
3659
- );
3660
- },
3661
- isIP(v) {
3662
- 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(
3663
- v
3664
- );
3665
- },
3666
- isEnglish(v) {
3667
- return /^[a-zA-Z]+$/.test(v);
3668
- },
3669
- isChinese(v) {
3670
- return /^[\u4E00-\u9FA5]+$/.test(v);
3671
- },
3672
- isHTML(v) {
3673
- return /<("[^"]*"|'[^']*'|[^'">])*>/.test(v);
3674
- },
3675
- isXML(v) {
3676
- return /^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$/.test(v);
3677
- },
3678
- isIDCard(v) {
3679
- return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(v);
3682
+ disconnect() {
3683
+ if (this.client) {
3684
+ try {
3685
+ console.log("ws断开连接" + this.url);
3686
+ this.client.close();
3687
+ this.client = null;
3688
+ } catch (ex) {
3689
+ this.client = null;
3690
+ }
3691
+ }
3680
3692
  }
3681
- };
3693
+ connCheckStatus(times) {
3694
+ if (this.checkTimes > times) return;
3695
+ setTimeout(() => {
3696
+ this.checkTimes++;
3697
+ if (this.state !== WebSocket.CONNECTING && this.state !== WebSocket.OPEN) {
3698
+ this.connect();
3699
+ }
3700
+ this.connCheckStatus(times);
3701
+ }, 2e3);
3702
+ }
3703
+ send(message) {
3704
+ if (this.client && this.state === WebSocket.OPEN) {
3705
+ this.client.send(message);
3706
+ return true;
3707
+ }
3708
+ console.error(this.url + "消息发送失败:" + message);
3709
+ return this;
3710
+ }
3711
+ heartbeat() {
3712
+ setTimeout(() => {
3713
+ if (this.state === WebSocket.OPEN) {
3714
+ this.send("HeartBeat");
3715
+ }
3716
+ console.log("HeartBeat," + this.url);
3717
+ setTimeout(this.heartbeat, 3e4);
3718
+ }, 1e3);
3719
+ }
3720
+ get state() {
3721
+ var _a2;
3722
+ return (_a2 = this.client) == null ? void 0 : _a2.readyState;
3723
+ }
3724
+ }
3682
3725
  const _MqttClient = class _MqttClient extends EventDispatcher {
3683
3726
  constructor(url = `ws://${window.document.domain}:20007/mqtt`, config = {}) {
3684
3727
  super();
@@ -3695,7 +3738,8 @@ const _MqttClient = class _MqttClient extends EventDispatcher {
3695
3738
  clientId: Util.guid(),
3696
3739
  username: this.context.MQTT_USERNAME,
3697
3740
  password: this.context.MQTT_PASSWORD,
3698
- clean: true
3741
+ clean: true,
3742
+ rejectUnauthorized: this.context.REJECT_UNAUTHORIZED
3699
3743
  };
3700
3744
  this.url = url;
3701
3745
  this.client = connect(this.url, this.options);
@@ -3795,7 +3839,8 @@ const _MqttClient = class _MqttClient extends EventDispatcher {
3795
3839
  */
3796
3840
  __publicField(_MqttClient, "defaultContext", {
3797
3841
  MQTT_TIMEOUTM: 2e3,
3798
- MQTT_MAX_RETRY: 3
3842
+ MQTT_MAX_RETRY: 3,
3843
+ REJECT_UNAUTHORIZED: true
3799
3844
  });
3800
3845
  let MqttClient = _MqttClient;
3801
3846
  const _Storage = class _Storage {
@@ -4080,7 +4125,7 @@ class WebGL {
4080
4125
  var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
4081
4126
  if (!compiled) {
4082
4127
  var error = gl.getShaderInfoLog(shader);
4083
- console.error("Failed to compile shader: " + error);
4128
+ ExceptionUtil.throwException("Failed to compile shader: " + error);
4084
4129
  gl.deleteShader(shader);
4085
4130
  return null;
4086
4131
  }