xterm
[VSoRC/.git] / node_modules / xterm / src / browser / services / CharSizeService.ts
diff --git a/node_modules/xterm/src/browser/services/CharSizeService.ts b/node_modules/xterm/src/browser/services/CharSizeService.ts
new file mode 100644 (file)
index 0000000..0749a42
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2016 The xterm.js authors. All rights reserved.
+ * @license MIT
+ */
+
+import { IOptionsService } from 'common/services/Services';
+import { IEvent, EventEmitter } from 'common/EventEmitter';
+import { ICharSizeService } from 'browser/services/Services';
+
+export class CharSizeService implements ICharSizeService {
+  serviceBrand: any;
+
+  public width: number = 0;
+  public height: number = 0;
+  private _measureStrategy: IMeasureStrategy;
+
+  public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; }
+
+  private _onCharSizeChange = new EventEmitter<void>();
+  public get onCharSizeChange(): IEvent<void> { return this._onCharSizeChange.event; }
+
+  constructor(
+    readonly document: Document,
+    readonly parentElement: HTMLElement,
+    @IOptionsService private readonly _optionsService: IOptionsService
+  ) {
+    this._measureStrategy = new DomMeasureStrategy(document, parentElement, this._optionsService);
+  }
+
+  public measure(): void {
+    const result = this._measureStrategy.measure();
+    if (result.width !== this.width || result.height !== this.height) {
+      this.width = result.width;
+      this.height = result.height;
+      this._onCharSizeChange.fire();
+    }
+  }
+}
+
+interface IMeasureStrategy {
+  measure(): IReadonlyMeasureResult;
+}
+
+interface IReadonlyMeasureResult {
+  readonly width: number;
+  readonly height: number;
+}
+
+interface IMeasureResult {
+  width: number;
+  height: number;
+}
+
+// TODO: For supporting browsers we should also provide a CanvasCharDimensionsProvider that uses ctx.measureText
+class DomMeasureStrategy implements IMeasureStrategy {
+  private _result: IMeasureResult = { width: 0, height: 0 };
+  private _measureElement: HTMLElement;
+
+  constructor(
+    private _document: Document,
+    private _parentElement: HTMLElement,
+    private _optionsService: IOptionsService
+  ) {
+    this._measureElement = this._document.createElement('span');
+    this._measureElement.classList.add('xterm-char-measure-element');
+    this._measureElement.textContent = 'W';
+    this._measureElement.setAttribute('aria-hidden', 'true');
+    this._parentElement.appendChild(this._measureElement);
+  }
+
+  public measure(): IReadonlyMeasureResult {
+    this._measureElement.style.fontFamily = this._optionsService.options.fontFamily;
+    this._measureElement.style.fontSize = `${this._optionsService.options.fontSize}px`;
+
+    // Note that this triggers a synchronous layout
+    const geometry = this._measureElement.getBoundingClientRect();
+
+    // If values are 0 then the element is likely currently display:none, in which case we should
+    // retain the previous value.
+    if (geometry.width !== 0 && geometry.height !== 0) {
+      this._result.width = geometry.width;
+      this._result.height = Math.ceil(geometry.height);
+    }
+
+    return this._result;
+  }
+}