UNPKG

@senx/warpview-editor

Version:

WarpView Editor Elements

801 lines (800 loc) 138 kB
/* * 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,