2019-10-24 19:52:19 +03:00
|
|
|
///
|
2020-02-20 10:26:43 +02:00
|
|
|
/// Copyright © 2016-2020 The Thingsboard Authors
|
2019-10-24 19:52:19 +03:00
|
|
|
///
|
|
|
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
/// you may not use this file except in compliance with the License.
|
|
|
|
|
/// You may obtain a copy of the License at
|
|
|
|
|
///
|
|
|
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
///
|
|
|
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
/// See the License for the specific language governing permissions and
|
|
|
|
|
/// limitations under the License.
|
|
|
|
|
///
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
Component,
|
|
|
|
|
ElementRef,
|
|
|
|
|
EventEmitter,
|
|
|
|
|
Input,
|
|
|
|
|
OnChanges,
|
|
|
|
|
OnDestroy,
|
|
|
|
|
OnInit,
|
2020-04-21 11:53:26 +03:00
|
|
|
Output,
|
2019-10-24 19:52:19 +03:00
|
|
|
SimpleChanges,
|
2020-04-21 11:53:26 +03:00
|
|
|
ViewChild,
|
|
|
|
|
ViewEncapsulation
|
2019-10-24 19:52:19 +03:00
|
|
|
} from '@angular/core';
|
|
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
|
|
|
import { PageComponent } from '@shared/components/page.component';
|
|
|
|
|
import { Store } from '@ngrx/store';
|
|
|
|
|
import { AppState } from '@core/core.state';
|
|
|
|
|
import { CustomActionDescriptor } from '@shared/models/widget.models';
|
|
|
|
|
import * as ace from 'ace-builds';
|
|
|
|
|
import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
|
|
|
|
|
import { css_beautify, html_beautify } from 'js-beautify';
|
2020-04-22 13:21:14 +03:00
|
|
|
import { ResizeObserver } from '@juggle/resize-observer';
|
2019-10-24 19:52:19 +03:00
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'tb-custom-action-pretty-resources-tabs',
|
|
|
|
|
templateUrl: './custom-action-pretty-resources-tabs.component.html',
|
|
|
|
|
styleUrls: ['./custom-action-pretty-resources-tabs.component.scss'],
|
|
|
|
|
encapsulation: ViewEncapsulation.None
|
|
|
|
|
})
|
|
|
|
|
export class CustomActionPrettyResourcesTabsComponent extends PageComponent implements OnInit, OnChanges, OnDestroy {
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
action: CustomActionDescriptor;
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
hasCustomFunction: boolean;
|
|
|
|
|
|
|
|
|
|
@Output()
|
|
|
|
|
actionUpdated: EventEmitter<CustomActionDescriptor> = new EventEmitter<CustomActionDescriptor>();
|
|
|
|
|
|
|
|
|
|
@ViewChild('htmlInput', {static: true})
|
|
|
|
|
htmlInputElmRef: ElementRef;
|
|
|
|
|
|
|
|
|
|
@ViewChild('cssInput', {static: true})
|
|
|
|
|
cssInputElmRef: ElementRef;
|
|
|
|
|
|
|
|
|
|
htmlFullscreen = false;
|
|
|
|
|
cssFullscreen = false;
|
|
|
|
|
|
|
|
|
|
aceEditors: ace.Ace.Editor[] = [];
|
|
|
|
|
editorsResizeCafs: {[editorId: string]: CancelAnimationFrame} = {};
|
2020-04-22 14:35:27 +03:00
|
|
|
aceResize$: ResizeObserver;
|
2019-10-24 19:52:19 +03:00
|
|
|
htmlEditor: ace.Ace.Editor;
|
|
|
|
|
cssEditor: ace.Ace.Editor;
|
|
|
|
|
setValuesPending = false;
|
|
|
|
|
|
|
|
|
|
constructor(protected store: Store<AppState>,
|
|
|
|
|
private translate: TranslateService,
|
|
|
|
|
private raf: RafService) {
|
|
|
|
|
super(store);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngOnInit(): void {
|
|
|
|
|
this.initAceEditors();
|
|
|
|
|
if (this.setValuesPending) {
|
|
|
|
|
this.setAceEditorValues();
|
|
|
|
|
this.setValuesPending = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngOnDestroy(): void {
|
2020-04-22 14:35:27 +03:00
|
|
|
this.aceResize$.disconnect();
|
2019-10-24 19:52:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngOnChanges(changes: SimpleChanges): void {
|
|
|
|
|
for (const propName of Object.keys(changes)) {
|
|
|
|
|
const change = changes[propName];
|
|
|
|
|
if (propName === 'action') {
|
|
|
|
|
if (this.aceEditors.length) {
|
|
|
|
|
this.setAceEditorValues();
|
|
|
|
|
} else {
|
|
|
|
|
this.setValuesPending = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public notifyActionUpdated() {
|
|
|
|
|
this.actionUpdated.emit(this.validate() ? this.action : null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private validate(): boolean {
|
2020-02-27 17:06:32 +02:00
|
|
|
if (this.action.customResources) {
|
|
|
|
|
for (const resource of this.action.customResources) {
|
|
|
|
|
if (!resource.url) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-10-24 19:52:19 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public addResource() {
|
2020-02-27 17:06:32 +02:00
|
|
|
if (!this.action.customResources) {
|
|
|
|
|
this.action.customResources = [];
|
|
|
|
|
}
|
2019-10-24 19:52:19 +03:00
|
|
|
this.action.customResources.push({url: ''});
|
|
|
|
|
this.notifyActionUpdated();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public removeResource(index: number) {
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
if (this.action.customResources.splice(index, 1).length > 0) {
|
|
|
|
|
this.notifyActionUpdated();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public beautifyCss(): void {
|
|
|
|
|
const res = css_beautify(this.action.customCss, {indent_size: 4});
|
|
|
|
|
if (this.action.customCss !== res) {
|
|
|
|
|
this.action.customCss = res;
|
|
|
|
|
this.cssEditor.setValue(this.action.customCss ? this.action.customCss : '', -1);
|
|
|
|
|
this.notifyActionUpdated();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public beautifyHtml(): void {
|
|
|
|
|
const res = html_beautify(this.action.customHtml, {indent_size: 4, wrap_line_length: 60});
|
|
|
|
|
if (this.action.customHtml !== res) {
|
|
|
|
|
this.action.customHtml = res;
|
|
|
|
|
this.htmlEditor.setValue(this.action.customHtml ? this.action.customHtml : '', -1);
|
|
|
|
|
this.notifyActionUpdated();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private initAceEditors() {
|
2020-04-22 14:46:47 +03:00
|
|
|
this.aceResize$ = new ResizeObserver((entries) => {
|
|
|
|
|
entries.forEach((entry) => {
|
2020-04-22 14:35:27 +03:00
|
|
|
const editor = this.aceEditors.find(aceEditor => aceEditor.container === entry.target);
|
|
|
|
|
this.onAceEditorResize(editor);
|
|
|
|
|
})
|
|
|
|
|
});
|
2019-10-24 19:52:19 +03:00
|
|
|
this.htmlEditor = this.createAceEditor(this.htmlInputElmRef, 'html');
|
|
|
|
|
this.htmlEditor.on('input', () => {
|
|
|
|
|
const editorValue = this.htmlEditor.getValue();
|
|
|
|
|
if (this.action.customHtml !== editorValue) {
|
|
|
|
|
this.action.customHtml = editorValue;
|
|
|
|
|
this.notifyActionUpdated();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
this.cssEditor = this.createAceEditor(this.cssInputElmRef, 'css');
|
|
|
|
|
this.cssEditor.on('input', () => {
|
|
|
|
|
const editorValue = this.cssEditor.getValue();
|
|
|
|
|
if (this.action.customCss !== editorValue) {
|
|
|
|
|
this.action.customCss = editorValue;
|
|
|
|
|
this.notifyActionUpdated();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private createAceEditor(editorElementRef: ElementRef, mode: string): ace.Ace.Editor {
|
|
|
|
|
const editorElement = editorElementRef.nativeElement;
|
|
|
|
|
let editorOptions: Partial<ace.Ace.EditorOptions> = {
|
|
|
|
|
mode: `ace/mode/${mode}`,
|
|
|
|
|
showGutter: true,
|
|
|
|
|
showPrintMargin: true
|
|
|
|
|
};
|
|
|
|
|
const advancedOptions = {
|
|
|
|
|
enableSnippets: true,
|
|
|
|
|
enableBasicAutocompletion: true,
|
|
|
|
|
enableLiveAutocompletion: true
|
|
|
|
|
};
|
|
|
|
|
editorOptions = {...editorOptions, ...advancedOptions};
|
|
|
|
|
const aceEditor = ace.edit(editorElement, editorOptions);
|
|
|
|
|
aceEditor.session.setUseWrapMode(true);
|
|
|
|
|
this.aceEditors.push(aceEditor);
|
2020-04-22 14:35:27 +03:00
|
|
|
this.aceResize$.observe(editorElement);
|
2019-10-24 19:52:19 +03:00
|
|
|
return aceEditor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private setAceEditorValues() {
|
|
|
|
|
this.htmlEditor.setValue(this.action.customHtml ? this.action.customHtml : '', -1);
|
|
|
|
|
this.cssEditor.setValue(this.action.customCss ? this.action.customCss : '', -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private onAceEditorResize(aceEditor: ace.Ace.Editor) {
|
|
|
|
|
if (this.editorsResizeCafs[aceEditor.id]) {
|
|
|
|
|
this.editorsResizeCafs[aceEditor.id]();
|
|
|
|
|
delete this.editorsResizeCafs[aceEditor.id];
|
|
|
|
|
}
|
|
|
|
|
this.editorsResizeCafs[aceEditor.id] = this.raf.raf(() => {
|
|
|
|
|
aceEditor.resize();
|
|
|
|
|
aceEditor.renderer.updateFull();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|