Added target and terminal support in Dashboard
[src/xds/xds-agent.git] / webapp / src / app / pages / targets / terminals / terminal.component.ts
diff --git a/webapp/src/app/pages/targets/terminals/terminal.component.ts b/webapp/src/app/pages/targets/terminals/terminal.component.ts
new file mode 100644 (file)
index 0000000..0478a08
--- /dev/null
@@ -0,0 +1,135 @@
+/**
+* @license
+* Copyright (C) 2018 "IoT.bzh"
+* Author Sebastien Douheret <sebastien@iot.bzh>
+*
+* 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, ViewChild, Input, Output, HostListener, EventEmitter, AfterViewInit } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+
+import { Terminal } from 'xterm';
+import * as fit from 'xterm/lib/addons/fit/fit';
+
+export interface ITerminalFont {
+  fontFamily: string;
+  fontSize: string;
+  lineHeight: number;
+  charWidth: number;
+  charHeight: number;
+}
+
+
+@Component({
+  selector: 'xds-terminal',
+  styles: [],
+  template: `
+    <div #terminalContainer></div>
+  `,
+})
+export class TerminalComponent implements AfterViewInit {
+
+  private _xterm: Terminal;
+  private _initDone: boolean;
+
+  @ViewChild('terminalContainer') termContainer: ElementRef;
+
+  @Output() stdin = new EventEmitter<any>();
+  @Output() resize = new EventEmitter<{ cols: number, rows: number }>();
+
+
+  constructor() {
+    this._initDone = false;
+    Terminal.applyAddon(fit);
+
+    this._xterm = new Terminal({
+      cursorBlink: true,
+      // useStyle: true,
+      scrollback: 1000,
+      rows: 24,
+      cols: 80,
+    });
+  }
+
+  // getting the nativeElement only possible after view init
+  ngAfterViewInit() {
+
+    // this now finds the #terminal element
+    this._xterm.open(this.termContainer.nativeElement);
+
+    // the number of rows will determine the size of the terminal screen
+    (<any>this._xterm).fit();
+
+    // Bind input key
+    this._xterm.on('data', (data) => {
+      // console.log(data.charCodeAt(0));
+      this.stdin.emit(this._sanitizeInput(data));
+      return false;
+    });
+
+    this._initDone = true;
+  }
+
+  @Input('stdout')
+  set writeData(data) {
+    if (this._initDone && data !== undefined) {
+      this._xterm.write(data);
+    }
+  }
+
+  @Input('disable')
+  set disable(value: boolean) {
+    if (!this._initDone) {
+      return;
+    }
+
+    this._xterm.setOption('disableStdin', value);
+
+    if (value) {
+      this._xterm.blur();
+    } else {
+      this._xterm.focus();
+    }
+    this._resize();
+  }
+
+  @HostListener('window:resize', ['$event'])
+  onWindowResize(event) {
+    this._resize();
+  }
+
+  /*** Private functions ***/
+
+  private _sanitizeInput(d) {
+    // TODO sanitize ?
+    return d;
+  }
+
+  private _resize() {
+    const geom = fit.proposeGeometry(this._xterm);
+
+    // console.log('DEBUG  cols ' + String(geom.cols) + ' rows ' + String(geom.rows));
+
+    if (geom.cols < 0 || geom.cols > 2000 || geom.rows < 0 || geom.rows > 2000) {
+      return;
+    }
+
+    // Update xterm size
+    this._xterm.resize(geom.cols, geom.rows);
+
+    // Send resize event to update remote terminal
+    this.resize.emit({ cols: geom.cols, rows: geom.rows });
+  }
+
+}