@things-factory/kpi 9.0.28 → 9.0.30

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.
Files changed (131) hide show
  1. package/client/google-map/common-google-map.ts +332 -0
  2. package/client/google-map/google-map-loader.ts +29 -0
  3. package/client/google-map/script-loader.ts +173 -0
  4. package/client/pages/kpi-dashboard/cards/kpi-level1-card.ts +248 -0
  5. package/client/pages/kpi-dashboard/cards/kpi-level2-comparison.ts +369 -0
  6. package/client/pages/kpi-dashboard/cards/kpi-level3-comparison.ts +443 -0
  7. package/client/pages/kpi-dashboard/components/kpi-chart-toggle.ts +73 -0
  8. package/client/pages/kpi-dashboard/components/kpi-map-panel.ts +222 -0
  9. package/client/pages/kpi-dashboard/kpi-dashboard-map.ts +786 -0
  10. package/client/pages/kpi-dashboard/kpi-dashboard.ts +416 -0
  11. package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +761 -0
  12. package/client/pages/kpi-statistic/kpi-statistic-importer.ts +90 -0
  13. package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +505 -0
  14. package/client/route.ts +12 -0
  15. package/dist-client/google-map/common-google-map.d.ts +34 -0
  16. package/dist-client/google-map/common-google-map.js +300 -0
  17. package/dist-client/google-map/common-google-map.js.map +1 -0
  18. package/dist-client/google-map/google-map-loader.d.ts +6 -0
  19. package/dist-client/google-map/google-map-loader.js +22 -0
  20. package/dist-client/google-map/google-map-loader.js.map +1 -0
  21. package/dist-client/google-map/script-loader.d.ts +3 -0
  22. package/dist-client/google-map/script-loader.js +144 -0
  23. package/dist-client/google-map/script-loader.js.map +1 -0
  24. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +17 -0
  25. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +279 -0
  26. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
  27. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +19 -0
  28. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +385 -0
  29. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
  30. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +23 -0
  31. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +465 -0
  32. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
  33. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
  34. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +79 -0
  35. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
  36. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +23 -0
  37. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +223 -0
  38. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
  39. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +38 -0
  40. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +813 -0
  41. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
  42. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +21 -0
  43. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +398 -0
  44. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
  45. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.d.ts +53 -0
  46. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +718 -0
  47. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -0
  48. package/dist-client/pages/kpi-statistic/kpi-statistic-importer.d.ts +23 -0
  49. package/dist-client/pages/kpi-statistic/kpi-statistic-importer.js +92 -0
  50. package/dist-client/pages/kpi-statistic/kpi-statistic-importer.js.map +1 -0
  51. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +66 -0
  52. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +487 -0
  53. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -0
  54. package/dist-client/route.d.ts +1 -1
  55. package/dist-client/route.js +9 -0
  56. package/dist-client/route.js.map +1 -1
  57. package/dist-client/tsconfig.tsbuildinfo +1 -1
  58. package/dist-server/index.d.ts +1 -0
  59. package/dist-server/index.js +1 -0
  60. package/dist-server/index.js.map +1 -1
  61. package/dist-server/migrations/index.d.ts +1 -0
  62. package/dist-server/migrations/index.js +12 -0
  63. package/dist-server/migrations/index.js.map +1 -0
  64. package/dist-server/service/index.d.ts +3 -2
  65. package/dist-server/service/index.js +4 -0
  66. package/dist-server/service/index.js.map +1 -1
  67. package/dist-server/service/kpi/kpi-mutation.js +9 -0
  68. package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
  69. package/dist-server/service/kpi/kpi-query.js +2 -0
  70. package/dist-server/service/kpi/kpi-query.js.map +1 -1
  71. package/dist-server/service/kpi-alert/kpi-alert-query.js +1 -0
  72. package/dist-server/service/kpi-alert/kpi-alert-query.js.map +1 -1
  73. package/dist-server/service/kpi-category/kpi-category-mutation.js +4 -0
  74. package/dist-server/service/kpi-category/kpi-category-mutation.js.map +1 -1
  75. package/dist-server/service/kpi-category/kpi-category-query.js +2 -0
  76. package/dist-server/service/kpi-category/kpi-category-query.js.map +1 -1
  77. package/dist-server/service/kpi-metric/kpi-metric-mutation.js +6 -0
  78. package/dist-server/service/kpi-metric/kpi-metric-mutation.js.map +1 -1
  79. package/dist-server/service/kpi-metric/kpi-metric-query.js +2 -0
  80. package/dist-server/service/kpi-metric/kpi-metric-query.js.map +1 -1
  81. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +7 -0
  82. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
  83. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +2 -0
  84. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -1
  85. package/dist-server/service/kpi-statistic/index.d.ts +6 -0
  86. package/dist-server/service/kpi-statistic/index.js +10 -0
  87. package/dist-server/service/kpi-statistic/index.js.map +1 -0
  88. package/dist-server/service/kpi-statistic/kpi-statistic-mutation.d.ts +10 -0
  89. package/dist-server/service/kpi-statistic/kpi-statistic-mutation.js +198 -0
  90. package/dist-server/service/kpi-statistic/kpi-statistic-mutation.js.map +1 -0
  91. package/dist-server/service/kpi-statistic/kpi-statistic-query.d.ts +13 -0
  92. package/dist-server/service/kpi-statistic/kpi-statistic-query.js +92 -0
  93. package/dist-server/service/kpi-statistic/kpi-statistic-query.js.map +1 -0
  94. package/dist-server/service/kpi-statistic/kpi-statistic-type.d.ts +59 -0
  95. package/dist-server/service/kpi-statistic/kpi-statistic-type.js +200 -0
  96. package/dist-server/service/kpi-statistic/kpi-statistic-type.js.map +1 -0
  97. package/dist-server/service/kpi-statistic/kpi-statistic.d.ts +38 -0
  98. package/dist-server/service/kpi-statistic/kpi-statistic.js +177 -0
  99. package/dist-server/service/kpi-statistic/kpi-statistic.js.map +1 -0
  100. package/dist-server/service/kpi-value/kpi-value-mutation.js +9 -0
  101. package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
  102. package/dist-server/service/kpi-value/kpi-value-query.js +2 -0
  103. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -1
  104. package/dist-server/tsconfig.tsbuildinfo +1 -1
  105. package/helps/kpi/kpi-statistic.md +160 -0
  106. package/package.json +2 -2
  107. package/server/index.ts +1 -0
  108. package/server/migrations/index.ts +9 -0
  109. package/server/service/index.ts +8 -0
  110. package/server/service/kpi/kpi-mutation.ts +9 -0
  111. package/server/service/kpi/kpi-query.ts +2 -0
  112. package/server/service/kpi-alert/kpi-alert-query.ts +2 -1
  113. package/server/service/kpi-category/kpi-category-mutation.ts +4 -0
  114. package/server/service/kpi-category/kpi-category-query.ts +2 -0
  115. package/server/service/kpi-metric/kpi-metric-mutation.ts +6 -0
  116. package/server/service/kpi-metric/kpi-metric-query.ts +2 -0
  117. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +7 -0
  118. package/server/service/kpi-metric-value/kpi-metric-value-query.ts +3 -1
  119. package/server/service/kpi-statistic/index.ts +7 -0
  120. package/server/service/kpi-statistic/kpi-statistic-mutation.ts +194 -0
  121. package/server/service/kpi-statistic/kpi-statistic-query.ts +59 -0
  122. package/server/service/kpi-statistic/kpi-statistic-type.ts +152 -0
  123. package/server/service/kpi-statistic/kpi-statistic.ts +167 -0
  124. package/server/service/kpi-value/kpi-value-mutation.ts +9 -0
  125. package/server/service/kpi-value/kpi-value-query.ts +2 -0
  126. package/things-factory.config.js +4 -1
  127. package/translations/en.json +24 -1
  128. package/translations/ja.json +43 -20
  129. package/translations/ko.json +30 -7
  130. package/translations/ms.json +35 -12
  131. package/translations/zh.json +39 -16
