@senx/warpview-editor
Version:
WarpView Editor Elements
801 lines (800 loc) • 138 kB
JavaScript
/*
* Copyright 2020 SenX S.A.S.
*
* 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.
*/
/* tslint:disable:no-string-literal */
import { editor, MarkerSeverity, Range } from 'monaco-editor';
import { Utils } from '../../model/utils';
import { Config } from '../../model/config';
import { Logger } from '../../model/logger';
import { BubblingEvents } from '../../model/bubblingEvent';
import { Component, ElementRef, EventEmitter, HostListener, Input, Output, ViewChild, ViewEncapsulation } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { of } from 'rxjs';
import { ProviderRegistrar } from './providers/ProviderRegistrar';
import { EditorUtils } from './providers/editorUtils';
import { createReviewManager } from './providers/CodeReview';
import dayjs from 'dayjs';
import { WarpScriptParser } from '../../model/warpScriptParser';
var create = editor.create;
import * as i0 from "@angular/core";
import * as i1 from "@angular/common/http";
import * as i2 from "../warp-view-result/warp-view-result";
import * as i3 from "../warp-view-raw-result/warp-view-raw-result.component";
import * as i4 from "../warp-view-image-result/warp-view-image-result";
import * as i5 from "@angular/common";
export class WarpViewEditorComponent {
constructor(el, http) {
this.el = el;
this.http = http;
this.url = '';
this._showExecute = true;
this.warpViewEditorStatusEvent = new EventEmitter();
this.warpViewEditorErrorEvent = new EventEmitter();
this.warpViewEditorWarpscriptChanged = new EventEmitter();
this.warpViewEditorWarpscriptResult = new EventEmitter();
this.warpViewEditorLoaded = new EventEmitter();
this.warpViewEditorSize = new EventEmitter();
this.warpViewEditorBreakPoint = new EventEmitter();
this.warpViewEditorCtrlClick = new EventEmitter();
this.warpViewEditorDatavizRequested = new EventEmitter();
this.warpViewEditorCodeReview = new EventEmitter();
this.loading = false;
this.selectedResultTab = -1;
this.headers = this.getItems();
this.innerConfig = new Config();
// tslint:disable-next-line:variable-name
this._theme = 'light';
// tslint:disable-next-line:variable-name
this._debug = false;
this._displayMessages = true;
this._showDataviz = false;
this._lang = 'warpscript';
this.reviewManagerConfig = {
formatDate: (createdAt) => dayjs(createdAt).format('YYYY-MM-DD HH:mm'),
};
this._showResult = true;
this._imageTab = false;
this.monacoTheme = 'vs';
this.breakpoints = {};
this.decoration = [];
this.previousParentHeight = -1;
this.previousParentWidth = -1;
this.LOG = new Logger(WarpViewEditorComponent, this._debug);
el.nativeElement.execute = this.execute.bind(this);
el.nativeElement.abort = this.abort.bind(this);
el.nativeElement.highlight = this.highlight.bind(this);
el.nativeElement.resize = this.resize.bind(this);
}
set lang(lang) {
this._lang = lang;
if (!!editor && !!this.ed) {
editor.setModelLanguage(this.ed.getModel(), this._lang);
}
}
get lang() {
return this._lang;
}
set debug(debug) {
if (typeof debug === 'string') {
debug = 'true' === debug;
}
this._debug = debug;
this.LOG.setDebug(debug);
}
get debug() {
return this._debug;
}
set theme(newValue) {
this.LOG.debug(['themeHandler'], 'The new value of theme is: ', newValue);
if ('dark' === newValue) {
this.monacoTheme = 'vs-dark';
}
else {
this.monacoTheme = 'vs';
}
this.LOG.debug(['themeHandler'], 'The new value of theme is: ', this.monacoTheme);
this._theme = newValue;
if (editor) {
editor.setTheme(this.monacoTheme);
}
}
get theme() {
return this._theme;
}
set warpscript(newValue) {
this.LOG.debug(['warpscriptHandler'], 'The new value of warpscript is: ', newValue);
if (this.ed) {
this.ed.setValue(newValue);
}
this._warpscript = newValue;
this.loading = false;
}
get warpscript() {
return this._warpscript;
}
get showDataviz() {
return this._showDataviz;
}
set showDataviz(value) {
this._showDataviz = '' + value !== 'false';
}
get showExecute() {
return this._showExecute;
}
set showExecute(value) {
this._showExecute = '' + value !== 'false';
}
get showResult() {
return this._showResult;
}
set showResult(value) {
this._showResult = '' + value !== 'false';
}
set config(config) {
let conf = (typeof config === 'string') ? JSON.parse(config || '{}') : config || {};
this.innerConfig = Utils.mergeDeep(this.innerConfig, conf);
this.LOG.debug(['config'], this.innerConfig, conf);
if (this.ed) {
this.LOG.debug(['config'], this.innerConfig);
this.ed.updateOptions(this.setOptions());
if (this.innerConfig.codeReview && !!this.innerConfig.codeReview.enabled) {
this.reviewManagerConfig.addButton = this.innerConfig.codeReview.addButton;
this.reviewManagerConfig.cancelButton = this.innerConfig.codeReview.cancelButton;
this.reviewManagerConfig.replyButton = this.innerConfig.codeReview.replyButton;
this.reviewManagerConfig.removeButton = this.innerConfig.codeReview.removeButton;
this.reviewManager.currentUser = this.innerConfig.codeReview.currentUser;
this.reviewManager.setReadOnlyMode(this.innerConfig.codeReview.readonly);
this.reviewManagerConfig.editButton = this.innerConfig.codeReview.editButton;
this.reviewManager.updateConfig(this.reviewManagerConfig);
}
}
}
get config() {
return this.innerConfig;
}
get displayMessages() {
return this._displayMessages;
}
set displayMessages(value) {
this._displayMessages = '' + value !== 'false';
}
get widthPx() {
return this._widthPx;
}
set widthPx(value) {
this._widthPx = parseInt('' + value, 10);
}
get heightLine() {
return this._heightLine;
}
set heightLine(value) {
this._heightLine = parseInt('' + value, 10);
}
get heightPx() {
return this._heightPx;
}
set heightPx(value) {
this._heightPx = parseInt('' + value, 10);
}
get imageTab() {
return this._imageTab;
}
set imageTab(value) {
this._imageTab = '' + value !== 'false';
}
get initialSize() {
return this._initialSize;
}
set initialSize(value) {
this._initialSize = typeof value === 'string' ? JSON.parse(value) : value;
}
// noinspection JSUnusedGlobalSymbols
ngOnInit() {
this.LOG.debug(['ngOnInit'], 'innerConfig: ', this.innerConfig);
if ('dark' === this._theme) {
this.monacoTheme = 'vs-dark';
}
this.LOG.debug(['ngOnInit'], 'ngOnInit theme is: ', this._theme);
self.MonacoEnvironment = {
getWorkerUrl: () => URL.createObjectURL(new Blob([`
self.MonacoEnvironment = {
baseUrl: 'https://unpkg.com/monaco-editor@0.18.1/min/'
};
importScripts('https://unpkg.com/monaco-editor@0.18.1/min/vs/base/worker/workerMain.js');
`], { type: 'text/javascript' }))
};
ProviderRegistrar.register(this.innerConfig);
}
resizeWatcher() {
const editorParentWidth = this.editor.nativeElement.parentElement.clientWidth;
const editorParentHeight = this.editor.nativeElement.parentElement.clientHeight
- parseInt(window.getComputedStyle(this.editor.nativeElement.parentElement).getPropertyValue('padding-top'), 10)
- parseInt(window.getComputedStyle(this.editor.nativeElement.parentElement).getPropertyValue('padding-bottom'), 10);
let warpviewParentHeight = this.el.nativeElement.parentElement.clientHeight
- parseInt(window.getComputedStyle(this.el.nativeElement.parentElement).getPropertyValue('padding-top'), 10)
- parseInt(window.getComputedStyle(this.el.nativeElement.parentElement).getPropertyValue('padding-bottom'), 10);
warpviewParentHeight = Math.max(warpviewParentHeight, WarpViewEditorComponent.MIN_HEIGHT);
// fix the 5px editor height in chrome by setting the wrapper height at element level
if (Math.abs(this.wrapper.nativeElement.clientHeight - warpviewParentHeight) > 30) {
this.wrapper.nativeElement.style.height = warpviewParentHeight + 'px';
}
// watch for editor parent' size change
if (editorParentHeight !== this.previousParentHeight || editorParentWidth !== this.previousParentWidth) {
this.previousParentHeight = editorParentHeight;
this.previousParentWidth = editorParentWidth;
const editorH = Math.floor(editorParentHeight) - (this.buttons ? this.buttons.nativeElement.clientHeight : 0);
const editorW = Math.floor(this.editor.nativeElement.parentElement.clientWidth);
this.ed.layout({ height: editorH, width: editorW });
this.editor.nativeElement.style.overflow = 'hidden';
}
}
setOptions() {
return {
quickSuggestionsDelay: this.innerConfig.editor.quickSuggestionsDelay,
quickSuggestions: this.innerConfig.editor.quickSuggestions,
suggestOnTriggerCharacters: this.innerConfig.editor.quickSuggestions,
// monaco auto layout is ok if parent has a fixed size, not 100% or a calc ( % px ) formula.
automaticLayout: !!this._heightPx,
hover: { enabled: this.innerConfig.hover },
readOnly: this.innerConfig.readOnly,
contextmenu: true,
// fixedOverflowWidgets: true,
folding: true,
glyphMargin: this.innerConfig.editor.enableDebug || this.innerConfig.codeReview.enabled
};
}
ngAfterViewInit() {
this.LOG.debug(['ngAfterViewInit'], 'height', this._heightPx);
if (!!this._heightPx) {
// if height-px is set, size is fixed.
this.el.nativeElement.style.height = this._heightPx + 'px';
this.wrapper.nativeElement.style.height = this._heightPx + 'px';
this.resize(true);
}
else {
// compute the layout manually in a 200ms timer
this.resizeWatcherInt = setInterval(this.resizeWatcher.bind(this), 200);
}
try {
this.innerCode = this.contentWrapper.nativeElement.textContent;
// add blank lines when needed
for (let i = this.innerCode.split('\n').length; i < this.innerConfig.editor.minLineNumber; i++) {
this.innerCode += '\n';
}
// trim spaces and line breaks at the beginning (side effect of angular)
let firstIndex = 0;
while (this.innerCode[firstIndex] === ' ' || this.innerCode[firstIndex] === '\n') {
firstIndex++;
}
this.innerCode = this.innerCode.substring(firstIndex);
this.LOG.debug(['ngAfterViewInit'], 'warpscript', this._warpscript);
this.LOG.debug(['ngAfterViewInit'], 'inner: ', this.innerCode.split('\n'));
this.LOG.debug(['ngAfterViewInit'], 'innerConfig: ', this.innerConfig);
const edOpts = this.setOptions();
this.lastKnownWS = this._warpscript || this.innerCode;
editor.setTheme(this.monacoTheme);
this.LOG.debug(['ngAfterViewInit'], 'edOpts: ', edOpts);
this.ed = create(this.editor.nativeElement, edOpts);
this.ed.setValue(this.lastKnownWS);
editor.setModelLanguage(this.ed.getModel(), this._lang);
if (this.innerConfig.editor.enableDebug) {
this.ed.onMouseDown(e => {
if (e.event.leftButton) {
if (e.target.type === 2 || e.target.type === 3 || e.target.type === 4) {
this.toggleBreakPoint(e.target.position.lineNumber);
}
}
});
}
this.ed.getModel().updateOptions({ tabSize: this.innerConfig.editor.tabSize });
if (this.ed) {
this.warpViewEditorLoaded.emit('loaded');
// angular events does not bubble up outside angular component.
BubblingEvents.emitBubblingEvent(this.el, 'warpViewEditorLoaded', 'loaded');
this.LOG.debug(['ngAfterViewInit'], 'loaded');
this.ed.getModel().onDidChangeContent((event) => {
if (this.lastKnownWS !== this.ed.getValue()) {
this.debounce(() => {
this.LOG.debug(['ngAfterViewInit'], 'ws changed', event);
this.warpViewEditorWarpscriptChanged.emit(this.ed.getValue());
BubblingEvents.emitBubblingEvent(this.el, 'warpViewEditorWarpscriptChanged', this.ed.getValue());
// this.wsAudit(this.ed.getValue());
}, 200)();
}
});
// manage the ctrl click, create an event with the statement, the endpoint, the warpfleet repos.
this.ed.onMouseDown(e => {
if (e.target.range && ((!this.isMac() && !!e.event.ctrlKey) || (this.isMac() && !!e.event.metaKey))) {
// ctrl click on which word ?
const name = (this.ed.getModel().getWordAtPosition(e.target.range?.getStartPosition()) || { word: undefined }).word;
// parse the warpscript
const ws = this.ed.getValue();
const specialHeaders = WarpScriptParser.extractSpecialComments(ws);
const repos = [];
const statements = WarpScriptParser.parseWarpScriptStatements(ws);
statements.forEach((st, i) => {
if (st === 'WF.ADDREPO' && i > 0) {
const previousStatement = statements[i - 1];
if ((previousStatement.startsWith('"') && previousStatement.endsWith('"'))
|| (previousStatement.startsWith('\'') && previousStatement.endsWith('\''))) {
// this is a valid string.
repos.push(previousStatement.substring(1, previousStatement.length - 1));
}
}
});
const docParams = {
endpoint: specialHeaders.endpoint || this.url,
macroName: name,
wfRepos: repos
};
this.warpViewEditorCtrlClick.emit(docParams);
BubblingEvents.emitBubblingEvent(this.el, 'warpViewEditorCtrlClick', docParams);
}
// this.wsAudit(this.ed.getValue());
});
if (this.innerConfig.codeReview && !!this.innerConfig.codeReview.enabled) {
this.reviewManagerConfig.addButton = this.innerConfig.codeReview.addButton;
this.reviewManagerConfig.cancelButton = this.innerConfig.codeReview.cancelButton;
this.reviewManagerConfig.replyButton = this.innerConfig.codeReview.replyButton;
this.reviewManagerConfig.removeButton = this.innerConfig.codeReview.removeButton;
this.reviewManagerConfig.editButton = this.innerConfig.codeReview.editButton;
this.reviewManager = createReviewManager(this.ed, this.innerConfig.codeReview.currentUser, this.existingComments, (updatedComments) => this.warpViewEditorCodeReview.emit(updatedComments), this.reviewManagerConfig);
this.reviewManager.setReadOnlyMode(this.innerConfig.codeReview.readonly);
}
}
}
catch (e) {
this.LOG.error(['ngAfterViewInit'], 'componentDidLoad', e);
}
}
ngOnDestroy() {
this.LOG.debug(['ngOnDestroy'], 'Component removed from the DOM');
if (this.resizeWatcherInt) {
clearInterval(this.resizeWatcherInt);
}
if (this.ed) {
this.ed.dispose();
}
if (this.request) {
this.request.unsubscribe();
}
}
abort(session) {
if (this.request) {
const specialHeaders = WarpScriptParser.extractSpecialComments(this.ed.getValue());
const executionUrl = specialHeaders.endpoint || this.url;
// BubblingEvents.emitBubblingEvent(this.el, 'warpViewEditorErrorEvent', this.error);
if (!!session) {
this.http.post(executionUrl, `<% '${session}' 'WSKILLSESSION' EVAL %> <% -1 %> <% %> TRY`, {
// @ts-ignore
observe: 'response',
// @ts-ignore
responseType: 'text',
headers: {
...this.innerConfig.httpHeaders || {},
'Accept': 'application/json',
}
})
.pipe(catchError(this.handleError(undefined)))
.subscribe((res) => {
if (!!res) {
this.LOG.debug(['abort'], 'response', res.body);
const r = JSON.parse(res.body);
if (!!r[0]) {
if (r[0] === 0) {
this.sendError('It appears that your Warp 10 is running on multiple backend', executionUrl);
}
else if (r[0] === -1) {
this.sendError(`Unable to WSABORT on ${executionUrl}. Did you activate StackPSWarpScriptExtension?`, executionUrl);
}
this.sendStatus({
endpoint: executionUrl,
message: `${WarpViewEditorComponent.getLabel(this._lang)} aborted.`,
ops: parseInt(res.headers.get('x-warp10-ops'), 10),
elapsed: parseInt(res.headers.get('x-warp10-elapsed'), 10),
fetched: parseInt(res.headers.get('x-warp10-fetched'), 10),
});
}
else {
this.sendError(`An error occurs for session: ${session}`, executionUrl);
}
}
this.request.unsubscribe();
delete this.request;
this.loading = false;
});
}
else {
this.sendStatus({
endpoint: executionUrl,
message: `${WarpViewEditorComponent.getLabel(this._lang)} aborted.`,
ops: 0,
elapsed: 0,
fetched: 0,
});
this.request.unsubscribe();
delete this.request;
this.loading = false;
}
}
}
highlight(line) {
const currentKey = 'hl-' + line;
Object.keys(this.breakpoints).forEach(k => {
if (k.startsWith('hl')) {
delete this.breakpoints[k];
}
});
this.breakpoints[currentKey] = {
range: new Range(line, 1, line, 1),
options: {
isWholeLine: true,
className: 'warpviewContentClass'
}
};
this.decoration = this.ed.deltaDecorations(this.decoration, Utils.toArray(this.breakpoints));
}
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
timeout = null;
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
;
toggleBreakPoint(line) {
const currentKey = 'bp-' + line;
if (this.breakpoints[currentKey]) {
delete this.breakpoints[currentKey];
}
else {
this.breakpoints[currentKey] = {
range: new Range(line, 1, line, 1),
options: {
isWholeLine: true,
glyphMarginClassName: 'warpviewGlyphMarginClass'
}
};
}
this.warpViewEditorBreakPoint.emit(this.breakpoints);
BubblingEvents.emitBubblingEvent(this.el, 'warpViewEditorBreakPoint', this.breakpoints);
this.decoration = this.ed.deltaDecorations(this.decoration, Utils.toArray(this.breakpoints));
}
handleError(result) {
return (error) => {
this.LOG.error(['handleError'], { e: error });
if (error.status === 0) {
this.error = `Unable to reach ${error.url}`;
}
else {
if (error.headers.get('X-Warp10-Error-Message') && error.headers.get('X-Warp10-Error-Line')) {
this.error = 'line #' + error.headers.get('X-Warp10-Error-Line') + ': ' + error.headers.get('X-Warp10-Error-Message');
}
else {
this.error = error.statusText;
}
}
this.sendError(this.error, error.url);
this.loading = false;
return of(error.error);
};
}
execute(session, bootstrap) {
if (this.ed) {
this.result = undefined;
this.status = undefined;
this.error = undefined;
let code = this.ed.getValue().replace(/ /gi, ' ');
if (EditorUtils.FLOWS_LANGUAGE === this.lang) {
code = `<'
${code}
'>
FLOWS
`;
}
this.LOG.debug(['execute'], 'this.ed.getValue()', session, code);
this.loading = true;
// parse comments to look for inline url or preview modifiers
const specialHeaders = WarpScriptParser.extractSpecialComments(code);
const previewType = specialHeaders.displayPreviewOpt ?? 'none';
if (previewType === 'I') {
this.selectedResultTab = 2; // select image tab.
}
else if (this.selectedResultTab === 2) {
this.selectedResultTab = 0; // on next execution, select results tab.
}
const executionUrl = specialHeaders.endpoint ?? this.url;
this.LOG.debug(['execute'], 'specialHeaders', this.innerConfig.addLocalHeader);
// Get Warp10 version
// @ts-ignore
let headers = {
...this.innerConfig.httpHeaders || {},
'Content-Type': 'text/plain;charset=UTF-8'
};
if (this.innerConfig.addLocalHeader) {
headers['Access-Control-Request-Private-Network'] = 'true';
}
if (!!session) {
headers['X-Warp10-WarpScriptSession'] = session;
}
this.request = this.http.post(executionUrl, (!specialHeaders.endpoint && bootstrap ? bootstrap + ' ' : '') + code, {
observe: 'response',
// @ts-ignore
responseType: 'text',
headers
})
.pipe(catchError(this.handleError(undefined)))
.subscribe((res) => {
if (!!res) {
this.LOG.debug(['execute'], 'response', res.body);
this.warpViewEditorWarpscriptResult.emit(res.body ?? res);
BubblingEvents.emitBubblingEvent(this.el, 'warpViewEditorWarpscriptResult', res.body || res);
if (!!res.headers) {
this.sendStatus({
endpoint: executionUrl,
message: `Your script execution took
${EditorUtils.formatElapsedTime(parseInt(res.headers.get('x-warp10-elapsed'), 10))}
serverside, fetched
${res.headers.get('x-warp10-fetched')} datapoints and performed
${res.headers.get('x-warp10-ops')} ${WarpViewEditorComponent.getLabel(this.lang)} operations.`,
ops: parseInt(res.headers.get('x-warp10-ops'), 10),
elapsed: parseInt(res.headers.get('x-warp10-elapsed'), 10),
fetched: parseInt(res.headers.get('x-warp10-fetched'), 10),
});
}
try {
this.LOG.debug(['execute'], 'res', res);
this.result = res.body || res;
}
catch (e) {
if (e.name && e.message && e.at && e.text) {
this.error = `${e.name}: ${e.message} at char ${e.at} => ${e.text}`;
}
else {
this.error = e.toString();
}
this.result = res.body;
this.LOG.error(['execute 1'], this.error);
this.sendError(this.error, executionUrl);
}
}
this.loading = false;
});
}
else {
this.loading = false;
this.LOG.error(['execute'], 'no active editor');
}
}
requestDataviz() {
this.warpViewEditorDatavizRequested.emit(this.result);
BubblingEvents.emitBubblingEvent(this.el, 'warpViewEditorDatavizRequested', this.result);
}
onResized($event) {
this.LOG.debug(['onResized'], $event.detail.editor);
this.warpViewEditorSize.emit($event.detail.editor);
}
isMac() {
return navigator.platform.toUpperCase().indexOf('MAC') >= 0;
}
onKeyDown($event) {
this.LOG.debug(['onKeyDown'], $event);
if ((!this.isMac() && !!$event.ctrlKey) || (this.isMac() && !!$event.metaKey)) {
Array.from(this.editor.nativeElement.getElementsByClassName('mtk8'))
.concat(Array.from(this.editor.nativeElement.getElementsByClassName('mtk22')))
.concat(Array.from(this.editor.nativeElement.getElementsByClassName('mtk23')))
.forEach(e => {
if (!e.textContent.startsWith('$')) {
e.classList.add('mouseOver');
}
});
}
}
onKeyUp($event) {
this.LOG.debug(['onKeyUp'], $event);
Array.from(this.editor.nativeElement.getElementsByClassName('mtk8'))
.concat(Array.from(this.editor.nativeElement.getElementsByClassName('mtk22')))
.concat(Array.from(this.editor.nativeElement.getElementsByClassName('mtk23')))
.forEach(e => e.classList.remove('mouseOver'));
}
resize(initial) {
window.setTimeout(() => {
if (initial && (!!this._heightPx)) {
this.editor.nativeElement.style.height = `calc(100% - ${this.buttons ?
this.buttons.nativeElement.clientHeight
: 100}px )`;
}
if (initial) {
this.warpViewEditorLoaded.emit();
BubblingEvents.emitBubblingEvent(this.el, 'warpViewEditorLoaded', 'loaded');
this.LOG.debug(['resize'], 'loaded');
}
}, initial ? 500 : 100);
}
getItems() {
const headers = [];
if (this._showResult) {
headers.push({ name: 'editor', size: this._initialSize ? this._initialSize.p || 50 : 50 });
headers.push({ name: 'result', size: this._initialSize ? 100 - this._initialSize.p || 50 : 50 });
}
else {
headers.push({ name: 'editor', size: 100 });
}
return headers;
}
responsiveStyle() {
return { height: '100%', width: '100%', overflow: 'hidden' };
}
sendError(error, executionUrl) {
this.error = error;
BubblingEvents.emitBubblingEvent(this.el, 'warpViewEditorErrorEvent', { error: this.error, endpoint: executionUrl });
this.warpViewEditorErrorEvent.emit({ error: this.error, endpoint: executionUrl });
}
sendStatus(status) {
this.status = { ...status };
BubblingEvents.emitBubblingEvent(this.el, 'warpViewEditorStatusEvent', this.status);
this.warpViewEditorStatusEvent.emit(this.status);
}
static getLabel(lang) {
switch (lang) {
case 'flows':
return 'FLoWS';
case 'warpscript':
return 'WarpScript';
default:
return 'warpscript';
}
}
wsAudit(ws) {
const specialHeaders = WarpScriptParser.extractSpecialComments(ws);
const executionUrl = specialHeaders.endpoint || this.url;
let headers = {
...this.innerConfig.httpHeaders || {},
'Content-Type': 'text/plain;charset=UTF-8'
};
if (this.innerConfig.addLocalHeader) {
headers['Access-Control-Request-Private-Network'] = 'true';
}
this.request = this.http.post(executionUrl, `<% 'WSAUDITMODE' EVAL %> <% %> <% %> TRY
<% ${ws} %>
<% 'WSAUDIT' EVAL SWAP DROP %> <% DROP [] %> <% %> TRY`, { headers })
.subscribe(res => {
if (!!res) {
const tokenizedWS = ws.split('\n').map(l => l.split(' '));
const parsed = WarpScriptParser.parseWarpScriptStatements(ws, true);
let markers = (res[0] || []).map(err => {
const l = err.line;
let j = 0;
let firstChar = tokenizedWS[l - 1][j];
let c = firstChar === '' ? 0 : 1;
while (firstChar === '') {
c += 1;
firstChar = tokenizedWS[l - 1][j++];
}
for (let i = 0; i < err.position; i++) {
c += tokenizedWS[l - 1][i].length + 1;
}
return {
startLineNumber: l,
endLineNumber: l,
startColumn: c,
endColumn: c + err.statement.length,
message: this.getMessage(err.type),
severity: MarkerSeverity.Error
};
});
editor.setModelMarkers(this.ed.getModel(), 'owner', markers);
}
});
}
getMessage(type) {
switch (type) {
case 'UNKNOWN':
return 'Unknown function';
case 'WS_EXCEPTION':
return 'WarpScript Exception';
}
return '';
}
}
WarpViewEditorComponent.MIN_HEIGHT = 250;
WarpViewEditorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: WarpViewEditorComponent, deps: [{ token: i0.ElementRef }, { token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
WarpViewEditorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: WarpViewEditorComponent, selector: "warpview-editor", inputs: { url: "url", existingComments: "existingComments", lang: "lang", debug: "debug", theme: "theme", warpscript: "warpscript", showDataviz: "showDataviz", showExecute: "showExecute", showResult: "showResult", config: "config", displayMessages: "displayMessages", widthPx: "widthPx", heightLine: "heightLine", heightPx: "heightPx", imageTab: "imageTab", initialSize: "initialSize", abort: "abort", highlight: "highlight", execute: "execute", resize: "resize" }, outputs: { warpViewEditorStatusEvent: "warpViewEditorStatusEvent", warpViewEditorErrorEvent: "warpViewEditorErrorEvent", warpViewEditorWarpscriptChanged: "warpViewEditorWarpscriptChanged", warpViewEditorWarpscriptResult: "warpViewEditorWarpscriptResult", warpViewEditorLoaded: "warpViewEditorLoaded", warpViewEditorSize: "warpViewEditorSize", warpViewEditorBreakPoint: "warpViewEditorBreakPoint", warpViewEditorCtrlClick: "warpViewEditorCtrlClick", warpViewEditorDatavizRequested: "warpViewEditorDatavizRequested", warpViewEditorCodeReview: "warpViewEditorCodeReview" }, host: { listeners: { "document:resize": "onResized($event)", "resized": "onResized($event)" } }, viewQueries: [{ propertyName: "wrapper", first: true, predicate: ["wrapper"], descendants: true, static: true }, { propertyName: "editor", first: true, predicate: ["editor"], descendants: true, static: true }, { propertyName: "buttons", first: true, predicate: ["buttons"], descendants: true, static: true }, { propertyName: "contentWrapper", first: true, predicate: ["content"], descendants: true, static: true }], ngImport: i0, template: "<!--\n ~ Copyright 2020 SenX S.A.S.\n ~\n ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ http://www.apache.org/licenses/LICENSE-2.0\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n<div [class]=\"'warp-view-editor wrapper-main ' + _theme\" #wrapper >\n <div class=\"warpscript\" #content>\n <ng-content ngProjectAs=\"input\"></ng-content>\n </div>\n <div class=\"loader\" *ngIf=\"loading\">\n <div class=\"spinner\"></div>\n </div>\n <!--suppress AngularUndefinedBinding -->\n <wc-split [items]=\"getItems()\" style=\"height: 100%\" min-height=\"250\">\n <div slot=\"editor\" class=\"editor-wrapper\" style=\"height: 100%; position: relative\">\n <div #editor (keydown)=\"onKeyDown($event)\" (keyup)=\"onKeyUp($event)\"></div>\n <div [class]=\"'warpview-buttons ' + innerConfig.buttons.class\" #buttons>\n <button type='button' [class]=\"innerConfig.datavizButton.class\"\n *ngIf=\"showDataviz && result\"\n (click)=\"requestDataviz()\" [innerHTML]=\"innerConfig.datavizButton.label\">\n </button>\n <button type='button' [class]=\"innerConfig.execButton.class\"\n *ngIf=\"showExecute\"\n (click)=\"execute()\" [innerHTML]=\"innerConfig.execButton.label\"></button>\n <div class='messages' *ngIf=\"error || result || status\">\n <div *ngIf=\"status && _displayMessages\" [attr.class]=\"innerConfig.messageClass\" [innerHTML]=\"status.message\"></div>\n <div *ngIf=\"error && _displayMessages\" [attr.class]=\"innerConfig.errorClass\" [innerHTML]=\"error\"></div>\n </div>\n </div>\n </div>\n <div slot=\"result\" *ngIf=\"showResult\" style=\"height: 100%\">\n <!--suppress AngularUndefinedBinding -->\n <wc-tabs class='wctabs' [selection]=\"selectedResultTab\" style=\"height: 100%\">\n <wc-tabs-header slot='header' name='tab1'>Results</wc-tabs-header>\n <wc-tabs-header slot='header' name='tab2'>Raw JSON</wc-tabs-header>\n\n <wc-tabs-header slot='header' name='tab3' *ngIf=\"imageTab\">Images</wc-tabs-header>\n\n <wc-tabs-content slot='content' name='tab1'>\n <div class=\"tab-wrapper\">\n <warpview-result [theme]=\"theme\" [result]=\"result\" [config]='innerConfig'></warpview-result>\n </div>\n </wc-tabs-content>\n\n <!--suppress AngularUndefinedBinding -->\n <wc-tabs-content slot='content' name='tab2' [responsive]=\"true\">\n <div class=\"tab-wrapper\" [ngStyle]=\"responsiveStyle()\">\n <warpview-raw-result [theme]=\"theme\" [result]=\"result\" [config]=\"innerConfig\" [debug]=\"debug\"></warpview-raw-result>\n </div>\n </wc-tabs-content>\n\n <wc-tabs-content slot='content' name='tab3' *ngIf=\"imageTab\">\n <div class=\"tab-wrapper\">\n <warpview-image-result [theme]=\"theme\" [result]=\"result\" [config]=\"innerConfig\" [debug]=\"_debug\"></warpview-image-result>\n </div>\n </wc-tabs-content>\n\n </wc-tabs>\n </div>\n </wc-split>\n</div>\n", styles: ["/*!\n * Copyright 2020 SenX S.A.S.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */:host{height:100%;width:100%}:host .warpview-buttons{display:flex;align-items:flex-start}:host>div{width:100%;height:100%}:host .warpscript{display:none}:host .editor-wrapper{overflow:visible;height:100%}:host .editor-wrapper ::ng-deep .monaco-hover{z-index:9999}:host .editor-wrapper>div{overflow:visible!important}:host .wctabs{height:100%}:host .editor{position:absolute;left:0;top:0;width:100%;height:100%;max-height:100%!important;margin:0;padding:0}:host div[slot]{height:100%}:host .tab-wrapper{height:100%}:host .messages{width:100%;padding:5px}:host .messages>*{margin:0;padding:5px;font-size:12px}:host .warp-view-editor.wrapper-main{width:calc(100% - 20px);position:relative;inset:0;margin:10px}:host .warp-view-editor.wrapper-main .loader{background-color:#0000004d;position:absolute;z-index:1;inset:0}:host .warp-view-editor.wrapper-main .loader .spinner{animation:spin 1s linear infinite;border-color:var(--warp-view-spinner-color, #5899DA) transparent transparent transparent;border-style:solid;border-radius:50%;width:50px;height:50px;position:absolute;overflow:visible;z-index:999;margin:auto;top:calc(50% - 25px);left:calc(50% - 25px);right:calc(50% - 25px);bottom:calc(50% - 25px)}:host .warp-view-editor.wrapper-main.dark{background-color:#1e1e1e;color:#f8f9fa;--wc-tab-header-border-color: #404040;--wc-tab-header-selected-border-color: #404040;--wc-tab-header-bg-color: transparent;--wc-tab-header-color: #f8f9fa;--wc-tab-header-selected-color: #1e1e1e;--wc-tab-header-selected-bg-color: #f8f9fa;--wc-tab-header-disabled-color: rgba(255, 255, 255, .5);--wc-tab-header-disabled-bg-color: rgba(0, 0, 0, .5);--wc-split-gutter-color: #404040;--warp-view-spinner-color: #f3f3f3;--warp-view-image-border-color: #a0a0a0}:host .warp-view-editor.wrapper-main.light{background-color:#fff!important;color:#000}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}:host wc-tabs{height:100%}\n"], components: [{ type: i2.WarpViewResult, selector: "warpview-result", inputs: ["theme", "config", "loading", "result"] }, { type: i3.WarpViewRawResultComponent, selector: "warpview-raw-result", inputs: ["debug", "theme", "result", "config", "heightLine", "heightPx"] }, { type: i4.WarpViewImageResult, selector: "warpview-image-result", inputs: ["debug", "result", "theme", "config"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: WarpViewEditorComponent, decorators: [{
type: Component,
args: [{ selector: 'warpview-editor', encapsulation: ViewEncapsulation.Emulated, template: "<!--\n ~ Copyright 2020 SenX S.A.S.\n ~\n ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n ~ you may not use this file except in compliance with the License.\n ~ You may obtain a copy of the License at\n ~\n ~ http://www.apache.org/licenses/LICENSE-2.0\n ~\n ~ Unless required by applicable law or agreed to in writing, software\n ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ~ See the License for the specific language governing permissions and\n ~ limitations under the License.\n -->\n<div [class]=\"'warp-view-editor wrapper-main ' + _theme\" #wrapper >\n <div class=\"warpscript\" #content>\n <ng-content ngProjectAs=\"input\"></ng-content>\n </div>\n <div class=\"loader\" *ngIf=\"loading\">\n <div class=\"spinner\"></div>\n </div>\n <!--suppress AngularUndefinedBinding -->\n <wc-split [items]=\"getItems()\" style=\"height: 100%\" min-height=\"250\">\n <div slot=\"editor\" class=\"editor-wrapper\" style=\"height: 100%; position: relative\">\n <div #editor (keydown)=\"onKeyDown($event)\" (keyup)=\"onKeyUp($event)\"></div>\n <div [class]=\"'warpview-buttons ' + innerConfig.buttons.class\" #buttons>\n <button type='button' [class]=\"innerConfig.datavizButton.class\"\n *ngIf=\"showDataviz && result\"\n (click)=\"requestDataviz()\" [innerHTML]=\"innerConfig.datavizButton.label\">\n </button>\n <button type='button' [class]=\"innerConfig.execButton.class\"\n *ngIf=\"showExecute\"\n (click)=\"execute()\" [innerHTML]=\"innerConfig.execButton.label\"></button>\n <div class='messages' *ngIf=\"error || result || status\">\n <div *ngIf=\"status && _displayMessages\" [attr.class]=\"innerConfig.messageClass\" [innerHTML]=\"status.message\"></div>\n <div *ngIf=\"error && _displayMessages\" [attr.class]=\"innerConfig.errorClass\" [innerHTML]=\"error\"></div>\n </div>\n </div>\n </div>\n <div slot=\"result\" *ngIf=\"showResult\" style=\"height: 100%\">\n <!--suppress AngularUndefinedBinding -->\n <wc-tabs class='wctabs' [selection]=\"selectedResultTab\" style=\"height: 100%\">\n <wc-tabs-header slot='header' name='tab1'>Results</wc-tabs-header>\n <wc-tabs-header slot='header' name='tab2'>Raw JSON</wc-tabs-header>\n\n <wc-tabs-header slot='header' name='tab3' *ngIf=\"imageTab\">Images</wc-tabs-header>\n\n <wc-tabs-content slot='content' name='tab1'>\n <div class=\"tab-wrapper\">\n <warpview-result [theme]=\"theme\" [result]=\"result\" [config]='innerConfig'></warpview-result>\n </div>\n </wc-tabs-content>\n\n <!--suppress AngularUndefinedBinding -->\n <wc-tabs-content slot='content' name='tab2' [responsive]=\"true\">\n <div class=\"tab-wrapper\" [ngStyle]=\"responsiveStyle()\">\n <warpview-raw-result [theme]=\"theme\" [result]=\"result\" [config]=\"innerConfig\" [debug]=\"debug\"></warpview-raw-result>\n </div>\n </wc-tabs-content>\n\n <wc-tabs-content slot='content' name='tab3' *ngIf=\"imageTab\">\n <div class=\"tab-wrapper\">\n <warpview-image-result [theme]=\"theme\" [result]=\"result\" [config]=\"innerConfig\" [debug]=\"_debug\"></warpview-image-result>\n </div>\n </wc-tabs-content>\n\n </wc-tabs>\n </div>\n </wc-split>\n</div>\n", styles: ["/*!\n * Copyright 2020 SenX S.A.S.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */:host{height:100%;width:100%}:host .warpview-buttons{display:flex;align-items:flex-start}:host>div{width:100%;height:100%}:host .warpscript{display:none}:host .editor-wrapper{overflow:visible;height:100%}:host .editor-wrapper ::ng-deep .monaco-hover{z-index:9999}:host .editor-wrapper>div{overflow:visible!important}:host .wctabs{height:100%}:host .editor{position:absolute;left:0;top:0;width:100%;height:100%;max-height:100%!important;margin:0;padding:0}:host div[slot]{height:100%}:host .tab-wrapper{height:100%}:host .messages{width:100%;padding:5px}:host .messages>*{margin:0;padding:5px;font-size:12px}:host .warp-view-editor.wrapper-main{width:calc(100% - 20px);position:relative;inset:0;margin:10px}:host .warp-view-editor.wrapper-main .loader{background-color:#0000004d;position:absolute;z-index:1;inset:0}:host .warp-view-editor.wrapper-main .loader .spinner{animation:spin 1s linear infinite;border-color:var(--warp-view-spinner-color, #5899DA) transparent transparent transparent;border-style:solid;border-radius:50%;width:50px;height:50px;position:absolute;overflow:visible;z-index:999;margin:auto;top:calc(50% - 25px);left:calc(50% - 25px);right:calc(50% - 25px);bottom:calc(50% - 25px)}:host .warp-view-editor.wrapper-main.dark{background-color:#1e1e1e;color:#f8f9fa;--wc-tab-header-border-color: #404040;--wc-tab-header-selected-border-color: #404040;--wc-tab-header-bg-color: transparent;--wc-tab-header-color: #f8f9fa;--wc-tab-header-selected-color: #1e1e1e;--wc-tab-header-selected-bg-color: #f8f9fa;--wc-tab-header-disabled-color: rgba(255, 255, 255, .5);--wc-tab-header-disabled-bg-color: rgba(0, 0, 0, .5);--wc-split-gutter-color: #404040;--warp-view-spinner-color: #f3f3f3;--warp-view-image-border-color: #a0a0a0}:host .warp-view-editor.wrapper-main.light{background-color:#fff!important;color:#000}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}:host wc-tabs{height:100%}\n"] }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.HttpClient }]; }, propDecorators: { url: [{
type: Input
}], existingComments: [{
type: Input
}], lang: [{
type: Input
}], debug: [{
type: Input
}], theme: [{
type: Input
}], warpscript: [{
type: Input,
args: ['warpscript']
}], showDataviz: [{
type: Input,
args: ['showDataviz']
}], showExecute: [{
type: Input,
args: ['showExecute']
}], showResult: [{
type: Input,
args: ['showResult']
}], config: [{
type: Input,
args: ['config']
}], displayMessages: [{
type: Input,
args: ['displayMessages']
}], widthPx: [{
type: Input,
args: ['widthPx']
}], heightLine: [{
type: Input,
args: ['heightLine']
}], heightPx: [{
type: Input,
args: ['heightPx']
}], imageTab: [{
type: Input,
args: ['imageTab']
}], initialSize: [{
type: Input,
args: ['initialSize']
}], warpViewEditorStatusEvent: [{
type: Output,
args: ['warpViewEditorStatusEvent']
}], warpViewEditorErrorEvent: [{
type: Output,
args: ['warpViewEditorErrorEvent']
}], warpViewEditorWarpscriptChanged: [{
type: Output,
args: ['warpViewEditorWarpscriptChanged']
}], warpViewEditorWarpscriptResult: [{
type: Output,
args: ['warpViewEditorWarpscriptResult']
}], warpViewEditorLoaded: [{
type: Output,
args: ['warpViewEditorLoaded']
}], warpViewEditorSize: [{
type: Output,
args: ['warpViewEditorSize']
}], warpViewEditorBreakPoint: [{
type: Output,
args: ['warpViewEditorBreakPoint']
}], warpViewEditorCtrlClick: [{
type: Output,
args: ['warpViewEditorCtrlClick']
}], warpViewEditorDatavizRequested: [{
type: Output,
args: ['warpViewEditorDatavizRequested']
}], warpViewEditorCodeReview: [{
type: Output,
args: ['warpViewEditorCodeReview']
}], wrapper: [{
type: ViewChild,
args: ['wrapper', { static: true }]
}], editor: [{
type: ViewChild,