Showing
23 changed files
with
535 additions
and
287 deletions
... | ... | @@ -86,25 +86,9 @@ |
86 | 86 | ] |
87 | 87 | }, |
88 | 88 | "scripts": [ |
89 | - "node_modules/jquery/dist/jquery.min.js", | |
90 | - "node_modules/jquery.terminal/js/jquery.terminal.min.js", | |
91 | - "node_modules/flot/lib/jquery.colorhelpers.js", | |
92 | - "node_modules/flot/src/jquery.flot.js", | |
93 | - "node_modules/flot/src/plugins/jquery.flot.time.js", | |
94 | - "node_modules/flot/src/plugins/jquery.flot.selection.js", | |
95 | - "node_modules/flot/src/plugins/jquery.flot.pie.js", | |
96 | - "node_modules/flot/src/plugins/jquery.flot.crosshair.js", | |
97 | - "node_modules/flot/src/plugins/jquery.flot.stack.js", | |
98 | - "node_modules/flot/src/plugins/jquery.flot.symbol.js", | |
99 | - "node_modules/flot.curvedlines/curvedLines.js", | |
100 | 89 | "node_modules/tinycolor2/dist/tinycolor-min.js", |
101 | - "node_modules/tooltipster/dist/js/tooltipster.bundle.min.js", | |
102 | - "node_modules/split.js/dist/split.js", | |
103 | - "node_modules/js-beautify/js/lib/beautify.js", | |
104 | - "node_modules/js-beautify/js/lib/beautify-css.js", | |
105 | - "node_modules/js-beautify/js/lib/beautify-html.js", | |
106 | - "node_modules/systemjs/dist/system.js", | |
107 | - "node_modules/jstree/dist/jstree.min.js" | |
90 | + "node_modules/split.js/dist/split.min.js", | |
91 | + "node_modules/systemjs/dist/system.js" | |
108 | 92 | ], |
109 | 93 | "customWebpackConfig": { |
110 | 94 | "path": "./extra-webpack.config.js" |
... | ... | @@ -130,7 +114,11 @@ |
130 | 114 | "tinycolor2", |
131 | 115 | "json-schema-defaults", |
132 | 116 | "leaflet-providers", |
133 | - "lodash" | |
117 | + "lodash", | |
118 | + "jquery", | |
119 | + "jquery.terminal", | |
120 | + "tooltipster", | |
121 | + "jstree" | |
134 | 122 | ] |
135 | 123 | }, |
136 | 124 | "configurations": { | ... | ... |
... | ... | @@ -35,6 +35,13 @@ module.exports = (config, options) => { |
35 | 35 | }) |
36 | 36 | ); |
37 | 37 | config.plugins.push( |
38 | + new webpack.ProvidePlugin( | |
39 | + { | |
40 | + $: "jquery" | |
41 | + } | |
42 | + ) | |
43 | + ); | |
44 | + config.plugins.push( | |
38 | 45 | new CompressionPlugin({ |
39 | 46 | filename: "[path][base].gz[query]", |
40 | 47 | algorithm: "gzip", |
... | ... | @@ -44,6 +51,9 @@ module.exports = (config, options) => { |
44 | 51 | deleteOriginalAssets: false, |
45 | 52 | }) |
46 | 53 | ); |
54 | + config.plugins.push( | |
55 | + new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) | |
56 | + ); | |
47 | 57 | |
48 | 58 | if (config.mode === 'production') { |
49 | 59 | const index = config.plugins.findIndex(p => p instanceof AngularCompilerPlugin.AngularCompilerPlugin); | ... | ... |
... | ... | @@ -24,6 +24,9 @@ import { DialogComponent } from '@shared/components/dialog.component'; |
24 | 24 | import { Router } from '@angular/router'; |
25 | 25 | import { ContentType, contentTypesMap } from '@shared/models/constants'; |
26 | 26 | import { getAce } from '@shared/models/ace/ace.models'; |
27 | +import { Observable } from 'rxjs/internal/Observable'; | |
28 | +import { beautifyJs } from '@shared/models/beautify.models'; | |
29 | +import { of } from 'rxjs'; | |
27 | 30 | |
28 | 31 | export interface EventContentDialogData { |
29 | 32 | content: string; |
... | ... | @@ -64,33 +67,42 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia |
64 | 67 | createEditor(editorElementRef: ElementRef, content: string) { |
65 | 68 | const editorElement = editorElementRef.nativeElement; |
66 | 69 | let mode = 'java'; |
70 | + let content$: Observable<string> = null; | |
67 | 71 | if (this.contentType) { |
68 | 72 | mode = contentTypesMap.get(this.contentType).code; |
69 | 73 | if (this.contentType === ContentType.JSON && content) { |
70 | - content = js_beautify(content, {indent_size: 4}); | |
74 | + content$ = beautifyJs(content, {indent_size: 4}); | |
71 | 75 | } |
72 | 76 | } |
73 | - let editorOptions: Partial<Ace.EditorOptions> = { | |
74 | - mode: `ace/mode/${mode}`, | |
75 | - theme: 'ace/theme/github', | |
76 | - showGutter: false, | |
77 | - showPrintMargin: false, | |
78 | - readOnly: true | |
79 | - }; | |
77 | + if (!content$) { | |
78 | + content$ = of(content); | |
79 | + } | |
80 | + | |
81 | + content$.subscribe( | |
82 | + (processedContent) => { | |
83 | + let editorOptions: Partial<Ace.EditorOptions> = { | |
84 | + mode: `ace/mode/${mode}`, | |
85 | + theme: 'ace/theme/github', | |
86 | + showGutter: false, | |
87 | + showPrintMargin: false, | |
88 | + readOnly: true | |
89 | + }; | |
80 | 90 | |
81 | - const advancedOptions = { | |
82 | - enableSnippets: false, | |
83 | - enableBasicAutocompletion: false, | |
84 | - enableLiveAutocompletion: false | |
85 | - }; | |
91 | + const advancedOptions = { | |
92 | + enableSnippets: false, | |
93 | + enableBasicAutocompletion: false, | |
94 | + enableLiveAutocompletion: false | |
95 | + }; | |
86 | 96 | |
87 | - editorOptions = {...editorOptions, ...advancedOptions}; | |
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); | |
97 | + editorOptions = {...editorOptions, ...advancedOptions}; | |
98 | + getAce().subscribe( | |
99 | + (ace) => { | |
100 | + const editor = ace.edit(editorElement, editorOptions); | |
101 | + editor.session.setUseWrapMode(false); | |
102 | + editor.setValue(processedContent, -1); | |
103 | + this.updateEditorSize(editorElement, processedContent, editor); | |
104 | + } | |
105 | + ); | |
94 | 106 | } |
95 | 107 | ); |
96 | 108 | } | ... | ... |
... | ... | @@ -29,7 +29,7 @@ |
29 | 29 | </mat-form-field> |
30 | 30 | <div *ngIf="alarmScheduleForm.get('type').value !== alarmScheduleType.ANY_TIME"> |
31 | 31 | <tb-timezone-select |
32 | - [defaultTimezone]="defaultTimezone" | |
32 | + [defaultTimezone]="defaultTimezone$ | async" | |
33 | 33 | required |
34 | 34 | formControlName="timezone"> |
35 | 35 | </tb-timezone-select> | ... | ... |
... | ... | @@ -32,11 +32,15 @@ import { |
32 | 32 | AlarmSchedule, |
33 | 33 | AlarmScheduleType, |
34 | 34 | AlarmScheduleTypeTranslationMap, |
35 | - dayOfWeekTranslations, getAlarmScheduleRangeText, timeOfDayToUTCTimestamp, utcTimestampToTimeOfDay | |
35 | + dayOfWeekTranslations, | |
36 | + getAlarmScheduleRangeText, | |
37 | + timeOfDayToUTCTimestamp, | |
38 | + utcTimestampToTimeOfDay | |
36 | 39 | } from '@shared/models/device.models'; |
37 | 40 | import { isDefined, isDefinedAndNotNull } from '@core/utils'; |
38 | -import * as _moment from 'moment-timezone'; | |
39 | 41 | import { MatCheckboxChange } from '@angular/material/checkbox'; |
42 | +import { getDefaultTimezone } from '@shared/models/time/time.models'; | |
43 | +import { share } from 'rxjs/operators'; | |
40 | 44 | |
41 | 45 | @Component({ |
42 | 46 | selector: 'tb-alarm-schedule', |
... | ... | @@ -58,7 +62,9 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, |
58 | 62 | |
59 | 63 | alarmScheduleForm: FormGroup; |
60 | 64 | |
61 | - defaultTimezone = _moment.tz.guess(); | |
65 | + defaultTimezone$ = getDefaultTimezone().pipe( | |
66 | + share() | |
67 | + ); | |
62 | 68 | |
63 | 69 | alarmScheduleTypes = Object.keys(AlarmScheduleType); |
64 | 70 | alarmScheduleType = AlarmScheduleType; |
... | ... | @@ -93,9 +99,11 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, |
93 | 99 | items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i)), this.validateItems) |
94 | 100 | }); |
95 | 101 | this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => { |
96 | - this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: this.defaultTimezone}, {emitEvent: false}); | |
97 | - this.updateValidators(type, true); | |
98 | - this.alarmScheduleForm.updateValueAndValidity(); | |
102 | + getDefaultTimezone().subscribe((defaultTimezone) => { | |
103 | + this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false}); | |
104 | + this.updateValidators(type, true); | |
105 | + this.alarmScheduleForm.updateValueAndValidity(); | |
106 | + }); | |
99 | 107 | }); |
100 | 108 | this.alarmScheduleForm.valueChanges.subscribe(() => { |
101 | 109 | this.updateModel(); | ... | ... |
... | ... | @@ -34,13 +34,13 @@ import { AppState } from '@core/core.state'; |
34 | 34 | import { CustomActionDescriptor } from '@shared/models/widget.models'; |
35 | 35 | import { Ace } from 'ace-builds'; |
36 | 36 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
37 | -import { css_beautify, html_beautify } from 'js-beautify'; | |
38 | 37 | import { ResizeObserver } from '@juggle/resize-observer'; |
39 | 38 | import { CustomPrettyActionEditorCompleter } from '@home/components/widget/action/custom-action.models'; |
40 | 39 | import { Observable } from 'rxjs/internal/Observable'; |
41 | 40 | import { forkJoin, from } from 'rxjs'; |
42 | 41 | import { map, tap } from 'rxjs/operators'; |
43 | 42 | import { getAce } from '@shared/models/ace/ace.models'; |
43 | +import { beautifyCss, beautifyHtml } from '@shared/models/beautify.models'; | |
44 | 44 | |
45 | 45 | @Component({ |
46 | 46 | selector: 'tb-custom-action-pretty-resources-tabs', |
... | ... | @@ -140,21 +140,27 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl |
140 | 140 | } |
141 | 141 | |
142 | 142 | public beautifyCss(): void { |
143 | - const res = css_beautify(this.action.customCss, {indent_size: 4}); | |
144 | - if (this.action.customCss !== res) { | |
145 | - this.action.customCss = res; | |
146 | - this.cssEditor.setValue(this.action.customCss ? this.action.customCss : '', -1); | |
147 | - this.notifyActionUpdated(); | |
148 | - } | |
143 | + beautifyCss(this.action.customCss, {indent_size: 4}).subscribe( | |
144 | + (res) => { | |
145 | + if (this.action.customCss !== res) { | |
146 | + this.action.customCss = res; | |
147 | + this.cssEditor.setValue(this.action.customCss ? this.action.customCss : '', -1); | |
148 | + this.notifyActionUpdated(); | |
149 | + } | |
150 | + } | |
151 | + ); | |
149 | 152 | } |
150 | 153 | |
151 | 154 | public beautifyHtml(): void { |
152 | - const res = html_beautify(this.action.customHtml, {indent_size: 4, wrap_line_length: 60}); | |
153 | - if (this.action.customHtml !== res) { | |
154 | - this.action.customHtml = res; | |
155 | - this.htmlEditor.setValue(this.action.customHtml ? this.action.customHtml : '', -1); | |
156 | - this.notifyActionUpdated(); | |
157 | - } | |
155 | + beautifyHtml(this.action.customHtml, {indent_size: 4, wrap_line_length: 60}).subscribe( | |
156 | + (res) => { | |
157 | + if (this.action.customHtml !== res) { | |
158 | + this.action.customHtml = res; | |
159 | + this.htmlEditor.setValue(this.action.customHtml ? this.action.customHtml : '', -1); | |
160 | + this.notifyActionUpdated(); | |
161 | + } | |
162 | + } | |
163 | + ); | |
158 | 164 | } |
159 | 165 | |
160 | 166 | private initAceEditors(): Observable<any> { | ... | ... |
... | ... | @@ -111,11 +111,32 @@ export class WidgetComponentService { |
111 | 111 | const initSubject = new ReplaySubject(); |
112 | 112 | this.init$ = initSubject.asObservable(); |
113 | 113 | |
114 | - (window as any).tinycolor = tinycolor; | |
115 | - (window as any).cssjs = cssjs; | |
116 | - (window as any).moment = moment; | |
114 | + const w = (this.window as any); | |
115 | + | |
116 | + w.tinycolor = tinycolor; | |
117 | + w.cssjs = cssjs; | |
118 | + w.moment = moment; | |
119 | + w.$ = $; | |
120 | + w.jQuery = $; | |
117 | 121 | |
118 | 122 | const widgetModulesTasks: Observable<any>[] = []; |
123 | + widgetModulesTasks.push(from(import('jquery.terminal'))); | |
124 | + | |
125 | + widgetModulesTasks.push(from(import('flot/src/jquery.flot.js')).pipe( | |
126 | + mergeMap(() => { | |
127 | + const flotJsPluginsTasks: Observable<any>[] = []; | |
128 | + flotJsPluginsTasks.push(from(import('flot/lib/jquery.colorhelpers.js'))); | |
129 | + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.time.js'))); | |
130 | + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.selection.js'))); | |
131 | + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.pie.js'))); | |
132 | + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.crosshair.js'))); | |
133 | + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.stack.js'))); | |
134 | + flotJsPluginsTasks.push(from(import('flot/src/plugins/jquery.flot.symbol.js'))); | |
135 | + flotJsPluginsTasks.push(from(import('flot.curvedlines/curvedLines.js'))); | |
136 | + return forkJoin(flotJsPluginsTasks); | |
137 | + }) | |
138 | + )); | |
139 | + | |
119 | 140 | widgetModulesTasks.push(from(import('@home/components/widget/lib/flot-widget')).pipe( |
120 | 141 | tap((mod) => { |
121 | 142 | (window as any).TbFlot = mod.TbFlot; | ... | ... |
... | ... | @@ -29,7 +29,7 @@ import { |
29 | 29 | import { EntitiesTableComponent } from '../../components/entity/entities-table.component'; |
30 | 30 | import { Authority } from '@shared/models/authority.enum'; |
31 | 31 | import { RuleChainsTableConfigResolver } from '@modules/home/pages/rulechain/rulechains-table-config.resolver'; |
32 | -import { Observable } from 'rxjs'; | |
32 | +import { from, Observable } from 'rxjs'; | |
33 | 33 | import { BreadCrumbConfig, BreadCrumbLabelFunction } from '@shared/components/breadcrumb'; |
34 | 34 | import { ResolvedRuleChainMetaData, RuleChain } from '@shared/models/rule-chain.models'; |
35 | 35 | import { RuleChainService } from '@core/http/rule-chain.service'; |
... | ... | @@ -76,6 +76,17 @@ export class RuleNodeComponentsResolver implements Resolve<Array<RuleNodeCompone |
76 | 76 | } |
77 | 77 | |
78 | 78 | @Injectable() |
79 | +export class TooltipsterResolver implements Resolve<any> { | |
80 | + | |
81 | + constructor() { | |
82 | + } | |
83 | + | |
84 | + resolve(route: ActivatedRouteSnapshot): Observable<any> { | |
85 | + return from(import('tooltipster')); | |
86 | + } | |
87 | +} | |
88 | + | |
89 | +@Injectable() | |
79 | 90 | export class RuleChainImportGuard implements CanActivate { |
80 | 91 | |
81 | 92 | constructor(private itembuffer: ItemBufferService, |
... | ... | @@ -144,7 +155,8 @@ const routes: Routes = [ |
144 | 155 | resolve: { |
145 | 156 | ruleChain: RuleChainResolver, |
146 | 157 | ruleChainMetaData: ResolvedRuleChainMetaDataResolver, |
147 | - ruleNodeComponents: RuleNodeComponentsResolver | |
158 | + ruleNodeComponents: RuleNodeComponentsResolver, | |
159 | + tooltipster: TooltipsterResolver | |
148 | 160 | } |
149 | 161 | }, |
150 | 162 | { |
... | ... | @@ -162,7 +174,8 @@ const routes: Routes = [ |
162 | 174 | import: true |
163 | 175 | }, |
164 | 176 | resolve: { |
165 | - ruleNodeComponents: RuleNodeComponentsResolver | |
177 | + ruleNodeComponents: RuleNodeComponentsResolver, | |
178 | + tooltipster: TooltipsterResolver | |
166 | 179 | } |
167 | 180 | } |
168 | 181 | ] |
... | ... | @@ -178,6 +191,7 @@ const routes: Routes = [ |
178 | 191 | RuleChainResolver, |
179 | 192 | ResolvedRuleChainMetaDataResolver, |
180 | 193 | RuleNodeComponentsResolver, |
194 | + TooltipsterResolver, | |
181 | 195 | RuleChainImportGuard |
182 | 196 | ] |
183 | 197 | }) | ... | ... |
... | ... | @@ -34,7 +34,6 @@ import { TranslateService } from '@ngx-translate/core'; |
34 | 34 | import { getCurrentIsLoading } from '@app/core/interceptors/load.selectors'; |
35 | 35 | import { Ace } from 'ace-builds'; |
36 | 36 | import { getAce, Range } from '@shared/models/ace/ace.models'; |
37 | -import { css_beautify, html_beautify } from 'js-beautify'; | |
38 | 37 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
39 | 38 | import { WINDOW } from '@core/services/window.service'; |
40 | 39 | import { WindowMessage } from '@shared/models/window-message.model'; |
... | ... | @@ -51,6 +50,7 @@ import Timeout = NodeJS.Timeout; |
51 | 50 | import { widgetEditorCompleter } from '@home/pages/widget/widget-editor.models'; |
52 | 51 | import { Observable } from 'rxjs/internal/Observable'; |
53 | 52 | import { map, tap } from 'rxjs/operators'; |
53 | +import { beautifyCss, beautifyHtml, beautifyJs } from '@shared/models/beautify.models'; | |
54 | 54 | |
55 | 55 | // @dynamic |
56 | 56 | @Component({ |
... | ... | @@ -618,48 +618,63 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe |
618 | 618 | } |
619 | 619 | |
620 | 620 | beautifyCss(): void { |
621 | - const res = css_beautify(this.widget.templateCss, {indent_size: 4}); | |
622 | - if (this.widget.templateCss !== res) { | |
623 | - this.isDirty = true; | |
624 | - this.widget.templateCss = res; | |
625 | - this.cssEditor.setValue(this.widget.templateCss ? this.widget.templateCss : '', -1); | |
626 | - } | |
621 | + beautifyCss(this.widget.templateCss, {indent_size: 4}).subscribe( | |
622 | + (res) => { | |
623 | + if (this.widget.templateCss !== res) { | |
624 | + this.isDirty = true; | |
625 | + this.widget.templateCss = res; | |
626 | + this.cssEditor.setValue(this.widget.templateCss ? this.widget.templateCss : '', -1); | |
627 | + } | |
628 | + } | |
629 | + ); | |
627 | 630 | } |
628 | 631 | |
629 | 632 | beautifyHtml(): void { |
630 | - const res = html_beautify(this.widget.templateHtml, {indent_size: 4, wrap_line_length: 60}); | |
631 | - if (this.widget.templateHtml !== res) { | |
632 | - this.isDirty = true; | |
633 | - this.widget.templateHtml = res; | |
634 | - this.htmlEditor.setValue(this.widget.templateHtml ? this.widget.templateHtml : '', -1); | |
635 | - } | |
633 | + beautifyHtml(this.widget.templateHtml, {indent_size: 4, wrap_line_length: 60}).subscribe( | |
634 | + (res) => { | |
635 | + if (this.widget.templateHtml !== res) { | |
636 | + this.isDirty = true; | |
637 | + this.widget.templateHtml = res; | |
638 | + this.htmlEditor.setValue(this.widget.templateHtml ? this.widget.templateHtml : '', -1); | |
639 | + } | |
640 | + } | |
641 | + ); | |
636 | 642 | } |
637 | 643 | |
638 | 644 | beautifyJson(): void { |
639 | - const res = js_beautify(this.widget.settingsSchema, {indent_size: 4}); | |
640 | - if (this.widget.settingsSchema !== res) { | |
641 | - this.isDirty = true; | |
642 | - this.widget.settingsSchema = res; | |
643 | - this.jsonSettingsEditor.setValue(this.widget.settingsSchema ? this.widget.settingsSchema : '', -1); | |
644 | - } | |
645 | + beautifyJs(this.widget.settingsSchema, {indent_size: 4}).subscribe( | |
646 | + (res) => { | |
647 | + if (this.widget.settingsSchema !== res) { | |
648 | + this.isDirty = true; | |
649 | + this.widget.settingsSchema = res; | |
650 | + this.jsonSettingsEditor.setValue(this.widget.settingsSchema ? this.widget.settingsSchema : '', -1); | |
651 | + } | |
652 | + } | |
653 | + ); | |
645 | 654 | } |
646 | 655 | |
647 | 656 | beautifyDataKeyJson(): void { |
648 | - const res = js_beautify(this.widget.dataKeySettingsSchema, {indent_size: 4}); | |
649 | - if (this.widget.dataKeySettingsSchema !== res) { | |
650 | - this.isDirty = true; | |
651 | - this.widget.dataKeySettingsSchema = res; | |
652 | - this.dataKeyJsonSettingsEditor.setValue(this.widget.dataKeySettingsSchema ? this.widget.dataKeySettingsSchema : '', -1); | |
653 | - } | |
657 | + beautifyJs(this.widget.dataKeySettingsSchema, {indent_size: 4}).subscribe( | |
658 | + (res) => { | |
659 | + if (this.widget.dataKeySettingsSchema !== res) { | |
660 | + this.isDirty = true; | |
661 | + this.widget.dataKeySettingsSchema = res; | |
662 | + this.dataKeyJsonSettingsEditor.setValue(this.widget.dataKeySettingsSchema ? this.widget.dataKeySettingsSchema : '', -1); | |
663 | + } | |
664 | + } | |
665 | + ); | |
654 | 666 | } |
655 | 667 | |
656 | 668 | beautifyJs(): void { |
657 | - const res = js_beautify(this.widget.controllerScript, {indent_size: 4, wrap_line_length: 60}); | |
658 | - if (this.widget.controllerScript !== res) { | |
659 | - this.isDirty = true; | |
660 | - this.widget.controllerScript = res; | |
661 | - this.jsEditor.setValue(this.widget.controllerScript ? this.widget.controllerScript : '', -1); | |
662 | - } | |
669 | + beautifyJs(this.widget.controllerScript, {indent_size: 4, wrap_line_length: 60}).subscribe( | |
670 | + (res) => { | |
671 | + if (this.widget.controllerScript !== res) { | |
672 | + this.isDirty = true; | |
673 | + this.widget.controllerScript = res; | |
674 | + this.jsEditor.setValue(this.widget.controllerScript ? this.widget.controllerScript : '', -1); | |
675 | + } | |
676 | + } | |
677 | + ); | |
663 | 678 | } |
664 | 679 | |
665 | 680 | removeResource(index: number) { | ... | ... |
... | ... | @@ -41,6 +41,7 @@ import { TestScriptInputParams } from '@shared/models/rule-node.models'; |
41 | 41 | import { RuleChainService } from '@core/http/rule-chain.service'; |
42 | 42 | import { mergeMap } from 'rxjs/operators'; |
43 | 43 | import { ActionNotificationShow } from '@core/notification/notification.actions'; |
44 | +import { beautifyJs } from '@shared/models/beautify.models'; | |
44 | 45 | |
45 | 46 | export interface NodeScriptTestDialogData { |
46 | 47 | script: string; |
... | ... | @@ -110,12 +111,17 @@ export class NodeScriptTestDialogComponent extends DialogComponent<NodeScriptTes |
110 | 111 | this.nodeScriptTestFormGroup = this.fb.group({ |
111 | 112 | payload: this.fb.group({ |
112 | 113 | msgType: [this.data.msgType, [Validators.required]], |
113 | - msg: [js_beautify(JSON.stringify(this.data.msg), {indent_size: 4}), []], | |
114 | + msg: [null, []], | |
114 | 115 | }), |
115 | 116 | metadata: [this.data.metadata, [Validators.required]], |
116 | 117 | script: [this.data.script, []], |
117 | 118 | output: ['', []] |
118 | 119 | }); |
120 | + beautifyJs(JSON.stringify(this.data.msg), {indent_size: 4}).subscribe( | |
121 | + (res) => { | |
122 | + this.nodeScriptTestFormGroup.get('payload').get('msg').patchValue(res, {emitEvent: false}); | |
123 | + } | |
124 | + ); | |
119 | 125 | } |
120 | 126 | |
121 | 127 | ngAfterViewInit(): void { |
... | ... | @@ -166,7 +172,11 @@ export class NodeScriptTestDialogComponent extends DialogComponent<NodeScriptTes |
166 | 172 | |
167 | 173 | test(): void { |
168 | 174 | this.testNodeScript().subscribe((output) => { |
169 | - this.nodeScriptTestFormGroup.get('output').setValue(js_beautify(output, {indent_size: 4})); | |
175 | + beautifyJs(output, {indent_size: 4}).subscribe( | |
176 | + (res) => { | |
177 | + this.nodeScriptTestFormGroup.get('output').setValue(res); | |
178 | + } | |
179 | + ); | |
170 | 180 | }); |
171 | 181 | } |
172 | 182 | ... | ... |
... | ... | @@ -37,6 +37,7 @@ import { TranslateService } from '@ngx-translate/core'; |
37 | 37 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
38 | 38 | import { ResizeObserver } from '@juggle/resize-observer'; |
39 | 39 | import { TbEditorCompleter } from '@shared/models/ace/completion.models'; |
40 | +import { beautifyJs } from '@shared/models/beautify.models'; | |
40 | 41 | |
41 | 42 | @Component({ |
42 | 43 | selector: 'tb-js-func', |
... | ... | @@ -214,9 +215,12 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, |
214 | 215 | } |
215 | 216 | |
216 | 217 | beautifyJs() { |
217 | - const res = js_beautify(this.modelValue, {indent_size: 4, wrap_line_length: 60}); | |
218 | - this.jsEditor.setValue(res ? res : '', -1); | |
219 | - this.updateView(); | |
218 | + beautifyJs(this.modelValue, {indent_size: 4, wrap_line_length: 60}).subscribe( | |
219 | + (res) => { | |
220 | + this.jsEditor.setValue(res ? res : '', -1); | |
221 | + this.updateView(); | |
222 | + } | |
223 | + ); | |
220 | 224 | } |
221 | 225 | |
222 | 226 | validateOnSubmit(): void { | ... | ... |
... | ... | @@ -36,6 +36,7 @@ import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
36 | 36 | import { guid } from '@core/utils'; |
37 | 37 | import { ResizeObserver } from '@juggle/resize-observer'; |
38 | 38 | import { getAce } from '@shared/models/ace/ace.models'; |
39 | +import { beautifyJs } from '@shared/models/beautify.models'; | |
39 | 40 | |
40 | 41 | @Component({ |
41 | 42 | selector: 'tb-json-content', |
... | ... | @@ -286,9 +287,12 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid |
286 | 287 | } |
287 | 288 | |
288 | 289 | beautifyJSON() { |
289 | - const res = js_beautify(this.contentBody, {indent_size: 4, wrap_line_length: 60}); | |
290 | - this.jsonEditor.setValue(res ? res : '', -1); | |
291 | - this.updateView(); | |
290 | + beautifyJs(this.contentBody, {indent_size: 4, wrap_line_length: 60}).subscribe( | |
291 | + (res) => { | |
292 | + this.jsonEditor.setValue(res ? res : '', -1); | |
293 | + this.updateView(); | |
294 | + } | |
295 | + ); | |
292 | 296 | } |
293 | 297 | |
294 | 298 | minifyJSON() { | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import { IEditorProps } from 'react-ace/src/types'; |
22 | 22 | import { map, mergeMap } from 'rxjs/operators'; |
23 | 23 | import { loadAceDependencies } from '@shared/models/ace/ace.models'; |
24 | 24 | import { from } from 'rxjs'; |
25 | +import { Observable } from 'rxjs/internal/Observable'; | |
25 | 26 | |
26 | 27 | const ReactAce = React.lazy(() => { |
27 | 28 | return loadAceDependencies().pipe( |
... | ... | @@ -33,7 +34,7 @@ const ReactAce = React.lazy(() => { |
33 | 34 | |
34 | 35 | interface ThingsboardAceEditorProps extends JsonFormFieldProps { |
35 | 36 | mode: string; |
36 | - onTidy: (value: string) => string; | |
37 | + onTidy: (value: string) => Observable<string>; | |
37 | 38 | } |
38 | 39 | |
39 | 40 | interface ThingsboardAceEditorState extends JsonFormFieldState { |
... | ... | @@ -84,15 +85,18 @@ class ThingsboardAceEditor extends React.Component<ThingsboardAceEditorProps, Th |
84 | 85 | onTidy() { |
85 | 86 | if (!this.props.form.readonly) { |
86 | 87 | let value = this.state.value; |
87 | - value = this.props.onTidy(value); | |
88 | - this.setState({ | |
89 | - value | |
90 | - }); | |
91 | - this.props.onChangeValidate({ | |
92 | - target: { | |
93 | - value | |
94 | - } | |
95 | - }); | |
88 | + this.props.onTidy(value).subscribe( | |
89 | + (processedValue) => { | |
90 | + this.setState({ | |
91 | + value: processedValue | |
92 | + }); | |
93 | + this.props.onChangeValidate({ | |
94 | + target: { | |
95 | + value: processedValue | |
96 | + } | |
97 | + }); | |
98 | + } | |
99 | + ); | |
96 | 100 | } |
97 | 101 | } |
98 | 102 | ... | ... |
... | ... | @@ -16,7 +16,8 @@ |
16 | 16 | import * as React from 'react'; |
17 | 17 | import ThingsboardAceEditor from './json-form-ace-editor'; |
18 | 18 | import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; |
19 | -import { css_beautify } from 'js-beautify'; | |
19 | +import { Observable } from 'rxjs/internal/Observable'; | |
20 | +import { beautifyCss } from '@shared/models/beautify.models'; | |
20 | 21 | |
21 | 22 | class ThingsboardCss extends React.Component<JsonFormFieldProps, JsonFormFieldState> { |
22 | 23 | |
... | ... | @@ -25,8 +26,8 @@ class ThingsboardCss extends React.Component<JsonFormFieldProps, JsonFormFieldSt |
25 | 26 | this.onTidyCss = this.onTidyCss.bind(this); |
26 | 27 | } |
27 | 28 | |
28 | - onTidyCss(css: string): string { | |
29 | - return css_beautify(css, {indent_size: 4}); | |
29 | + onTidyCss(css: string): Observable<string> { | |
30 | + return beautifyCss(css, {indent_size: 4}); | |
30 | 31 | } |
31 | 32 | |
32 | 33 | render() { | ... | ... |
... | ... | @@ -15,8 +15,9 @@ |
15 | 15 | */ |
16 | 16 | import * as React from 'react'; |
17 | 17 | import ThingsboardAceEditor from './json-form-ace-editor'; |
18 | -import { html_beautify } from 'js-beautify'; | |
19 | 18 | import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; |
19 | +import { Observable } from 'rxjs/internal/Observable'; | |
20 | +import { beautifyHtml } from '@shared/models/beautify.models'; | |
20 | 21 | |
21 | 22 | class ThingsboardHtml extends React.Component<JsonFormFieldProps, JsonFormFieldState> { |
22 | 23 | |
... | ... | @@ -25,8 +26,8 @@ class ThingsboardHtml extends React.Component<JsonFormFieldProps, JsonFormFieldS |
25 | 26 | this.onTidyHtml = this.onTidyHtml.bind(this); |
26 | 27 | } |
27 | 28 | |
28 | - onTidyHtml(html: string): string { | |
29 | - return html_beautify(html, {indent_size: 4}); | |
29 | + onTidyHtml(html: string): Observable<string> { | |
30 | + return beautifyHtml(html, {indent_size: 4}); | |
30 | 31 | } |
31 | 32 | |
32 | 33 | render() { | ... | ... |
... | ... | @@ -16,6 +16,8 @@ |
16 | 16 | import * as React from 'react'; |
17 | 17 | import ThingsboardAceEditor from './json-form-ace-editor'; |
18 | 18 | import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; |
19 | +import { Observable } from 'rxjs/internal/Observable'; | |
20 | +import { beautifyJs } from '@shared/models/beautify.models'; | |
19 | 21 | |
20 | 22 | class ThingsboardJavaScript extends React.Component<JsonFormFieldProps, JsonFormFieldState> { |
21 | 23 | |
... | ... | @@ -24,8 +26,8 @@ class ThingsboardJavaScript extends React.Component<JsonFormFieldProps, JsonForm |
24 | 26 | this.onTidyJavascript = this.onTidyJavascript.bind(this); |
25 | 27 | } |
26 | 28 | |
27 | - onTidyJavascript(javascript: string): string { | |
28 | - return js_beautify(javascript, {indent_size: 4, wrap_line_length: 60}); | |
29 | + onTidyJavascript(javascript: string): Observable<string> { | |
30 | + return beautifyJs(javascript, {indent_size: 4, wrap_line_length: 60}); | |
29 | 31 | } |
30 | 32 | |
31 | 33 | render() { | ... | ... |
... | ... | @@ -16,6 +16,8 @@ |
16 | 16 | import * as React from 'react'; |
17 | 17 | import ThingsboardAceEditor from './json-form-ace-editor'; |
18 | 18 | import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; |
19 | +import { Observable } from 'rxjs/internal/Observable'; | |
20 | +import { beautifyJs } from '@shared/models/beautify.models'; | |
19 | 21 | |
20 | 22 | class ThingsboardJson extends React.Component<JsonFormFieldProps, JsonFormFieldState> { |
21 | 23 | |
... | ... | @@ -24,8 +26,8 @@ class ThingsboardJson extends React.Component<JsonFormFieldProps, JsonFormFieldS |
24 | 26 | this.onTidyJson = this.onTidyJson.bind(this); |
25 | 27 | } |
26 | 28 | |
27 | - onTidyJson(json: string): string { | |
28 | - return js_beautify(json, {indent_size: 4}); | |
29 | + onTidyJson(json: string): Observable<string> { | |
30 | + return beautifyJs(json, {indent_size: 4}); | |
29 | 31 | } |
30 | 32 | |
31 | 33 | render() { | ... | ... |
... | ... | @@ -145,128 +145,131 @@ export class NavTreeComponent implements OnInit { |
145 | 145 | }; |
146 | 146 | } |
147 | 147 | |
148 | - this.treeElement = $('.tb-nav-tree-container', this.elementRef.nativeElement).jstree(config); | |
148 | + import('jstree').then(() => { | |
149 | 149 | |
150 | - this.treeElement.on('changed.jstree', (e: any, data) => { | |
151 | - const node: NavTreeNode = data.instance.get_selected(true)[0]; | |
152 | - if (this.onNodeSelected) { | |
153 | - this.ngZone.run(() => this.onNodeSelected(node, e as Event)); | |
154 | - } | |
155 | - }); | |
156 | - | |
157 | - this.treeElement.on('model.jstree', (e: any, data) => { | |
158 | - if (this.onNodesInserted) { | |
159 | - this.ngZone.run(() => this.onNodesInserted(data.nodes, data.parent)); | |
160 | - } | |
161 | - }); | |
150 | + this.treeElement = $('.tb-nav-tree-container', this.elementRef.nativeElement).jstree(config); | |
162 | 151 | |
163 | - if (this.editCallbacks) { | |
164 | - this.editCallbacks.selectNode = id => { | |
165 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
166 | - if (node) { | |
167 | - this.treeElement.jstree('deselect_all', true); | |
168 | - this.treeElement.jstree('select_node', node); | |
169 | - } | |
170 | - }; | |
171 | - this.editCallbacks.deselectAll = () => { | |
172 | - this.treeElement.jstree('deselect_all'); | |
173 | - }; | |
174 | - this.editCallbacks.getNode = (id) => { | |
175 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
176 | - return node; | |
177 | - }; | |
178 | - this.editCallbacks.getParentNodeId = (id) => { | |
179 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
180 | - if (node) { | |
181 | - return this.treeElement.jstree('get_parent', node); | |
152 | + this.treeElement.on('changed.jstree', (e: any, data) => { | |
153 | + const node: NavTreeNode = data.instance.get_selected(true)[0]; | |
154 | + if (this.onNodeSelected) { | |
155 | + this.ngZone.run(() => this.onNodeSelected(node, e as Event)); | |
182 | 156 | } |
183 | - }; | |
184 | - this.editCallbacks.openNode = (id, cb) => { | |
185 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
186 | - if (node) { | |
187 | - this.treeElement.jstree('open_node', node, cb); | |
188 | - } | |
189 | - }; | |
190 | - this.editCallbacks.nodeIsOpen = (id) => { | |
191 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
192 | - if (node) { | |
193 | - return this.treeElement.jstree('is_open', node); | |
194 | - } else { | |
195 | - return true; | |
196 | - } | |
197 | - }; | |
198 | - this.editCallbacks.nodeIsLoaded = (id) => { | |
199 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
200 | - if (node) { | |
201 | - return this.treeElement.jstree('is_loaded', node); | |
202 | - } else { | |
203 | - return true; | |
157 | + }); | |
158 | + | |
159 | + this.treeElement.on('model.jstree', (e: any, data) => { | |
160 | + if (this.onNodesInserted) { | |
161 | + this.ngZone.run(() => this.onNodesInserted(data.nodes, data.parent)); | |
204 | 162 | } |
205 | - }; | |
206 | - this.editCallbacks.refreshNode = (id) => { | |
207 | - if (id === '#') { | |
208 | - this.treeElement.jstree('refresh'); | |
209 | - this.treeElement.jstree('redraw'); | |
210 | - } else { | |
163 | + }); | |
164 | + | |
165 | + if (this.editCallbacks) { | |
166 | + this.editCallbacks.selectNode = id => { | |
167 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
168 | + if (node) { | |
169 | + this.treeElement.jstree('deselect_all', true); | |
170 | + this.treeElement.jstree('select_node', node); | |
171 | + } | |
172 | + }; | |
173 | + this.editCallbacks.deselectAll = () => { | |
174 | + this.treeElement.jstree('deselect_all'); | |
175 | + }; | |
176 | + this.editCallbacks.getNode = (id) => { | |
177 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
178 | + return node; | |
179 | + }; | |
180 | + this.editCallbacks.getParentNodeId = (id) => { | |
211 | 181 | const node: NavTreeNode = this.treeElement.jstree('get_node', id); |
212 | 182 | if (node) { |
213 | - const opened = this.treeElement.jstree('is_open', node); | |
214 | - this.treeElement.jstree('refresh_node', node); | |
183 | + return this.treeElement.jstree('get_parent', node); | |
184 | + } | |
185 | + }; | |
186 | + this.editCallbacks.openNode = (id, cb) => { | |
187 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
188 | + if (node) { | |
189 | + this.treeElement.jstree('open_node', node, cb); | |
190 | + } | |
191 | + }; | |
192 | + this.editCallbacks.nodeIsOpen = (id) => { | |
193 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
194 | + if (node) { | |
195 | + return this.treeElement.jstree('is_open', node); | |
196 | + } else { | |
197 | + return true; | |
198 | + } | |
199 | + }; | |
200 | + this.editCallbacks.nodeIsLoaded = (id) => { | |
201 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
202 | + if (node) { | |
203 | + return this.treeElement.jstree('is_loaded', node); | |
204 | + } else { | |
205 | + return true; | |
206 | + } | |
207 | + }; | |
208 | + this.editCallbacks.refreshNode = (id) => { | |
209 | + if (id === '#') { | |
210 | + this.treeElement.jstree('refresh'); | |
215 | 211 | this.treeElement.jstree('redraw'); |
216 | - if (node.children && opened/* && !node.children.length*/) { | |
217 | - this.treeElement.jstree('open_node', node); | |
212 | + } else { | |
213 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
214 | + if (node) { | |
215 | + const opened = this.treeElement.jstree('is_open', node); | |
216 | + this.treeElement.jstree('refresh_node', node); | |
217 | + this.treeElement.jstree('redraw'); | |
218 | + if (node.children && opened/* && !node.children.length*/) { | |
219 | + this.treeElement.jstree('open_node', node); | |
220 | + } | |
218 | 221 | } |
219 | 222 | } |
220 | - } | |
221 | - }; | |
222 | - this.editCallbacks.updateNode = (id, newName) => { | |
223 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
224 | - if (node) { | |
225 | - this.treeElement.jstree('rename_node', node, newName); | |
226 | - } | |
227 | - }; | |
228 | - this.editCallbacks.createNode = (parentId, node, pos) => { | |
229 | - const parentNode: NavTreeNode = this.treeElement.jstree('get_node', parentId); | |
230 | - if (parentNode) { | |
231 | - this.treeElement.jstree('create_node', parentNode, node, pos); | |
232 | - } | |
233 | - }; | |
234 | - this.editCallbacks.deleteNode = (id) => { | |
235 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
236 | - if (node) { | |
237 | - this.treeElement.jstree('delete_node', node); | |
238 | - } | |
239 | - }; | |
240 | - this.editCallbacks.disableNode = (id) => { | |
241 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
242 | - if (node) { | |
243 | - this.treeElement.jstree('disable_node', node); | |
244 | - } | |
245 | - }; | |
246 | - this.editCallbacks.enableNode = (id) => { | |
247 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
248 | - if (node) { | |
249 | - this.treeElement.jstree('enable_node', node); | |
250 | - } | |
251 | - }; | |
252 | - this.editCallbacks.setNodeHasChildren = (id, hasChildren) => { | |
253 | - const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
254 | - if (node) { | |
255 | - if (!node.children || (Array.isArray(node.children) && !node.children.length)) { | |
256 | - node.children = hasChildren; | |
257 | - node.state.loaded = !hasChildren; | |
258 | - node.state.opened = false; | |
259 | - this.treeElement.jstree('_node_changed', node.id); | |
260 | - this.treeElement.jstree('redraw'); | |
223 | + }; | |
224 | + this.editCallbacks.updateNode = (id, newName) => { | |
225 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
226 | + if (node) { | |
227 | + this.treeElement.jstree('rename_node', node, newName); | |
261 | 228 | } |
262 | - } | |
263 | - }; | |
264 | - this.editCallbacks.search = (searchText) => { | |
265 | - this.treeElement.jstree('search', searchText); | |
266 | - }; | |
267 | - this.editCallbacks.clearSearch = () => { | |
268 | - this.treeElement.jstree('clear_search'); | |
269 | - }; | |
270 | - } | |
229 | + }; | |
230 | + this.editCallbacks.createNode = (parentId, node, pos) => { | |
231 | + const parentNode: NavTreeNode = this.treeElement.jstree('get_node', parentId); | |
232 | + if (parentNode) { | |
233 | + this.treeElement.jstree('create_node', parentNode, node, pos); | |
234 | + } | |
235 | + }; | |
236 | + this.editCallbacks.deleteNode = (id) => { | |
237 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
238 | + if (node) { | |
239 | + this.treeElement.jstree('delete_node', node); | |
240 | + } | |
241 | + }; | |
242 | + this.editCallbacks.disableNode = (id) => { | |
243 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
244 | + if (node) { | |
245 | + this.treeElement.jstree('disable_node', node); | |
246 | + } | |
247 | + }; | |
248 | + this.editCallbacks.enableNode = (id) => { | |
249 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
250 | + if (node) { | |
251 | + this.treeElement.jstree('enable_node', node); | |
252 | + } | |
253 | + }; | |
254 | + this.editCallbacks.setNodeHasChildren = (id, hasChildren) => { | |
255 | + const node: NavTreeNode = this.treeElement.jstree('get_node', id); | |
256 | + if (node) { | |
257 | + if (!node.children || (Array.isArray(node.children) && !node.children.length)) { | |
258 | + node.children = hasChildren; | |
259 | + node.state.loaded = !hasChildren; | |
260 | + node.state.opened = false; | |
261 | + this.treeElement.jstree('_node_changed', node.id); | |
262 | + this.treeElement.jstree('redraw'); | |
263 | + } | |
264 | + } | |
265 | + }; | |
266 | + this.editCallbacks.search = (searchText) => { | |
267 | + this.treeElement.jstree('search', searchText); | |
268 | + }; | |
269 | + this.editCallbacks.clearSearch = () => { | |
270 | + this.treeElement.jstree('clear_search'); | |
271 | + }; | |
272 | + } | |
273 | + }); | |
271 | 274 | } |
272 | 275 | } | ... | ... |
... | ... | @@ -16,21 +16,14 @@ |
16 | 16 | |
17 | 17 | import { AfterViewInit, Component, forwardRef, Input, NgZone, OnInit, ViewChild } from '@angular/core'; |
18 | 18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; |
19 | -import { Observable, of } from 'rxjs'; | |
19 | +import { Observable } from 'rxjs'; | |
20 | 20 | import { map, mergeMap, share, tap } from 'rxjs/operators'; |
21 | 21 | import { Store } from '@ngrx/store'; |
22 | 22 | import { AppState } from '@app/core/core.state'; |
23 | 23 | import { TranslateService } from '@ngx-translate/core'; |
24 | 24 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
25 | -import * as _moment from 'moment-timezone'; | |
26 | 25 | import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; |
27 | - | |
28 | -interface TimezoneInfo { | |
29 | - id: string; | |
30 | - name: string; | |
31 | - offset: string; | |
32 | - nOffset: number; | |
33 | -} | |
26 | +import { getTimezoneInfo, getTimezones, TimezoneInfo } from '@shared/models/time/time.models'; | |
34 | 27 | |
35 | 28 | @Component({ |
36 | 29 | selector: 'tb-timezone-select', |
... | ... | @@ -50,28 +43,14 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af |
50 | 43 | |
51 | 44 | defaultTimezoneId: string = null; |
52 | 45 | |
53 | - defaultTimezoneInfo: TimezoneInfo = null; | |
54 | - | |
55 | - timezones: TimezoneInfo[] = _moment.tz.names().map((zoneName) => { | |
56 | - const tz = _moment.tz(zoneName); | |
57 | - return { | |
58 | - id: zoneName, | |
59 | - name: zoneName.replace(/_/g, ' '), | |
60 | - offset: `UTC${tz.format('Z')}`, | |
61 | - nOffset: tz.utcOffset() | |
62 | - } | |
63 | - }); | |
46 | + timezones$ = getTimezones().pipe( | |
47 | + share() | |
48 | + ); | |
64 | 49 | |
65 | 50 | @Input() |
66 | 51 | set defaultTimezone(timezone: string) { |
67 | 52 | if (this.defaultTimezoneId !== timezone) { |
68 | 53 | this.defaultTimezoneId = timezone; |
69 | - if (this.defaultTimezoneId) { | |
70 | - this.defaultTimezoneInfo = | |
71 | - this.timezones.find((timezoneInfo) => timezoneInfo.id === this.defaultTimezoneId); | |
72 | - } else { | |
73 | - this.defaultTimezoneInfo = null; | |
74 | - } | |
75 | 54 | } |
76 | 55 | } |
77 | 56 | |
... | ... | @@ -150,23 +129,27 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af |
150 | 129 | |
151 | 130 | writeValue(value: string | null): void { |
152 | 131 | this.searchText = ''; |
153 | - let foundTimezone: TimezoneInfo = null; | |
154 | 132 | if (value !== null) { |
155 | - foundTimezone = this.timezones.find(timezoneInfo => timezoneInfo.id === value); | |
156 | - } | |
157 | - if (foundTimezone !== null) { | |
158 | - this.modelValue = value; | |
159 | - this.selectTimezoneFormGroup.get('timezone').patchValue(foundTimezone, {emitEvent: false}); | |
133 | + getTimezoneInfo(value, this.defaultTimezoneId).subscribe( | |
134 | + (foundTimezone) => { | |
135 | + if (foundTimezone !== null) { | |
136 | + this.selectTimezoneFormGroup.get('timezone').patchValue(foundTimezone, {emitEvent: false}); | |
137 | + if (foundTimezone.id !== value) { | |
138 | + setTimeout(() => { | |
139 | + this.updateView(foundTimezone.id); | |
140 | + }, 0); | |
141 | + } else { | |
142 | + this.modelValue = value; | |
143 | + } | |
144 | + } else { | |
145 | + this.modelValue = null; | |
146 | + this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false}); | |
147 | + } | |
148 | + } | |
149 | + ); | |
160 | 150 | } else { |
161 | - if (this.defaultTimezoneInfo) { | |
162 | - this.selectTimezoneFormGroup.get('timezone').patchValue(this.defaultTimezoneInfo, {emitEvent: false}); | |
163 | - setTimeout(() => { | |
164 | - this.updateView(this.defaultTimezoneInfo.id); | |
165 | - }, 0); | |
166 | - } else { | |
167 | - this.modelValue = null; | |
168 | - this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false}); | |
169 | - } | |
151 | + this.modelValue = null; | |
152 | + this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false}); | |
170 | 153 | } |
171 | 154 | this.dirty = true; |
172 | 155 | } |
... | ... | @@ -182,10 +165,15 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af |
182 | 165 | if (this.ignoreClosePanel) { |
183 | 166 | this.ignoreClosePanel = false; |
184 | 167 | } else { |
185 | - if (!this.modelValue && this.defaultTimezoneInfo) { | |
186 | - this.ngZone.run(() => { | |
187 | - this.selectTimezoneFormGroup.get('timezone').reset(this.defaultTimezoneInfo, {emitEvent: true}); | |
188 | - }); | |
168 | + if (!this.modelValue && this.defaultTimezoneId) { | |
169 | + getTimezoneInfo(this.defaultTimezoneId).subscribe( | |
170 | + (defaultTimezoneInfo) => { | |
171 | + if (defaultTimezoneInfo !== null) { | |
172 | + this.ngZone.run(() => { | |
173 | + this.selectTimezoneFormGroup.get('timezone').reset(defaultTimezoneInfo, {emitEvent: true}); | |
174 | + }); | |
175 | + } | |
176 | + }); | |
189 | 177 | } |
190 | 178 | } |
191 | 179 | } |
... | ... | @@ -203,12 +191,13 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af |
203 | 191 | |
204 | 192 | fetchTimezones(searchText?: string): Observable<Array<TimezoneInfo>> { |
205 | 193 | this.searchText = searchText; |
206 | - let result = this.timezones; | |
207 | 194 | if (searchText && searchText.length) { |
208 | - result = this.timezones.filter((timezoneInfo) => | |
209 | - timezoneInfo.name.toLowerCase().includes(searchText.toLowerCase())); | |
195 | + return getTimezones().pipe( | |
196 | + map((timezones) => timezones.filter((timezoneInfo) => | |
197 | + timezoneInfo.name.toLowerCase().includes(searchText.toLowerCase()))) | |
198 | + ); | |
210 | 199 | } |
211 | - return of(result); | |
200 | + return getTimezones(); | |
212 | 201 | } |
213 | 202 | |
214 | 203 | clear() { | ... | ... |
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 { Observable } from 'rxjs/internal/Observable'; | |
18 | +import { from, of } from 'rxjs'; | |
19 | +import { map, tap } from 'rxjs/operators'; | |
20 | + | |
21 | +let jsBeautifyModule: any; | |
22 | +let htmlBeautifyModule: any; | |
23 | +let cssBeautifyModule: any; | |
24 | + | |
25 | +function loadJsBeautify(): Observable<any> { | |
26 | + if (jsBeautifyModule) { | |
27 | + return of(jsBeautifyModule); | |
28 | + } else { | |
29 | + return from(import('js-beautify/js/lib/beautify.js')).pipe( | |
30 | + tap((module) => { | |
31 | + jsBeautifyModule = module; | |
32 | + }) | |
33 | + ); | |
34 | + } | |
35 | +} | |
36 | + | |
37 | +function loadHtmlBeautify(): Observable<any> { | |
38 | + if (htmlBeautifyModule) { | |
39 | + return of(htmlBeautifyModule); | |
40 | + } else { | |
41 | + return from(import('js-beautify/js/lib/beautify-html.js')).pipe( | |
42 | + tap((module) => { | |
43 | + htmlBeautifyModule = module; | |
44 | + }) | |
45 | + ); | |
46 | + } | |
47 | +} | |
48 | + | |
49 | +function loadCssBeautify(): Observable<any> { | |
50 | + if (cssBeautifyModule) { | |
51 | + return of(cssBeautifyModule); | |
52 | + } else { | |
53 | + return from(import('js-beautify/js/lib/beautify-css.js')).pipe( | |
54 | + tap((module) => { | |
55 | + cssBeautifyModule = module; | |
56 | + }) | |
57 | + ); | |
58 | + } | |
59 | +} | |
60 | + | |
61 | +export function beautifyJs(source: string, options?: JSBeautifyOptions): Observable<string> { | |
62 | + return loadJsBeautify().pipe( | |
63 | + map((mod) => { | |
64 | + return mod.js_beautify(source, options); | |
65 | + }) | |
66 | + ); | |
67 | +} | |
68 | + | |
69 | +export function beautifyCss(source: string, options?: CSSBeautifyOptions): Observable<string> { | |
70 | + return loadCssBeautify().pipe( | |
71 | + map((mod) => mod.css_beautify(source, options)) | |
72 | + ); | |
73 | +} | |
74 | + | |
75 | +export function beautifyHtml(source: string, options?: HTMLBeautifyOptions): Observable<string> { | |
76 | + return loadHtmlBeautify().pipe( | |
77 | + map((mod) => mod.html_beautify(source, options)) | |
78 | + ); | |
79 | +} | ... | ... |
... | ... | @@ -25,7 +25,7 @@ import { RuleChainId } from '@shared/models/id/rule-chain-id'; |
25 | 25 | import { EntityInfoData } from '@shared/models/entity.models'; |
26 | 26 | import { KeyFilter } from '@shared/models/query/query.models'; |
27 | 27 | import { TimeUnit } from '@shared/models/time/time.models'; |
28 | -import * as _moment from 'moment-timezone'; | |
28 | +import * as _moment from 'moment'; | |
29 | 29 | import { AbstractControl, ValidationErrors } from '@angular/forms'; |
30 | 30 | |
31 | 31 | export enum DeviceProfileType { | ... | ... |
... | ... | @@ -17,6 +17,9 @@ |
17 | 17 | import { TimeService } from '@core/services/time.service'; |
18 | 18 | import { deepClone, isDefined, isUndefined } from '@app/core/utils'; |
19 | 19 | import * as moment_ from 'moment'; |
20 | +import { Observable } from 'rxjs/internal/Observable'; | |
21 | +import { from, of } from 'rxjs'; | |
22 | +import { map, tap } from 'rxjs/operators'; | |
20 | 23 | |
21 | 24 | const moment = moment_; |
22 | 25 | |
... | ... | @@ -481,3 +484,63 @@ export const timeUnitTranslationMap = new Map<TimeUnit, string>( |
481 | 484 | [TimeUnit.DAYS, 'timeunit.days'] |
482 | 485 | ] |
483 | 486 | ); |
487 | + | |
488 | +export interface TimezoneInfo { | |
489 | + id: string; | |
490 | + name: string; | |
491 | + offset: string; | |
492 | + nOffset: number; | |
493 | +} | |
494 | + | |
495 | +let timezones: TimezoneInfo[] = null; | |
496 | +let defaultTimezone: string = null; | |
497 | + | |
498 | +export function getTimezones(): Observable<TimezoneInfo[]> { | |
499 | + if (timezones) { | |
500 | + return of(timezones); | |
501 | + } else { | |
502 | + return from(import('moment-timezone')).pipe( | |
503 | + map((monentTz) => { | |
504 | + return monentTz.tz.names().map((zoneName) => { | |
505 | + const tz = monentTz.tz(zoneName); | |
506 | + return { | |
507 | + id: zoneName, | |
508 | + name: zoneName.replace(/_/g, ' '), | |
509 | + offset: `UTC${tz.format('Z')}`, | |
510 | + nOffset: tz.utcOffset() | |
511 | + }; | |
512 | + }); | |
513 | + }), | |
514 | + tap((zones) => { | |
515 | + timezones = zones; | |
516 | + }) | |
517 | + ); | |
518 | + } | |
519 | +} | |
520 | + | |
521 | +export function getTimezoneInfo(timezoneId: string, defaultTimezoneId?: string): Observable<TimezoneInfo> { | |
522 | + return getTimezones().pipe( | |
523 | + map((timezoneList) => { | |
524 | + let foundTimezone = timezoneList.find(timezoneInfo => timezoneInfo.id === timezoneId); | |
525 | + if (!foundTimezone && defaultTimezoneId) { | |
526 | + foundTimezone = timezoneList.find(timezoneInfo => timezoneInfo.id === defaultTimezoneId); | |
527 | + } | |
528 | + return foundTimezone; | |
529 | + }) | |
530 | + ); | |
531 | +} | |
532 | + | |
533 | +export function getDefaultTimezone(): Observable<string> { | |
534 | + if (defaultTimezone) { | |
535 | + return of(defaultTimezone); | |
536 | + } else { | |
537 | + return from(import('moment-timezone')).pipe( | |
538 | + map((monentTz) => { | |
539 | + return monentTz.tz.guess(); | |
540 | + }), | |
541 | + tap((zone) => { | |
542 | + defaultTimezone = zone; | |
543 | + }) | |
544 | + ); | |
545 | + } | |
546 | +} | ... | ... |
... | ... | @@ -37,6 +37,18 @@ |
37 | 37 | ], |
38 | 38 | "ace": [ |
39 | 39 | "node_modules/ace-builds/src-noconflict/ace.js" |
40 | + ], | |
41 | + "jquery": [ | |
42 | + "node_modules/jquery/dist/jquery.min.js" | |
43 | + ], | |
44 | + "jquery.terminal": [ | |
45 | + "node_modules/jquery.terminal/js/jquery.terminal.js" | |
46 | + ], | |
47 | + "tooltipster": [ | |
48 | + "node_modules/tooltipster/dist/js/tooltipster.bundle.min.js" | |
49 | + ], | |
50 | + "jstree": [ | |
51 | + "node_modules/jstree/dist/jstree.min.js" | |
40 | 52 | ] |
41 | 53 | }, |
42 | 54 | "lib": [ | ... | ... |