@@ -0,0 +1,332 @@
1
+ import { LitElement, html, css } from 'lit'
2
+ import { customElement, property, state } from 'lit/decorators.js'
3
+ import { ScrollbarStyles } from '@operato/styles'
4
+
5
+ import GoogleMapLoader from './google-map-loader.js'
6
+
7
+ declare global {
8
+ interface Window {
9
+ google: any
10
+ markerClusterer?: any
11
+ }
12
+ }
13
+
14
+ declare const google: any
15
+
16
+ declare namespace google.maps {
17
+ interface MapsLibrary {
18
+ Map: any
19
+ InfoWindow: any
20
+ }
21
+
22
+ interface MarkerLibrary {
23
+ AdvancedMarkerElement: any
24
+ PinElement: any
25
+ }
26
+ }
27
+
28
+ @customElement('common-google-map')
29
+ export class CommonGoogleMap extends LitElement {
30
+ static styles = [
31
+ ScrollbarStyles,
32
+ css`
33
+ :host {
34
+ display: flex;
35
+ }
36
+
37
+ [map] {
38
+ flex: 1;
39
+ }
40
+
41
+ .gm-style .gm-style-iw-c {
42
+ padding: 0;
43
+ }
44
+
45
+ .gm-style .gm-style-iw-d {
46
+ overflow: auto !important;
47
+ }
48
+ .gm-style .gm-style-iw-d + button {
49
+ top: 0 !important;
50
+ right: 0 !important;
51
+ }
52
+ `
53
+ ]
54
+
55
+ @property({ type: Object }) center
56
+ @property({ type: Number }) zoom
57
+ @property({ type: Array }) locations: any[] = []
58
+ @property({ type: Object }) focused
59
+ @property({ type: Array }) polygons
60
+ @property({ type: Array }) polylines
61
+ @property({ type: Array }) markers
62
+ @property({ type: Array }) boundCoords
63
+ @property({ type: Object }) controls
64
+ @property({ type: Number }) clusterZoom = 10
65
+
66
+ @state() map: any = null
67
+ @state() defaultCenter: any = { lat: 36.5, lng: 127.5 }
68
+ private _infoWindow: any = null
69
+ private _markerClusterer: any = null
70
+
71
+ get anchor() {
72
+ return this.renderRoot.querySelector('[map]')
73
+ }
74
+
75
+ async readyMap() {
76
+ await GoogleMapLoader.load()
77
+
78
+ // MarkerClusterer 라이브러리 로드
79
+ if (!window.markerClusterer) {
80
+ await GoogleMapLoader.loadMarkerClusterer()
81
+ }
82
+
83
+ if (this.map) {
84
+ return
85
+ }
86
+
87
+ // DOM이 준비될 때까지 기다림
88
+ await this.updateComplete
89
+
90
+ // anchor가 준비될 때까지 기다림
91
+ let attempts = 0
92
+ const maxAttempts = 20
93
+ while (attempts < maxAttempts) {
94
+ const anchor = this.anchor as HTMLElement
95
+ if (anchor && anchor.offsetWidth > 0) {
96
+ break
97
+ }
98
+ await new Promise(resolve => setTimeout(resolve, 50))
99
+ attempts++
100
+ }
101
+
102
+ if (!this.anchor) {
103
+ console.error('Map anchor element not found')
104
+ return
105
+ }
106
+
107
+ var show = async (center, zoom) => {
108
+ try {
109
+ // Google Maps 최신 API 사용
110
+ const { Map } = (await google.maps.importLibrary('maps')) as google.maps.MapsLibrary
111
+
112
+ const map = new Map(this.anchor, {
113
+ zoom,
114
+ center,
115
+ mapId: 'DEMO_MAP_ID'
116
+ })
117
+
118
+ this.markers && this.markers.forEach(marker => marker.setMap(map))
119
+
120
+ this.map = map
121
+
122
+ this.dispatchEvent(
123
+ new CustomEvent('map-change', {
124
+ detail: this.map
125
+ })
126
+ )
127
+
128
+ this.resetBounds()
129
+ } catch (e) {
130
+ console.error(e)
131
+ }
132
+ }
133
+
134
+ var { center, zoom = 10 } = this
135
+
136
+ /* center 속성이 설정되어있지 않으면, 현재 위치를 구해서 지도의 center로 설정한다. */
137
+ if (!center && 'geolocation' in navigator && !this.boundCoords?.length) {
138
+ navigator.geolocation.getCurrentPosition(
139
+ ({ coords: { latitude: lat, longitude: lng } }) => show({ lat, lng }, zoom),
140
+ err => {
141
+ console.warn(`navigator.geolocation.getCurrentPosition failed. (${err.code}): ${err.message}`)
142
+ show(this.defaultCenter, zoom)
143
+ },
144
+ {
145
+ /* https://stackoverflow.com/questions/3397585/navigator-geolocation-getcurrentposition-sometimes-works-sometimes-doesnt */
146
+ timeout: 500
147
+ }
148
+ )
149
+ } else {
150
+ show(center, zoom)
151
+ }
152
+ }
153
+
154
+ async buildMarkers(locations: any[] = []) {
155
+ if (!this.map) {
156
+ return
157
+ }
158
+
159
+ if (this.markers) {
160
+ this.markers.forEach(marker => marker.setMap(null))
161
+ this.markers = []
162
+ }
163
+
164
+ // 기존 클러스터 제거
165
+ if (this._markerClusterer) {
166
+ this._markerClusterer.clearMarkers()
167
+ this._markerClusterer = null
168
+ }
169
+
170
+ // Google Maps 최신 API 사용
171
+ const { AdvancedMarkerElement, PinElement } = (await google.maps.importLibrary(
172
+ 'marker'
173
+ )) as google.maps.MarkerLibrary
174
+
175
+ this.markers = locations
176
+ .map(location => {
177
+ // location 객체가 유효한지 확인
178
+ if (!location || typeof location !== 'object') {
179
+ console.warn('Invalid location object:', location)
180
+ return null
181
+ }
182
+
183
+ // lat, lng 값이 유효한지 확인
184
+ const lat = parseFloat(location.lat)
185
+ const lng = parseFloat(location.lng)
186
+
187
+ if (isNaN(lat) || isNaN(lng)) {
188
+ console.warn('Invalid lat/lng values:', location)
189
+ return null
190
+ }
191
+
192
+ // LatLng 객체 생성
193
+ const position = new google.maps.LatLng(lat, lng)
194
+
195
+ // AdvancedMarkerElement 사용
196
+ const marker = new AdvancedMarkerElement({
197
+ position: position,
198
+ map: null // 클러스터에서 관리하므로 지도에 직접 추가하지 않음
199
+ })
200
+
201
+ marker.addListener('click', () => {
202
+ if (location?.content) {
203
+ var infowindow = this.infoWindow
204
+ infowindow.open(this.map, marker)
205
+ infowindow.setContent(location.content)
206
+ }
207
+ })
208
+
209
+ return marker
210
+ })
211
+ .filter(marker => marker !== null) // null 마커 제거
212
+
213
+ // Google Maps 공식 MarkerClusterer 사용 (예시와 동일한 방식)
214
+ if (this.markers.length > 0 && window.markerClusterer) {
215
+ this._markerClusterer = new window.markerClusterer.MarkerClusterer({
216
+ markers: this.markers,
217
+ map: this.map
218
+ })
219
+ }
220
+ }
221
+
222
+ get infoWindow() {
223
+ if (!this._infoWindow && this.map) {
224
+ this._infoWindow = new google.maps.InfoWindow({
225
+ content: 'loading...'
226
+ })
227
+ }
228
+
229
+ return this._infoWindow
230
+ }
231
+
232
+ setFocus(focus, icon) {
233
+ focus.setZIndex(1)
234
+ focus.setIcon(icon)
235
+ }
236
+
237
+ resetFocus(focus, icon) {
238
+ focus.setZIndex(0)
239
+ focus.setIcon(icon)
240
+ }
241
+
242
+ async changeFocus(after, before) {
243
+ await this.readyMap()
244
+
245
+ // map이 준비되지 않았으면 포커스 변경하지 않음
246
+ if (!this.map) {
247
+ return
248
+ }
249
+
250
+ var locations = this.locations || []
251
+
252
+ if (before) {
253
+ var idx = locations.findIndex(location => {
254
+ // location 객체의 구조를 안전하게 확인
255
+ const beforePos = before?.position
256
+ const locationPos = location?.position
257
+
258
+ return (
259
+ location?.name == before?.name && locationPos?.lat == beforePos?.lat && locationPos?.lng == beforePos?.lng
260
+ )
261
+ })
262
+ idx !== -1 && this.markers && this.resetFocus(this.markers[idx], locations[idx]?.icon)
263
+ }
264
+
265
+ if (after) {
266
+ var idx = locations.findIndex(location => {
267
+ // location 객체의 구조를 안전하게 확인
268
+ const afterPos = after?.position
269
+ const locationPos = location?.position
270
+
271
+ return location?.name == after?.name && locationPos?.lat == afterPos?.lat && locationPos?.lng == afterPos?.lng
272
+ })
273
+ idx !== -1 && this.markers && this.setFocus(this.markers[idx], after?.icon)
274
+ }
275
+ }
276
+
277
+ async updated(changes) {
278
+ if (!this.map) {
279
+ await this.readyMap()
280
+ }
281
+
282
+ if (changes.has('locations')) {
283
+ this.buildMarkers(this.locations)
284
+ }
285
+
286
+ if (changes.has('focused')) {
287
+ this.changeFocus(this.focused, changes.get('focused'))
288
+ }
289
+
290
+ if (changes.has('center')) {
291
+ this.map.setCenter(this.center)
292
+ }
293
+
294
+ if (changes.has('polygons')) {
295
+ ;(changes.get('polygons') || []).forEach(geofence => geofence.setMap(null))
296
+ ;(this.polygons || []).forEach(geofence => geofence.setMap(this.map))
297
+ }
298
+
299
+ if (changes.has('polylines')) {
300
+ ;(changes.get('polylines') || []).forEach(polyline => polyline.setMap(null))
301
+ ;(this.polylines || []).forEach(polyline => polyline.setMap(this.map))
302
+ }
303
+
304
+ if (changes.has('markers')) {
305
+ ;(changes.get('markers') || []).forEach(marker => marker.setMap(null))
306
+ ;(this.markers || []).forEach(marker => marker.setMap(this.map))
307
+ }
308
+
309
+ if (changes.has('boundCoords')) {
310
+ this.resetBounds()
311
+ }
312
+
313
+ // 클러스터링 설정 변경 시 마커 재구성
314
+ if (changes.has('clusterZoom')) {
315
+ this.buildMarkers(this.locations)
316
+ }
317
+ }
318
+
319
+ render() {
320
+ return html` <div map></div> `
321
+ }
322
+
323
+ resetBounds() {
324
+ if (!this.boundCoords || this.boundCoords.length < 1 || !this.map) {
325
+ return
326
+ }
327
+
328
+ var bounds = new google.maps.LatLngBounds()
329
+ this.boundCoords.forEach(coord => bounds.extend(coord))
330
+ this.map.fitBounds(bounds)
331
+ }
332
+ }
@@ -0,0 +1,29 @@
1
+ import ScriptLoader from './script-loader.js'
2
+
3
+ export default class GoogleMapLoader {
4
+ static loaded = false
5
+ static markerClustererLoaded = false
6
+
7
+ static async load() {
8
+ if (GoogleMapLoader.loaded) {
9
+ return
10
+ }
11
+ var key = 'AIzaSyBgQZb-SFqjQBC_XTxNiz0XapejNwV9PgA'
12
+
13
+ await ScriptLoader.load(
14
+ 'https://maps.googleapis.com/maps/api/js' + (key ? '?key=' + key : '') + '&libraries=places'
15
+ )
16
+ GoogleMapLoader.loaded = true
17
+ }
18
+
19
+ static async loadMarkerClusterer() {
20
+ if (GoogleMapLoader.markerClustererLoaded) {
21
+ return
22
+ }
23
+
24
+ // Google Maps 공식 MarkerClusterer 라이브러리 로드
25
+ await ScriptLoader.load('https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js')
26
+
27
+ GoogleMapLoader.markerClustererLoaded = true
28
+ }
29
+ }
@@ -0,0 +1,173 @@
1
+ /*
2
+ * 1. SCRIPTS, STYLES 에는 각 소스별 상태를 관리한다.
3
+ * SCRIPTS, STYLES : {
4
+ * 'http://echarts.baidu.com/dist/echarts.min.js': true | false (true means loaded, false means not loaded yet)
5
+ * }
6
+ *
7
+ * 2. PROMISES : 각 소스군별 pending resolver, reject 을 관리한다.
8
+ * PROMISES: [{
9
+ * resolve,
10
+ * reject,
11
+ * srcs: ['src1', 'src2']
12
+ * }]
13
+ *
14
+ * [특징]
15
+ * 1. CORS 제약에서 자유롭다.
16
+ * 2. 같은 스크립트를 반복해서 로드하지 않는다.
17
+ * 3. 로드에 실패한 스크립트도 다시 반복해서 로드를 시도할 수 있다.
18
+ */
19
+
20
+ // 타입 정의 추가
21
+ interface LoadPromise {
22
+ resolve: (value?: any) => void
23
+ reject: (error: any) => void
24
+ scripts: string[]
25
+ styles?: string[]
26
+ }
27
+
28
+ // SystemJS를 활용하는 경우에는 다음과 같이 import 할 수 있다.
29
+ //
30
+ // import System from 'systemjs/dist/system-production'
31
+
32
+ var SCRIPTS: Record<string, boolean> = {}
33
+ var STYLES: Record<string, boolean> = {}
34
+
35
+ var PROMISES: (LoadPromise | null)[] = []
36
+
37
+ function onload(e) {
38
+ var types = e.target.tagName == 'SCRIPT' ? SCRIPTS : STYLES
39
+ var src = e.target.src || e.target.href
40
+
41
+ types[src] = true
42
+
43
+ PROMISES.forEach((ready, index) => {
44
+ if (!ready) return
45
+
46
+ let { resolve, scripts, styles } = ready
47
+
48
+ if (types == SCRIPTS) {
49
+ let idx = scripts.indexOf(src)
50
+ if (idx > -1 && idx < scripts.length - 1) request_script(scripts[idx + 1])
51
+ }
52
+
53
+ for (let i = 0; i < scripts.length; i++) if (!SCRIPTS[scripts[i]]) return
54
+
55
+ if (styles) {
56
+ for (let i = 0; i < styles.length; i++) if (!STYLES[styles[i]]) return
57
+ }
58
+
59
+ resolve()
60
+
61
+ PROMISES[index] = null
62
+ })
63
+
64
+ PROMISES = PROMISES.filter(x => x)
65
+ }
66
+
67
+ function onerror(e) {
68
+ var src = e.target.src
69
+ var types = e.target.tagName == 'SCRIPT' ? SCRIPTS : STYLES
70
+
71
+ PROMISES.forEach((ready, index) => {
72
+ if (!ready) return
73
+
74
+ let { reject, scripts, styles } = ready
75
+
76
+ let done = false
77
+
78
+ if (types === SCRIPTS) {
79
+ for (let i = 0; i < scripts.length; i++) {
80
+ if (scripts[i] == src) {
81
+ done = true
82
+ break
83
+ }
84
+ }
85
+ } else if (styles) {
86
+ for (let i = 0; i < styles.length; i++) {
87
+ if (styles[i] == src) {
88
+ done = true
89
+ break
90
+ }
91
+ }
92
+ }
93
+
94
+ if (done) {
95
+ reject(e)
96
+
97
+ PROMISES[index] = null
98
+ }
99
+ })
100
+
101
+ PROMISES = PROMISES.filter(x => x)
102
+
103
+ delete types[src]
104
+ document.head.removeChild(e.target)
105
+ }
106
+
107
+ function request_script(src) {
108
+ SCRIPTS[src] = false
109
+
110
+ var script = document.createElement('script')
111
+ script.onload = onload
112
+ script.onerror = onerror
113
+
114
+ script.type = 'text/javascript'
115
+ script.src = src
116
+ document.head.appendChild(script)
117
+ }
118
+
119
+ function request_style(src) {
120
+ STYLES[src] = false
121
+
122
+ var link = document.createElement('link')
123
+ link.onload = onload
124
+ link.onerror = onerror
125
+
126
+ link.type = 'text/css'
127
+ link.rel = 'stylesheet'
128
+ link.media = 'screen,print'
129
+ link.href = src
130
+
131
+ document.head.appendChild(link)
132
+ }
133
+
134
+ export default class ScriptLoader {
135
+ static load(scripts: string | string[], styles?: string | string[]) {
136
+ if (typeof scripts == 'string') scripts = [scripts]
137
+ if (typeof styles == 'string') styles = [styles]
138
+
139
+ return new Promise<void>(function (resolve, reject) {
140
+ if ((scripts && !(scripts instanceof Array)) || (styles && !(styles instanceof Array))) {
141
+ reject('invalid sources for load')
142
+ return
143
+ }
144
+
145
+ /* check state of each src */
146
+ var done = true
147
+
148
+ // style first
149
+ styles &&
150
+ styles.forEach(src => {
151
+ if (!STYLES.hasOwnProperty(src)) request_style(src)
152
+
153
+ if (!STYLES[src]) done = false
154
+ })
155
+
156
+ var first
157
+ if (scripts && scripts.length > 0) {
158
+ scripts.forEach(src => {
159
+ if (!SCRIPTS.hasOwnProperty(src)) first = first || src
160
+ else if (!SCRIPTS[src]) done = false
161
+ })
162
+ }
163
+
164
+ if (first) request_script(first)
165
+ else if (done) {
166
+ resolve()
167
+ return
168
+ }
169
+
170
+ PROMISES.push({ resolve, reject, scripts, styles })
171
+ })
172
+ }
173
+ }