diff --git a/src/renderer/ColorManager.test.ts b/src/renderer/ColorManager.test.ts index 22407604..2dc60408 100644 --- a/src/renderer/ColorManager.test.ts +++ b/src/renderer/ColorManager.test.ts @@ -3,14 +3,21 @@ * @license MIT */ +import jsdom = require('jsdom'); import { assert } from 'chai'; import { ColorManager } from './ColorManager'; describe('ColorManager', () => { let cm: ColorManager; + let dom: jsdom.JSDOM; + let document: Document; + let window: Window; beforeEach(() => { - cm = new ColorManager(); + dom = new jsdom.JSDOM(''); + window = dom.window; + document = window.document; + cm = new ColorManager(document); }); describe('constructor', () => { diff --git a/src/renderer/ColorManager.ts b/src/renderer/ColorManager.ts index 90ef0431..ddb928a5 100644 --- a/src/renderer/ColorManager.ts +++ b/src/renderer/ColorManager.ts @@ -67,8 +67,10 @@ function toPaddedHex(c: number): string { */ export class ColorManager implements IColorManager { public colors: IColorSet; + private _document: Document; - constructor() { + constructor(document: Document) { + this._document = document; this.colors = { foreground: DEFAULT_FOREGROUND, background: DEFAULT_BACKGROUND, @@ -85,26 +87,54 @@ export class ColorManager implements IColorManager { * colors will be used where colors are not defined. */ public setTheme(theme: ITheme): void { - this.colors.foreground = theme.foreground || DEFAULT_FOREGROUND; - this.colors.background = theme.background || DEFAULT_BACKGROUND; - this.colors.cursor = theme.cursor || DEFAULT_CURSOR; - this.colors.cursorAccent = theme.cursorAccent || DEFAULT_CURSOR_ACCENT; - this.colors.selection = theme.selection || DEFAULT_SELECTION; - this.colors.ansi[0] = theme.black || DEFAULT_ANSI_COLORS[0]; - this.colors.ansi[1] = theme.red || DEFAULT_ANSI_COLORS[1]; - this.colors.ansi[2] = theme.green || DEFAULT_ANSI_COLORS[2]; - this.colors.ansi[3] = theme.yellow || DEFAULT_ANSI_COLORS[3]; - this.colors.ansi[4] = theme.blue || DEFAULT_ANSI_COLORS[4]; - this.colors.ansi[5] = theme.magenta || DEFAULT_ANSI_COLORS[5]; - this.colors.ansi[6] = theme.cyan || DEFAULT_ANSI_COLORS[6]; - this.colors.ansi[7] = theme.white || DEFAULT_ANSI_COLORS[7]; - this.colors.ansi[8] = theme.brightBlack || DEFAULT_ANSI_COLORS[8]; - this.colors.ansi[9] = theme.brightRed || DEFAULT_ANSI_COLORS[9]; - this.colors.ansi[10] = theme.brightGreen || DEFAULT_ANSI_COLORS[10]; - this.colors.ansi[11] = theme.brightYellow || DEFAULT_ANSI_COLORS[11]; - this.colors.ansi[12] = theme.brightBlue || DEFAULT_ANSI_COLORS[12]; - this.colors.ansi[13] = theme.brightMagenta || DEFAULT_ANSI_COLORS[13]; - this.colors.ansi[14] = theme.brightCyan || DEFAULT_ANSI_COLORS[14]; - this.colors.ansi[15] = theme.brightWhite || DEFAULT_ANSI_COLORS[15]; + this.colors.foreground = this._validateColor(theme.foreground, DEFAULT_FOREGROUND); + this.colors.background = this._validateColor(theme.background, DEFAULT_BACKGROUND); + this.colors.cursor = this._validateColor(theme.cursor, DEFAULT_CURSOR); + this.colors.cursorAccent = this._validateColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT); + this.colors.selection = this._validateColor(theme.selection, DEFAULT_SELECTION); + this.colors.ansi[0] = this._validateColor(theme.black, DEFAULT_ANSI_COLORS[0]); + this.colors.ansi[1] = this._validateColor(theme.red, DEFAULT_ANSI_COLORS[1]); + this.colors.ansi[2] = this._validateColor(theme.green, DEFAULT_ANSI_COLORS[2]); + this.colors.ansi[3] = this._validateColor(theme.yellow, DEFAULT_ANSI_COLORS[3]); + this.colors.ansi[4] = this._validateColor(theme.blue, DEFAULT_ANSI_COLORS[4]); + this.colors.ansi[5] = this._validateColor(theme.magenta, DEFAULT_ANSI_COLORS[5]); + this.colors.ansi[6] = this._validateColor(theme.cyan, DEFAULT_ANSI_COLORS[6]); + this.colors.ansi[7] = this._validateColor(theme.white, DEFAULT_ANSI_COLORS[7]); + this.colors.ansi[8] = this._validateColor(theme.brightBlack, DEFAULT_ANSI_COLORS[8]); + this.colors.ansi[9] = this._validateColor(theme.brightRed, DEFAULT_ANSI_COLORS[9]); + this.colors.ansi[10] = this._validateColor(theme.brightGreen, DEFAULT_ANSI_COLORS[10]); + this.colors.ansi[11] = this._validateColor(theme.brightYellow, DEFAULT_ANSI_COLORS[11]); + this.colors.ansi[12] = this._validateColor(theme.brightBlue, DEFAULT_ANSI_COLORS[12]); + this.colors.ansi[13] = this._validateColor(theme.brightMagenta, DEFAULT_ANSI_COLORS[13]); + this.colors.ansi[14] = this._validateColor(theme.brightCyan, DEFAULT_ANSI_COLORS[14]); + this.colors.ansi[15] = this._validateColor(theme.brightWhite, DEFAULT_ANSI_COLORS[15]); + } + + private _validateColor(color: string, fallback: string): string { + if (!color) { + return fallback; + } + + const isColorValid = this._isColorValid(color); + + if (!isColorValid) { + console.warn(`Color: ${color} is invalid using fallback ${fallback}`); + } + + return isColorValid ? color : fallback; + } + + private _isColorValid(color: string): boolean { + const litmus = 'red'; + const d = this._document.createElement('div'); + d.style.color = litmus; + d.style.color = color; + + // Element's style.color will be reverted to litmus or set to '' if an invalid color is given + if (color !== litmus && (d.style.color === litmus || d.style.color === '')) { + return false; + } + + return true; } } diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index fa1e34e6..fd2b56e4 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -31,7 +31,7 @@ export class Renderer extends EventEmitter implements IRenderer { constructor(private _terminal: ITerminal, theme: ITheme) { super(); - this.colorManager = new ColorManager(); + this.colorManager = new ColorManager(document); if (theme) { this.colorManager.setTheme(theme); }