Showing
33 changed files
with
1293 additions
and
798 deletions
@@ -27,22 +27,22 @@ | @@ -27,22 +27,22 @@ | ||
27 | "src/assets", | 27 | "src/assets", |
28 | { | 28 | { |
29 | "glob": "worker-html.js", | 29 | "glob": "worker-html.js", |
30 | - "input": "./node_modules/ace-builds/src-min/", | 30 | + "input": "./node_modules/ace-builds/src-noconflict/", |
31 | "output": "/" | 31 | "output": "/" |
32 | }, | 32 | }, |
33 | { | 33 | { |
34 | "glob": "worker-css.js", | 34 | "glob": "worker-css.js", |
35 | - "input": "./node_modules/ace-builds/src-min/", | 35 | + "input": "./node_modules/ace-builds/src-noconflict/", |
36 | "output": "/" | 36 | "output": "/" |
37 | }, | 37 | }, |
38 | { | 38 | { |
39 | "glob": "worker-json.js", | 39 | "glob": "worker-json.js", |
40 | - "input": "./node_modules/ace-builds/src-min/", | 40 | + "input": "./node_modules/ace-builds/src-noconflict/", |
41 | "output": "/" | 41 | "output": "/" |
42 | }, | 42 | }, |
43 | { | 43 | { |
44 | "glob": "worker-javascript.js", | 44 | "glob": "worker-javascript.js", |
45 | - "input": "./node_modules/ace-builds/src-min/", | 45 | + "input": "./node_modules/ace-builds/src-noconflict/", |
46 | "output": "/" | 46 | "output": "/" |
47 | }, | 47 | }, |
48 | { | 48 | { |
@@ -102,24 +102,6 @@ | @@ -102,24 +102,6 @@ | ||
102 | "node_modules/js-beautify/js/lib/beautify.js", | 102 | "node_modules/js-beautify/js/lib/beautify.js", |
103 | "node_modules/js-beautify/js/lib/beautify-css.js", | 103 | "node_modules/js-beautify/js/lib/beautify-css.js", |
104 | "node_modules/js-beautify/js/lib/beautify-html.js", | 104 | "node_modules/js-beautify/js/lib/beautify-html.js", |
105 | - "node_modules/ace-builds/src-min/ace.js", | ||
106 | - "node_modules/ace-builds/src-min/ext-language_tools.js", | ||
107 | - "node_modules/ace-builds/src-min/ext-searchbox.js", | ||
108 | - "node_modules/ace-builds/src-min/theme-github.js", | ||
109 | - "node_modules/ace-builds/src-min/mode-text.js", | ||
110 | - "node_modules/ace-builds/src-min/mode-markdown.js", | ||
111 | - "node_modules/ace-builds/src-min/mode-html.js", | ||
112 | - "node_modules/ace-builds/src-min/mode-css.js", | ||
113 | - "node_modules/ace-builds/src-min/mode-json.js", | ||
114 | - "node_modules/ace-builds/src-min/mode-java.js", | ||
115 | - "node_modules/ace-builds/src-min/mode-javascript.js", | ||
116 | - "node_modules/ace-builds/src-min/snippets/text.js", | ||
117 | - "node_modules/ace-builds/src-min/snippets/markdown.js", | ||
118 | - "node_modules/ace-builds/src-min/snippets/html.js", | ||
119 | - "node_modules/ace-builds/src-min/snippets/css.js", | ||
120 | - "node_modules/ace-builds/src-min/snippets/json.js", | ||
121 | - "node_modules/ace-builds/src-min/snippets/java.js", | ||
122 | - "node_modules/ace-builds/src-min/snippets/javascript.js", | ||
123 | "node_modules/systemjs/dist/system.js", | 105 | "node_modules/systemjs/dist/system.js", |
124 | "node_modules/jstree/dist/jstree.min.js" | 106 | "node_modules/jstree/dist/jstree.min.js" |
125 | ], | 107 | ], |
@@ -143,7 +125,11 @@ | @@ -143,7 +125,11 @@ | ||
143 | "hoist-non-react-statics", | 125 | "hoist-non-react-statics", |
144 | "classnames", | 126 | "classnames", |
145 | "raf", | 127 | "raf", |
146 | - "moment-timezone" | 128 | + "moment-timezone", |
129 | + "tinycolor2", | ||
130 | + "json-schema-defaults", | ||
131 | + "leaflet-providers", | ||
132 | + "lodash" | ||
147 | ] | 133 | ] |
148 | }, | 134 | }, |
149 | "configurations": { | 135 | "configurations": { |
@@ -69,16 +69,19 @@ export function addCondition(schema: JsonSettingsSchema, condition: string, excl | @@ -69,16 +69,19 @@ export function addCondition(schema: JsonSettingsSchema, condition: string, excl | ||
69 | return { | 69 | return { |
70 | key: element, | 70 | key: element, |
71 | condition | 71 | condition |
72 | - } | 72 | + }; |
73 | } | 73 | } |
74 | if (typeof element === 'object') { | 74 | if (typeof element === 'object') { |
75 | if (element.condition) { | 75 | if (element.condition) { |
76 | - element.condition += ' && ' + condition | 76 | + element.condition += ' && ' + condition; |
77 | + } | ||
78 | + else { | ||
79 | + element.condition = condition; | ||
77 | } | 80 | } |
78 | - else element.condition = condition; | ||
79 | } | 81 | } |
80 | } | 82 | } |
81 | return element; | 83 | return element; |
82 | }); | 84 | }); |
83 | return schema; | 85 | return schema; |
84 | } | 86 | } |
87 | + |
@@ -19,10 +19,10 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; | @@ -19,10 +19,10 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; | ||
19 | import { Store } from '@ngrx/store'; | 19 | import { Store } from '@ngrx/store'; |
20 | import { AppState } from '@core/core.state'; | 20 | import { AppState } from '@core/core.state'; |
21 | import { ActionStatus, AuditLog } from '@shared/models/audit-log.models'; | 21 | import { ActionStatus, AuditLog } from '@shared/models/audit-log.models'; |
22 | - | ||
23 | -import * as ace from 'ace-builds'; | 22 | +import { Ace } from 'ace-builds'; |
24 | import { DialogComponent } from '@shared/components/dialog.component'; | 23 | import { DialogComponent } from '@shared/components/dialog.component'; |
25 | import { Router } from '@angular/router'; | 24 | import { Router } from '@angular/router'; |
25 | +import { getAce } from '@shared/models/ace/ace.models'; | ||
26 | 26 | ||
27 | export interface AuditLogDetailsDialogData { | 27 | export interface AuditLogDetailsDialogData { |
28 | auditLog: AuditLog; | 28 | auditLog: AuditLog; |
@@ -37,11 +37,9 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta | @@ -37,11 +37,9 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta | ||
37 | 37 | ||
38 | @ViewChild('actionDataEditor', {static: true}) | 38 | @ViewChild('actionDataEditor', {static: true}) |
39 | actionDataEditorElmRef: ElementRef; | 39 | actionDataEditorElmRef: ElementRef; |
40 | - private actionDataEditor: ace.Ace.Editor; | ||
41 | 40 | ||
42 | @ViewChild('failureDetailsEditor', {static: true}) | 41 | @ViewChild('failureDetailsEditor', {static: true}) |
43 | failureDetailsEditorElmRef: ElementRef; | 42 | failureDetailsEditorElmRef: ElementRef; |
44 | - private failureDetailsEditor: ace.Ace.Editor; | ||
45 | 43 | ||
46 | auditLog: AuditLog; | 44 | auditLog: AuditLog; |
47 | displayFailureDetails: boolean; | 45 | displayFailureDetails: boolean; |
@@ -62,15 +60,15 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta | @@ -62,15 +60,15 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta | ||
62 | this.actionData = this.auditLog.actionData ? JSON.stringify(this.auditLog.actionData, null, 2) : ''; | 60 | this.actionData = this.auditLog.actionData ? JSON.stringify(this.auditLog.actionData, null, 2) : ''; |
63 | this.actionFailureDetails = this.auditLog.actionFailureDetails; | 61 | this.actionFailureDetails = this.auditLog.actionFailureDetails; |
64 | 62 | ||
65 | - this.actionDataEditor = this.createEditor(this.actionDataEditorElmRef, this.actionData); | 63 | + this.createEditor(this.actionDataEditorElmRef, this.actionData); |
66 | if (this.displayFailureDetails) { | 64 | if (this.displayFailureDetails) { |
67 | - this.failureDetailsEditor = this.createEditor(this.failureDetailsEditorElmRef, this.actionFailureDetails); | 65 | + this.createEditor(this.failureDetailsEditorElmRef, this.actionFailureDetails); |
68 | } | 66 | } |
69 | } | 67 | } |
70 | 68 | ||
71 | - createEditor(editorElementRef: ElementRef, content: string): ace.Ace.Editor { | 69 | + createEditor(editorElementRef: ElementRef, content: string): void { |
72 | const editorElement = editorElementRef.nativeElement; | 70 | const editorElement = editorElementRef.nativeElement; |
73 | - let editorOptions: Partial<ace.Ace.EditorOptions> = { | 71 | + let editorOptions: Partial<Ace.EditorOptions> = { |
74 | mode: 'ace/mode/java', | 72 | mode: 'ace/mode/java', |
75 | theme: 'ace/theme/github', | 73 | theme: 'ace/theme/github', |
76 | showGutter: false, | 74 | showGutter: false, |
@@ -85,14 +83,17 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta | @@ -85,14 +83,17 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta | ||
85 | }; | 83 | }; |
86 | 84 | ||
87 | editorOptions = {...editorOptions, ...advancedOptions}; | 85 | editorOptions = {...editorOptions, ...advancedOptions}; |
88 | - const editor = ace.edit(editorElement, editorOptions); | ||
89 | - editor.session.setUseWrapMode(false); | ||
90 | - editor.setValue(content, -1); | ||
91 | - this.updateEditorSize(editorElement, content, editor); | ||
92 | - return editor; | 86 | + getAce().subscribe( |
87 | + (ace) => { | ||
88 | + const editor = ace.edit(editorElement, editorOptions); | ||
89 | + editor.session.setUseWrapMode(false); | ||
90 | + editor.setValue(content, -1); | ||
91 | + this.updateEditorSize(editorElement, content, editor); | ||
92 | + } | ||
93 | + ); | ||
93 | } | 94 | } |
94 | 95 | ||
95 | - updateEditorSize(editorElement: any, content: string, editor: ace.Ace.Editor) { | 96 | + updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) { |
96 | let newHeight = 200; | 97 | let newHeight = 200; |
97 | let newWidth = 600; | 98 | let newWidth = 600; |
98 | if (content && content.length > 0) { | 99 | if (content && content.length > 0) { |
@@ -19,10 +19,11 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; | @@ -19,10 +19,11 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; | ||
19 | import { Store } from '@ngrx/store'; | 19 | import { Store } from '@ngrx/store'; |
20 | import { AppState } from '@core/core.state'; | 20 | import { AppState } from '@core/core.state'; |
21 | 21 | ||
22 | -import * as ace from 'ace-builds'; | 22 | +import { Ace } from 'ace-builds'; |
23 | import { DialogComponent } from '@shared/components/dialog.component'; | 23 | import { DialogComponent } from '@shared/components/dialog.component'; |
24 | import { Router } from '@angular/router'; | 24 | import { Router } from '@angular/router'; |
25 | import { ContentType, contentTypesMap } from '@shared/models/constants'; | 25 | import { ContentType, contentTypesMap } from '@shared/models/constants'; |
26 | +import { getAce } from '@shared/models/ace/ace.models'; | ||
26 | 27 | ||
27 | export interface EventContentDialogData { | 28 | export interface EventContentDialogData { |
28 | content: string; | 29 | content: string; |
@@ -39,7 +40,6 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia | @@ -39,7 +40,6 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia | ||
39 | 40 | ||
40 | @ViewChild('eventContentEditor', {static: true}) | 41 | @ViewChild('eventContentEditor', {static: true}) |
41 | eventContentEditorElmRef: ElementRef; | 42 | eventContentEditorElmRef: ElementRef; |
42 | - private eventContentEditor: ace.Ace.Editor; | ||
43 | 43 | ||
44 | content: string; | 44 | content: string; |
45 | title: string; | 45 | title: string; |
@@ -58,10 +58,10 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia | @@ -58,10 +58,10 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia | ||
58 | this.title = this.data.title; | 58 | this.title = this.data.title; |
59 | this.contentType = this.data.contentType; | 59 | this.contentType = this.data.contentType; |
60 | 60 | ||
61 | - this.eventContentEditor = this.createEditor(this.eventContentEditorElmRef, this.content); | 61 | + this.createEditor(this.eventContentEditorElmRef, this.content); |
62 | } | 62 | } |
63 | 63 | ||
64 | - createEditor(editorElementRef: ElementRef, content: string): ace.Ace.Editor { | 64 | + createEditor(editorElementRef: ElementRef, content: string) { |
65 | const editorElement = editorElementRef.nativeElement; | 65 | const editorElement = editorElementRef.nativeElement; |
66 | let mode = 'java'; | 66 | let mode = 'java'; |
67 | if (this.contentType) { | 67 | if (this.contentType) { |
@@ -70,7 +70,7 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia | @@ -70,7 +70,7 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia | ||
70 | content = js_beautify(content, {indent_size: 4}); | 70 | content = js_beautify(content, {indent_size: 4}); |
71 | } | 71 | } |
72 | } | 72 | } |
73 | - let editorOptions: Partial<ace.Ace.EditorOptions> = { | 73 | + let editorOptions: Partial<Ace.EditorOptions> = { |
74 | mode: `ace/mode/${mode}`, | 74 | mode: `ace/mode/${mode}`, |
75 | theme: 'ace/theme/github', | 75 | theme: 'ace/theme/github', |
76 | showGutter: false, | 76 | showGutter: false, |
@@ -85,14 +85,17 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia | @@ -85,14 +85,17 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia | ||
85 | }; | 85 | }; |
86 | 86 | ||
87 | editorOptions = {...editorOptions, ...advancedOptions}; | 87 | editorOptions = {...editorOptions, ...advancedOptions}; |
88 | - const editor = ace.edit(editorElement, editorOptions); | ||
89 | - editor.session.setUseWrapMode(false); | ||
90 | - editor.setValue(content, -1); | ||
91 | - this.updateEditorSize(editorElement, content, editor); | ||
92 | - return editor; | 88 | + getAce().subscribe( |
89 | + (ace) => { | ||
90 | + const editor = ace.edit(editorElement, editorOptions); | ||
91 | + editor.session.setUseWrapMode(false); | ||
92 | + editor.setValue(content, -1); | ||
93 | + this.updateEditorSize(editorElement, content, editor); | ||
94 | + } | ||
95 | + ); | ||
93 | } | 96 | } |
94 | 97 | ||
95 | - updateEditorSize(editorElement: any, content: string, editor: ace.Ace.Editor) { | 98 | + updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) { |
96 | let newHeight = 400; | 99 | let newHeight = 400; |
97 | let newWidth = 600; | 100 | let newWidth = 600; |
98 | if (content && content.length > 0) { | 101 | if (content && content.length > 0) { |
@@ -220,7 +220,7 @@ export class KeyFilterDialogComponent extends | @@ -220,7 +220,7 @@ export class KeyFilterDialogComponent extends | ||
220 | let keyNameObservable: Observable<Array<string>>; | 220 | let keyNameObservable: Observable<Array<string>>; |
221 | switch (this.keyFilterFormGroup.get('key.type').value) { | 221 | switch (this.keyFilterFormGroup.get('key.type').value) { |
222 | case EntityKeyType.ENTITY_FIELD: | 222 | case EntityKeyType.ENTITY_FIELD: |
223 | - keyNameObservable = of(Object.values(entityFields).map(entityField => entityField.keyName).sort()); | 223 | + keyNameObservable = of(Object.keys(entityFields).map(itm => entityFields[itm]).map(entityField => entityField.keyName).sort()); |
224 | break; | 224 | break; |
225 | case EntityKeyType.ATTRIBUTE: | 225 | case EntityKeyType.ATTRIBUTE: |
226 | keyNameObservable = this.deviceProfileService.getDeviceProfileDevicesAttributesKeys( | 226 | keyNameObservable = this.deviceProfileService.getDeviceProfileDevicesAttributesKeys( |
@@ -32,11 +32,15 @@ import { PageComponent } from '@shared/components/page.component'; | @@ -32,11 +32,15 @@ import { PageComponent } from '@shared/components/page.component'; | ||
32 | import { Store } from '@ngrx/store'; | 32 | import { Store } from '@ngrx/store'; |
33 | import { AppState } from '@core/core.state'; | 33 | import { AppState } from '@core/core.state'; |
34 | import { CustomActionDescriptor } from '@shared/models/widget.models'; | 34 | import { CustomActionDescriptor } from '@shared/models/widget.models'; |
35 | -import * as ace from 'ace-builds'; | 35 | +import { Ace } from 'ace-builds'; |
36 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; | 36 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
37 | import { css_beautify, html_beautify } from 'js-beautify'; | 37 | import { css_beautify, html_beautify } from 'js-beautify'; |
38 | import { ResizeObserver } from '@juggle/resize-observer'; | 38 | import { ResizeObserver } from '@juggle/resize-observer'; |
39 | import { CustomPrettyActionEditorCompleter } from '@home/components/widget/action/custom-action.models'; | 39 | import { CustomPrettyActionEditorCompleter } from '@home/components/widget/action/custom-action.models'; |
40 | +import { Observable } from 'rxjs/internal/Observable'; | ||
41 | +import { forkJoin, from } from 'rxjs'; | ||
42 | +import { map, tap } from 'rxjs/operators'; | ||
43 | +import { getAce } from '@shared/models/ace/ace.models'; | ||
40 | 44 | ||
41 | @Component({ | 45 | @Component({ |
42 | selector: 'tb-custom-action-pretty-resources-tabs', | 46 | selector: 'tb-custom-action-pretty-resources-tabs', |
@@ -64,11 +68,11 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | @@ -64,11 +68,11 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | ||
64 | htmlFullscreen = false; | 68 | htmlFullscreen = false; |
65 | cssFullscreen = false; | 69 | cssFullscreen = false; |
66 | 70 | ||
67 | - aceEditors: ace.Ace.Editor[] = []; | 71 | + aceEditors: Ace.Editor[] = []; |
68 | editorsResizeCafs: {[editorId: string]: CancelAnimationFrame} = {}; | 72 | editorsResizeCafs: {[editorId: string]: CancelAnimationFrame} = {}; |
69 | aceResize$: ResizeObserver; | 73 | aceResize$: ResizeObserver; |
70 | - htmlEditor: ace.Ace.Editor; | ||
71 | - cssEditor: ace.Ace.Editor; | 74 | + htmlEditor: Ace.Editor; |
75 | + cssEditor: Ace.Editor; | ||
72 | setValuesPending = false; | 76 | setValuesPending = false; |
73 | 77 | ||
74 | customPrettyActionEditorCompleter = CustomPrettyActionEditorCompleter; | 78 | customPrettyActionEditorCompleter = CustomPrettyActionEditorCompleter; |
@@ -80,11 +84,12 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | @@ -80,11 +84,12 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | ||
80 | } | 84 | } |
81 | 85 | ||
82 | ngOnInit(): void { | 86 | ngOnInit(): void { |
83 | - this.initAceEditors(); | ||
84 | - if (this.setValuesPending) { | ||
85 | - this.setAceEditorValues(); | ||
86 | - this.setValuesPending = false; | ||
87 | - } | 87 | + this.initAceEditors().subscribe(() => { |
88 | + if (this.setValuesPending) { | ||
89 | + this.setAceEditorValues(); | ||
90 | + this.setValuesPending = false; | ||
91 | + } | ||
92 | + }); | ||
88 | } | 93 | } |
89 | 94 | ||
90 | ngOnDestroy(): void { | 95 | ngOnDestroy(): void { |
@@ -94,7 +99,7 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | @@ -94,7 +99,7 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | ||
94 | ngOnChanges(changes: SimpleChanges): void { | 99 | ngOnChanges(changes: SimpleChanges): void { |
95 | for (const propName of Object.keys(changes)) { | 100 | for (const propName of Object.keys(changes)) { |
96 | if (propName === 'action') { | 101 | if (propName === 'action') { |
97 | - if (this.aceEditors.length) { | 102 | + if (this.aceEditors.length === 2) { |
98 | this.setAceEditorValues(); | 103 | this.setAceEditorValues(); |
99 | } else { | 104 | } else { |
100 | this.setValuesPending = true; | 105 | this.setValuesPending = true; |
@@ -152,34 +157,46 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | @@ -152,34 +157,46 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | ||
152 | } | 157 | } |
153 | } | 158 | } |
154 | 159 | ||
155 | - private initAceEditors() { | 160 | + private initAceEditors(): Observable<any> { |
156 | this.aceResize$ = new ResizeObserver((entries) => { | 161 | this.aceResize$ = new ResizeObserver((entries) => { |
157 | entries.forEach((entry) => { | 162 | entries.forEach((entry) => { |
158 | const editor = this.aceEditors.find(aceEditor => aceEditor.container === entry.target); | 163 | const editor = this.aceEditors.find(aceEditor => aceEditor.container === entry.target); |
159 | this.onAceEditorResize(editor); | 164 | this.onAceEditorResize(editor); |
160 | }); | 165 | }); |
161 | }); | 166 | }); |
162 | - this.htmlEditor = this.createAceEditor(this.htmlInputElmRef, 'html'); | ||
163 | - this.htmlEditor.on('input', () => { | ||
164 | - const editorValue = this.htmlEditor.getValue(); | ||
165 | - if (this.action.customHtml !== editorValue) { | ||
166 | - this.action.customHtml = editorValue; | ||
167 | - this.notifyActionUpdated(); | ||
168 | - } | ||
169 | - }); | ||
170 | - this.cssEditor = this.createAceEditor(this.cssInputElmRef, 'css'); | ||
171 | - this.cssEditor.on('input', () => { | ||
172 | - const editorValue = this.cssEditor.getValue(); | ||
173 | - if (this.action.customCss !== editorValue) { | ||
174 | - this.action.customCss = editorValue; | ||
175 | - this.notifyActionUpdated(); | ||
176 | - } | ||
177 | - }); | 167 | + const editorsObservables: Observable<any>[] = []; |
168 | + | ||
169 | + editorsObservables.push(this.createAceEditor(this.htmlInputElmRef, 'html').pipe( | ||
170 | + tap((editor) => { | ||
171 | + this.htmlEditor = editor; | ||
172 | + this.htmlEditor.on('input', () => { | ||
173 | + const editorValue = this.htmlEditor.getValue(); | ||
174 | + if (this.action.customHtml !== editorValue) { | ||
175 | + this.action.customHtml = editorValue; | ||
176 | + this.notifyActionUpdated(); | ||
177 | + } | ||
178 | + }); | ||
179 | + }) | ||
180 | + )); | ||
181 | + | ||
182 | + editorsObservables.push(this.createAceEditor(this.cssInputElmRef, 'css').pipe( | ||
183 | + tap((editor) => { | ||
184 | + this.cssEditor = editor; | ||
185 | + this.cssEditor.on('input', () => { | ||
186 | + const editorValue = this.cssEditor.getValue(); | ||
187 | + if (this.action.customCss !== editorValue) { | ||
188 | + this.action.customCss = editorValue; | ||
189 | + this.notifyActionUpdated(); | ||
190 | + } | ||
191 | + }); | ||
192 | + }) | ||
193 | + )); | ||
194 | + return forkJoin(editorsObservables); | ||
178 | } | 195 | } |
179 | 196 | ||
180 | - private createAceEditor(editorElementRef: ElementRef, mode: string): ace.Ace.Editor { | 197 | + private createAceEditor(editorElementRef: ElementRef, mode: string): Observable<Ace.Editor> { |
181 | const editorElement = editorElementRef.nativeElement; | 198 | const editorElement = editorElementRef.nativeElement; |
182 | - let editorOptions: Partial<ace.Ace.EditorOptions> = { | 199 | + let editorOptions: Partial<Ace.EditorOptions> = { |
183 | mode: `ace/mode/${mode}`, | 200 | mode: `ace/mode/${mode}`, |
184 | showGutter: true, | 201 | showGutter: true, |
185 | showPrintMargin: true | 202 | showPrintMargin: true |
@@ -190,11 +207,15 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | @@ -190,11 +207,15 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | ||
190 | enableLiveAutocompletion: true | 207 | enableLiveAutocompletion: true |
191 | }; | 208 | }; |
192 | editorOptions = {...editorOptions, ...advancedOptions}; | 209 | editorOptions = {...editorOptions, ...advancedOptions}; |
193 | - const aceEditor = ace.edit(editorElement, editorOptions); | ||
194 | - aceEditor.session.setUseWrapMode(true); | ||
195 | - this.aceEditors.push(aceEditor); | ||
196 | - this.aceResize$.observe(editorElement); | ||
197 | - return aceEditor; | 210 | + return getAce().pipe( |
211 | + map((ace) => { | ||
212 | + const aceEditor = ace.edit(editorElement, editorOptions); | ||
213 | + aceEditor.session.setUseWrapMode(true); | ||
214 | + this.aceEditors.push(aceEditor); | ||
215 | + this.aceResize$.observe(editorElement); | ||
216 | + return aceEditor; | ||
217 | + }) | ||
218 | + ); | ||
198 | } | 219 | } |
199 | 220 | ||
200 | private setAceEditorValues() { | 221 | private setAceEditorValues() { |
@@ -202,7 +223,7 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | @@ -202,7 +223,7 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl | ||
202 | this.cssEditor.setValue(this.action.customCss ? this.action.customCss : '', -1); | 223 | this.cssEditor.setValue(this.action.customCss ? this.action.customCss : '', -1); |
203 | } | 224 | } |
204 | 225 | ||
205 | - private onAceEditorResize(aceEditor: ace.Ace.Editor) { | 226 | + private onAceEditorResize(aceEditor: Ace.Editor) { |
206 | if (this.editorsResizeCafs[aceEditor.id]) { | 227 | if (this.editorsResizeCafs[aceEditor.id]) { |
207 | this.editorsResizeCafs[aceEditor.id](); | 228 | this.editorsResizeCafs[aceEditor.id](); |
208 | delete this.editorsResizeCafs[aceEditor.id]; | 229 | delete this.editorsResizeCafs[aceEditor.id]; |
@@ -103,7 +103,7 @@ export class GatewayFormComponent extends PageComponent implements OnInit, OnDes | @@ -103,7 +103,7 @@ export class GatewayFormComponent extends PageComponent implements OnInit, OnDes | ||
103 | gatewayType: string; | 103 | gatewayType: string; |
104 | gatewayConfigurationGroup: FormGroup; | 104 | gatewayConfigurationGroup: FormGroup; |
105 | securityTypes = SecurityTypeTranslationMap; | 105 | securityTypes = SecurityTypeTranslationMap; |
106 | - gatewayLogLevels = Object.values(GatewayLogLevel); | 106 | + gatewayLogLevels = Object.keys(GatewayLogLevel).map(itm => GatewayLogLevel[itm]); |
107 | connectorTypes = Object.keys(ConnectorType); | 107 | connectorTypes = Object.keys(ConnectorType); |
108 | storageTypes = StorageTypeTranslationMap; | 108 | storageTypes = StorageTypeTranslationMap; |
109 | 109 |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { FormattedData, MapProviders, ReplaceInfo } from '@home/components/widget/lib/maps/map-models'; | ||
18 | +import { | ||
19 | + createLabelFromDatasource, | ||
20 | + hashCode, | ||
21 | + isDefined, | ||
22 | + isDefinedAndNotNull, isFunction, | ||
23 | + isNumber, | ||
24 | + isUndefined, | ||
25 | + padValue | ||
26 | +} from '@core/utils'; | ||
27 | +import { Observable, Observer, of } from 'rxjs'; | ||
28 | +import { map } from 'rxjs/operators'; | ||
29 | +import { Datasource, DatasourceData } from '@shared/models/widget.models'; | ||
30 | +import _ from 'lodash'; | ||
31 | +import { mapProviderSchema, providerSets } from '@home/components/widget/lib/maps/schemes'; | ||
32 | +import { addCondition, mergeSchemes } from '@core/schema-utils'; | ||
33 | + | ||
34 | +export function getProviderSchema(mapProvider: MapProviders, ignoreImageMap = false) { | ||
35 | + const providerSchema = _.cloneDeep(mapProviderSchema); | ||
36 | + if (mapProvider) { | ||
37 | + providerSchema.schema.properties.provider.default = mapProvider; | ||
38 | + } | ||
39 | + if (ignoreImageMap) { | ||
40 | + providerSchema.form[0].items = providerSchema.form[0]?.items.filter(item => item.value !== 'image-map'); | ||
41 | + } | ||
42 | + return mergeSchemes([providerSchema, | ||
43 | + ...Object.keys(providerSets)?.map( | ||
44 | + (key: string) => { | ||
45 | + const setting = providerSets[key]; | ||
46 | + return addCondition(setting?.schema, `model.provider === '${setting.name}'`); | ||
47 | + })]); | ||
48 | +} | ||
49 | + | ||
50 | +export function getRatio(firsMoment: number, secondMoment: number, intermediateMoment: number): number { | ||
51 | + return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); | ||
52 | +} | ||
53 | + | ||
54 | +export function interpolateOnLineSegment( | ||
55 | + pointA: FormattedData, | ||
56 | + pointB: FormattedData, | ||
57 | + latKeyName: string, | ||
58 | + lngKeyName: string, | ||
59 | + ratio: number | ||
60 | +): { [key: string]: number } { | ||
61 | + return { | ||
62 | + [latKeyName]: (pointA[latKeyName] + (pointB[latKeyName] - pointA[latKeyName]) * ratio), | ||
63 | + [lngKeyName]: (pointA[lngKeyName] + (pointB[lngKeyName] - pointA[lngKeyName]) * ratio) | ||
64 | + }; | ||
65 | +} | ||
66 | + | ||
67 | +export function findAngle(startPoint: FormattedData, endPoint: FormattedData, latKeyName: string, lngKeyName: string): number { | ||
68 | + if (isUndefined(startPoint) || isUndefined(endPoint)) { | ||
69 | + return 0; | ||
70 | + } | ||
71 | + let angle = -Math.atan2(endPoint[latKeyName] - startPoint[latKeyName], endPoint[lngKeyName] - startPoint[lngKeyName]); | ||
72 | + angle = angle * 180 / Math.PI; | ||
73 | + return parseInt(angle.toFixed(2), 10); | ||
74 | +} | ||
75 | + | ||
76 | + | ||
77 | +export function getDefCenterPosition(position) { | ||
78 | + if (typeof (position) === 'string') { | ||
79 | + return position.split(','); | ||
80 | + } | ||
81 | + if (typeof (position) === 'object') { | ||
82 | + return position; | ||
83 | + } | ||
84 | + return [0, 0]; | ||
85 | +} | ||
86 | + | ||
87 | + | ||
88 | +const imageAspectMap = {}; | ||
89 | + | ||
90 | +function imageLoader(imageUrl: string): Observable<HTMLImageElement> { | ||
91 | + return new Observable((observer: Observer<HTMLImageElement>) => { | ||
92 | + const image = document.createElement('img'); // support IE | ||
93 | + image.style.position = 'absolute'; | ||
94 | + image.style.left = '-99999px'; | ||
95 | + image.style.top = '-99999px'; | ||
96 | + image.onload = () => { | ||
97 | + observer.next(image); | ||
98 | + document.body.removeChild(image); | ||
99 | + observer.complete(); | ||
100 | + }; | ||
101 | + image.onerror = err => { | ||
102 | + observer.error(err); | ||
103 | + document.body.removeChild(image); | ||
104 | + observer.complete(); | ||
105 | + }; | ||
106 | + document.body.appendChild(image); | ||
107 | + image.src = imageUrl; | ||
108 | + }); | ||
109 | +} | ||
110 | + | ||
111 | +export function aspectCache(imageUrl: string): Observable<number> { | ||
112 | + if (imageUrl?.length) { | ||
113 | + const hash = hashCode(imageUrl); | ||
114 | + let aspect = imageAspectMap[hash]; | ||
115 | + if (aspect) { | ||
116 | + return of(aspect); | ||
117 | + } | ||
118 | + return imageLoader(imageUrl).pipe(map(image => { | ||
119 | + aspect = image.width / image.height; | ||
120 | + imageAspectMap[hash] = aspect; | ||
121 | + return aspect; | ||
122 | + })); | ||
123 | + } | ||
124 | +} | ||
125 | + | ||
126 | +export type TranslateFunc = (key: string, defaultTranslation?: string) => string; | ||
127 | + | ||
128 | +const varsRegex = /\${([^}]*)}/g; | ||
129 | +const linkActionRegex = /<link-act name=['"]([^['"]*)['"]>([^<]*)<\/link-act>/g; | ||
130 | +const buttonActionRegex = /<button-act name=['"]([^['"]*)['"]>([^<]*)<\/button-act>/g; | ||
131 | + | ||
132 | +function createLinkElement(actionName: string, actionText: string): string { | ||
133 | + return `<a href="javascript:void(0);" class="tb-custom-action" data-action-name=${actionName}>${actionText}</a>`; | ||
134 | +} | ||
135 | + | ||
136 | +function createButtonElement(actionName: string, actionText: string) { | ||
137 | + return `<button mat-button class="tb-custom-action" data-action-name=${actionName}>${actionText}</button>`; | ||
138 | +} | ||
139 | + | ||
140 | +function parseTemplate(template: string, data: { $datasource?: Datasource, [key: string]: any }, | ||
141 | + translateFn?: TranslateFunc) { | ||
142 | + let res = ''; | ||
143 | + try { | ||
144 | + if (translateFn) { | ||
145 | + template = translateFn(template); | ||
146 | + } | ||
147 | + template = createLabelFromDatasource(data.$datasource, template); | ||
148 | + | ||
149 | + let match = /\${([^}]*)}/g.exec(template); | ||
150 | + while (match !== null) { | ||
151 | + const variable = match[0]; | ||
152 | + let label = match[1]; | ||
153 | + let valDec = 2; | ||
154 | + const splitValues = label.split(':'); | ||
155 | + if (splitValues.length > 1) { | ||
156 | + label = splitValues[0]; | ||
157 | + valDec = parseFloat(splitValues[1]); | ||
158 | + } | ||
159 | + | ||
160 | + if (label.startsWith('#')) { | ||
161 | + const keyIndexStr = label.substring(1); | ||
162 | + const n = Math.floor(Number(keyIndexStr)); | ||
163 | + if (String(n) === keyIndexStr && n >= 0) { | ||
164 | + label = data.$datasource.dataKeys[n].label; | ||
165 | + } | ||
166 | + } | ||
167 | + | ||
168 | + const value = data[label] || ''; | ||
169 | + let textValue: string; | ||
170 | + if (isNumber(value)) { | ||
171 | + textValue = padValue(value, valDec); | ||
172 | + } else { | ||
173 | + textValue = value; | ||
174 | + } | ||
175 | + template = template.replace(variable, textValue); | ||
176 | + match = /\${([^}]*)}/g.exec(template); | ||
177 | + } | ||
178 | + | ||
179 | + let actionTags: string; | ||
180 | + let actionText: string; | ||
181 | + let actionName: string; | ||
182 | + let action: string; | ||
183 | + | ||
184 | + match = linkActionRegex.exec(template); | ||
185 | + while (match !== null) { | ||
186 | + [actionTags, actionName, actionText] = match; | ||
187 | + action = createLinkElement(actionName, actionText); | ||
188 | + template = template.replace(actionTags, action); | ||
189 | + match = linkActionRegex.exec(template); | ||
190 | + } | ||
191 | + | ||
192 | + match = buttonActionRegex.exec(template); | ||
193 | + while (match !== null) { | ||
194 | + [actionTags, actionName, actionText] = match; | ||
195 | + action = createButtonElement(actionName, actionText); | ||
196 | + template = template.replace(actionTags, action); | ||
197 | + match = buttonActionRegex.exec(template); | ||
198 | + } | ||
199 | + | ||
200 | + // const compiled = _.template(template); | ||
201 | + // res = compiled(data); | ||
202 | + res = template; | ||
203 | + } catch (ex) { | ||
204 | + console.log(ex, template); | ||
205 | + } | ||
206 | + return res; | ||
207 | +} | ||
208 | + | ||
209 | +export function processPattern(template: string, data: { $datasource?: Datasource, [key: string]: any }): Array<ReplaceInfo> { | ||
210 | + const replaceInfo = []; | ||
211 | + try { | ||
212 | + const reg = /\${([^}]*)}/g; | ||
213 | + let match = reg.exec(template); | ||
214 | + while (match !== null) { | ||
215 | + const variableInfo: ReplaceInfo = { | ||
216 | + dataKeyName: '', | ||
217 | + valDec: 2, | ||
218 | + variable: '' | ||
219 | + }; | ||
220 | + const variable = match[0]; | ||
221 | + let label = match[1]; | ||
222 | + let valDec = 2; | ||
223 | + const splitValues = label.split(':'); | ||
224 | + if (splitValues.length > 1) { | ||
225 | + label = splitValues[0]; | ||
226 | + valDec = parseFloat(splitValues[1]); | ||
227 | + } | ||
228 | + | ||
229 | + variableInfo.variable = variable; | ||
230 | + variableInfo.valDec = valDec; | ||
231 | + | ||
232 | + if (label.startsWith('#')) { | ||
233 | + const keyIndexStr = label.substring(1); | ||
234 | + const n = Math.floor(Number(keyIndexStr)); | ||
235 | + if (String(n) === keyIndexStr && n >= 0) { | ||
236 | + variableInfo.dataKeyName = data.$datasource.dataKeys[n].label; | ||
237 | + } | ||
238 | + } else { | ||
239 | + variableInfo.dataKeyName = label; | ||
240 | + } | ||
241 | + replaceInfo.push(variableInfo); | ||
242 | + | ||
243 | + match = reg.exec(template); | ||
244 | + } | ||
245 | + } catch (ex) { | ||
246 | + console.log(ex, template); | ||
247 | + } | ||
248 | + return replaceInfo; | ||
249 | +} | ||
250 | + | ||
251 | +export function fillPattern(markerLabelText: string, replaceInfoLabelMarker: Array<ReplaceInfo>, data: FormattedData) { | ||
252 | + let text = createLabelFromDatasource(data.$datasource, markerLabelText); | ||
253 | + if (replaceInfoLabelMarker) { | ||
254 | + for (const variableInfo of replaceInfoLabelMarker) { | ||
255 | + let txtVal = ''; | ||
256 | + if (variableInfo.dataKeyName && isDefinedAndNotNull(data[variableInfo.dataKeyName])) { | ||
257 | + const varData = data[variableInfo.dataKeyName]; | ||
258 | + if (isNumber(varData)) { | ||
259 | + txtVal = padValue(varData, variableInfo.valDec); | ||
260 | + } else { | ||
261 | + txtVal = varData; | ||
262 | + } | ||
263 | + } | ||
264 | + text = text.replace(variableInfo.variable, txtVal); | ||
265 | + } | ||
266 | + } | ||
267 | + return text; | ||
268 | +} | ||
269 | + | ||
270 | +function prepareProcessPattern(template: string, translateFn?: TranslateFunc): string { | ||
271 | + if (translateFn) { | ||
272 | + template = translateFn(template); | ||
273 | + } | ||
274 | + let actionTags: string; | ||
275 | + let actionText: string; | ||
276 | + let actionName: string; | ||
277 | + let action: string; | ||
278 | + | ||
279 | + let match = linkActionRegex.exec(template); | ||
280 | + while (match !== null) { | ||
281 | + [actionTags, actionName, actionText] = match; | ||
282 | + action = createLinkElement(actionName, actionText); | ||
283 | + template = template.replace(actionTags, action); | ||
284 | + match = linkActionRegex.exec(template); | ||
285 | + } | ||
286 | + | ||
287 | + match = buttonActionRegex.exec(template); | ||
288 | + while (match !== null) { | ||
289 | + [actionTags, actionName, actionText] = match; | ||
290 | + action = createButtonElement(actionName, actionText); | ||
291 | + template = template.replace(actionTags, action); | ||
292 | + match = buttonActionRegex.exec(template); | ||
293 | + } | ||
294 | + return template; | ||
295 | +} | ||
296 | + | ||
297 | +export const parseWithTranslation = { | ||
298 | + | ||
299 | + translateFn: null, | ||
300 | + | ||
301 | + translate(key: string, defaultTranslation?: string): string { | ||
302 | + if (this.translateFn) { | ||
303 | + return this.translateFn(key, defaultTranslation); | ||
304 | + } else { | ||
305 | + throw console.error('Translate not assigned'); | ||
306 | + } | ||
307 | + }, | ||
308 | + parseTemplate(template: string, data: object, forceTranslate = false): string { | ||
309 | + return parseTemplate(forceTranslate ? this.translate(template) : template, data, this.translate.bind(this)); | ||
310 | + }, | ||
311 | + prepareProcessPattern(template: string, forceTranslate = false): string { | ||
312 | + return prepareProcessPattern(forceTranslate ? this.translate(template) : template, this.translate.bind(this)); | ||
313 | + }, | ||
314 | + setTranslate(translateFn: TranslateFunc) { | ||
315 | + this.translateFn = translateFn; | ||
316 | + } | ||
317 | +}; | ||
318 | + | ||
319 | +export function parseData(input: DatasourceData[]): FormattedData[] { | ||
320 | + return _(input).groupBy(el => el?.datasource?.entityName) | ||
321 | + .values().value().map((entityArray, i) => { | ||
322 | + const obj: FormattedData = { | ||
323 | + entityName: entityArray[0]?.datasource?.entityName, | ||
324 | + entityId: entityArray[0]?.datasource?.entityId, | ||
325 | + entityType: entityArray[0]?.datasource?.entityType, | ||
326 | + $datasource: entityArray[0]?.datasource, | ||
327 | + dsIndex: i, | ||
328 | + deviceType: null | ||
329 | + }; | ||
330 | + entityArray.filter(el => el.data.length).forEach(el => { | ||
331 | + const indexDate = el?.data?.length ? el.data.length - 1 : 0; | ||
332 | + obj[el?.dataKey?.label] = el?.data[indexDate][1]; | ||
333 | + obj[el?.dataKey?.label + '|ts'] = el?.data[indexDate][0]; | ||
334 | + if (el?.dataKey?.label === 'type') { | ||
335 | + obj.deviceType = el?.data[indexDate][1]; | ||
336 | + } | ||
337 | + }); | ||
338 | + return obj; | ||
339 | + }); | ||
340 | +} | ||
341 | + | ||
342 | +export function parseArray(input: DatasourceData[]): FormattedData[][] { | ||
343 | + return _(input).groupBy(el => el?.datasource?.entityName) | ||
344 | + .values().value().map((entityArray) => | ||
345 | + entityArray[0].data.map((el, i) => { | ||
346 | + const obj: FormattedData = { | ||
347 | + entityName: entityArray[0]?.datasource?.entityName, | ||
348 | + entityId: entityArray[0]?.datasource?.entityId, | ||
349 | + entityType: entityArray[0]?.datasource?.entityType, | ||
350 | + $datasource: entityArray[0]?.datasource, | ||
351 | + dsIndex: i, | ||
352 | + time: el[0], | ||
353 | + deviceType: null | ||
354 | + }; | ||
355 | + entityArray.filter(e => e.data.length && e.data[i]).forEach(entity => { | ||
356 | + obj[entity?.dataKey?.label] = entity?.data[i][1]; | ||
357 | + obj[entity?.dataKey?.label + '|ts'] = entity?.data[0][0]; | ||
358 | + if (entity?.dataKey?.label === 'type') { | ||
359 | + obj.deviceType = entity?.data[0][1]; | ||
360 | + } | ||
361 | + }); | ||
362 | + return obj; | ||
363 | + }) | ||
364 | + ); | ||
365 | +} | ||
366 | + | ||
367 | +export function parseFunction(source: any, params: string[] = ['def']): (...args: any[]) => any { | ||
368 | + let res = null; | ||
369 | + if (source?.length) { | ||
370 | + try { | ||
371 | + res = new Function(...params, source); | ||
372 | + } | ||
373 | + catch (err) { | ||
374 | + res = null; | ||
375 | + } | ||
376 | + } | ||
377 | + return res; | ||
378 | +} | ||
379 | + | ||
380 | +export function safeExecute(func: (...args: any[]) => any, params = []) { | ||
381 | + let res = null; | ||
382 | + if (func && typeof (func) === 'function') { | ||
383 | + try { | ||
384 | + res = func(...params); | ||
385 | + } | ||
386 | + catch (err) { | ||
387 | + console.log('error in external function:', err); | ||
388 | + res = null; | ||
389 | + } | ||
390 | + } | ||
391 | + return res; | ||
392 | +} | ||
393 | + | ||
394 | +export function functionValueCalculator(useFunction: boolean, func: (...args: any[]) => any, params = [], defaultValue: any) { | ||
395 | + let res; | ||
396 | + if (useFunction && isDefined(func) && isFunction(func)) { | ||
397 | + try { | ||
398 | + res = func(...params); | ||
399 | + if (!isDefinedAndNotNull(res) || res === '') { | ||
400 | + res = defaultValue; | ||
401 | + } | ||
402 | + } catch (err) { | ||
403 | + res = defaultValue; | ||
404 | + console.log('error in external function:', err); | ||
405 | + } | ||
406 | + } else { | ||
407 | + res = defaultValue; | ||
408 | + } | ||
409 | + return res; | ||
410 | +} | ||
411 | + | ||
412 | +export function calculateNewPointCoordinate(coordinate: number, imageSize: number): number { | ||
413 | + let pointCoordinate = coordinate / imageSize; | ||
414 | + if (pointCoordinate < 0) { | ||
415 | + pointCoordinate = 0; | ||
416 | + } else if (pointCoordinate > 1) { | ||
417 | + pointCoordinate = 1; | ||
418 | + } | ||
419 | + return pointCoordinate; | ||
420 | +} | ||
421 | + | ||
422 | +export function createLoadingDiv(loadingText: string): JQuery<HTMLElement> { | ||
423 | + return $(` | ||
424 | + <div style=" | ||
425 | + z-index: 12; | ||
426 | + position: absolute; | ||
427 | + top: 0; | ||
428 | + bottom: 0; | ||
429 | + left: 0; | ||
430 | + right: 0; | ||
431 | + flex-direction: column; | ||
432 | + align-content: center; | ||
433 | + align-items: center; | ||
434 | + justify-content: center; | ||
435 | + display: flex; | ||
436 | + background: rgba(255,255,255,0.7); | ||
437 | + font-size: 16px; | ||
438 | + font-family: Roboto; | ||
439 | + font-weight: 400; | ||
440 | + text-transform: uppercase; | ||
441 | + "> | ||
442 | + <span>${loadingText}</span> | ||
443 | + </div> | ||
444 | + `); | ||
445 | +} |
@@ -43,12 +43,14 @@ import { Observable, of } from 'rxjs'; | @@ -43,12 +43,14 @@ import { Observable, of } from 'rxjs'; | ||
43 | import { Polyline } from './polyline'; | 43 | import { Polyline } from './polyline'; |
44 | import { Polygon } from './polygon'; | 44 | import { Polygon } from './polygon'; |
45 | import { | 45 | import { |
46 | - createLoadingDiv, | ||
47 | createTooltip, | 46 | createTooltip, |
47 | +} from '@home/components/widget/lib/maps/maps-utils'; | ||
48 | +import { | ||
49 | + createLoadingDiv, | ||
48 | parseArray, | 50 | parseArray, |
49 | parseData, | 51 | parseData, |
50 | safeExecute | 52 | safeExecute |
51 | -} from '@home/components/widget/lib/maps/maps-utils'; | 53 | +} from '@home/components/widget/lib/maps/common-maps-utils'; |
52 | import { WidgetContext } from '@home/models/widget-component.models'; | 54 | import { WidgetContext } from '@home/models/widget-component.models'; |
53 | import { deepClone, isDefinedAndNotNull, isEmptyStr, isString } from '@core/utils'; | 55 | import { deepClone, isDefinedAndNotNull, isEmptyStr, isString } from '@core/utils'; |
54 | 56 | ||
@@ -141,7 +143,7 @@ export default abstract class LeafletMap { | @@ -141,7 +143,7 @@ export default abstract class LeafletMap { | ||
141 | const header = document.createElement('p'); | 143 | const header = document.createElement('p'); |
142 | header.appendChild(document.createTextNode('Select entity:')); | 144 | header.appendChild(document.createTextNode('Select entity:')); |
143 | header.setAttribute('style', 'font-size: 14px; margin: 8px 0'); | 145 | header.setAttribute('style', 'font-size: 14px; margin: 8px 0'); |
144 | - datasourcesList.append(header); | 146 | + datasourcesList.appendChild(header); |
145 | this.datasources.forEach(ds => { | 147 | this.datasources.forEach(ds => { |
146 | const dsItem = document.createElement('p'); | 148 | const dsItem = document.createElement('p'); |
147 | dsItem.appendChild(document.createTextNode(ds.entityName)); | 149 | dsItem.appendChild(document.createTextNode(ds.entityName)); |
@@ -158,9 +160,9 @@ export default abstract class LeafletMap { | @@ -158,9 +160,9 @@ export default abstract class LeafletMap { | ||
158 | this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options); | 160 | this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options); |
159 | }); | 161 | }); |
160 | }; | 162 | }; |
161 | - datasourcesList.append(dsItem); | 163 | + datasourcesList.appendChild(dsItem); |
162 | }); | 164 | }); |
163 | - datasourcesList.append(document.createElement('br')); | 165 | + datasourcesList.appendChild(document.createElement('br')); |
164 | const deleteBtn = document.createElement('a'); | 166 | const deleteBtn = document.createElement('a'); |
165 | deleteBtn.appendChild(document.createTextNode('Discard changes')); | 167 | deleteBtn.appendChild(document.createTextNode('Discard changes')); |
166 | deleteBtn.onclick = () => { | 168 | deleteBtn.onclick = () => { |
@@ -170,7 +172,7 @@ export default abstract class LeafletMap { | @@ -170,7 +172,7 @@ export default abstract class LeafletMap { | ||
170 | this.addMarkers.splice(markerIndex, 1); | 172 | this.addMarkers.splice(markerIndex, 1); |
171 | } | 173 | } |
172 | }; | 174 | }; |
173 | - datasourcesList.append(deleteBtn); | 175 | + datasourcesList.appendChild(deleteBtn); |
174 | const popup = L.popup(); | 176 | const popup = L.popup(); |
175 | popup.setContent(datasourcesList); | 177 | popup.setContent(datasourcesList); |
176 | newMarker.bindPopup(popup).openPopup(); | 178 | newMarker.bindPopup(popup).openPopup(); |
@@ -222,7 +224,7 @@ export default abstract class LeafletMap { | @@ -222,7 +224,7 @@ export default abstract class LeafletMap { | ||
222 | const header = document.createElement('p'); | 224 | const header = document.createElement('p'); |
223 | header.appendChild(document.createTextNode('Select entity:')); | 225 | header.appendChild(document.createTextNode('Select entity:')); |
224 | header.setAttribute('style', 'font-size: 14px; margin: 8px 0'); | 226 | header.setAttribute('style', 'font-size: 14px; margin: 8px 0'); |
225 | - datasourcesList.append(header); | 227 | + datasourcesList.appendChild(header); |
226 | this.datasources.forEach(ds => { | 228 | this.datasources.forEach(ds => { |
227 | const dsItem = document.createElement('p'); | 229 | const dsItem = document.createElement('p'); |
228 | dsItem.appendChild(document.createTextNode(ds.entityName)); | 230 | dsItem.appendChild(document.createTextNode(ds.entityName)); |
@@ -238,9 +240,9 @@ export default abstract class LeafletMap { | @@ -238,9 +240,9 @@ export default abstract class LeafletMap { | ||
238 | this.deletePolygon(ds.entityName); | 240 | this.deletePolygon(ds.entityName); |
239 | }); | 241 | }); |
240 | }; | 242 | }; |
241 | - datasourcesList.append(dsItem); | 243 | + datasourcesList.appendChild(dsItem); |
242 | }); | 244 | }); |
243 | - datasourcesList.append(document.createElement('br')); | 245 | + datasourcesList.appendChild(document.createElement('br')); |
244 | const deleteBtn = document.createElement('a'); | 246 | const deleteBtn = document.createElement('a'); |
245 | deleteBtn.appendChild(document.createTextNode('Discard changes')); | 247 | deleteBtn.appendChild(document.createTextNode('Discard changes')); |
246 | deleteBtn.onclick = () => { | 248 | deleteBtn.onclick = () => { |
@@ -250,7 +252,7 @@ export default abstract class LeafletMap { | @@ -250,7 +252,7 @@ export default abstract class LeafletMap { | ||
250 | this.addPolygons.splice(polygonIndex, 1); | 252 | this.addPolygons.splice(polygonIndex, 1); |
251 | } | 253 | } |
252 | }; | 254 | }; |
253 | - datasourcesList.append(deleteBtn); | 255 | + datasourcesList.appendChild(deleteBtn); |
254 | const popup = L.popup(); | 256 | const popup = L.popup(); |
255 | popup.setContent(datasourcesList); | 257 | popup.setContent(datasourcesList); |
256 | newPolygon.bindPopup(popup).openPopup(); | 258 | newPolygon.bindPopup(popup).openPopup(); |
@@ -289,7 +291,7 @@ export default abstract class LeafletMap { | @@ -289,7 +291,7 @@ export default abstract class LeafletMap { | ||
289 | if (!this.loadingDiv) { | 291 | if (!this.loadingDiv) { |
290 | this.loadingDiv = createLoadingDiv(this.ctx.translate.instant('common.loading')); | 292 | this.loadingDiv = createLoadingDiv(this.ctx.translate.instant('common.loading')); |
291 | } | 293 | } |
292 | - this.$container.append(this.loadingDiv[0]); | 294 | + this.$container.appendChild(this.loadingDiv[0]); |
293 | } else { | 295 | } else { |
294 | if (this.loadingDiv) { | 296 | if (this.loadingDiv) { |
295 | this.loadingDiv.remove(); | 297 | this.loadingDiv.remove(); |
@@ -609,14 +611,13 @@ export default abstract class LeafletMap { | @@ -609,14 +611,13 @@ export default abstract class LeafletMap { | ||
609 | } | 611 | } |
610 | 612 | ||
611 | updatePoints(pointsData: FormattedData[][], getTooltip: (point: FormattedData) => string) { | 613 | updatePoints(pointsData: FormattedData[][], getTooltip: (point: FormattedData) => string) { |
612 | - if(pointsData.length) { | 614 | + if (pointsData.length) { |
613 | if (this.points) { | 615 | if (this.points) { |
614 | this.map.removeLayer(this.points); | 616 | this.map.removeLayer(this.points); |
615 | } | 617 | } |
616 | this.points = new FeatureGroup(); | 618 | this.points = new FeatureGroup(); |
617 | } | 619 | } |
618 | - for(let i = 0; i < pointsData.length; i++) { | ||
619 | - const pointsList = pointsData[i]; | 620 | + for (const pointsList of pointsData) { |
620 | pointsList.filter(pdata => !!this.convertPosition(pdata)).forEach(data => { | 621 | pointsList.filter(pdata => !!this.convertPosition(pdata)).forEach(data => { |
621 | const point = L.circleMarker(this.convertPosition(data), { | 622 | const point = L.circleMarker(this.convertPosition(data), { |
622 | color: this.options.pointColor, | 623 | color: this.options.pointColor, |
@@ -630,7 +631,7 @@ export default abstract class LeafletMap { | @@ -630,7 +631,7 @@ export default abstract class LeafletMap { | ||
630 | this.points.addLayer(point); | 631 | this.points.addLayer(point); |
631 | }); | 632 | }); |
632 | } | 633 | } |
633 | - if(pointsData.length) { | 634 | + if (pointsData.length) { |
634 | this.map.addLayer(this.points); | 635 | this.map.addLayer(this.points); |
635 | } | 636 | } |
636 | } | 637 | } |
@@ -14,7 +14,6 @@ | @@ -14,7 +14,6 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { LatLngTuple } from 'leaflet'; | ||
18 | import { Datasource } from '@app/shared/models/widget.models'; | 17 | import { Datasource } from '@app/shared/models/widget.models'; |
19 | import { EntityType } from '@shared/models/entity-type.models'; | 18 | import { EntityType } from '@shared/models/entity-type.models'; |
20 | import tinycolor from 'tinycolor2'; | 19 | import tinycolor from 'tinycolor2'; |
@@ -47,7 +46,7 @@ export type MapSettings = { | @@ -47,7 +46,7 @@ export type MapSettings = { | ||
47 | provider?: MapProviders; | 46 | provider?: MapProviders; |
48 | credentials?: any; // declare credentials format | 47 | credentials?: any; // declare credentials format |
49 | gmApiKey?: string; | 48 | gmApiKey?: string; |
50 | - defaultCenterPosition?: LatLngTuple; | 49 | + defaultCenterPosition?: [number, number]; |
51 | markerClusteringSetting?; | 50 | markerClusteringSetting?; |
52 | useDefaultCenterPosition?: boolean; | 51 | useDefaultCenterPosition?: boolean; |
53 | gmDefaultMapType?: string; | 52 | gmDefaultMapType?: string; |
@@ -15,9 +15,10 @@ | @@ -15,9 +15,10 @@ | ||
15 | /// | 15 | /// |
16 | 16 | ||
17 | import { JsonSettingsSchema } from '@shared/models/widget.models'; | 17 | import { JsonSettingsSchema } from '@shared/models/widget.models'; |
18 | -import { MapProviders } from './map-models'; | 18 | +import { MapProviders } from '@home/components/widget/lib/maps/map-models'; |
19 | 19 | ||
20 | export interface MapWidgetInterface { | 20 | export interface MapWidgetInterface { |
21 | + map?: any; | ||
21 | resize(); | 22 | resize(); |
22 | update(); | 23 | update(); |
23 | onInit(); | 24 | onInit(); |
@@ -31,20 +31,25 @@ import { | @@ -31,20 +31,25 @@ import { | ||
31 | routeMapSettingsSchema | 31 | routeMapSettingsSchema |
32 | } from './schemes'; | 32 | } from './schemes'; |
33 | import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface'; | 33 | import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface'; |
34 | -import { addCondition, addGroupInfo, addToSchema, initSchema, mergeSchemes } from '@core/schema-utils'; | 34 | +import { |
35 | + addCondition, | ||
36 | + addGroupInfo, | ||
37 | + addToSchema, | ||
38 | + initSchema, | ||
39 | + mergeSchemes | ||
40 | +} from '@core/schema-utils'; | ||
35 | import { WidgetContext } from '@app/modules/home/models/widget-component.models'; | 41 | import { WidgetContext } from '@app/modules/home/models/widget-component.models'; |
36 | -import { getDefCenterPosition, parseFunction, parseWithTranslation } from './maps-utils'; | 42 | +import { getDefCenterPosition, getProviderSchema, parseFunction, parseWithTranslation } from './common-maps-utils'; |
37 | import { Datasource, DatasourceData, JsonSettingsSchema, WidgetActionDescriptor } from '@shared/models/widget.models'; | 43 | import { Datasource, DatasourceData, JsonSettingsSchema, WidgetActionDescriptor } from '@shared/models/widget.models'; |
38 | import { EntityId } from '@shared/models/id/entity-id'; | 44 | import { EntityId } from '@shared/models/id/entity-id'; |
39 | import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; | 45 | import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; |
40 | import { AttributeService } from '@core/http/attribute.service'; | 46 | import { AttributeService } from '@core/http/attribute.service'; |
41 | import { TranslateService } from '@ngx-translate/core'; | 47 | import { TranslateService } from '@ngx-translate/core'; |
42 | import { UtilsService } from '@core/services/utils.service'; | 48 | import { UtilsService } from '@core/services/utils.service'; |
43 | -import _ from 'lodash'; | ||
44 | import { EntityDataPageLink } from '@shared/models/query/query.models'; | 49 | import { EntityDataPageLink } from '@shared/models/query/query.models'; |
45 | import { isDefined } from '@core/utils'; | 50 | import { isDefined } from '@core/utils'; |
46 | import { forkJoin, Observable, of } from 'rxjs'; | 51 | import { forkJoin, Observable, of } from 'rxjs'; |
47 | -import { providerSets } from '@home/components/widget/lib/maps/providers'; | 52 | +import { providerClass } from '@home/components/widget/lib/maps/providers'; |
48 | 53 | ||
49 | // @dynamic | 54 | // @dynamic |
50 | export class MapWidgetController implements MapWidgetInterface { | 55 | export class MapWidgetController implements MapWidgetInterface { |
@@ -70,7 +75,7 @@ export class MapWidgetController implements MapWidgetInterface { | @@ -70,7 +75,7 @@ export class MapWidgetController implements MapWidgetInterface { | ||
70 | this.settings.markerClick = this.getDescriptors('markerClick'); | 75 | this.settings.markerClick = this.getDescriptors('markerClick'); |
71 | this.settings.polygonClick = this.getDescriptors('polygonClick'); | 76 | this.settings.polygonClick = this.getDescriptors('polygonClick'); |
72 | 77 | ||
73 | - const MapClass = providerSets[this.provider]?.MapClass; | 78 | + const MapClass = providerClass[this.provider]; |
74 | if (!MapClass) { | 79 | if (!MapClass) { |
75 | return; | 80 | return; |
76 | } | 81 | } |
@@ -101,19 +106,7 @@ export class MapWidgetController implements MapWidgetInterface { | @@ -101,19 +106,7 @@ export class MapWidgetController implements MapWidgetInterface { | ||
101 | } | 106 | } |
102 | 107 | ||
103 | public static getProvidersSchema(mapProvider: MapProviders, ignoreImageMap = false) { | 108 | public static getProvidersSchema(mapProvider: MapProviders, ignoreImageMap = false) { |
104 | - const providerSchema = _.cloneDeep(mapProviderSchema); | ||
105 | - if (mapProvider) { | ||
106 | - providerSchema.schema.properties.provider.default = mapProvider; | ||
107 | - } | ||
108 | - if (ignoreImageMap) { | ||
109 | - providerSchema.form[0].items = providerSchema.form[0]?.items.filter(item => item.value !== 'image-map'); | ||
110 | - } | ||
111 | - return mergeSchemes([providerSchema, | ||
112 | - ...Object.keys(providerSets)?.map( | ||
113 | - (key: string) => { | ||
114 | - const setting = providerSets[key]; | ||
115 | - return addCondition(setting?.schema, `model.provider === '${setting.name}'`); | ||
116 | - })]); | 109 | + return getProviderSchema(mapProvider, ignoreImageMap); |
117 | } | 110 | } |
118 | 111 | ||
119 | public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema { | 112 | public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema { |
@@ -15,20 +15,8 @@ | @@ -15,20 +15,8 @@ | ||
15 | /// | 15 | /// |
16 | 16 | ||
17 | import L from 'leaflet'; | 17 | import L from 'leaflet'; |
18 | -import { FormattedData, MarkerSettings, PolygonSettings, PolylineSettings, ReplaceInfo } from './map-models'; | ||
19 | -import { Datasource, DatasourceData } from '@app/shared/models/widget.models'; | ||
20 | -import _ from 'lodash'; | ||
21 | -import { Observable, Observer, of } from 'rxjs'; | ||
22 | -import { map } from 'rxjs/operators'; | ||
23 | -import { | ||
24 | - createLabelFromDatasource, | ||
25 | - hashCode, | ||
26 | - isDefined, | ||
27 | - isDefinedAndNotNull, isFunction, | ||
28 | - isNumber, | ||
29 | - isUndefined, | ||
30 | - padValue | ||
31 | -} from '@core/utils'; | 18 | +import { MarkerSettings, PolygonSettings, PolylineSettings } from './map-models'; |
19 | +import { Datasource } from '@app/shared/models/widget.models'; | ||
32 | 20 | ||
33 | export function createTooltip(target: L.Layer, | 21 | export function createTooltip(target: L.Layer, |
34 | settings: MarkerSettings | PolylineSettings | PolygonSettings, | 22 | settings: MarkerSettings | PolylineSettings | PolygonSettings, |
@@ -68,400 +56,3 @@ export function bindPopupActions(popup: L.Popup, settings: MarkerSettings | Poly | @@ -68,400 +56,3 @@ export function bindPopupActions(popup: L.Popup, settings: MarkerSettings | Poly | ||
68 | } | 56 | } |
69 | }); | 57 | }); |
70 | } | 58 | } |
71 | - | ||
72 | -export function getRatio(firsMoment: number, secondMoment: number, intermediateMoment: number): number { | ||
73 | - return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); | ||
74 | -} | ||
75 | - | ||
76 | -export function interpolateOnLineSegment( | ||
77 | - pointA: FormattedData, | ||
78 | - pointB: FormattedData, | ||
79 | - latKeyName: string, | ||
80 | - lngKeyName: string, | ||
81 | - ratio: number | ||
82 | -): { [key: string]: number } { | ||
83 | - return { | ||
84 | - [latKeyName]: (pointA[latKeyName] + (pointB[latKeyName] - pointA[latKeyName]) * ratio), | ||
85 | - [lngKeyName]: (pointA[lngKeyName] + (pointB[lngKeyName] - pointA[lngKeyName]) * ratio) | ||
86 | - }; | ||
87 | -} | ||
88 | - | ||
89 | -export function findAngle(startPoint: FormattedData, endPoint: FormattedData, latKeyName: string, lngKeyName: string): number { | ||
90 | - if (isUndefined(startPoint) || isUndefined(endPoint)) { | ||
91 | - return 0; | ||
92 | - } | ||
93 | - let angle = -Math.atan2(endPoint[latKeyName] - startPoint[latKeyName], endPoint[lngKeyName] - startPoint[lngKeyName]); | ||
94 | - angle = angle * 180 / Math.PI; | ||
95 | - return parseInt(angle.toFixed(2), 10); | ||
96 | -} | ||
97 | - | ||
98 | - | ||
99 | -export function getDefCenterPosition(position) { | ||
100 | - if (typeof (position) === 'string') { | ||
101 | - return position.split(','); | ||
102 | - } | ||
103 | - if (typeof (position) === 'object') { | ||
104 | - return position; | ||
105 | - } | ||
106 | - return [0, 0]; | ||
107 | -} | ||
108 | - | ||
109 | - | ||
110 | -const imageAspectMap = {}; | ||
111 | - | ||
112 | -function imageLoader(imageUrl: string): Observable<HTMLImageElement> { | ||
113 | - return new Observable((observer: Observer<HTMLImageElement>) => { | ||
114 | - const image = document.createElement('img'); // support IE | ||
115 | - image.style.position = 'absolute'; | ||
116 | - image.style.left = '-99999px'; | ||
117 | - image.style.top = '-99999px'; | ||
118 | - image.onload = () => { | ||
119 | - observer.next(image); | ||
120 | - document.body.removeChild(image); | ||
121 | - observer.complete(); | ||
122 | - }; | ||
123 | - image.onerror = err => { | ||
124 | - observer.error(err); | ||
125 | - document.body.removeChild(image); | ||
126 | - observer.complete(); | ||
127 | - }; | ||
128 | - document.body.appendChild(image); | ||
129 | - image.src = imageUrl; | ||
130 | - }); | ||
131 | -} | ||
132 | - | ||
133 | -export function aspectCache(imageUrl: string): Observable<number> { | ||
134 | - if (imageUrl?.length) { | ||
135 | - const hash = hashCode(imageUrl); | ||
136 | - let aspect = imageAspectMap[hash]; | ||
137 | - if (aspect) { | ||
138 | - return of(aspect); | ||
139 | - } | ||
140 | - return imageLoader(imageUrl).pipe(map(image => { | ||
141 | - aspect = image.width / image.height; | ||
142 | - imageAspectMap[hash] = aspect; | ||
143 | - return aspect; | ||
144 | - })); | ||
145 | - } | ||
146 | -} | ||
147 | - | ||
148 | -export type TranslateFunc = (key: string, defaultTranslation?: string) => string; | ||
149 | - | ||
150 | -const varsRegex = /\${([^}]*)}/g; | ||
151 | -const linkActionRegex = /<link-act name=['"]([^['"]*)['"]>([^<]*)<\/link-act>/g; | ||
152 | -const buttonActionRegex = /<button-act name=['"]([^['"]*)['"]>([^<]*)<\/button-act>/g; | ||
153 | - | ||
154 | -function createLinkElement(actionName: string, actionText: string): string { | ||
155 | - return `<a href="javascript:void(0);" class="tb-custom-action" data-action-name=${actionName}>${actionText}</a>`; | ||
156 | -} | ||
157 | - | ||
158 | -function createButtonElement(actionName: string, actionText: string) { | ||
159 | - return `<button mat-button class="tb-custom-action" data-action-name=${actionName}>${actionText}</button>`; | ||
160 | -} | ||
161 | - | ||
162 | -function parseTemplate(template: string, data: { $datasource?: Datasource, [key: string]: any }, | ||
163 | - translateFn?: TranslateFunc) { | ||
164 | - let res = ''; | ||
165 | - try { | ||
166 | - if (translateFn) { | ||
167 | - template = translateFn(template); | ||
168 | - } | ||
169 | - template = createLabelFromDatasource(data.$datasource, template); | ||
170 | - | ||
171 | - let match = /\${([^}]*)}/g.exec(template); | ||
172 | - while (match !== null) { | ||
173 | - const variable = match[0]; | ||
174 | - let label = match[1]; | ||
175 | - let valDec = 2; | ||
176 | - const splitValues = label.split(':'); | ||
177 | - if (splitValues.length > 1) { | ||
178 | - label = splitValues[0]; | ||
179 | - valDec = parseFloat(splitValues[1]); | ||
180 | - } | ||
181 | - | ||
182 | - if (label.startsWith('#')) { | ||
183 | - const keyIndexStr = label.substring(1); | ||
184 | - const n = Math.floor(Number(keyIndexStr)); | ||
185 | - if (String(n) === keyIndexStr && n >= 0) { | ||
186 | - label = data.$datasource.dataKeys[n].label; | ||
187 | - } | ||
188 | - } | ||
189 | - | ||
190 | - const value = data[label] || ''; | ||
191 | - let textValue: string; | ||
192 | - if (isNumber(value)) { | ||
193 | - textValue = padValue(value, valDec); | ||
194 | - } else { | ||
195 | - textValue = value; | ||
196 | - } | ||
197 | - template = template.replace(variable, textValue); | ||
198 | - match = /\${([^}]*)}/g.exec(template); | ||
199 | - } | ||
200 | - | ||
201 | - let actionTags: string; | ||
202 | - let actionText: string; | ||
203 | - let actionName: string; | ||
204 | - let action: string; | ||
205 | - | ||
206 | - match = linkActionRegex.exec(template); | ||
207 | - while (match !== null) { | ||
208 | - [actionTags, actionName, actionText] = match; | ||
209 | - action = createLinkElement(actionName, actionText); | ||
210 | - template = template.replace(actionTags, action); | ||
211 | - match = linkActionRegex.exec(template); | ||
212 | - } | ||
213 | - | ||
214 | - match = buttonActionRegex.exec(template); | ||
215 | - while (match !== null) { | ||
216 | - [actionTags, actionName, actionText] = match; | ||
217 | - action = createButtonElement(actionName, actionText); | ||
218 | - template = template.replace(actionTags, action); | ||
219 | - match = buttonActionRegex.exec(template); | ||
220 | - } | ||
221 | - | ||
222 | - // const compiled = _.template(template); | ||
223 | - // res = compiled(data); | ||
224 | - res = template; | ||
225 | - } catch (ex) { | ||
226 | - console.log(ex, template); | ||
227 | - } | ||
228 | - return res; | ||
229 | -} | ||
230 | - | ||
231 | -export function processPattern(template: string, data: { $datasource?: Datasource, [key: string]: any }): Array<ReplaceInfo> { | ||
232 | - const replaceInfo = []; | ||
233 | - try { | ||
234 | - const reg = /\${([^}]*)}/g; | ||
235 | - let match = reg.exec(template); | ||
236 | - while (match !== null) { | ||
237 | - const variableInfo: ReplaceInfo = { | ||
238 | - dataKeyName: '', | ||
239 | - valDec: 2, | ||
240 | - variable: '' | ||
241 | - }; | ||
242 | - const variable = match[0]; | ||
243 | - let label = match[1]; | ||
244 | - let valDec = 2; | ||
245 | - const splitValues = label.split(':'); | ||
246 | - if (splitValues.length > 1) { | ||
247 | - label = splitValues[0]; | ||
248 | - valDec = parseFloat(splitValues[1]); | ||
249 | - } | ||
250 | - | ||
251 | - variableInfo.variable = variable; | ||
252 | - variableInfo.valDec = valDec; | ||
253 | - | ||
254 | - if (label.startsWith('#')) { | ||
255 | - const keyIndexStr = label.substring(1); | ||
256 | - const n = Math.floor(Number(keyIndexStr)); | ||
257 | - if (String(n) === keyIndexStr && n >= 0) { | ||
258 | - variableInfo.dataKeyName = data.$datasource.dataKeys[n].label; | ||
259 | - } | ||
260 | - } else { | ||
261 | - variableInfo.dataKeyName = label; | ||
262 | - } | ||
263 | - replaceInfo.push(variableInfo); | ||
264 | - | ||
265 | - match = reg.exec(template); | ||
266 | - } | ||
267 | - } catch (ex) { | ||
268 | - console.log(ex, template); | ||
269 | - } | ||
270 | - return replaceInfo; | ||
271 | -} | ||
272 | - | ||
273 | -export function fillPattern(markerLabelText: string, replaceInfoLabelMarker: Array<ReplaceInfo>, data: FormattedData) { | ||
274 | - let text = createLabelFromDatasource(data.$datasource, markerLabelText); | ||
275 | - if (replaceInfoLabelMarker) { | ||
276 | - for (const variableInfo of replaceInfoLabelMarker) { | ||
277 | - let txtVal = ''; | ||
278 | - if (variableInfo.dataKeyName && isDefinedAndNotNull(data[variableInfo.dataKeyName])) { | ||
279 | - const varData = data[variableInfo.dataKeyName]; | ||
280 | - if (isNumber(varData)) { | ||
281 | - txtVal = padValue(varData, variableInfo.valDec); | ||
282 | - } else { | ||
283 | - txtVal = varData; | ||
284 | - } | ||
285 | - } | ||
286 | - text = text.replace(variableInfo.variable, txtVal); | ||
287 | - } | ||
288 | - } | ||
289 | - return text; | ||
290 | -} | ||
291 | - | ||
292 | -function prepareProcessPattern(template: string, translateFn?: TranslateFunc): string { | ||
293 | - if (translateFn) { | ||
294 | - template = translateFn(template); | ||
295 | - } | ||
296 | - let actionTags: string; | ||
297 | - let actionText: string; | ||
298 | - let actionName: string; | ||
299 | - let action: string; | ||
300 | - | ||
301 | - let match = linkActionRegex.exec(template); | ||
302 | - while (match !== null) { | ||
303 | - [actionTags, actionName, actionText] = match; | ||
304 | - action = createLinkElement(actionName, actionText); | ||
305 | - template = template.replace(actionTags, action); | ||
306 | - match = linkActionRegex.exec(template); | ||
307 | - } | ||
308 | - | ||
309 | - match = buttonActionRegex.exec(template); | ||
310 | - while (match !== null) { | ||
311 | - [actionTags, actionName, actionText] = match; | ||
312 | - action = createButtonElement(actionName, actionText); | ||
313 | - template = template.replace(actionTags, action); | ||
314 | - match = buttonActionRegex.exec(template); | ||
315 | - } | ||
316 | - return template; | ||
317 | -} | ||
318 | - | ||
319 | -export const parseWithTranslation = { | ||
320 | - | ||
321 | - translateFn: null, | ||
322 | - | ||
323 | - translate(key: string, defaultTranslation?: string): string { | ||
324 | - if (this.translateFn) { | ||
325 | - return this.translateFn(key, defaultTranslation); | ||
326 | - } else { | ||
327 | - throw console.error('Translate not assigned'); | ||
328 | - } | ||
329 | - }, | ||
330 | - parseTemplate(template: string, data: object, forceTranslate = false): string { | ||
331 | - return parseTemplate(forceTranslate ? this.translate(template) : template, data, this.translate.bind(this)); | ||
332 | - }, | ||
333 | - prepareProcessPattern(template: string, forceTranslate = false): string { | ||
334 | - return prepareProcessPattern(forceTranslate ? this.translate(template) : template, this.translate.bind(this)); | ||
335 | - }, | ||
336 | - setTranslate(translateFn: TranslateFunc) { | ||
337 | - this.translateFn = translateFn; | ||
338 | - } | ||
339 | -}; | ||
340 | - | ||
341 | -export function parseData(input: DatasourceData[]): FormattedData[] { | ||
342 | - return _(input).groupBy(el => el?.datasource?.entityName) | ||
343 | - .values().value().map((entityArray, i) => { | ||
344 | - const obj: FormattedData = { | ||
345 | - entityName: entityArray[0]?.datasource?.entityName, | ||
346 | - entityId: entityArray[0]?.datasource?.entityId, | ||
347 | - entityType: entityArray[0]?.datasource?.entityType, | ||
348 | - $datasource: entityArray[0]?.datasource, | ||
349 | - dsIndex: i, | ||
350 | - deviceType: null | ||
351 | - }; | ||
352 | - entityArray.filter(el => el.data.length).forEach(el => { | ||
353 | - const indexDate = el?.data?.length ? el.data.length - 1 : 0; | ||
354 | - obj[el?.dataKey?.label] = el?.data[indexDate][1]; | ||
355 | - obj[el?.dataKey?.label + '|ts'] = el?.data[indexDate][0]; | ||
356 | - if (el?.dataKey?.label === 'type') { | ||
357 | - obj.deviceType = el?.data[indexDate][1]; | ||
358 | - } | ||
359 | - }); | ||
360 | - return obj; | ||
361 | - }); | ||
362 | -} | ||
363 | - | ||
364 | -export function parseArray(input: DatasourceData[]): FormattedData[][] { | ||
365 | - return _(input).groupBy(el => el?.datasource?.entityName) | ||
366 | - .values().value().map((entityArray) => | ||
367 | - entityArray[0].data.map((el, i) => { | ||
368 | - const obj: FormattedData = { | ||
369 | - entityName: entityArray[0]?.datasource?.entityName, | ||
370 | - entityId: entityArray[0]?.datasource?.entityId, | ||
371 | - entityType: entityArray[0]?.datasource?.entityType, | ||
372 | - $datasource: entityArray[0]?.datasource, | ||
373 | - dsIndex: i, | ||
374 | - time: el[0], | ||
375 | - deviceType: null | ||
376 | - }; | ||
377 | - entityArray.filter(e => e.data.length && e.data[i]).forEach(entity => { | ||
378 | - obj[entity?.dataKey?.label] = entity?.data[i][1]; | ||
379 | - obj[entity?.dataKey?.label + '|ts'] = entity?.data[0][0]; | ||
380 | - if (entity?.dataKey?.label === 'type') { | ||
381 | - obj.deviceType = entity?.data[0][1]; | ||
382 | - } | ||
383 | - }); | ||
384 | - return obj; | ||
385 | - }) | ||
386 | - ); | ||
387 | -} | ||
388 | - | ||
389 | -export function parseFunction(source: any, params: string[] = ['def']): (...args: any[]) => any { | ||
390 | - let res = null; | ||
391 | - if (source?.length) { | ||
392 | - try { | ||
393 | - res = new Function(...params, source); | ||
394 | - } | ||
395 | - catch (err) { | ||
396 | - res = null; | ||
397 | - } | ||
398 | - } | ||
399 | - return res; | ||
400 | -} | ||
401 | - | ||
402 | -export function safeExecute(func: (...args: any[]) => any, params = []) { | ||
403 | - let res = null; | ||
404 | - if (func && typeof (func) === 'function') { | ||
405 | - try { | ||
406 | - res = func(...params); | ||
407 | - } | ||
408 | - catch (err) { | ||
409 | - console.log('error in external function:', err); | ||
410 | - res = null; | ||
411 | - } | ||
412 | - } | ||
413 | - return res; | ||
414 | -} | ||
415 | - | ||
416 | -export function functionValueCalculator(useFunction: boolean, func: (...args: any[]) => any, params = [], defaultValue: any) { | ||
417 | - let res; | ||
418 | - if (useFunction && isDefined(func) && isFunction(func)) { | ||
419 | - try { | ||
420 | - res = func(...params); | ||
421 | - if (!isDefinedAndNotNull(res) || res === '') { | ||
422 | - res = defaultValue; | ||
423 | - } | ||
424 | - } catch (err) { | ||
425 | - res = defaultValue; | ||
426 | - console.log('error in external function:', err); | ||
427 | - } | ||
428 | - } else { | ||
429 | - res = defaultValue; | ||
430 | - } | ||
431 | - return res; | ||
432 | -} | ||
433 | - | ||
434 | -export function calculateNewPointCoordinate(coordinate: number, imageSize: number): number { | ||
435 | - let pointCoordinate = coordinate / imageSize; | ||
436 | - if (pointCoordinate < 0) { | ||
437 | - pointCoordinate = 0; | ||
438 | - } else if (pointCoordinate > 1) { | ||
439 | - pointCoordinate = 1; | ||
440 | - } | ||
441 | - return pointCoordinate; | ||
442 | -} | ||
443 | - | ||
444 | -export function createLoadingDiv(loadingText: string): JQuery<HTMLElement> { | ||
445 | - return $(` | ||
446 | - <div style=" | ||
447 | - z-index: 12; | ||
448 | - position: absolute; | ||
449 | - top: 0; | ||
450 | - bottom: 0; | ||
451 | - left: 0; | ||
452 | - right: 0; | ||
453 | - flex-direction: column; | ||
454 | - align-content: center; | ||
455 | - align-items: center; | ||
456 | - justify-content: center; | ||
457 | - display: flex; | ||
458 | - background: rgba(255,255,255,0.7); | ||
459 | - font-size: 16px; | ||
460 | - font-family: Roboto; | ||
461 | - font-weight: 400; | ||
462 | - text-transform: uppercase; | ||
463 | - "> | ||
464 | - <span>${loadingText}</span> | ||
465 | - </div> | ||
466 | - `); | ||
467 | -} |
@@ -17,14 +17,16 @@ | @@ -17,14 +17,16 @@ | ||
17 | import L, { Icon, LeafletMouseEvent } from 'leaflet'; | 17 | import L, { Icon, LeafletMouseEvent } from 'leaflet'; |
18 | import { FormattedData, MarkerSettings } from './map-models'; | 18 | import { FormattedData, MarkerSettings } from './map-models'; |
19 | import { | 19 | import { |
20 | - aspectCache, | ||
21 | bindPopupActions, | 20 | bindPopupActions, |
22 | createTooltip, | 21 | createTooltip, |
22 | +} from './maps-utils'; | ||
23 | +import { | ||
24 | + aspectCache, | ||
23 | fillPattern, | 25 | fillPattern, |
24 | parseWithTranslation, | 26 | parseWithTranslation, |
25 | processPattern, | 27 | processPattern, |
26 | safeExecute | 28 | safeExecute |
27 | -} from './maps-utils'; | 29 | +} from './common-maps-utils'; |
28 | import tinycolor from 'tinycolor2'; | 30 | import tinycolor from 'tinycolor2'; |
29 | import { isDefined, isDefinedAndNotNull } from '@core/utils'; | 31 | import { isDefined, isDefinedAndNotNull } from '@core/utils'; |
30 | import LeafletMap from './leaflet-map'; | 32 | import LeafletMap from './leaflet-map'; |
@@ -15,7 +15,8 @@ | @@ -15,7 +15,8 @@ | ||
15 | /// | 15 | /// |
16 | 16 | ||
17 | import L, { LatLngExpression, LeafletMouseEvent } from 'leaflet'; | 17 | import L, { LatLngExpression, LeafletMouseEvent } from 'leaflet'; |
18 | -import { createTooltip, functionValueCalculator, parseWithTranslation, safeExecute } from './maps-utils'; | 18 | +import { createTooltip } from './maps-utils'; |
19 | +import { functionValueCalculator, parseWithTranslation, safeExecute } from './common-maps-utils'; | ||
19 | import 'leaflet-editable/src/Leaflet.Editable'; | 20 | import 'leaflet-editable/src/Leaflet.Editable'; |
20 | import { FormattedData, PolygonSettings } from './map-models'; | 21 | import { FormattedData, PolygonSettings } from './map-models'; |
21 | 22 |
@@ -18,7 +18,7 @@ import L, { PolylineDecoratorOptions } from 'leaflet'; | @@ -18,7 +18,7 @@ import L, { PolylineDecoratorOptions } from 'leaflet'; | ||
18 | import 'leaflet-polylinedecorator'; | 18 | import 'leaflet-polylinedecorator'; |
19 | 19 | ||
20 | import { FormattedData, PolylineSettings } from './map-models'; | 20 | import { FormattedData, PolylineSettings } from './map-models'; |
21 | -import { functionValueCalculator, safeExecute } from '@home/components/widget/lib/maps/maps-utils'; | 21 | +import { functionValueCalculator } from '@home/components/widget/lib/maps/common-maps-utils'; |
22 | 22 | ||
23 | export class Polyline { | 23 | export class Polyline { |
24 | 24 |
@@ -19,7 +19,7 @@ import LeafletMap from '../leaflet-map'; | @@ -19,7 +19,7 @@ import LeafletMap from '../leaflet-map'; | ||
19 | import { MapImage, PosFuncton, UnitedMapSettings } from '../map-models'; | 19 | import { MapImage, PosFuncton, UnitedMapSettings } from '../map-models'; |
20 | import { Observable, ReplaySubject } from 'rxjs'; | 20 | import { Observable, ReplaySubject } from 'rxjs'; |
21 | import { filter, map, mergeMap } from 'rxjs/operators'; | 21 | import { filter, map, mergeMap } from 'rxjs/operators'; |
22 | -import { aspectCache, calculateNewPointCoordinate, parseFunction } from '@home/components/widget/lib/maps/maps-utils'; | 22 | +import { aspectCache, calculateNewPointCoordinate, parseFunction } from '@home/components/widget/lib/maps/common-maps-utils'; |
23 | import { WidgetContext } from '@home/models/widget-component.models'; | 23 | import { WidgetContext } from '@home/models/widget-component.models'; |
24 | import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models'; | 24 | import { DataSet, DatasourceType, widgetType } from '@shared/models/widget.models'; |
25 | import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; | 25 | import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
@@ -14,11 +14,6 @@ | @@ -14,11 +14,6 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { | ||
18 | - googleMapSettingsSchema, hereMapSettingsSchema, imageMapSettingsSchema, | ||
19 | - openstreetMapSettingsSchema, | ||
20 | - tencentMapSettingsSchema | ||
21 | -} from '@home/components/widget/lib/maps/schemes'; | ||
22 | import { OpenStreetMap } from './openstreet-map'; | 17 | import { OpenStreetMap } from './openstreet-map'; |
23 | import { TencentMap } from './tencent-map'; | 18 | import { TencentMap } from './tencent-map'; |
24 | import { GoogleMap } from './google-map'; | 19 | import { GoogleMap } from './google-map'; |
@@ -26,38 +21,11 @@ import { HEREMap } from './here-map'; | @@ -26,38 +21,11 @@ import { HEREMap } from './here-map'; | ||
26 | import { ImageMap } from './image-map'; | 21 | import { ImageMap } from './image-map'; |
27 | import { Type } from '@angular/core'; | 22 | import { Type } from '@angular/core'; |
28 | import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; | 23 | import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; |
29 | -import { JsonSettingsSchema } from '@shared/models/widget.models'; | ||
30 | 24 | ||
31 | -interface IProvider { | ||
32 | - MapClass: Type<LeafletMap>; | ||
33 | - schema: JsonSettingsSchema; | ||
34 | - name: string; | ||
35 | -} | ||
36 | - | ||
37 | -export const providerSets: { [key: string]: IProvider } = { | ||
38 | - 'openstreet-map': { | ||
39 | - MapClass: OpenStreetMap, | ||
40 | - schema: openstreetMapSettingsSchema, | ||
41 | - name: 'openstreet-map' | ||
42 | - }, | ||
43 | - 'tencent-map': { | ||
44 | - MapClass: TencentMap, | ||
45 | - schema: tencentMapSettingsSchema, | ||
46 | - name: 'tencent-map' | ||
47 | - }, | ||
48 | - 'google-map': { | ||
49 | - MapClass: GoogleMap, | ||
50 | - schema: googleMapSettingsSchema, | ||
51 | - name: 'google-map' | ||
52 | - }, | ||
53 | - here: { | ||
54 | - MapClass: HEREMap, | ||
55 | - schema: hereMapSettingsSchema, | ||
56 | - name: 'here' | ||
57 | - }, | ||
58 | - 'image-map': { | ||
59 | - MapClass: ImageMap, | ||
60 | - schema: imageMapSettingsSchema, | ||
61 | - name: 'image-map' | ||
62 | - } | 25 | +export const providerClass: { [key: string]: Type<LeafletMap> } = { |
26 | + 'openstreet-map': OpenStreetMap, | ||
27 | + 'tencent-map': TencentMap, | ||
28 | + 'google-map': GoogleMap, | ||
29 | + here: HEREMap, | ||
30 | + 'image-map': ImageMap | ||
63 | }; | 31 | }; |
@@ -14,6 +14,8 @@ | @@ -14,6 +14,8 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | +import { JsonSettingsSchema } from '@shared/models/widget.models'; | ||
18 | + | ||
17 | export const googleMapSettingsSchema = | 19 | export const googleMapSettingsSchema = |
18 | { | 20 | { |
19 | schema: { | 21 | schema: { |
@@ -1096,3 +1098,31 @@ export const tripAnimationSchema = { | @@ -1096,3 +1098,31 @@ export const tripAnimationSchema = { | ||
1096 | ] | 1098 | ] |
1097 | }] | 1099 | }] |
1098 | }; | 1100 | }; |
1101 | + | ||
1102 | +interface IProvider { | ||
1103 | + schema: JsonSettingsSchema; | ||
1104 | + name: string; | ||
1105 | +} | ||
1106 | + | ||
1107 | +export const providerSets: { [key: string]: IProvider } = { | ||
1108 | + 'openstreet-map': { | ||
1109 | + schema: openstreetMapSettingsSchema, | ||
1110 | + name: 'openstreet-map' | ||
1111 | + }, | ||
1112 | + 'tencent-map': { | ||
1113 | + schema: tencentMapSettingsSchema, | ||
1114 | + name: 'tencent-map' | ||
1115 | + }, | ||
1116 | + 'google-map': { | ||
1117 | + schema: googleMapSettingsSchema, | ||
1118 | + name: 'google-map' | ||
1119 | + }, | ||
1120 | + here: { | ||
1121 | + schema: hereMapSettingsSchema, | ||
1122 | + name: 'here' | ||
1123 | + }, | ||
1124 | + 'image-map': { | ||
1125 | + schema: imageMapSettingsSchema, | ||
1126 | + name: 'image-map' | ||
1127 | + } | ||
1128 | +}; |
@@ -21,7 +21,7 @@ import { UtilsService } from '@core/services/utils.service'; | @@ -21,7 +21,7 @@ import { UtilsService } from '@core/services/utils.service'; | ||
21 | import { Store } from '@ngrx/store'; | 21 | import { Store } from '@ngrx/store'; |
22 | import { AppState } from '@core/core.state'; | 22 | import { AppState } from '@core/core.state'; |
23 | import { isDefined, isNumber } from '@core/utils'; | 23 | import { isDefined, isNumber } from '@core/utils'; |
24 | -import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; | 24 | +import { CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; |
25 | import * as tinycolor_ from 'tinycolor2'; | 25 | import * as tinycolor_ from 'tinycolor2'; |
26 | import { ResizeObserver } from '@juggle/resize-observer'; | 26 | import { ResizeObserver } from '@juggle/resize-observer'; |
27 | import GenericOptions = CanvasGauges.GenericOptions; | 27 | import GenericOptions = CanvasGauges.GenericOptions; |
@@ -102,7 +102,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -102,7 +102,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
102 | private textMeasure: JQuery<HTMLElement>; | 102 | private textMeasure: JQuery<HTMLElement>; |
103 | private canvasBarElement: HTMLElement; | 103 | private canvasBarElement: HTMLElement; |
104 | 104 | ||
105 | - private canvasBar: CanvasDigitalGauge; | 105 | + private canvasBar: any; // CanvasDigitalGauge; |
106 | 106 | ||
107 | private knobResize$: ResizeObserver; | 107 | private knobResize$: ResizeObserver; |
108 | 108 | ||
@@ -165,7 +165,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -165,7 +165,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
165 | animation: false | 165 | animation: false |
166 | }; | 166 | }; |
167 | 167 | ||
168 | - this.canvasBar = new CanvasDigitalGauge(canvasBarData).draw(); | ||
169 | 168 | ||
170 | this.knob.on('click', (e) => { | 169 | this.knob.on('click', (e) => { |
171 | if (this.moving) { | 170 | if (this.moving) { |
@@ -277,9 +276,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -277,9 +276,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
277 | 276 | ||
278 | }); | 277 | }); |
279 | 278 | ||
280 | - const initialValue = isDefined(settings.initialValue) ? settings.initialValue : this.minValue; | ||
281 | - this.setValue(initialValue); | ||
282 | - | ||
283 | const subscription = this.ctx.defaultSubscription; | 279 | const subscription = this.ctx.defaultSubscription; |
284 | const rpcEnabled = subscription.rpcEnabled; | 280 | const rpcEnabled = subscription.rpcEnabled; |
285 | 281 | ||
@@ -297,13 +293,22 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -297,13 +293,22 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
297 | if (settings.setValueMethod && settings.setValueMethod.length) { | 293 | if (settings.setValueMethod && settings.setValueMethod.length) { |
298 | this.setValueMethod = settings.setValueMethod; | 294 | this.setValueMethod = settings.setValueMethod; |
299 | } | 295 | } |
300 | - if (!rpcEnabled) { | ||
301 | - this.onError('Target device is not set!'); | ||
302 | - } else { | ||
303 | - if (!this.isSimulated) { | ||
304 | - this.rpcRequestValue(); | 296 | + |
297 | + import('@home/components/widget/lib/canvas-digital-gauge').then( | ||
298 | + (gauge) => { | ||
299 | + this.canvasBar = new gauge.CanvasDigitalGauge(canvasBarData).draw(); | ||
300 | + const initialValue = isDefined(settings.initialValue) ? settings.initialValue : this.minValue; | ||
301 | + this.setValue(initialValue); | ||
302 | + if (!rpcEnabled) { | ||
303 | + this.onError('Target device is not set!'); | ||
304 | + } else { | ||
305 | + if (!this.isSimulated) { | ||
306 | + this.rpcRequestValue(); | ||
307 | + } | ||
308 | + } | ||
305 | } | 309 | } |
306 | - } | 310 | + ); |
311 | + | ||
307 | } | 312 | } |
308 | 313 | ||
309 | private degreeToRatio(degree: number): number { | 314 | private degreeToRatio(degree: number): number { |
@@ -328,7 +333,9 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -328,7 +333,9 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
328 | const height = this.knobContainer.height(); | 333 | const height = this.knobContainer.height(); |
329 | const size = Math.min(width, height); | 334 | const size = Math.min(width, height); |
330 | this.knob.css({width: size, height: size}); | 335 | this.knob.css({width: size, height: size}); |
331 | - this.canvasBar.update({width: size, height: size} as GenericOptions); | 336 | + if (this.canvasBar) { |
337 | + this.canvasBar.update({width: size, height: size} as GenericOptions); | ||
338 | + } | ||
332 | this.setFontSize(this.knobTitle, this.title, this.knobTitleContainer.height(), this.knobTitleContainer.width()); | 339 | this.setFontSize(this.knobTitle, this.title, this.knobTitleContainer.height(), this.knobTitleContainer.width()); |
333 | this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width()); | 340 | this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width()); |
334 | const minmaxHeight = this.knobMinmaxContainer.height(); | 341 | const minmaxHeight = this.knobMinmaxContainer.height(); |
@@ -546,7 +546,7 @@ class TimeseriesDatasource implements DataSource<TimeseriesRow> { | @@ -546,7 +546,7 @@ class TimeseriesDatasource implements DataSource<TimeseriesRow> { | ||
546 | } | 546 | } |
547 | } | 547 | } |
548 | } else { | 548 | } else { |
549 | - rows = Object.values(rowsMap); | 549 | + rows = Object.keys(rowsMap).map(itm => rowsMap[itm]); |
550 | } | 550 | } |
551 | return rows; | 551 | return rows; |
552 | } | 552 | } |
@@ -27,28 +27,28 @@ import { | @@ -27,28 +27,28 @@ import { | ||
27 | SecurityContext, | 27 | SecurityContext, |
28 | ViewChild | 28 | ViewChild |
29 | } from '@angular/core'; | 29 | } from '@angular/core'; |
30 | -import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2'; | ||
31 | -import { FormattedData, MapProviders, TripAnimationSettings } from '../lib/maps/map-models'; | 30 | +import { FormattedData, MapProviders, TripAnimationSettings } from '@home/components/widget/lib/maps/map-models'; |
32 | import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils'; | 31 | import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils'; |
33 | -import { mapPolygonSchema, pathSchema, pointSchema, tripAnimationSchema } from '../lib/maps/schemes'; | 32 | +import { mapPolygonSchema, pathSchema, pointSchema, tripAnimationSchema } from '@home/components/widget/lib/maps/schemes'; |
34 | import { DomSanitizer } from '@angular/platform-browser'; | 33 | import { DomSanitizer } from '@angular/platform-browser'; |
35 | import { WidgetContext } from '@app/modules/home/models/widget-component.models'; | 34 | import { WidgetContext } from '@app/modules/home/models/widget-component.models'; |
36 | import { | 35 | import { |
37 | - findAngle, | 36 | + findAngle, getProviderSchema, |
38 | getRatio, | 37 | getRatio, |
39 | interpolateOnLineSegment, | 38 | interpolateOnLineSegment, |
40 | parseArray, | 39 | parseArray, |
41 | parseFunction, | 40 | parseFunction, |
42 | parseWithTranslation, | 41 | parseWithTranslation, |
43 | safeExecute | 42 | safeExecute |
44 | -} from '../lib/maps/maps-utils'; | 43 | +} from '@home/components/widget/lib/maps/common-maps-utils'; |
45 | import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models'; | 44 | import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models'; |
46 | import moment from 'moment'; | 45 | import moment from 'moment'; |
47 | import { isUndefined } from '@core/utils'; | 46 | import { isUndefined } from '@core/utils'; |
48 | import { ResizeObserver } from '@juggle/resize-observer'; | 47 | import { ResizeObserver } from '@juggle/resize-observer'; |
48 | +import { MapWidgetInterface } from '@home/components/widget/lib/maps/map-widget.interface'; | ||
49 | 49 | ||
50 | -interface dataMap { | ||
51 | - [key: string] : FormattedData | 50 | +interface DataMap { |
51 | + [key: string]: FormattedData; | ||
52 | } | 52 | } |
53 | 53 | ||
54 | @Component({ | 54 | @Component({ |
@@ -67,7 +67,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -67,7 +67,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
67 | 67 | ||
68 | @ViewChild('map') mapContainer; | 68 | @ViewChild('map') mapContainer; |
69 | 69 | ||
70 | - mapWidget: MapWidgetController; | 70 | + mapWidget: MapWidgetInterface; |
71 | historicalData: FormattedData[][]; | 71 | historicalData: FormattedData[][]; |
72 | normalizationStep: number; | 72 | normalizationStep: number; |
73 | interpolatedTimeData = []; | 73 | interpolatedTimeData = []; |
@@ -85,7 +85,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -85,7 +85,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
85 | 85 | ||
86 | static getSettingsSchema(): JsonSettingsSchema { | 86 | static getSettingsSchema(): JsonSettingsSchema { |
87 | const schema = initSchema(); | 87 | const schema = initSchema(); |
88 | - addToSchema(schema, TbMapWidgetV2.getProvidersSchema(null, true)); | 88 | + addToSchema(schema, getProviderSchema(null, true)); |
89 | addGroupInfo(schema, 'Map Provider Settings'); | 89 | addGroupInfo(schema, 'Map Provider Settings'); |
90 | addToSchema(schema, tripAnimationSchema); | 90 | addToSchema(schema, tripAnimationSchema); |
91 | addGroupInfo(schema, 'Trip Animation Settings'); | 91 | addGroupInfo(schema, 'Trip Animation Settings'); |
@@ -106,7 +106,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -106,7 +106,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
106 | buttonColor: tinycolor(this.widgetConfig.color).setAlpha(0.54).toRgbString(), | 106 | buttonColor: tinycolor(this.widgetConfig.color).setAlpha(0.54).toRgbString(), |
107 | disabledButtonColor: tinycolor(this.widgetConfig.color).setAlpha(0.3).toRgbString(), | 107 | disabledButtonColor: tinycolor(this.widgetConfig.color).setAlpha(0.3).toRgbString(), |
108 | rotationAngle: 0 | 108 | rotationAngle: 0 |
109 | - } | 109 | + }; |
110 | this.settings = { ...settings, ...this.ctx.settings }; | 110 | this.settings = { ...settings, ...this.ctx.settings }; |
111 | this.useAnchors = this.settings.showPoints && this.settings.usePointAsAnchor; | 111 | this.useAnchors = this.settings.showPoints && this.settings.usePointAsAnchor; |
112 | this.settings.pointAsAnchorFunction = parseFunction(this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex']); | 112 | this.settings.pointAsAnchorFunction = parseFunction(this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex']); |
@@ -122,15 +122,19 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -122,15 +122,19 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
122 | } | 122 | } |
123 | this.mapWidget.map.map?.invalidateSize(); | 123 | this.mapWidget.map.map?.invalidateSize(); |
124 | this.cd.detectChanges(); | 124 | this.cd.detectChanges(); |
125 | - } | 125 | + }; |
126 | } | 126 | } |
127 | 127 | ||
128 | ngAfterViewInit() { | 128 | ngAfterViewInit() { |
129 | - this.mapWidget = new MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement); | ||
130 | - this.mapResize$ = new ResizeObserver(() => { | ||
131 | - this.mapWidget.resize(); | ||
132 | - }); | ||
133 | - this.mapResize$.observe(this.mapContainer.nativeElement); | 129 | + import('@home/components/widget/lib/maps/map-widget2').then( |
130 | + (mod) => { | ||
131 | + this.mapWidget = new mod.MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement); | ||
132 | + this.mapResize$ = new ResizeObserver(() => { | ||
133 | + this.mapWidget.resize(); | ||
134 | + }); | ||
135 | + this.mapResize$.observe(this.mapContainer.nativeElement); | ||
136 | + } | ||
137 | + ); | ||
134 | } | 138 | } |
135 | 139 | ||
136 | ngOnDestroy() { | 140 | ngOnDestroy() { |
@@ -142,8 +146,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -142,8 +146,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
142 | timeUpdated(time: number) { | 146 | timeUpdated(time: number) { |
143 | this.currentTime = time; | 147 | this.currentTime = time; |
144 | const currentPosition = this.interpolatedTimeData | 148 | const currentPosition = this.interpolatedTimeData |
145 | - .map(dataSource => dataSource[time]) | ||
146 | - for(let j = 0; j < this.interpolatedTimeData.length; j++) { | 149 | + .map(dataSource => dataSource[time]); |
150 | + for (let j = 0; j < this.interpolatedTimeData.length; j++) { | ||
147 | if (isUndefined(currentPosition[j])) { | 151 | if (isUndefined(currentPosition[j])) { |
148 | const timePoints = Object.keys(this.interpolatedTimeData[j]).map(item => parseInt(item, 10)); | 152 | const timePoints = Object.keys(this.interpolatedTimeData[j]).map(item => parseInt(item, 10)); |
149 | for (let i = 1; i < timePoints.length; i++) { | 153 | for (let i = 1; i < timePoints.length; i++) { |
@@ -155,13 +159,13 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -155,13 +159,13 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
155 | ...beforePosition, | 159 | ...beforePosition, |
156 | time, | 160 | time, |
157 | ...interpolateOnLineSegment(beforePosition, afterPosition, this.settings.latKeyName, this.settings.lngKeyName, ratio) | 161 | ...interpolateOnLineSegment(beforePosition, afterPosition, this.settings.latKeyName, this.settings.lngKeyName, ratio) |
158 | - } | 162 | + }; |
159 | break; | 163 | break; |
160 | } | 164 | } |
161 | } | 165 | } |
162 | } | 166 | } |
163 | } | 167 | } |
164 | - for(let j = 0; j < this.interpolatedTimeData.length; j++) { | 168 | + for (let j = 0; j < this.interpolatedTimeData.length; j++) { |
165 | if (isUndefined(currentPosition[j])) { | 169 | if (isUndefined(currentPosition[j])) { |
166 | currentPosition[j] = this.calculateLastPoints(this.interpolatedTimeData[j], time); | 170 | currentPosition[j] = this.calculateLastPoints(this.interpolatedTimeData[j], time); |
167 | } | 171 | } |
@@ -179,7 +183,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -179,7 +183,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
179 | } | 183 | } |
180 | this.mapWidget.map.updateMarkers(currentPosition, true, (trip) => { | 184 | this.mapWidget.map.updateMarkers(currentPosition, true, (trip) => { |
181 | this.activeTrip = trip; | 185 | this.activeTrip = trip; |
182 | - this.timeUpdated(this.currentTime) | 186 | + this.timeUpdated(this.currentTime); |
183 | }); | 187 | }); |
184 | } | 188 | } |
185 | } | 189 | } |
@@ -187,14 +191,14 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -187,14 +191,14 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
187 | setActiveTrip() { | 191 | setActiveTrip() { |
188 | } | 192 | } |
189 | 193 | ||
190 | - private calculateLastPoints(dataSource: dataMap, time: number): FormattedData { | 194 | + private calculateLastPoints(dataSource: DataMap, time: number): FormattedData { |
191 | const timeArr = Object.keys(dataSource); | 195 | const timeArr = Object.keys(dataSource); |
192 | - let index = timeArr.findIndex((dtime, index) => { | 196 | + let index = timeArr.findIndex((dtime) => { |
193 | return Number(dtime) >= time; | 197 | return Number(dtime) >= time; |
194 | }); | 198 | }); |
195 | 199 | ||
196 | - if(index !== -1) { | ||
197 | - if(Number(timeArr[index]) !== time && index !== 0) { | 200 | + if (index !== -1) { |
201 | + if (Number(timeArr[index]) !== time && index !== 0) { | ||
198 | index--; | 202 | index--; |
199 | } | 203 | } |
200 | } else { | 204 | } else { |
@@ -210,7 +214,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -210,7 +214,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
210 | this.maxTime = dataSource[dataSource.length - 1]?.time || -Infinity; | 214 | this.maxTime = dataSource[dataSource.length - 1]?.time || -Infinity; |
211 | this.interpolatedTimeData[index] = this.interpolateArray(dataSource); | 215 | this.interpolatedTimeData[index] = this.interpolateArray(dataSource); |
212 | }); | 216 | }); |
213 | - if(!this.activeTrip){ | 217 | + if (!this.activeTrip) { |
214 | this.activeTrip = this.interpolatedTimeData.map(dataSource => dataSource[this.minTime]).filter(ds => ds)[0]; | 218 | this.activeTrip = this.interpolatedTimeData.map(dataSource => dataSource[this.minTime]).filter(ds => ds)[0]; |
215 | } | 219 | } |
216 | if (this.useAnchors) { | 220 | if (this.useAnchors) { |
@@ -230,7 +234,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -230,7 +234,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
230 | 234 | ||
231 | private calcMainTooltip(points: FormattedData[]): void { | 235 | private calcMainTooltip(points: FormattedData[]): void { |
232 | const tooltips = []; | 236 | const tooltips = []; |
233 | - for (let point of points) { | 237 | + for (const point of points) { |
234 | tooltips.push(this.sanitizer.sanitize(SecurityContext.HTML, this.calcTooltip(point))); | 238 | tooltips.push(this.sanitizer.sanitize(SecurityContext.HTML, this.calcTooltip(point))); |
235 | } | 239 | } |
236 | this.mainTooltips = tooltips; | 240 | this.mainTooltips = tooltips; |
@@ -259,7 +263,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | @@ -259,7 +263,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy | ||
259 | } | 263 | } |
260 | const timeStamp = Object.keys(result); | 264 | const timeStamp = Object.keys(result); |
261 | for (let i = 0; i < timeStamp.length - 1; i++) { | 265 | for (let i = 0; i < timeStamp.length - 1; i++) { |
262 | - result[timeStamp[i]].rotationAngle += findAngle(result[timeStamp[i]], result[timeStamp[i + 1]], latKeyName, lngKeyName) | 266 | + result[timeStamp[i]].rotationAngle += findAngle(result[timeStamp[i]], result[timeStamp[i + 1]], latKeyName, lngKeyName); |
263 | } | 267 | } |
264 | return result; | 268 | return result; |
265 | } | 269 | } |
@@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
17 | import { Inject, Injectable, Optional, Type } from '@angular/core'; | 17 | import { Inject, Injectable, Optional, Type } from '@angular/core'; |
18 | import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service'; | 18 | import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service'; |
19 | import { WidgetService } from '@core/http/widget.service'; | 19 | import { WidgetService } from '@core/http/widget.service'; |
20 | -import { forkJoin, Observable, of, ReplaySubject, Subject, throwError } from 'rxjs'; | 20 | +import { forkJoin, from, Observable, of, ReplaySubject, Subject, throwError } from 'rxjs'; |
21 | import { | 21 | import { |
22 | ErrorWidgetType, | 22 | ErrorWidgetType, |
23 | MissingWidgetType, | 23 | MissingWidgetType, |
@@ -30,7 +30,7 @@ import cssjs from '@core/css/css'; | @@ -30,7 +30,7 @@ import cssjs from '@core/css/css'; | ||
30 | import { UtilsService } from '@core/services/utils.service'; | 30 | import { UtilsService } from '@core/services/utils.service'; |
31 | import { ResourcesService } from '@core/services/resources.service'; | 31 | import { ResourcesService } from '@core/services/resources.service'; |
32 | import { Widget, widgetActionSources, WidgetControllerDescriptor, WidgetType } from '@shared/models/widget.models'; | 32 | import { Widget, widgetActionSources, WidgetControllerDescriptor, WidgetType } from '@shared/models/widget.models'; |
33 | -import { catchError, map, mergeMap, switchMap } from 'rxjs/operators'; | 33 | +import { catchError, map, mergeMap, switchMap, tap } from 'rxjs/operators'; |
34 | import { isFunction, isUndefined } from '@core/utils'; | 34 | import { isFunction, isUndefined } from '@core/utils'; |
35 | import { TranslateService } from '@ngx-translate/core'; | 35 | import { TranslateService } from '@ngx-translate/core'; |
36 | import { DynamicWidgetComponent } from '@home/components/widget/dynamic-widget.component'; | 36 | import { DynamicWidgetComponent } from '@home/components/widget/dynamic-widget.component'; |
@@ -42,6 +42,10 @@ import { WidgetTypeId } from '@app/shared/models/id/widget-type-id'; | @@ -42,6 +42,10 @@ import { WidgetTypeId } from '@app/shared/models/id/widget-type-id'; | ||
42 | import { TenantId } from '@app/shared/models/id/tenant-id'; | 42 | import { TenantId } from '@app/shared/models/id/tenant-id'; |
43 | import { SharedModule } from '@shared/shared.module'; | 43 | import { SharedModule } from '@shared/shared.module'; |
44 | import { MODULES_MAP } from '@shared/public-api'; | 44 | import { MODULES_MAP } from '@shared/public-api'; |
45 | +import * as tinycolor_ from 'tinycolor2'; | ||
46 | +import moment from 'moment'; | ||
47 | + | ||
48 | +const tinycolor = tinycolor_; | ||
45 | 49 | ||
46 | // @dynamic | 50 | // @dynamic |
47 | @Injectable() | 51 | @Injectable() |
@@ -106,20 +110,77 @@ export class WidgetComponentService { | @@ -106,20 +110,77 @@ export class WidgetComponentService { | ||
106 | } | 110 | } |
107 | const initSubject = new ReplaySubject(); | 111 | const initSubject = new ReplaySubject(); |
108 | this.init$ = initSubject.asObservable(); | 112 | this.init$ = initSubject.asObservable(); |
109 | - const loadDefaultWidgetInfoTasks = [ | ||
110 | - this.loadWidgetResources(this.missingWidgetType, 'global-widget-missing-type', [SharedModule, WidgetComponentsModule]), | ||
111 | - this.loadWidgetResources(this.errorWidgetType, 'global-widget-error-type', [SharedModule, WidgetComponentsModule]), | ||
112 | - ]; | ||
113 | - forkJoin(loadDefaultWidgetInfoTasks).subscribe( | 113 | + |
114 | + (window as any).tinycolor = tinycolor; | ||
115 | + (window as any).cssjs = cssjs; | ||
116 | + (window as any).moment = moment; | ||
117 | + | ||
118 | + const widgetModulesTasks: Observable<any>[] = []; | ||
119 | + widgetModulesTasks.push(from(import('@home/components/widget/lib/flot-widget')).pipe( | ||
120 | + tap((mod) => { | ||
121 | + (window as any).TbFlot = mod.TbFlot; | ||
122 | + })) | ||
123 | + ); | ||
124 | + widgetModulesTasks.push(from(import('@home/components/widget/lib/analogue-compass')).pipe( | ||
125 | + tap((mod) => { | ||
126 | + (window as any).TbAnalogueCompass = mod.TbAnalogueCompass; | ||
127 | + })) | ||
128 | + ); | ||
129 | + widgetModulesTasks.push(from(import('@home/components/widget/lib/analogue-radial-gauge')).pipe( | ||
130 | + tap((mod) => { | ||
131 | + (window as any).TbAnalogueRadialGauge = mod.TbAnalogueRadialGauge; | ||
132 | + })) | ||
133 | + ); | ||
134 | + widgetModulesTasks.push(from(import('@home/components/widget/lib/analogue-linear-gauge')).pipe( | ||
135 | + tap((mod) => { | ||
136 | + (window as any).TbAnalogueLinearGauge = mod.TbAnalogueLinearGauge; | ||
137 | + })) | ||
138 | + ); | ||
139 | + widgetModulesTasks.push(from(import('@home/components/widget/lib/digital-gauge')).pipe( | ||
140 | + tap((mod) => { | ||
141 | + (window as any).TbCanvasDigitalGauge = mod.TbCanvasDigitalGauge; | ||
142 | + })) | ||
143 | + ); | ||
144 | + widgetModulesTasks.push(from(import('@home/components/widget/lib/maps/map-widget2')).pipe( | ||
145 | + tap((mod) => { | ||
146 | + (window as any).TbMapWidgetV2 = mod.TbMapWidgetV2; | ||
147 | + })) | ||
148 | + ); | ||
149 | + widgetModulesTasks.push(from(import('@home/components/widget/trip-animation/trip-animation.component')).pipe( | ||
150 | + tap((mod) => { | ||
151 | + (window as any).TbTripAnimationWidget = mod.TbTripAnimationWidget; | ||
152 | + })) | ||
153 | + ); | ||
154 | + | ||
155 | + forkJoin(widgetModulesTasks).subscribe( | ||
114 | () => { | 156 | () => { |
115 | - initSubject.next(); | 157 | + const loadDefaultWidgetInfoTasks = [ |
158 | + this.loadWidgetResources(this.missingWidgetType, 'global-widget-missing-type', [SharedModule, WidgetComponentsModule]), | ||
159 | + this.loadWidgetResources(this.errorWidgetType, 'global-widget-error-type', [SharedModule, WidgetComponentsModule]), | ||
160 | + ]; | ||
161 | + forkJoin(loadDefaultWidgetInfoTasks).subscribe( | ||
162 | + () => { | ||
163 | + initSubject.next(); | ||
164 | + }, | ||
165 | + (e) => { | ||
166 | + let errorMessages = ['Failed to load default widget types!']; | ||
167 | + if (e && e.length) { | ||
168 | + errorMessages = errorMessages.concat(e); | ||
169 | + } | ||
170 | + console.error('Failed to load default widget types!'); | ||
171 | + initSubject.error({ | ||
172 | + widgetInfo: this.errorWidgetType, | ||
173 | + errorMessages | ||
174 | + }); | ||
175 | + } | ||
176 | + ); | ||
116 | }, | 177 | }, |
117 | (e) => { | 178 | (e) => { |
118 | - let errorMessages = ['Failed to load default widget types!']; | 179 | + let errorMessages = ['Failed to load widget modules!']; |
119 | if (e && e.length) { | 180 | if (e && e.length) { |
120 | errorMessages = errorMessages.concat(e); | 181 | errorMessages = errorMessages.concat(e); |
121 | } | 182 | } |
122 | - console.error('Failed to load default widget types!'); | 183 | + console.error('Failed to load widget modules!'); |
123 | initSubject.error({ | 184 | initSubject.error({ |
124 | widgetInfo: this.errorWidgetType, | 185 | widgetInfo: this.errorWidgetType, |
125 | errorMessages | 186 | errorMessages |
@@ -32,7 +32,8 @@ import { NULL_UUID } from '@shared/models/id/has-uuid'; | @@ -32,7 +32,8 @@ import { NULL_UUID } from '@shared/models/id/has-uuid'; | ||
32 | import { Hotkey } from 'angular2-hotkeys'; | 32 | import { Hotkey } from 'angular2-hotkeys'; |
33 | import { TranslateService } from '@ngx-translate/core'; | 33 | import { TranslateService } from '@ngx-translate/core'; |
34 | import { getCurrentIsLoading } from '@app/core/interceptors/load.selectors'; | 34 | import { getCurrentIsLoading } from '@app/core/interceptors/load.selectors'; |
35 | -import * as ace from 'ace-builds'; | 35 | +import { Ace } from 'ace-builds'; |
36 | +import { getAce, Range } from '@shared/models/ace/ace.models'; | ||
36 | import { css_beautify, html_beautify } from 'js-beautify'; | 37 | import { css_beautify, html_beautify } from 'js-beautify'; |
37 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; | 38 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
38 | import { WINDOW } from '@core/services/window.service'; | 39 | import { WINDOW } from '@core/services/window.service'; |
@@ -44,10 +45,12 @@ import { | @@ -44,10 +45,12 @@ import { | ||
44 | SaveWidgetTypeAsDialogComponent, | 45 | SaveWidgetTypeAsDialogComponent, |
45 | SaveWidgetTypeAsDialogResult | 46 | SaveWidgetTypeAsDialogResult |
46 | } from '@home/pages/widget/save-widget-type-as-dialog.component'; | 47 | } from '@home/pages/widget/save-widget-type-as-dialog.component'; |
47 | -import { Subscription } from 'rxjs'; | 48 | +import { forkJoin, from, Subscription } from 'rxjs'; |
48 | import { ResizeObserver } from '@juggle/resize-observer'; | 49 | import { ResizeObserver } from '@juggle/resize-observer'; |
49 | import Timeout = NodeJS.Timeout; | 50 | import Timeout = NodeJS.Timeout; |
50 | import { widgetEditorCompleter } from '@home/pages/widget/widget-editor.models'; | 51 | import { widgetEditorCompleter } from '@home/pages/widget/widget-editor.models'; |
52 | +import { Observable } from 'rxjs/internal/Observable'; | ||
53 | +import { map, tap } from 'rxjs/operators'; | ||
51 | 54 | ||
52 | // @dynamic | 55 | // @dynamic |
53 | @Component({ | 56 | @Component({ |
@@ -119,13 +122,13 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe | @@ -119,13 +122,13 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe | ||
119 | javascriptFullscreen = false; | 122 | javascriptFullscreen = false; |
120 | iFrameFullscreen = false; | 123 | iFrameFullscreen = false; |
121 | 124 | ||
122 | - aceEditors: ace.Ace.Editor[] = []; | 125 | + aceEditors: Ace.Editor[] = []; |
123 | editorsResizeCafs: {[editorId: string]: CancelAnimationFrame} = {}; | 126 | editorsResizeCafs: {[editorId: string]: CancelAnimationFrame} = {}; |
124 | - htmlEditor: ace.Ace.Editor; | ||
125 | - cssEditor: ace.Ace.Editor; | ||
126 | - jsonSettingsEditor: ace.Ace.Editor; | ||
127 | - dataKeyJsonSettingsEditor: ace.Ace.Editor; | ||
128 | - jsEditor: ace.Ace.Editor; | 127 | + htmlEditor: Ace.Editor; |
128 | + cssEditor: Ace.Editor; | ||
129 | + jsonSettingsEditor: Ace.Editor; | ||
130 | + dataKeyJsonSettingsEditor: Ace.Editor; | ||
131 | + jsEditor: Ace.Editor; | ||
129 | aceResize$: ResizeObserver; | 132 | aceResize$: ResizeObserver; |
130 | 133 | ||
131 | onWindowMessageListener = this.onWindowMessage.bind(this); | 134 | onWindowMessageListener = this.onWindowMessage.bind(this); |
@@ -277,51 +280,85 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe | @@ -277,51 +280,85 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe | ||
277 | this.onAceEditorResize(editor); | 280 | this.onAceEditorResize(editor); |
278 | }); | 281 | }); |
279 | }); | 282 | }); |
280 | - this.htmlEditor = this.createAceEditor(this.htmlInputElmRef, 'html'); | ||
281 | - this.htmlEditor.on('input', () => { | ||
282 | - const editorValue = this.htmlEditor.getValue(); | ||
283 | - if (this.widget.templateHtml !== editorValue) { | ||
284 | - this.widget.templateHtml = editorValue; | ||
285 | - this.isDirty = true; | ||
286 | - } | ||
287 | - }); | ||
288 | - this.cssEditor = this.createAceEditor(this.cssInputElmRef, 'css'); | ||
289 | - this.cssEditor.on('input', () => { | ||
290 | - const editorValue = this.cssEditor.getValue(); | ||
291 | - if (this.widget.templateCss !== editorValue) { | ||
292 | - this.widget.templateCss = editorValue; | ||
293 | - this.isDirty = true; | ||
294 | - } | ||
295 | - }); | ||
296 | - this.jsonSettingsEditor = this.createAceEditor(this.settingsJsonInputElmRef, 'json'); | ||
297 | - this.jsonSettingsEditor.on('input', () => { | ||
298 | - const editorValue = this.jsonSettingsEditor.getValue(); | ||
299 | - if (this.widget.settingsSchema !== editorValue) { | ||
300 | - this.widget.settingsSchema = editorValue; | ||
301 | - this.isDirty = true; | ||
302 | - } | ||
303 | - }); | ||
304 | - this.dataKeyJsonSettingsEditor = this.createAceEditor(this.dataKeySettingsJsonInputElmRef, 'json'); | ||
305 | - this.dataKeyJsonSettingsEditor.on('input', () => { | ||
306 | - const editorValue = this.dataKeyJsonSettingsEditor.getValue(); | ||
307 | - if (this.widget.dataKeySettingsSchema !== editorValue) { | ||
308 | - this.widget.dataKeySettingsSchema = editorValue; | ||
309 | - this.isDirty = true; | ||
310 | - } | ||
311 | - }); | ||
312 | - this.jsEditor = this.createAceEditor(this.javascriptInputElmRef, 'javascript'); | ||
313 | - this.jsEditor.on('input', () => { | ||
314 | - const editorValue = this.jsEditor.getValue(); | ||
315 | - if (this.widget.controllerScript !== editorValue) { | ||
316 | - this.widget.controllerScript = editorValue; | ||
317 | - this.isDirty = true; | 283 | + |
284 | + const editorsObservables: Observable<any>[] = []; | ||
285 | + | ||
286 | + | ||
287 | + editorsObservables.push(this.createAceEditor(this.htmlInputElmRef, 'html').pipe( | ||
288 | + tap((editor) => { | ||
289 | + this.htmlEditor = editor; | ||
290 | + this.htmlEditor.on('input', () => { | ||
291 | + const editorValue = this.htmlEditor.getValue(); | ||
292 | + if (this.widget.templateHtml !== editorValue) { | ||
293 | + this.widget.templateHtml = editorValue; | ||
294 | + this.isDirty = true; | ||
295 | + } | ||
296 | + }); | ||
297 | + }) | ||
298 | + )); | ||
299 | + | ||
300 | + editorsObservables.push(this.createAceEditor(this.cssInputElmRef, 'css').pipe( | ||
301 | + tap((editor) => { | ||
302 | + this.cssEditor = editor; | ||
303 | + this.cssEditor.on('input', () => { | ||
304 | + const editorValue = this.cssEditor.getValue(); | ||
305 | + if (this.widget.templateCss !== editorValue) { | ||
306 | + this.widget.templateCss = editorValue; | ||
307 | + this.isDirty = true; | ||
308 | + } | ||
309 | + }); | ||
310 | + }) | ||
311 | + )); | ||
312 | + | ||
313 | + editorsObservables.push(this.createAceEditor(this.settingsJsonInputElmRef, 'json').pipe( | ||
314 | + tap((editor) => { | ||
315 | + this.jsonSettingsEditor = editor; | ||
316 | + this.jsonSettingsEditor.on('input', () => { | ||
317 | + const editorValue = this.jsonSettingsEditor.getValue(); | ||
318 | + if (this.widget.settingsSchema !== editorValue) { | ||
319 | + this.widget.settingsSchema = editorValue; | ||
320 | + this.isDirty = true; | ||
321 | + } | ||
322 | + }); | ||
323 | + }) | ||
324 | + )); | ||
325 | + | ||
326 | + editorsObservables.push(this.createAceEditor(this.dataKeySettingsJsonInputElmRef, 'json').pipe( | ||
327 | + tap((editor) => { | ||
328 | + this.dataKeyJsonSettingsEditor = editor; | ||
329 | + this.dataKeyJsonSettingsEditor.on('input', () => { | ||
330 | + const editorValue = this.dataKeyJsonSettingsEditor.getValue(); | ||
331 | + if (this.widget.dataKeySettingsSchema !== editorValue) { | ||
332 | + this.widget.dataKeySettingsSchema = editorValue; | ||
333 | + this.isDirty = true; | ||
334 | + } | ||
335 | + }); | ||
336 | + }) | ||
337 | + )); | ||
338 | + | ||
339 | + editorsObservables.push(this.createAceEditor(this.javascriptInputElmRef, 'javascript').pipe( | ||
340 | + tap((editor) => { | ||
341 | + this.jsEditor = editor; | ||
342 | + this.jsEditor.on('input', () => { | ||
343 | + const editorValue = this.jsEditor.getValue(); | ||
344 | + if (this.widget.controllerScript !== editorValue) { | ||
345 | + this.widget.controllerScript = editorValue; | ||
346 | + this.isDirty = true; | ||
347 | + } | ||
348 | + }); | ||
349 | + this.jsEditor.on('change', () => { | ||
350 | + this.cleanupJsErrors(); | ||
351 | + }); | ||
352 | + this.jsEditor.completers = [widgetEditorCompleter, ...(this.jsEditor.completers || [])]; | ||
353 | + }) | ||
354 | + )); | ||
355 | + | ||
356 | + forkJoin(editorsObservables).subscribe( | ||
357 | + () => { | ||
358 | + this.setAceEditorValues(); | ||
318 | } | 359 | } |
319 | - }); | ||
320 | - this.jsEditor.on('change', () => { | ||
321 | - this.cleanupJsErrors(); | ||
322 | - }); | ||
323 | - this.jsEditor.completers = [widgetEditorCompleter, ...(this.jsEditor.completers || [])]; | ||
324 | - this.setAceEditorValues(); | 360 | + ); |
361 | + | ||
325 | } | 362 | } |
326 | 363 | ||
327 | private setAceEditorValues() { | 364 | private setAceEditorValues() { |
@@ -332,9 +369,9 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe | @@ -332,9 +369,9 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe | ||
332 | this.jsEditor.setValue(this.widget.controllerScript ? this.widget.controllerScript : '', -1); | 369 | this.jsEditor.setValue(this.widget.controllerScript ? this.widget.controllerScript : '', -1); |
333 | } | 370 | } |
334 | 371 | ||
335 | - private createAceEditor(editorElementRef: ElementRef, mode: string): ace.Ace.Editor { | 372 | + private createAceEditor(editorElementRef: ElementRef, mode: string): Observable<Ace.Editor> { |
336 | const editorElement = editorElementRef.nativeElement; | 373 | const editorElement = editorElementRef.nativeElement; |
337 | - let editorOptions: Partial<ace.Ace.EditorOptions> = { | 374 | + let editorOptions: Partial<Ace.EditorOptions> = { |
338 | mode: `ace/mode/${mode}`, | 375 | mode: `ace/mode/${mode}`, |
339 | showGutter: true, | 376 | showGutter: true, |
340 | showPrintMargin: true | 377 | showPrintMargin: true |
@@ -345,14 +382,18 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe | @@ -345,14 +382,18 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe | ||
345 | enableLiveAutocompletion: true | 382 | enableLiveAutocompletion: true |
346 | }; | 383 | }; |
347 | editorOptions = {...editorOptions, ...advancedOptions}; | 384 | editorOptions = {...editorOptions, ...advancedOptions}; |
348 | - const aceEditor = ace.edit(editorElement, editorOptions); | ||
349 | - aceEditor.session.setUseWrapMode(true); | ||
350 | - this.aceEditors.push(aceEditor); | ||
351 | - this.aceResize$.observe(editorElement); | ||
352 | - return aceEditor; | 385 | + return getAce().pipe( |
386 | + map((ace) => { | ||
387 | + const aceEditor = ace.edit(editorElement, editorOptions); | ||
388 | + aceEditor.session.setUseWrapMode(true); | ||
389 | + this.aceEditors.push(aceEditor); | ||
390 | + this.aceResize$.observe(editorElement); | ||
391 | + return aceEditor; | ||
392 | + }) | ||
393 | + ); | ||
353 | } | 394 | } |
354 | 395 | ||
355 | - private onAceEditorResize(aceEditor: ace.Ace.Editor) { | 396 | + private onAceEditorResize(aceEditor: Ace.Editor) { |
356 | if (this.editorsResizeCafs[aceEditor.id]) { | 397 | if (this.editorsResizeCafs[aceEditor.id]) { |
357 | this.editorsResizeCafs[aceEditor.id](); | 398 | this.editorsResizeCafs[aceEditor.id](); |
358 | delete this.editorsResizeCafs[aceEditor.id]; | 399 | delete this.editorsResizeCafs[aceEditor.id]; |
@@ -443,11 +484,11 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe | @@ -443,11 +484,11 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe | ||
443 | if (details.columnNumber) { | 484 | if (details.columnNumber) { |
444 | column = details.columnNumber; | 485 | column = details.columnNumber; |
445 | } | 486 | } |
446 | - const errorMarkerId = this.jsEditor.session.addMarker(new ace.Range(line, 0, line, Infinity), | 487 | + const errorMarkerId = this.jsEditor.session.addMarker(new Range(line, 0, line, Infinity), |
447 | 'ace_active-line', 'screenLine'); | 488 | 'ace_active-line', 'screenLine'); |
448 | this.errorMarkers.push(errorMarkerId); | 489 | this.errorMarkers.push(errorMarkerId); |
449 | const annotations = this.jsEditor.session.getAnnotations(); | 490 | const annotations = this.jsEditor.session.getAnnotations(); |
450 | - const errorAnnotation: ace.Ace.Annotation = { | 491 | + const errorAnnotation: Ace.Annotation = { |
451 | row: line, | 492 | row: line, |
452 | column, | 493 | column, |
453 | text: details.message, | 494 | text: details.message, |
@@ -25,7 +25,8 @@ import { | @@ -25,7 +25,8 @@ import { | ||
25 | ViewEncapsulation | 25 | ViewEncapsulation |
26 | } from '@angular/core'; | 26 | } from '@angular/core'; |
27 | import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; | 27 | import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; |
28 | -import * as ace from 'ace-builds'; | 28 | +import { Ace } from 'ace-builds'; |
29 | +import { getAce, Range } from '@shared/models/ace/ace.models'; | ||
29 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 30 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
30 | import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; | 31 | import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; |
31 | import { Store } from '@ngrx/store'; | 32 | import { Store } from '@ngrx/store'; |
@@ -60,7 +61,7 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, | @@ -60,7 +61,7 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, | ||
60 | @ViewChild('javascriptEditor', {static: true}) | 61 | @ViewChild('javascriptEditor', {static: true}) |
61 | javascriptEditorElmRef: ElementRef; | 62 | javascriptEditorElmRef: ElementRef; |
62 | 63 | ||
63 | - private jsEditor: ace.Ace.Editor; | 64 | + private jsEditor: Ace.Editor; |
64 | private editorsResizeCaf: CancelAnimationFrame; | 65 | private editorsResizeCaf: CancelAnimationFrame; |
65 | private editorResize$: ResizeObserver; | 66 | private editorResize$: ResizeObserver; |
66 | private ignoreChange = false; | 67 | private ignoreChange = false; |
@@ -136,7 +137,7 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, | @@ -136,7 +137,7 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, | ||
136 | }); | 137 | }); |
137 | } | 138 | } |
138 | const editorElement = this.javascriptEditorElmRef.nativeElement; | 139 | const editorElement = this.javascriptEditorElmRef.nativeElement; |
139 | - let editorOptions: Partial<ace.Ace.EditorOptions> = { | 140 | + let editorOptions: Partial<Ace.EditorOptions> = { |
140 | mode: 'ace/mode/javascript', | 141 | mode: 'ace/mode/javascript', |
141 | showGutter: true, | 142 | showGutter: true, |
142 | showPrintMargin: true, | 143 | showPrintMargin: true, |
@@ -150,22 +151,27 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, | @@ -150,22 +151,27 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, | ||
150 | }; | 151 | }; |
151 | 152 | ||
152 | editorOptions = {...editorOptions, ...advancedOptions}; | 153 | editorOptions = {...editorOptions, ...advancedOptions}; |
153 | - this.jsEditor = ace.edit(editorElement, editorOptions); | ||
154 | - this.jsEditor.session.setUseWrapMode(true); | ||
155 | - this.jsEditor.setValue(this.modelValue ? this.modelValue : '', -1); | ||
156 | - this.jsEditor.on('change', () => { | ||
157 | - if (!this.ignoreChange) { | ||
158 | - this.cleanupJsErrors(); | ||
159 | - this.updateView(); | 154 | + getAce().subscribe( |
155 | + (ace) => { | ||
156 | + this.jsEditor = ace.edit(editorElement, editorOptions); | ||
157 | + this.jsEditor.session.setUseWrapMode(true); | ||
158 | + this.jsEditor.setValue(this.modelValue ? this.modelValue : '', -1); | ||
159 | + this.jsEditor.setReadOnly(this.disabled); | ||
160 | + this.jsEditor.on('change', () => { | ||
161 | + if (!this.ignoreChange) { | ||
162 | + this.cleanupJsErrors(); | ||
163 | + this.updateView(); | ||
164 | + } | ||
165 | + }); | ||
166 | + if (this.editorCompleter) { | ||
167 | + this.jsEditor.completers = [this.editorCompleter, ...(this.jsEditor.completers || [])]; | ||
168 | + } | ||
169 | + this.editorResize$ = new ResizeObserver(() => { | ||
170 | + this.onAceEditorResize(); | ||
171 | + }); | ||
172 | + this.editorResize$.observe(editorElement); | ||
160 | } | 173 | } |
161 | - }); | ||
162 | - if (this.editorCompleter) { | ||
163 | - this.jsEditor.completers = [this.editorCompleter, ...(this.jsEditor.completers || [])]; | ||
164 | - } | ||
165 | - this.editorResize$ = new ResizeObserver(() => { | ||
166 | - this.onAceEditorResize(); | ||
167 | - }); | ||
168 | - this.editorResize$.observe(editorElement); | 174 | + ); |
169 | } | 175 | } |
170 | 176 | ||
171 | ngOnDestroy(): void { | 177 | ngOnDestroy(): void { |
@@ -294,11 +300,11 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, | @@ -294,11 +300,11 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, | ||
294 | if (details.columnNumber) { | 300 | if (details.columnNumber) { |
295 | column = details.columnNumber; | 301 | column = details.columnNumber; |
296 | } | 302 | } |
297 | - const errorMarkerId = this.jsEditor.session.addMarker(new ace.Range(line, 0, line, Infinity), | 303 | + const errorMarkerId = this.jsEditor.session.addMarker(new Range(line, 0, line, Infinity), |
298 | 'ace_active-line', 'screenLine'); | 304 | 'ace_active-line', 'screenLine'); |
299 | this.errorMarkers.push(errorMarkerId); | 305 | this.errorMarkers.push(errorMarkerId); |
300 | const annotations = this.jsEditor.session.getAnnotations(); | 306 | const annotations = this.jsEditor.session.getAnnotations(); |
301 | - const errorAnnotation: ace.Ace.Annotation = { | 307 | + const errorAnnotation: Ace.Annotation = { |
302 | row: line, | 308 | row: line, |
303 | column, | 309 | column, |
304 | text: details.message, | 310 | text: details.message, |
@@ -26,7 +26,7 @@ import { | @@ -26,7 +26,7 @@ import { | ||
26 | ViewChild | 26 | ViewChild |
27 | } from '@angular/core'; | 27 | } from '@angular/core'; |
28 | import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; | 28 | import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; |
29 | -import * as ace from 'ace-builds'; | 29 | +import { Ace } from 'ace-builds'; |
30 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 30 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
31 | import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; | 31 | import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; |
32 | import { Store } from '@ngrx/store'; | 32 | import { Store } from '@ngrx/store'; |
@@ -35,6 +35,7 @@ import { ContentType, contentTypesMap } from '@shared/models/constants'; | @@ -35,6 +35,7 @@ import { ContentType, contentTypesMap } from '@shared/models/constants'; | ||
35 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; | 35 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
36 | import { guid } from '@core/utils'; | 36 | import { guid } from '@core/utils'; |
37 | import { ResizeObserver } from '@juggle/resize-observer'; | 37 | import { ResizeObserver } from '@juggle/resize-observer'; |
38 | +import { getAce } from '@shared/models/ace/ace.models'; | ||
38 | 39 | ||
39 | @Component({ | 40 | @Component({ |
40 | selector: 'tb-json-content', | 41 | selector: 'tb-json-content', |
@@ -58,7 +59,7 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid | @@ -58,7 +59,7 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid | ||
58 | @ViewChild('jsonEditor', {static: true}) | 59 | @ViewChild('jsonEditor', {static: true}) |
59 | jsonEditorElmRef: ElementRef; | 60 | jsonEditorElmRef: ElementRef; |
60 | 61 | ||
61 | - private jsonEditor: ace.Ace.Editor; | 62 | + private jsonEditor: Ace.Editor; |
62 | private editorsResizeCaf: CancelAnimationFrame; | 63 | private editorsResizeCaf: CancelAnimationFrame; |
63 | private editorResize$: ResizeObserver; | 64 | private editorResize$: ResizeObserver; |
64 | private ignoreChange = false; | 65 | private ignoreChange = false; |
@@ -123,7 +124,7 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid | @@ -123,7 +124,7 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid | ||
123 | if (this.contentType) { | 124 | if (this.contentType) { |
124 | mode = contentTypesMap.get(this.contentType).code; | 125 | mode = contentTypesMap.get(this.contentType).code; |
125 | } | 126 | } |
126 | - let editorOptions: Partial<ace.Ace.EditorOptions> = { | 127 | + let editorOptions: Partial<Ace.EditorOptions> = { |
127 | mode: `ace/mode/${mode}`, | 128 | mode: `ace/mode/${mode}`, |
128 | showGutter: true, | 129 | showGutter: true, |
129 | showPrintMargin: false, | 130 | showPrintMargin: false, |
@@ -137,22 +138,27 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid | @@ -137,22 +138,27 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid | ||
137 | }; | 138 | }; |
138 | 139 | ||
139 | editorOptions = {...editorOptions, ...advancedOptions}; | 140 | editorOptions = {...editorOptions, ...advancedOptions}; |
140 | - this.jsonEditor = ace.edit(editorElement, editorOptions); | ||
141 | - this.jsonEditor.session.setUseWrapMode(true); | ||
142 | - this.jsonEditor.setValue(this.contentBody ? this.contentBody : '', -1); | ||
143 | - this.jsonEditor.on('change', () => { | ||
144 | - if (!this.ignoreChange) { | ||
145 | - this.cleanupJsonErrors(); | ||
146 | - this.updateView(); | 141 | + getAce().subscribe( |
142 | + (ace) => { | ||
143 | + this.jsonEditor = ace.edit(editorElement, editorOptions); | ||
144 | + this.jsonEditor.session.setUseWrapMode(true); | ||
145 | + this.jsonEditor.setValue(this.contentBody ? this.contentBody : '', -1); | ||
146 | + this.jsonEditor.setReadOnly(this.disabled || this.readonly); | ||
147 | + this.jsonEditor.on('change', () => { | ||
148 | + if (!this.ignoreChange) { | ||
149 | + this.cleanupJsonErrors(); | ||
150 | + this.updateView(); | ||
151 | + } | ||
152 | + }); | ||
153 | + this.jsonEditor.on('blur', () => { | ||
154 | + this.contentValid = !this.validateContent || this.doValidate(true); | ||
155 | + }); | ||
156 | + this.editorResize$ = new ResizeObserver(() => { | ||
157 | + this.onAceEditorResize(); | ||
158 | + }); | ||
159 | + this.editorResize$.observe(editorElement); | ||
147 | } | 160 | } |
148 | - }); | ||
149 | - this.jsonEditor.on('blur', () => { | ||
150 | - this.contentValid = !this.validateContent || this.doValidate(true); | ||
151 | - }); | ||
152 | - this.editorResize$ = new ResizeObserver(() => { | ||
153 | - this.onAceEditorResize(); | ||
154 | - }); | ||
155 | - this.editorResize$.observe(editorElement); | 161 | + ); |
156 | } | 162 | } |
157 | 163 | ||
158 | ngOnDestroy(): void { | 164 | ngOnDestroy(): void { |
@@ -16,10 +16,20 @@ | @@ -16,10 +16,20 @@ | ||
16 | import * as React from 'react'; | 16 | import * as React from 'react'; |
17 | import ThingsboardBaseComponent from './json-form-base-component'; | 17 | import ThingsboardBaseComponent from './json-form-base-component'; |
18 | import reactCSS from 'reactcss'; | 18 | import reactCSS from 'reactcss'; |
19 | -import ReactAce from 'react-ace'; | ||
20 | import Button from '@material-ui/core/Button'; | 19 | import Button from '@material-ui/core/Button'; |
21 | import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; | 20 | import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; |
22 | import { IEditorProps } from 'react-ace/src/types'; | 21 | import { IEditorProps } from 'react-ace/src/types'; |
22 | +import { map, mergeMap } from 'rxjs/operators'; | ||
23 | +import { loadAceDependencies } from '@shared/models/ace/ace.models'; | ||
24 | +import { from } from 'rxjs'; | ||
25 | + | ||
26 | +const ReactAce = React.lazy(() => { | ||
27 | + return loadAceDependencies().pipe( | ||
28 | + mergeMap(() => { | ||
29 | + return from(import('react-ace')); | ||
30 | + }) | ||
31 | + ).toPromise(); | ||
32 | +}); | ||
23 | 33 | ||
24 | interface ThingsboardAceEditorProps extends JsonFormFieldProps { | 34 | interface ThingsboardAceEditorProps extends JsonFormFieldProps { |
25 | mode: string; | 35 | mode: string; |
@@ -153,22 +163,24 @@ class ThingsboardAceEditor extends React.Component<ThingsboardAceEditorProps, Th | @@ -153,22 +163,24 @@ class ThingsboardAceEditor extends React.Component<ThingsboardAceEditorProps, Th | ||
153 | 'Exit fullscreen' : 'Fullscreen'} | 163 | 'Exit fullscreen' : 'Fullscreen'} |
154 | </Button> | 164 | </Button> |
155 | </div> | 165 | </div> |
156 | - <ReactAce mode={this.props.mode} | ||
157 | - height={this.state.isFull ? '100%' : '150px'} | ||
158 | - width={this.state.isFull ? '100%' : '300px'} | ||
159 | - theme='github' | ||
160 | - onChange={this.onValueChanged} | ||
161 | - onFocus={this.onFocus} | ||
162 | - onBlur={this.onBlur} | ||
163 | - onLoad={this.onLoad} | ||
164 | - name={this.props.form.title} | ||
165 | - value={this.state.value} | ||
166 | - readOnly={this.props.form.readonly} | ||
167 | - editorProps={{$blockScrolling: Infinity}} | ||
168 | - enableBasicAutocompletion={true} | ||
169 | - enableSnippets={true} | ||
170 | - enableLiveAutocompletion={true} | ||
171 | - style={style}/> | 166 | + <React.Suspense fallback={<div>Loading...</div>}> |
167 | + <ReactAce mode={this.props.mode} | ||
168 | + height={this.state.isFull ? '100%' : '150px'} | ||
169 | + width={this.state.isFull ? '100%' : '300px'} | ||
170 | + theme='github' | ||
171 | + onChange={this.onValueChanged} | ||
172 | + onFocus={this.onFocus} | ||
173 | + onBlur={this.onBlur} | ||
174 | + onLoad={this.onLoad} | ||
175 | + name={this.props.form.title} | ||
176 | + value={this.state.value} | ||
177 | + readOnly={this.props.form.readonly} | ||
178 | + editorProps={{$blockScrolling: Infinity}} | ||
179 | + enableBasicAutocompletion={true} | ||
180 | + enableSnippets={true} | ||
181 | + enableLiveAutocompletion={true} | ||
182 | + style={style}/> | ||
183 | + </React.Suspense> | ||
172 | </div> | 184 | </div> |
173 | <div className='json-form-error' | 185 | <div className='json-form-error' |
174 | style={{opacity: this.props.valid ? '0' : '1'}}>{this.props.error}</div> | 186 | style={{opacity: this.props.valid ? '0' : '1'}}>{this.props.error}</div> |
@@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
16 | 16 | ||
17 | import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; | 17 | import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; |
18 | import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; | 18 | import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; |
19 | -import * as ace from 'ace-builds'; | 19 | +import { Ace } from 'ace-builds'; |
20 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 20 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
21 | import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; | 21 | import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; |
22 | import { Store } from '@ngrx/store'; | 22 | import { Store } from '@ngrx/store'; |
@@ -24,6 +24,7 @@ import { AppState } from '@core/core.state'; | @@ -24,6 +24,7 @@ import { AppState } from '@core/core.state'; | ||
24 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; | 24 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
25 | import { guid } from '@core/utils'; | 25 | import { guid } from '@core/utils'; |
26 | import { ResizeObserver } from '@juggle/resize-observer'; | 26 | import { ResizeObserver } from '@juggle/resize-observer'; |
27 | +import { getAce } from '@shared/models/ace/ace.models'; | ||
27 | 28 | ||
28 | @Component({ | 29 | @Component({ |
29 | selector: 'tb-json-object-edit', | 30 | selector: 'tb-json-object-edit', |
@@ -47,7 +48,7 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va | @@ -47,7 +48,7 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va | ||
47 | @ViewChild('jsonEditor', {static: true}) | 48 | @ViewChild('jsonEditor', {static: true}) |
48 | jsonEditorElmRef: ElementRef; | 49 | jsonEditorElmRef: ElementRef; |
49 | 50 | ||
50 | - private jsonEditor: ace.Ace.Editor; | 51 | + private jsonEditor: Ace.Editor; |
51 | private editorsResizeCaf: CancelAnimationFrame; | 52 | private editorsResizeCaf: CancelAnimationFrame; |
52 | private editorResize$: ResizeObserver; | 53 | private editorResize$: ResizeObserver; |
53 | 54 | ||
@@ -102,7 +103,7 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va | @@ -102,7 +103,7 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va | ||
102 | 103 | ||
103 | ngOnInit(): void { | 104 | ngOnInit(): void { |
104 | const editorElement = this.jsonEditorElmRef.nativeElement; | 105 | const editorElement = this.jsonEditorElmRef.nativeElement; |
105 | - let editorOptions: Partial<ace.Ace.EditorOptions> = { | 106 | + let editorOptions: Partial<Ace.EditorOptions> = { |
106 | mode: 'ace/mode/json', | 107 | mode: 'ace/mode/json', |
107 | showGutter: true, | 108 | showGutter: true, |
108 | showPrintMargin: false, | 109 | showPrintMargin: false, |
@@ -116,19 +117,24 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va | @@ -116,19 +117,24 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va | ||
116 | }; | 117 | }; |
117 | 118 | ||
118 | editorOptions = {...editorOptions, ...advancedOptions}; | 119 | editorOptions = {...editorOptions, ...advancedOptions}; |
119 | - this.jsonEditor = ace.edit(editorElement, editorOptions); | ||
120 | - this.jsonEditor.session.setUseWrapMode(false); | ||
121 | - this.jsonEditor.setValue(this.contentValue ? this.contentValue : '', -1); | ||
122 | - this.jsonEditor.on('change', () => { | ||
123 | - if (!this.ignoreChange) { | ||
124 | - this.cleanupJsonErrors(); | ||
125 | - this.updateView(); | 120 | + getAce().subscribe( |
121 | + (ace) => { | ||
122 | + this.jsonEditor = ace.edit(editorElement, editorOptions); | ||
123 | + this.jsonEditor.session.setUseWrapMode(false); | ||
124 | + this.jsonEditor.setValue(this.contentValue ? this.contentValue : '', -1); | ||
125 | + this.jsonEditor.setReadOnly(this.disabled || this.readonly); | ||
126 | + this.jsonEditor.on('change', () => { | ||
127 | + if (!this.ignoreChange) { | ||
128 | + this.cleanupJsonErrors(); | ||
129 | + this.updateView(); | ||
130 | + } | ||
131 | + }); | ||
132 | + this.editorResize$ = new ResizeObserver(() => { | ||
133 | + this.onAceEditorResize(); | ||
134 | + }); | ||
135 | + this.editorResize$.observe(editorElement); | ||
126 | } | 136 | } |
127 | - }); | ||
128 | - this.editorResize$ = new ResizeObserver(() => { | ||
129 | - this.onAceEditorResize(); | ||
130 | - }); | ||
131 | - this.editorResize$.observe(editorElement); | 137 | + ); |
132 | } | 138 | } |
133 | 139 | ||
134 | ngOnDestroy(): void { | 140 | ngOnDestroy(): void { |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { Ace } from 'ace-builds'; | ||
18 | +import { Observable } from 'rxjs/internal/Observable'; | ||
19 | +import { forkJoin, from, of } from 'rxjs'; | ||
20 | +import { map, mergeMap, tap } from 'rxjs/operators'; | ||
21 | + | ||
22 | +let aceDependenciesLoaded = false; | ||
23 | +let aceModule: any; | ||
24 | + | ||
25 | +export function loadAceDependencies(): Observable<any> { | ||
26 | + if (aceDependenciesLoaded) { | ||
27 | + return of(null); | ||
28 | + } else { | ||
29 | + const aceObservables: Observable<any>[] = []; | ||
30 | + aceObservables.push(from(import('ace-builds/src-noconflict/ext-language_tools'))); | ||
31 | + aceObservables.push(from(import('ace-builds/src-noconflict/ext-searchbox'))); | ||
32 | + aceObservables.push(from(import('ace-builds/src-noconflict/mode-java'))); | ||
33 | + aceObservables.push(from(import('ace-builds/src-noconflict/mode-css'))); | ||
34 | + aceObservables.push(from(import('ace-builds/src-noconflict/mode-json'))); | ||
35 | + aceObservables.push(from(import('ace-builds/src-noconflict/mode-javascript'))); | ||
36 | + aceObservables.push(from(import('ace-builds/src-noconflict/mode-text'))); | ||
37 | + aceObservables.push(from(import('ace-builds/src-noconflict/mode-markdown'))); | ||
38 | + aceObservables.push(from(import('ace-builds/src-noconflict/mode-html'))); | ||
39 | + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/java'))); | ||
40 | + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/css'))); | ||
41 | + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/json'))); | ||
42 | + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/javascript'))); | ||
43 | + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/text'))); | ||
44 | + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/markdown'))); | ||
45 | + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/html'))); | ||
46 | + aceObservables.push(from(import('ace-builds/src-noconflict/theme-github'))); | ||
47 | + return forkJoin(aceObservables).pipe( | ||
48 | + tap(() => { | ||
49 | + aceDependenciesLoaded = true; | ||
50 | + }) | ||
51 | + ); | ||
52 | + } | ||
53 | +} | ||
54 | + | ||
55 | +export function getAce(): Observable<any> { | ||
56 | + if (aceModule) { | ||
57 | + return of(aceModule); | ||
58 | + } else { | ||
59 | + return from(import('ace')).pipe( | ||
60 | + mergeMap((module) => { | ||
61 | + return loadAceDependencies().pipe( | ||
62 | + map(() => module) | ||
63 | + ); | ||
64 | + }), | ||
65 | + tap((module) => { | ||
66 | + aceModule = module; | ||
67 | + }) | ||
68 | + ); | ||
69 | + } | ||
70 | +} | ||
71 | + | ||
72 | +export class Range implements Ace.Range { | ||
73 | + | ||
74 | + public start: Ace.Point; | ||
75 | + public end: Ace.Point; | ||
76 | + | ||
77 | + constructor(startRow: number, startColumn: number, endRow: number, endColumn: number) { | ||
78 | + this.start = { | ||
79 | + row: startRow, | ||
80 | + column: startColumn | ||
81 | + }; | ||
82 | + | ||
83 | + this.end = { | ||
84 | + row: endRow, | ||
85 | + column: endColumn | ||
86 | + }; | ||
87 | + } | ||
88 | + | ||
89 | + static fromPoints(start: Ace.Point, end: Ace.Point): Ace.Range { | ||
90 | + return new Range(start.row, start.column, end.row, end.column); | ||
91 | + } | ||
92 | + | ||
93 | + clipRows(firstRow: number, lastRow: number): Ace.Range { | ||
94 | + let end: Ace.Point; | ||
95 | + let start: Ace.Point; | ||
96 | + if (this.end.row > lastRow) { | ||
97 | + end = {row: lastRow + 1, column: 0}; | ||
98 | + } else if (this.end.row < firstRow) { | ||
99 | + end = {row: firstRow, column: 0}; | ||
100 | + } | ||
101 | + | ||
102 | + if (this.start.row > lastRow) { | ||
103 | + start = {row: lastRow + 1, column: 0}; | ||
104 | + } else if (this.start.row < firstRow) { | ||
105 | + start = {row: firstRow, column: 0}; | ||
106 | + } | ||
107 | + return Range.fromPoints(start || this.start, end || this.end); | ||
108 | + } | ||
109 | + | ||
110 | + clone(): Ace.Range { | ||
111 | + return Range.fromPoints(this.start, this.end); | ||
112 | + } | ||
113 | + | ||
114 | + collapseRows(): Ace.Range { | ||
115 | + if (this.end.column === 0) { | ||
116 | + return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row - 1), 0); | ||
117 | + } else { | ||
118 | + return new Range(this.start.row, 0, this.end.row, 0); | ||
119 | + } | ||
120 | + } | ||
121 | + | ||
122 | + compare(row: number, column: number): number { | ||
123 | + if (!this.isMultiLine()) { | ||
124 | + if (row === this.start.row) { | ||
125 | + return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0); | ||
126 | + } | ||
127 | + } | ||
128 | + | ||
129 | + if (row < this.start.row) { | ||
130 | + return -1; | ||
131 | + } | ||
132 | + | ||
133 | + if (row > this.end.row) { | ||
134 | + return 1; | ||
135 | + } | ||
136 | + | ||
137 | + if (this.start.row === row) { | ||
138 | + return column >= this.start.column ? 0 : -1; | ||
139 | + } | ||
140 | + | ||
141 | + if (this.end.row === row) { | ||
142 | + return column <= this.end.column ? 0 : 1; | ||
143 | + } | ||
144 | + | ||
145 | + return 0; | ||
146 | + } | ||
147 | + | ||
148 | + compareEnd(row: number, column: number): number { | ||
149 | + if (this.end.row === row && this.end.column === column) { | ||
150 | + return 1; | ||
151 | + } else { | ||
152 | + return this.compare(row, column); | ||
153 | + } | ||
154 | + } | ||
155 | + | ||
156 | + compareInside(row: number, column: number): number { | ||
157 | + if (this.end.row === row && this.end.column === column) { | ||
158 | + return 1; | ||
159 | + } else if (this.start.row === row && this.start.column === column) { | ||
160 | + return -1; | ||
161 | + } else { | ||
162 | + return this.compare(row, column); | ||
163 | + } | ||
164 | + } | ||
165 | + | ||
166 | + comparePoint(p: Ace.Point): number { | ||
167 | + return this.compare(p.row, p.column); | ||
168 | + } | ||
169 | + | ||
170 | + compareRange(range: Ace.Range): number { | ||
171 | + let cmp: number; | ||
172 | + const end = range.end; | ||
173 | + const start = range.start; | ||
174 | + | ||
175 | + cmp = this.compare(end.row, end.column); | ||
176 | + if (cmp === 1) { | ||
177 | + cmp = this.compare(start.row, start.column); | ||
178 | + if (cmp === 1) { | ||
179 | + return 2; | ||
180 | + } else if (cmp === 0) { | ||
181 | + return 1; | ||
182 | + } else { | ||
183 | + return 0; | ||
184 | + } | ||
185 | + } else if (cmp === -1) { | ||
186 | + return -2; | ||
187 | + } else { | ||
188 | + cmp = this.compare(start.row, start.column); | ||
189 | + if (cmp === -1) { | ||
190 | + return -1; | ||
191 | + } else if (cmp === 1) { | ||
192 | + return 42; | ||
193 | + } else { | ||
194 | + return 0; | ||
195 | + } | ||
196 | + } | ||
197 | + } | ||
198 | + | ||
199 | + compareStart(row: number, column: number): number { | ||
200 | + if (this.start.row === row && this.start.column === column) { | ||
201 | + return -1; | ||
202 | + } else { | ||
203 | + return this.compare(row, column); | ||
204 | + } | ||
205 | + } | ||
206 | + | ||
207 | + contains(row: number, column: number): boolean { | ||
208 | + return this.compare(row, column) === 0; | ||
209 | + } | ||
210 | + | ||
211 | + containsRange(range: Ace.Range): boolean { | ||
212 | + return this.comparePoint(range.start) === 0 && this.comparePoint(range.end) === 0; | ||
213 | + } | ||
214 | + | ||
215 | + extend(row: number, column: number): Ace.Range { | ||
216 | + const cmp = this.compare(row, column); | ||
217 | + let end: Ace.Point; | ||
218 | + let start: Ace.Point; | ||
219 | + if (cmp === 0) { | ||
220 | + return this; | ||
221 | + } else if (cmp === -1) { | ||
222 | + start = {row, column}; | ||
223 | + } else { | ||
224 | + end = {row, column}; | ||
225 | + } | ||
226 | + return Range.fromPoints(start || this.start, end || this.end); | ||
227 | + } | ||
228 | + | ||
229 | + inside(row: number, column: number): boolean { | ||
230 | + if (this.compare(row, column) === 0) { | ||
231 | + if (this.isEnd(row, column) || this.isStart(row, column)) { | ||
232 | + return false; | ||
233 | + } else { | ||
234 | + return true; | ||
235 | + } | ||
236 | + } | ||
237 | + return false; | ||
238 | + } | ||
239 | + | ||
240 | + insideEnd(row: number, column: number): boolean { | ||
241 | + if (this.compare(row, column) === 0) { | ||
242 | + if (this.isStart(row, column)) { | ||
243 | + return false; | ||
244 | + } else { | ||
245 | + return true; | ||
246 | + } | ||
247 | + } | ||
248 | + return false; | ||
249 | + } | ||
250 | + | ||
251 | + insideStart(row: number, column: number): boolean { | ||
252 | + if (this.compare(row, column) === 0) { | ||
253 | + if (this.isEnd(row, column)) { | ||
254 | + return false; | ||
255 | + } else { | ||
256 | + return true; | ||
257 | + } | ||
258 | + } | ||
259 | + return false; | ||
260 | + } | ||
261 | + | ||
262 | + intersects(range: Ace.Range): boolean { | ||
263 | + const cmp = this.compareRange(range); | ||
264 | + return (cmp === -1 || cmp === 0 || cmp === 1); | ||
265 | + } | ||
266 | + | ||
267 | + isEmpty(): boolean { | ||
268 | + return (this.start.row === this.end.row && this.start.column === this.end.column); | ||
269 | + } | ||
270 | + | ||
271 | + isEnd(row: number, column: number): boolean { | ||
272 | + return this.end.row === row && this.end.column === column; | ||
273 | + } | ||
274 | + | ||
275 | + isEqual(range: Ace.Range): boolean { | ||
276 | + return this.start.row === range.start.row && | ||
277 | + this.end.row === range.end.row && | ||
278 | + this.start.column === range.start.column && | ||
279 | + this.end.column === range.end.column; | ||
280 | + } | ||
281 | + | ||
282 | + isMultiLine(): boolean { | ||
283 | + return (this.start.row !== this.end.row); | ||
284 | + } | ||
285 | + | ||
286 | + isStart(row: number, column: number): boolean { | ||
287 | + return this.start.row === row && this.start.column === column; | ||
288 | + } | ||
289 | + | ||
290 | + moveBy(row: number, column: number): void { | ||
291 | + this.start.row += row; | ||
292 | + this.start.column += column; | ||
293 | + this.end.row += row; | ||
294 | + this.end.column += column; | ||
295 | + } | ||
296 | + | ||
297 | + setEnd(row: number, column: number): void { | ||
298 | + if (typeof row === 'object') { | ||
299 | + this.end.column = (row as Ace.Point).column; | ||
300 | + this.end.row = (row as Ace.Point).row; | ||
301 | + } else { | ||
302 | + this.end.row = row; | ||
303 | + this.end.column = column; | ||
304 | + } | ||
305 | + } | ||
306 | + | ||
307 | + setStart(row: number, column: number): void { | ||
308 | + if (typeof row === 'object') { | ||
309 | + this.start.column = (row as Ace.Point).column; | ||
310 | + this.start.row = (row as Ace.Point).row; | ||
311 | + } else { | ||
312 | + this.start.row = row; | ||
313 | + this.start.column = column; | ||
314 | + } | ||
315 | + } | ||
316 | + | ||
317 | + toScreenRange(session: Ace.EditSession): Ace.Range { | ||
318 | + const screenPosStart = session.documentToScreenPosition(this.start); | ||
319 | + const screenPosEnd = session.documentToScreenPosition(this.end); | ||
320 | + | ||
321 | + return new Range( | ||
322 | + screenPosStart.row, screenPosStart.column, | ||
323 | + screenPosEnd.row, screenPosEnd.column | ||
324 | + ); | ||
325 | + } | ||
326 | + | ||
327 | +} |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import * as ace from 'ace-builds'; | 17 | +import { Ace } from 'ace-builds'; |
18 | 18 | ||
19 | export type tbMetaType = 'object' | 'function' | 'service' | 'property' | 'argument'; | 19 | export type tbMetaType = 'object' | 'function' | 'service' | 'property' | 'argument'; |
20 | 20 | ||
@@ -41,7 +41,7 @@ export interface TbEditorCompletion { | @@ -41,7 +41,7 @@ export interface TbEditorCompletion { | ||
41 | children?: TbEditorCompletions; | 41 | children?: TbEditorCompletions; |
42 | } | 42 | } |
43 | 43 | ||
44 | -interface TbEditorAceCompletion extends ace.Ace.Completion { | 44 | +interface TbEditorAceCompletion extends Ace.Completion { |
45 | isTbEditorAceCompletion: true; | 45 | isTbEditorAceCompletion: true; |
46 | snippet: string; | 46 | snippet: string; |
47 | description?: string; | 47 | description?: string; |
@@ -50,7 +50,7 @@ interface TbEditorAceCompletion extends ace.Ace.Completion { | @@ -50,7 +50,7 @@ interface TbEditorAceCompletion extends ace.Ace.Completion { | ||
50 | return?: FunctionArgType; | 50 | return?: FunctionArgType; |
51 | } | 51 | } |
52 | 52 | ||
53 | -export class TbEditorCompleter implements ace.Ace.Completer { | 53 | +export class TbEditorCompleter implements Ace.Completer { |
54 | 54 | ||
55 | identifierRegexps: RegExp[] = [ | 55 | identifierRegexps: RegExp[] = [ |
56 | /[a-zA-Z_0-9\$\-\u00A2-\u2000\u2070-\uFFFF.]/ | 56 | /[a-zA-Z_0-9\$\-\u00A2-\u2000\u2070-\uFFFF.]/ |
@@ -59,8 +59,8 @@ export class TbEditorCompleter implements ace.Ace.Completer { | @@ -59,8 +59,8 @@ export class TbEditorCompleter implements ace.Ace.Completer { | ||
59 | constructor(private editorCompletions: TbEditorCompletions) { | 59 | constructor(private editorCompletions: TbEditorCompletions) { |
60 | } | 60 | } |
61 | 61 | ||
62 | - getCompletions(editor: ace.Ace.Editor, session: ace.Ace.EditSession, | ||
63 | - position: ace.Ace.Point, prefix: string, callback: ace.Ace.CompleterCallback): void { | 62 | + getCompletions(editor: Ace.Editor, session: Ace.EditSession, |
63 | + position: Ace.Point, prefix: string, callback: Ace.CompleterCallback): void { | ||
64 | const result = this.prepareCompletions(prefix); | 64 | const result = this.prepareCompletions(prefix); |
65 | if (result) { | 65 | if (result) { |
66 | callback(null, result); | 66 | callback(null, result); |
@@ -91,7 +91,7 @@ export class TbEditorCompleter implements ace.Ace.Completer { | @@ -91,7 +91,7 @@ export class TbEditorCompleter implements ace.Ace.Completer { | ||
91 | return parts; | 91 | return parts; |
92 | } | 92 | } |
93 | 93 | ||
94 | - private prepareCompletions(prefix: string): ace.Ace.Completion[] { | 94 | + private prepareCompletions(prefix: string): Ace.Completion[] { |
95 | const path = this.resolvePath(prefix); | 95 | const path = this.resolvePath(prefix); |
96 | if (path !== null) { | 96 | if (path !== null) { |
97 | return this.toAceCompletionsList(this.editorCompletions, path); | 97 | return this.toAceCompletionsList(this.editorCompletions, path); |
@@ -100,8 +100,8 @@ export class TbEditorCompleter implements ace.Ace.Completer { | @@ -100,8 +100,8 @@ export class TbEditorCompleter implements ace.Ace.Completer { | ||
100 | } | 100 | } |
101 | } | 101 | } |
102 | 102 | ||
103 | - private toAceCompletionsList(completions: TbEditorCompletions, parentPath: string[]): ace.Ace.Completion[] { | ||
104 | - const result: ace.Ace.Completion[] = []; | 103 | + private toAceCompletionsList(completions: TbEditorCompletions, parentPath: string[]): Ace.Completion[] { |
104 | + const result: Ace.Completion[] = []; | ||
105 | let targetCompletions = completions; | 105 | let targetCompletions = completions; |
106 | let parentPrefix = ''; | 106 | let parentPrefix = ''; |
107 | if (parentPath.length) { | 107 | if (parentPath.length) { |
@@ -116,7 +116,7 @@ export class TbEditorCompleter implements ace.Ace.Completer { | @@ -116,7 +116,7 @@ export class TbEditorCompleter implements ace.Ace.Completer { | ||
116 | return result; | 116 | return result; |
117 | } | 117 | } |
118 | 118 | ||
119 | - private toAceCompletion(name: string, completion: TbEditorCompletion, parentPrefix: string): ace.Ace.Completion { | 119 | + private toAceCompletion(name: string, completion: TbEditorCompletion, parentPrefix: string): Ace.Completion { |
120 | const aceCompletion: TbEditorAceCompletion = { | 120 | const aceCompletion: TbEditorAceCompletion = { |
121 | isTbEditorAceCompletion: true, | 121 | isTbEditorAceCompletion: true, |
122 | snippet: parentPrefix + name, | 122 | snippet: parentPrefix + name, |
@@ -175,7 +175,7 @@ export class TbEditorCompleter implements ace.Ace.Completer { | @@ -175,7 +175,7 @@ export class TbEditorCompleter implements ace.Ace.Completer { | ||
175 | if (completion.args || completion.return) { | 175 | if (completion.args || completion.return) { |
176 | let functionInfoBlock = '<div class="tb-function-info">'; | 176 | let functionInfoBlock = '<div class="tb-function-info">'; |
177 | if (completion.args) { | 177 | if (completion.args) { |
178 | - functionInfoBlock += '<div class="tb-api-title">Parameters</div>' | 178 | + functionInfoBlock += '<div class="tb-api-title">Parameters</div>'; |
179 | let argsTable = '<table class="tb-api-table"><tbody>'; | 179 | let argsTable = '<table class="tb-api-table"><tbody>'; |
180 | const strArgs: string[] = []; | 180 | const strArgs: string[] = []; |
181 | for (const arg of completion.args) { | 181 | for (const arg of completion.args) { |
@@ -185,7 +185,7 @@ export class TbEditorCompleter implements ace.Ace.Completer { | @@ -185,7 +185,7 @@ export class TbEditorCompleter implements ace.Ace.Completer { | ||
185 | } | 185 | } |
186 | strArg += '</code></td><td>'; | 186 | strArg += '</code></td><td>'; |
187 | if (arg.type) { | 187 | if (arg.type) { |
188 | - strArg += `<code>${arg.type}</code>` | 188 | + strArg += `<code>${arg.type}</code>`; |
189 | } | 189 | } |
190 | strArg += '</td><td class="arg-description">'; | 190 | strArg += '</td><td class="arg-description">'; |
191 | if (arg.description) { | 191 | if (arg.description) { |
@@ -75,7 +75,6 @@ | @@ -75,7 +75,6 @@ | ||
75 | import './zone-flags'; | 75 | import './zone-flags'; |
76 | import 'zone.js/dist/zone'; // Included with Angular CLI. | 76 | import 'zone.js/dist/zone'; // Included with Angular CLI. |
77 | import 'core-js/es/array'; | 77 | import 'core-js/es/array'; |
78 | -import moment from 'moment'; | ||
79 | 78 | ||
80 | /*************************************************************************************************** | 79 | /*************************************************************************************************** |
81 | * APPLICATION IMPORTS | 80 | * APPLICATION IMPORTS |
@@ -87,26 +86,4 @@ import moment from 'moment'; | @@ -87,26 +86,4 @@ import moment from 'moment'; | ||
87 | * WIDGETS IMPORTS | 86 | * WIDGETS IMPORTS |
88 | */ | 87 | */ |
89 | 88 | ||
90 | -import cssjs from '@core/css/css'; | ||
91 | -import { TbFlot } from '@home/components/widget/lib/flot-widget'; | ||
92 | -import { TbAnalogueCompass } from '@home/components/widget/lib/analogue-compass'; | ||
93 | -import { TbAnalogueRadialGauge } from '@home/components/widget/lib/analogue-radial-gauge'; | ||
94 | -import { TbAnalogueLinearGauge } from '@home/components/widget/lib/analogue-linear-gauge'; | ||
95 | -import { TbCanvasDigitalGauge } from '@home/components/widget/lib/digital-gauge'; | ||
96 | -import { TbMapWidgetV2 } from '@home/components/widget/lib/maps/map-widget2'; | ||
97 | -import { TbTripAnimationWidget } from '@app/modules/home/components/widget/trip-animation/trip-animation.component'; | ||
98 | - | ||
99 | -import * as tinycolor_ from 'tinycolor2'; | ||
100 | - | ||
101 | -const tinycolor = tinycolor_; | ||
102 | - | ||
103 | -(window as any).tinycolor = tinycolor; | ||
104 | -(window as any).cssjs = cssjs; | ||
105 | -(window as any).moment = moment; | ||
106 | -(window as any).TbFlot = TbFlot; | ||
107 | -(window as any).TbAnalogueCompass = TbAnalogueCompass; | ||
108 | -(window as any).TbAnalogueRadialGauge = TbAnalogueRadialGauge; | ||
109 | -(window as any).TbAnalogueLinearGauge = TbAnalogueLinearGauge; | ||
110 | -(window as any).TbCanvasDigitalGauge = TbCanvasDigitalGauge; | ||
111 | -(window as any).TbMapWidgetV2 = TbMapWidgetV2; | ||
112 | -(window as any).TbTripAnimationWidget = TbTripAnimationWidget; | 89 | +(window as any).GAUGES_NO_AUTO_INIT = true; |
@@ -34,6 +34,9 @@ | @@ -34,6 +34,9 @@ | ||
34 | "@home/*": ["src/app/modules/home/*"], | 34 | "@home/*": ["src/app/modules/home/*"], |
35 | "jszip": [ | 35 | "jszip": [ |
36 | "node_modules/jszip/dist/jszip.min.js" | 36 | "node_modules/jszip/dist/jszip.min.js" |
37 | + ], | ||
38 | + "ace": [ | ||
39 | + "node_modules/ace-builds/src-noconflict/ace.js" | ||
37 | ] | 40 | ] |
38 | }, | 41 | }, |
39 | "lib": [ | 42 | "lib": [ |