mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into updated_wcwidth
This commit is contained in:
@@ -6,13 +6,14 @@
|
||||
import { createProgram, PROJECTION_MATRIX, throwIfFalsy } from './WebglUtils';
|
||||
import { WebglCharAtlas } from './atlas/WebglCharAtlas';
|
||||
import { IWebGL2RenderingContext, IWebGLVertexArrayObject, IRenderModel, IRasterizedGlyph } from './Types';
|
||||
import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_INDICIES_PER_CELL, RENDER_MODEL_FG_OFFSET } from './RenderModel';
|
||||
import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_INDICIES_PER_CELL, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_BG_OFFSET } from './RenderModel';
|
||||
import { fill } from 'common/TypedArrayUtils';
|
||||
import { slice } from './TypedArray';
|
||||
import { NULL_CELL_CODE, WHITESPACE_CELL_CODE, Attributes } from 'common/buffer/Constants';
|
||||
import { NULL_CELL_CODE, WHITESPACE_CELL_CODE, Attributes, FgFlags } from 'common/buffer/Constants';
|
||||
import { Terminal, IBufferLine } from 'xterm';
|
||||
import { IColorSet } from 'browser/Types';
|
||||
import { IColorSet, IColor } from 'browser/Types';
|
||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
|
||||
interface IVertices {
|
||||
attributes: Float32Array;
|
||||
@@ -254,18 +255,49 @@ export class GlyphRenderer {
|
||||
for (let x = startCol; x < endCol; x++) {
|
||||
const offset = (y * this._terminal.cols + x) * RENDER_MODEL_INDICIES_PER_CELL;
|
||||
const code = model.cells[offset];
|
||||
let fg = model.cells[offset + RENDER_MODEL_FG_OFFSET];
|
||||
if (fg & FgFlags.INVERSE) {
|
||||
const workCell = new AttributeData();
|
||||
workCell.fg = fg;
|
||||
workCell.bg = model.cells[offset + RENDER_MODEL_BG_OFFSET];
|
||||
// Get attributes from fg (excluding inverse) and resolve inverse by pullibng rgb colors
|
||||
// from bg. This is needed since the inverse fg color should be based on the original bg
|
||||
// color, not on the selection color
|
||||
fg = (fg & ~(Attributes.CM_MASK | Attributes.RGB_MASK | FgFlags.INVERSE));
|
||||
switch (workCell.getBgColorMode()) {
|
||||
case Attributes.CM_P16:
|
||||
case Attributes.CM_P256:
|
||||
const c = this._getColorFromAnsiIndex(workCell.getBgColor()).rgba;
|
||||
fg |= (c >> 8) & Attributes.RED_MASK | (c >> 8) & Attributes.GREEN_MASK | (c >> 8) & Attributes.BLUE_MASK;
|
||||
case Attributes.CM_RGB:
|
||||
const arr = AttributeData.toColorRGB(workCell.getBgColor());
|
||||
fg |= arr[0] << Attributes.RED_SHIFT | arr[1] << Attributes.GREEN_SHIFT | arr[2] << Attributes.BLUE_SHIFT;
|
||||
case Attributes.CM_DEFAULT:
|
||||
default:
|
||||
const c2 = this._colors.background.rgba;
|
||||
fg |= (c2 >> 8) & Attributes.RED_MASK | (c2 >> 8) & Attributes.GREEN_MASK | (c2 >> 8) & Attributes.BLUE_MASK;
|
||||
}
|
||||
fg |= Attributes.CM_RGB;
|
||||
}
|
||||
if (code & COMBINED_CHAR_BIT_MASK) {
|
||||
if (!line) {
|
||||
line = terminal.buffer.getLine(row);
|
||||
}
|
||||
const chars = line!.getCell(x)!.char;
|
||||
this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, model.cells[offset + RENDER_MODEL_FG_OFFSET], chars);
|
||||
this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, fg, chars);
|
||||
} else {
|
||||
this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, model.cells[offset + RENDER_MODEL_FG_OFFSET]);
|
||||
this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, fg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _getColorFromAnsiIndex(idx: number): IColor {
|
||||
if (idx >= this._colors.ansi.length) {
|
||||
throw new Error('No color found for idx ' + idx);
|
||||
}
|
||||
return this._colors.ansi[idx];
|
||||
}
|
||||
|
||||
public onResize(): void {
|
||||
const terminal = this._terminal;
|
||||
const gl = this._gl;
|
||||
|
||||
@@ -825,6 +825,30 @@ describe('WebGL Renderer Integration Tests', function(): void {
|
||||
});
|
||||
});
|
||||
|
||||
describe('selection', async () => {
|
||||
before(async () => setupBrowser());
|
||||
after(async () => browser.close());
|
||||
beforeEach(async () => page.evaluate(`window.term.reset()`));
|
||||
|
||||
it('should resolve the inverse foreground color based on the original background color, not the selection', async () => {
|
||||
const theme: ITheme = {
|
||||
foreground: '#FF0000',
|
||||
background: '#00FF00',
|
||||
selection: '#0000FF'
|
||||
};
|
||||
await page.evaluate(`window.term.setOption('theme', ${JSON.stringify(theme)});`);
|
||||
await writeSync(` █\\x1b[7m█\\x1b[0m`);
|
||||
await pollFor(page, () => getCellColor(1, 1), [0, 255, 0, 255]);
|
||||
await pollFor(page, () => getCellColor(2, 1), [255, 0, 0, 255]);
|
||||
await pollFor(page, () => getCellColor(3, 1), [0, 255, 0, 255]);
|
||||
await page.evaluate(`window.term.selectAll()`);
|
||||
// Selection only cell needs to be first to ensure renderer has kicked in
|
||||
await pollFor(page, () => getCellColor(1, 1), [0, 0, 255, 255]);
|
||||
await pollFor(page, () => getCellColor(2, 1), [255, 0, 0, 255]);
|
||||
await pollFor(page, () => getCellColor(3, 1), [0, 255, 0, 255]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('allowTransparency', async () => {
|
||||
before(async () => setupBrowser({ rendererType: 'dom', allowTransparency: true}));
|
||||
after(async () => browser.close());
|
||||
|
||||
@@ -11,7 +11,7 @@ import { throwIfFalsy } from '../WebglUtils';
|
||||
import { IColor } from 'browser/Types';
|
||||
import { IDisposable } from 'xterm';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { toCss, ensureContrastRatioRgba } from 'browser/Color';
|
||||
import { channels, rgba } from 'browser/Color';
|
||||
|
||||
// In practice we're probably never going to exhaust a texture this large. For debugging purposes,
|
||||
// however, it can be useful to set this to a really tiny value, to verify that LRU eviction works.
|
||||
@@ -221,7 +221,7 @@ export class WebglCharAtlas implements IDisposable {
|
||||
return this._getColorFromAnsiIndex(fgColor).css;
|
||||
case Attributes.CM_RGB:
|
||||
const arr = AttributeData.toColorRGB(fgColor);
|
||||
return toCss(arr[0], arr[1], arr[2]);
|
||||
return channels.toCss(arr[0], arr[1], arr[2]);
|
||||
case Attributes.CM_DEFAULT:
|
||||
default:
|
||||
if (inverse) {
|
||||
@@ -284,14 +284,14 @@ export class WebglCharAtlas implements IDisposable {
|
||||
|
||||
const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, inverse);
|
||||
const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, inverse, bold);
|
||||
const result = ensureContrastRatioRgba(bgRgba, fgRgba, this._config.minimumContrastRatio);
|
||||
const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._config.minimumContrastRatio);
|
||||
|
||||
if (!result) {
|
||||
this._config.colors.contrastCache.setCss(bg, fg, null);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const css = toCss(
|
||||
const css = channels.toCss(
|
||||
(result >> 24) & 0xFF,
|
||||
(result >> 16) & 0xFF,
|
||||
(result >> 8) & 0xFF
|
||||
|
||||
@@ -1270,4 +1270,63 @@ describe('InputHandler', () => {
|
||||
[131072, 131072], [131072, 131072], [131072, 300000 - 131072 - 131072]
|
||||
]);
|
||||
});
|
||||
describe('should correctly reset cells taken by wide chars', () => {
|
||||
let term: TestTerminal;
|
||||
beforeEach(() => {
|
||||
term = new TestTerminal({cols: 10, rows: 5, scrollback: 1});
|
||||
term.writeSync('¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥');
|
||||
});
|
||||
it('print', () => {
|
||||
term.writeSync('\x1b[H#');
|
||||
assert.deepEqual(getLines(term), ['# ¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[1;6H######');
|
||||
assert.deepEqual(getLines(term), ['# ¥ #####', '# ¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('#');
|
||||
assert.deepEqual(getLines(term), ['# ¥ #####', '##¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('#');
|
||||
assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[3;9H#');
|
||||
assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥#', '¥¥¥¥¥', '']);
|
||||
term.writeSync('#');
|
||||
assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '¥¥¥¥¥', '']);
|
||||
term.writeSync('#');
|
||||
assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '# ¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[4;10H#');
|
||||
assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '# ¥¥¥ #', '']);
|
||||
});
|
||||
it('EL', () => {
|
||||
term.writeSync('\x1b[1;6H\x1b[K#');
|
||||
assert.deepEqual(getLines(term), ['¥¥ #', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[2;5H\x1b[1K');
|
||||
assert.deepEqual(getLines(term), ['¥¥ #', ' ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[3;6H\x1b[1K');
|
||||
assert.deepEqual(getLines(term), ['¥¥ #', ' ¥¥', ' ¥¥', '¥¥¥¥¥', '']);
|
||||
});
|
||||
it('ICH', () => {
|
||||
term.writeSync('\x1b[1;6H\x1b[@');
|
||||
assert.deepEqual(getLines(term), ['¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[2;4H\x1b[2@');
|
||||
assert.deepEqual(getLines(term), ['¥¥ ¥', '¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[3;4H\x1b[3@');
|
||||
assert.deepEqual(getLines(term), ['¥¥ ¥', '¥ ¥¥', '¥ ¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[4;4H\x1b[4@');
|
||||
assert.deepEqual(getLines(term), ['¥¥ ¥', '¥ ¥¥', '¥ ¥', '¥ ¥', '']);
|
||||
});
|
||||
it('DCH', () => {
|
||||
term.writeSync('\x1b[1;6H\x1b[P');
|
||||
assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[2;6H\x1b[2P');
|
||||
assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[3;6H\x1b[3P');
|
||||
assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']);
|
||||
});
|
||||
it('ECH', () => {
|
||||
term.writeSync('\x1b[1;6H\x1b[X');
|
||||
assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[2;6H\x1b[2X');
|
||||
assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
|
||||
term.writeSync('\x1b[3;6H\x1b[3X');
|
||||
assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+26
-10
@@ -397,6 +397,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
|
||||
|
||||
this._dirtyRowService.markDirty(buffer.y);
|
||||
|
||||
// handle wide chars: reset start_cell-1 if we would overwrite the second cell of a wide char
|
||||
if (buffer.x && end - start > 0 && bufferRow.getWidth(buffer.x - 1) === 2) {
|
||||
bufferRow.setCellFromCodePoint(buffer.x - 1, 0, 1, curAttr.fg, curAttr.bg);
|
||||
}
|
||||
|
||||
for (let pos = start; pos < end; ++pos) {
|
||||
code = data[pos];
|
||||
|
||||
@@ -472,7 +478,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// insert mode: move characters to right
|
||||
if (insertMode) {
|
||||
// right shift cells according to the width
|
||||
bufferRow.insertCells(buffer.x, chWidth, buffer.getNullCell(curAttr));
|
||||
bufferRow.insertCells(buffer.x, chWidth, buffer.getNullCell(curAttr), curAttr);
|
||||
// test last cell - since the last cell has only room for
|
||||
// a halfwidth char any fullwidth shifted there is lost
|
||||
// and will be set to empty cell
|
||||
@@ -498,7 +504,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// This needs to check whether:
|
||||
// - fullwidth + surrogates: reset
|
||||
// - combining: only base char gets carried on (bug in xterm?)
|
||||
if (end) {
|
||||
if (end - start > 0) {
|
||||
bufferRow.loadCell(buffer.x - 1, this._workCell);
|
||||
if (this._workCell.getWidth() === 2 || this._workCell.getCode() > 0xFFFF) {
|
||||
this._parser.precedingCodepoint = 0;
|
||||
@@ -508,6 +514,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._parser.precedingCodepoint = this._workCell.content;
|
||||
}
|
||||
}
|
||||
|
||||
// handle wide chars: reset cell to the right if it is second cell of a wide char
|
||||
if (buffer.x < cols && end - start > 0 && bufferRow.getWidth(buffer.x) === 0 && !bufferRow.hasContent(buffer.x)) {
|
||||
bufferRow.setCellFromCodePoint(buffer.x, 0, 1, curAttr.fg, curAttr.bg);
|
||||
}
|
||||
|
||||
this._dirtyRowService.markDirty(buffer.y);
|
||||
}
|
||||
|
||||
@@ -854,7 +866,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
line.replaceCells(
|
||||
start,
|
||||
end,
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData())
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData()),
|
||||
this._eraseAttrData()
|
||||
);
|
||||
if (clearWrap) {
|
||||
line.isWrapped = false;
|
||||
@@ -1032,7 +1045,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
line.insertCells(
|
||||
this._bufferService.buffer.x,
|
||||
params.params[0] || 1,
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData())
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData()),
|
||||
this._eraseAttrData()
|
||||
);
|
||||
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
|
||||
}
|
||||
@@ -1049,7 +1063,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
line.deleteCells(
|
||||
this._bufferService.buffer.x,
|
||||
params.params[0] || 1,
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData())
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData()),
|
||||
this._eraseAttrData()
|
||||
);
|
||||
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
|
||||
}
|
||||
@@ -1109,7 +1124,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const param = params.params[0] || 1;
|
||||
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||
const line = buffer.lines.get(buffer.ybase + y);
|
||||
line.deleteCells(0, param, buffer.getNullCell(this._eraseAttrData()));
|
||||
line.deleteCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
line.isWrapped = false;
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
@@ -1137,7 +1152,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const param = params.params[0] || 1;
|
||||
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||
const line = buffer.lines.get(buffer.ybase + y);
|
||||
line.insertCells(0, param, buffer.getNullCell(this._eraseAttrData()));
|
||||
line.insertCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
line.isWrapped = false;
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
@@ -1155,7 +1170,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const param = params.params[0] || 1;
|
||||
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||
const line = this._bufferService.buffer.lines.get(buffer.ybase + y);
|
||||
line.insertCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()));
|
||||
line.insertCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
line.isWrapped = false;
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
@@ -1173,7 +1188,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const param = params.params[0] || 1;
|
||||
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||
const line = buffer.lines.get(buffer.ybase + y);
|
||||
line.deleteCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()));
|
||||
line.deleteCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
|
||||
line.isWrapped = false;
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
@@ -1190,7 +1205,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
line.replaceCells(
|
||||
this._bufferService.buffer.x,
|
||||
this._bufferService.buffer.x + (params.params[0] || 1),
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData())
|
||||
this._bufferService.buffer.getNullCell(this._eraseAttrData()),
|
||||
this._eraseAttrData()
|
||||
);
|
||||
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
|
||||
}
|
||||
|
||||
+232
-219
@@ -4,50 +4,243 @@
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { blend, fromCss, toPaddedHex, toCss, toRgba, fromRgba, opaque, rgbRelativeLuminance, contrastRatio, ensureContrastRatioRgba } from 'browser/Color';
|
||||
import { channels, color, css, rgb, rgba, toPaddedHex, contrastRatio } from 'browser/Color';
|
||||
|
||||
describe('Color', () => {
|
||||
describe('blend', () => {
|
||||
it('should blend colors based on the alpha channel', () => {
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF00', rgba: 0xFFFFFF00 }), { css: '#000000', rgba: 0x000000FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF10', rgba: 0xFFFFFF10 }), { css: '#101010', rgba: 0x101010FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF20', rgba: 0xFFFFFF20 }), { css: '#202020', rgba: 0x202020FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF30', rgba: 0xFFFFFF30 }), { css: '#303030', rgba: 0x303030FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF40', rgba: 0xFFFFFF40 }), { css: '#404040', rgba: 0x404040FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF50', rgba: 0xFFFFFF50 }), { css: '#505050', rgba: 0x505050FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF60', rgba: 0xFFFFFF60 }), { css: '#606060', rgba: 0x606060FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF70', rgba: 0xFFFFFF70 }), { css: '#707070', rgba: 0x707070FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF80', rgba: 0xFFFFFF80 }), { css: '#808080', rgba: 0x808080FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF90', rgba: 0xFFFFFF90 }), { css: '#909090', rgba: 0x909090FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFA0', rgba: 0xFFFFFFA0 }), { css: '#a0a0a0', rgba: 0xA0A0A0FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFB0', rgba: 0xFFFFFFB0 }), { css: '#b0b0b0', rgba: 0xB0B0B0FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFC0', rgba: 0xFFFFFFC0 }), { css: '#c0c0c0', rgba: 0xC0C0C0FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFD0', rgba: 0xFFFFFFD0 }), { css: '#d0d0d0', rgba: 0xD0D0D0FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFE0', rgba: 0xFFFFFFE0 }), { css: '#e0e0e0', rgba: 0xE0E0E0FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFF0', rgba: 0xFFFFFFF0 }), { css: '#f0f0f0', rgba: 0xF0F0F0FF });
|
||||
assert.deepEqual(blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFFF', rgba: 0xFFFFFFFF }), { css: '#FFFFFFFF', rgba: 0xFFFFFFFF });
|
||||
|
||||
describe('channels', () => {
|
||||
describe('toCss', () => {
|
||||
it('should convert an rgb array to css hex string', () => {
|
||||
assert.equal(channels.toCss(0x00, 0x00, 0x00), '#000000');
|
||||
assert.equal(channels.toCss(0x10, 0x10, 0x10), '#101010');
|
||||
assert.equal(channels.toCss(0x20, 0x20, 0x20), '#202020');
|
||||
assert.equal(channels.toCss(0x30, 0x30, 0x30), '#303030');
|
||||
assert.equal(channels.toCss(0x40, 0x40, 0x40), '#404040');
|
||||
assert.equal(channels.toCss(0x50, 0x50, 0x50), '#505050');
|
||||
assert.equal(channels.toCss(0x60, 0x60, 0x60), '#606060');
|
||||
assert.equal(channels.toCss(0x70, 0x70, 0x70), '#707070');
|
||||
assert.equal(channels.toCss(0x80, 0x80, 0x80), '#808080');
|
||||
assert.equal(channels.toCss(0x90, 0x90, 0x90), '#909090');
|
||||
assert.equal(channels.toCss(0xa0, 0xa0, 0xa0), '#a0a0a0');
|
||||
assert.equal(channels.toCss(0xb0, 0xb0, 0xb0), '#b0b0b0');
|
||||
assert.equal(channels.toCss(0xc0, 0xc0, 0xc0), '#c0c0c0');
|
||||
assert.equal(channels.toCss(0xd0, 0xd0, 0xd0), '#d0d0d0');
|
||||
assert.equal(channels.toCss(0xe0, 0xe0, 0xe0), '#e0e0e0');
|
||||
assert.equal(channels.toCss(0xf0, 0xf0, 0xf0), '#f0f0f0');
|
||||
assert.equal(channels.toCss(0xff, 0xff, 0xff), '#ffffff');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toRgba', () => {
|
||||
it('should convert an rgb array to an rgba number', () => {
|
||||
assert.equal(channels.toRgba(0x00, 0x00, 0x00), 0x000000FF);
|
||||
assert.equal(channels.toRgba(0x10, 0x10, 0x10), 0x101010FF);
|
||||
assert.equal(channels.toRgba(0x20, 0x20, 0x20), 0x202020FF);
|
||||
assert.equal(channels.toRgba(0x30, 0x30, 0x30), 0x303030FF);
|
||||
assert.equal(channels.toRgba(0x40, 0x40, 0x40), 0x404040FF);
|
||||
assert.equal(channels.toRgba(0x50, 0x50, 0x50), 0x505050FF);
|
||||
assert.equal(channels.toRgba(0x60, 0x60, 0x60), 0x606060FF);
|
||||
assert.equal(channels.toRgba(0x70, 0x70, 0x70), 0x707070FF);
|
||||
assert.equal(channels.toRgba(0x80, 0x80, 0x80), 0x808080FF);
|
||||
assert.equal(channels.toRgba(0x90, 0x90, 0x90), 0x909090FF);
|
||||
assert.equal(channels.toRgba(0xa0, 0xa0, 0xa0), 0xa0a0a0FF);
|
||||
assert.equal(channels.toRgba(0xb0, 0xb0, 0xb0), 0xb0b0b0FF);
|
||||
assert.equal(channels.toRgba(0xc0, 0xc0, 0xc0), 0xc0c0c0FF);
|
||||
assert.equal(channels.toRgba(0xd0, 0xd0, 0xd0), 0xd0d0d0FF);
|
||||
assert.equal(channels.toRgba(0xe0, 0xe0, 0xe0), 0xe0e0e0FF);
|
||||
assert.equal(channels.toRgba(0xf0, 0xf0, 0xf0), 0xf0f0f0FF);
|
||||
assert.equal(channels.toRgba(0xff, 0xff, 0xff), 0xffffffFF);
|
||||
});
|
||||
it('should convert an rgba array to an rgba number', () => {
|
||||
assert.equal(channels.toRgba(0x00, 0x00, 0x00, 0x00), 0x00000000);
|
||||
assert.equal(channels.toRgba(0x10, 0x10, 0x10, 0x10), 0x10101010);
|
||||
assert.equal(channels.toRgba(0x20, 0x20, 0x20, 0x20), 0x20202020);
|
||||
assert.equal(channels.toRgba(0x30, 0x30, 0x30, 0x30), 0x30303030);
|
||||
assert.equal(channels.toRgba(0x40, 0x40, 0x40, 0x40), 0x40404040);
|
||||
assert.equal(channels.toRgba(0x50, 0x50, 0x50, 0x50), 0x50505050);
|
||||
assert.equal(channels.toRgba(0x60, 0x60, 0x60, 0x60), 0x60606060);
|
||||
assert.equal(channels.toRgba(0x70, 0x70, 0x70, 0x70), 0x70707070);
|
||||
assert.equal(channels.toRgba(0x80, 0x80, 0x80, 0x80), 0x80808080);
|
||||
assert.equal(channels.toRgba(0x90, 0x90, 0x90, 0x90), 0x90909090);
|
||||
assert.equal(channels.toRgba(0xa0, 0xa0, 0xa0, 0xa0), 0xa0a0a0a0);
|
||||
assert.equal(channels.toRgba(0xb0, 0xb0, 0xb0, 0xb0), 0xb0b0b0b0);
|
||||
assert.equal(channels.toRgba(0xc0, 0xc0, 0xc0, 0xc0), 0xc0c0c0c0);
|
||||
assert.equal(channels.toRgba(0xd0, 0xd0, 0xd0, 0xd0), 0xd0d0d0d0);
|
||||
assert.equal(channels.toRgba(0xe0, 0xe0, 0xe0, 0xe0), 0xe0e0e0e0);
|
||||
assert.equal(channels.toRgba(0xf0, 0xf0, 0xf0, 0xf0), 0xf0f0f0f0);
|
||||
assert.equal(channels.toRgba(0xff, 0xff, 0xff, 0xff), 0xffffffff);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromCss', () => {
|
||||
it('should covert a CSS string to an IColor', () => {
|
||||
assert.deepEqual(fromCss('#000000'), { css: '#000000', rgba: 0x000000FF });
|
||||
assert.deepEqual(fromCss('#101010'), { css: '#101010', rgba: 0x101010FF });
|
||||
assert.deepEqual(fromCss('#202020'), { css: '#202020', rgba: 0x202020FF });
|
||||
assert.deepEqual(fromCss('#303030'), { css: '#303030', rgba: 0x303030FF });
|
||||
assert.deepEqual(fromCss('#404040'), { css: '#404040', rgba: 0x404040FF });
|
||||
assert.deepEqual(fromCss('#505050'), { css: '#505050', rgba: 0x505050FF });
|
||||
assert.deepEqual(fromCss('#606060'), { css: '#606060', rgba: 0x606060FF });
|
||||
assert.deepEqual(fromCss('#707070'), { css: '#707070', rgba: 0x707070FF });
|
||||
assert.deepEqual(fromCss('#808080'), { css: '#808080', rgba: 0x808080FF });
|
||||
assert.deepEqual(fromCss('#909090'), { css: '#909090', rgba: 0x909090FF });
|
||||
assert.deepEqual(fromCss('#a0a0a0'), { css: '#a0a0a0', rgba: 0xa0a0a0FF });
|
||||
assert.deepEqual(fromCss('#b0b0b0'), { css: '#b0b0b0', rgba: 0xb0b0b0FF });
|
||||
assert.deepEqual(fromCss('#c0c0c0'), { css: '#c0c0c0', rgba: 0xc0c0c0FF });
|
||||
assert.deepEqual(fromCss('#d0d0d0'), { css: '#d0d0d0', rgba: 0xd0d0d0FF });
|
||||
assert.deepEqual(fromCss('#e0e0e0'), { css: '#e0e0e0', rgba: 0xe0e0e0FF });
|
||||
assert.deepEqual(fromCss('#f0f0f0'), { css: '#f0f0f0', rgba: 0xf0f0f0FF });
|
||||
assert.deepEqual(fromCss('#ffffff'), { css: '#ffffff', rgba: 0xffffffFF });
|
||||
describe('color', () => {
|
||||
describe('blend', () => {
|
||||
it('should blend colors based on the alpha channel', () => {
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF00', rgba: 0xFFFFFF00 }), { css: '#000000', rgba: 0x000000FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF10', rgba: 0xFFFFFF10 }), { css: '#101010', rgba: 0x101010FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF20', rgba: 0xFFFFFF20 }), { css: '#202020', rgba: 0x202020FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF30', rgba: 0xFFFFFF30 }), { css: '#303030', rgba: 0x303030FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF40', rgba: 0xFFFFFF40 }), { css: '#404040', rgba: 0x404040FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF50', rgba: 0xFFFFFF50 }), { css: '#505050', rgba: 0x505050FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF60', rgba: 0xFFFFFF60 }), { css: '#606060', rgba: 0x606060FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF70', rgba: 0xFFFFFF70 }), { css: '#707070', rgba: 0x707070FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF80', rgba: 0xFFFFFF80 }), { css: '#808080', rgba: 0x808080FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFF90', rgba: 0xFFFFFF90 }), { css: '#909090', rgba: 0x909090FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFA0', rgba: 0xFFFFFFA0 }), { css: '#a0a0a0', rgba: 0xA0A0A0FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFB0', rgba: 0xFFFFFFB0 }), { css: '#b0b0b0', rgba: 0xB0B0B0FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFC0', rgba: 0xFFFFFFC0 }), { css: '#c0c0c0', rgba: 0xC0C0C0FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFD0', rgba: 0xFFFFFFD0 }), { css: '#d0d0d0', rgba: 0xD0D0D0FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFE0', rgba: 0xFFFFFFE0 }), { css: '#e0e0e0', rgba: 0xE0E0E0FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFF0', rgba: 0xFFFFFFF0 }), { css: '#f0f0f0', rgba: 0xF0F0F0FF });
|
||||
assert.deepEqual(color.blend({ css: '#000000', rgba: 0x000000FF }, { css: '#FFFFFFFF', rgba: 0xFFFFFFFF }), { css: '#FFFFFFFF', rgba: 0xFFFFFFFF });
|
||||
});
|
||||
});
|
||||
|
||||
describe('opaque', () => {
|
||||
it('should make the color opaque', () => {
|
||||
assert.deepEqual(color.opaque({ css: '#00000000', rgba: 0x00000000 }), { css: '#000000', rgba: 0x000000FF });
|
||||
assert.deepEqual(color.opaque({ css: '#10101010', rgba: 0x10101010 }), { css: '#101010', rgba: 0x101010FF });
|
||||
assert.deepEqual(color.opaque({ css: '#20202020', rgba: 0x20202020 }), { css: '#202020', rgba: 0x202020FF });
|
||||
assert.deepEqual(color.opaque({ css: '#30303030', rgba: 0x30303030 }), { css: '#303030', rgba: 0x303030FF });
|
||||
assert.deepEqual(color.opaque({ css: '#40404040', rgba: 0x40404040 }), { css: '#404040', rgba: 0x404040FF });
|
||||
assert.deepEqual(color.opaque({ css: '#50505050', rgba: 0x50505050 }), { css: '#505050', rgba: 0x505050FF });
|
||||
assert.deepEqual(color.opaque({ css: '#60606060', rgba: 0x60606060 }), { css: '#606060', rgba: 0x606060FF });
|
||||
assert.deepEqual(color.opaque({ css: '#70707070', rgba: 0x70707070 }), { css: '#707070', rgba: 0x707070FF });
|
||||
assert.deepEqual(color.opaque({ css: '#80808080', rgba: 0x80808080 }), { css: '#808080', rgba: 0x808080FF });
|
||||
assert.deepEqual(color.opaque({ css: '#90909090', rgba: 0x90909090 }), { css: '#909090', rgba: 0x909090FF });
|
||||
assert.deepEqual(color.opaque({ css: '#a0a0a0a0', rgba: 0xa0a0a0a0 }), { css: '#a0a0a0', rgba: 0xa0a0a0FF });
|
||||
assert.deepEqual(color.opaque({ css: '#b0b0b0b0', rgba: 0xb0b0b0b0 }), { css: '#b0b0b0', rgba: 0xb0b0b0FF });
|
||||
assert.deepEqual(color.opaque({ css: '#c0c0c0c0', rgba: 0xc0c0c0c0 }), { css: '#c0c0c0', rgba: 0xc0c0c0FF });
|
||||
assert.deepEqual(color.opaque({ css: '#d0d0d0d0', rgba: 0xd0d0d0d0 }), { css: '#d0d0d0', rgba: 0xd0d0d0FF });
|
||||
assert.deepEqual(color.opaque({ css: '#e0e0e0e0', rgba: 0xe0e0e0e0 }), { css: '#e0e0e0', rgba: 0xe0e0e0FF });
|
||||
assert.deepEqual(color.opaque({ css: '#f0f0f0f0', rgba: 0xf0f0f0f0 }), { css: '#f0f0f0', rgba: 0xf0f0f0FF });
|
||||
assert.deepEqual(color.opaque({ css: '#ffffffff', rgba: 0xffffffff }), { css: '#ffffff', rgba: 0xffffffFF });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('css', () => {
|
||||
describe('toColor', () => {
|
||||
it('should covert a CSS string to an IColor', () => {
|
||||
assert.deepEqual(css.toColor('#000000'), { css: '#000000', rgba: 0x000000FF });
|
||||
assert.deepEqual(css.toColor('#101010'), { css: '#101010', rgba: 0x101010FF });
|
||||
assert.deepEqual(css.toColor('#202020'), { css: '#202020', rgba: 0x202020FF });
|
||||
assert.deepEqual(css.toColor('#303030'), { css: '#303030', rgba: 0x303030FF });
|
||||
assert.deepEqual(css.toColor('#404040'), { css: '#404040', rgba: 0x404040FF });
|
||||
assert.deepEqual(css.toColor('#505050'), { css: '#505050', rgba: 0x505050FF });
|
||||
assert.deepEqual(css.toColor('#606060'), { css: '#606060', rgba: 0x606060FF });
|
||||
assert.deepEqual(css.toColor('#707070'), { css: '#707070', rgba: 0x707070FF });
|
||||
assert.deepEqual(css.toColor('#808080'), { css: '#808080', rgba: 0x808080FF });
|
||||
assert.deepEqual(css.toColor('#909090'), { css: '#909090', rgba: 0x909090FF });
|
||||
assert.deepEqual(css.toColor('#a0a0a0'), { css: '#a0a0a0', rgba: 0xa0a0a0FF });
|
||||
assert.deepEqual(css.toColor('#b0b0b0'), { css: '#b0b0b0', rgba: 0xb0b0b0FF });
|
||||
assert.deepEqual(css.toColor('#c0c0c0'), { css: '#c0c0c0', rgba: 0xc0c0c0FF });
|
||||
assert.deepEqual(css.toColor('#d0d0d0'), { css: '#d0d0d0', rgba: 0xd0d0d0FF });
|
||||
assert.deepEqual(css.toColor('#e0e0e0'), { css: '#e0e0e0', rgba: 0xe0e0e0FF });
|
||||
assert.deepEqual(css.toColor('#f0f0f0'), { css: '#f0f0f0', rgba: 0xf0f0f0FF });
|
||||
assert.deepEqual(css.toColor('#ffffff'), { css: '#ffffff', rgba: 0xffffffFF });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('rgb', () => {
|
||||
describe('relativeLuminance', () => {
|
||||
it('should calculate the relative luminance of the color', () => {
|
||||
assert.equal(rgb.relativeLuminance(0x000000), 0);
|
||||
assert.equal(rgb.relativeLuminance(0x101010).toFixed(4), '0.0052');
|
||||
assert.equal(rgb.relativeLuminance(0x202020).toFixed(4), '0.0144');
|
||||
assert.equal(rgb.relativeLuminance(0x303030).toFixed(4), '0.0296');
|
||||
assert.equal(rgb.relativeLuminance(0x404040).toFixed(4), '0.0513');
|
||||
assert.equal(rgb.relativeLuminance(0x505050).toFixed(4), '0.0802');
|
||||
assert.equal(rgb.relativeLuminance(0x606060).toFixed(4), '0.1170');
|
||||
assert.equal(rgb.relativeLuminance(0x707070).toFixed(4), '0.1620');
|
||||
assert.equal(rgb.relativeLuminance(0x808080).toFixed(4), '0.2159');
|
||||
assert.equal(rgb.relativeLuminance(0x909090).toFixed(4), '0.2789');
|
||||
assert.equal(rgb.relativeLuminance(0xA0A0A0).toFixed(4), '0.3515');
|
||||
assert.equal(rgb.relativeLuminance(0xB0B0B0).toFixed(4), '0.4342');
|
||||
assert.equal(rgb.relativeLuminance(0xC0C0C0).toFixed(4), '0.5271');
|
||||
assert.equal(rgb.relativeLuminance(0xD0D0D0).toFixed(4), '0.6308');
|
||||
assert.equal(rgb.relativeLuminance(0xE0E0E0).toFixed(4), '0.7454');
|
||||
assert.equal(rgb.relativeLuminance(0xF0F0F0).toFixed(4), '0.8714');
|
||||
assert.equal(rgb.relativeLuminance(0xFFFFFF), 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('rgba', () => {
|
||||
describe('ensureContrastRatio', () => {
|
||||
it('should return undefined if the color already meets the contrast ratio (black bg)', () => {
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 1), undefined);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 2), undefined);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 3), undefined);
|
||||
});
|
||||
it('should return a color that meets the contrast ratio (black bg)', () => {
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 4), 0x707070ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 5), 0x7f7f7fff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 6), 0x8c8c8cff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 7), 0x989898ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 8), 0xa3a3a3ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 9), 0xadadadff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 10), 0xb6b6b6ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 11), 0xbebebeff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 12), 0xc5c5c5ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 13), 0xd1d1d1ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 14), 0xd6d6d6ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 15), 0xdbdbdbff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 16), 0xe3e3e3ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 17), 0xe9e9e9ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 18), 0xeeeeeeff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 19), 0xf4f4f4ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 20), 0xfafafaff);
|
||||
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 21), 0xffffffff);
|
||||
});
|
||||
it('should return undefined if the color already meets the contrast ratio (white bg)', () => {
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 1), undefined);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 2), undefined);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 3), undefined);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 4), undefined);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 5), undefined);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 6), undefined);
|
||||
});
|
||||
it('should return a color that meets the contrast ratio (white bg)', () => {
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 7), 0x565656ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 8), 0x4d4d4dff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 9), 0x454545ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 10), 0x3e3e3eff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 11), 0x373737ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 12), 0x313131ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 13), 0x313131ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 14), 0x272727ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 15), 0x232323ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 16), 0x1f1f1fff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 17), 0x1b1b1bff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 18), 0x151515ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 19), 0x101010ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 20), 0x080808ff);
|
||||
assert.equal(rgba.ensureContrastRatio(0xffffffff, 0x606060ff, 21), 0x000000ff);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toChannels', () => {
|
||||
it('should convert an rgba number to an rgba array', () => {
|
||||
assert.deepEqual(rgba.toChannels(0x00000000), [0x00, 0x00, 0x00, 0x00]);
|
||||
assert.deepEqual(rgba.toChannels(0x10101010), [0x10, 0x10, 0x10, 0x10]);
|
||||
assert.deepEqual(rgba.toChannels(0x20202020), [0x20, 0x20, 0x20, 0x20]);
|
||||
assert.deepEqual(rgba.toChannels(0x30303030), [0x30, 0x30, 0x30, 0x30]);
|
||||
assert.deepEqual(rgba.toChannels(0x40404040), [0x40, 0x40, 0x40, 0x40]);
|
||||
assert.deepEqual(rgba.toChannels(0x50505050), [0x50, 0x50, 0x50, 0x50]);
|
||||
assert.deepEqual(rgba.toChannels(0x60606060), [0x60, 0x60, 0x60, 0x60]);
|
||||
assert.deepEqual(rgba.toChannels(0x70707070), [0x70, 0x70, 0x70, 0x70]);
|
||||
assert.deepEqual(rgba.toChannels(0x80808080), [0x80, 0x80, 0x80, 0x80]);
|
||||
assert.deepEqual(rgba.toChannels(0x90909090), [0x90, 0x90, 0x90, 0x90]);
|
||||
assert.deepEqual(rgba.toChannels(0xa0a0a0a0), [0xa0, 0xa0, 0xa0, 0xa0]);
|
||||
assert.deepEqual(rgba.toChannels(0xb0b0b0b0), [0xb0, 0xb0, 0xb0, 0xb0]);
|
||||
assert.deepEqual(rgba.toChannels(0xc0c0c0c0), [0xc0, 0xc0, 0xc0, 0xc0]);
|
||||
assert.deepEqual(rgba.toChannels(0xd0d0d0d0), [0xd0, 0xd0, 0xd0, 0xd0]);
|
||||
assert.deepEqual(rgba.toChannels(0xe0e0e0e0), [0xe0, 0xe0, 0xe0, 0xe0]);
|
||||
assert.deepEqual(rgba.toChannels(0xf0f0f0f0), [0xf0, 0xf0, 0xf0, 0xf0]);
|
||||
assert.deepEqual(rgba.toChannels(0xffffffff), [0xff, 0xff, 0xff, 0xff]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -73,134 +266,6 @@ describe('Color', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('toCss', () => {
|
||||
it('should convert an rgb array to css hex string', () => {
|
||||
assert.equal(toCss(0x00, 0x00, 0x00), '#000000');
|
||||
assert.equal(toCss(0x10, 0x10, 0x10), '#101010');
|
||||
assert.equal(toCss(0x20, 0x20, 0x20), '#202020');
|
||||
assert.equal(toCss(0x30, 0x30, 0x30), '#303030');
|
||||
assert.equal(toCss(0x40, 0x40, 0x40), '#404040');
|
||||
assert.equal(toCss(0x50, 0x50, 0x50), '#505050');
|
||||
assert.equal(toCss(0x60, 0x60, 0x60), '#606060');
|
||||
assert.equal(toCss(0x70, 0x70, 0x70), '#707070');
|
||||
assert.equal(toCss(0x80, 0x80, 0x80), '#808080');
|
||||
assert.equal(toCss(0x90, 0x90, 0x90), '#909090');
|
||||
assert.equal(toCss(0xa0, 0xa0, 0xa0), '#a0a0a0');
|
||||
assert.equal(toCss(0xb0, 0xb0, 0xb0), '#b0b0b0');
|
||||
assert.equal(toCss(0xc0, 0xc0, 0xc0), '#c0c0c0');
|
||||
assert.equal(toCss(0xd0, 0xd0, 0xd0), '#d0d0d0');
|
||||
assert.equal(toCss(0xe0, 0xe0, 0xe0), '#e0e0e0');
|
||||
assert.equal(toCss(0xf0, 0xf0, 0xf0), '#f0f0f0');
|
||||
assert.equal(toCss(0xff, 0xff, 0xff), '#ffffff');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toRgba', () => {
|
||||
it('should convert an rgb array to an rgba number', () => {
|
||||
assert.equal(toRgba(0x00, 0x00, 0x00), 0x000000FF);
|
||||
assert.equal(toRgba(0x10, 0x10, 0x10), 0x101010FF);
|
||||
assert.equal(toRgba(0x20, 0x20, 0x20), 0x202020FF);
|
||||
assert.equal(toRgba(0x30, 0x30, 0x30), 0x303030FF);
|
||||
assert.equal(toRgba(0x40, 0x40, 0x40), 0x404040FF);
|
||||
assert.equal(toRgba(0x50, 0x50, 0x50), 0x505050FF);
|
||||
assert.equal(toRgba(0x60, 0x60, 0x60), 0x606060FF);
|
||||
assert.equal(toRgba(0x70, 0x70, 0x70), 0x707070FF);
|
||||
assert.equal(toRgba(0x80, 0x80, 0x80), 0x808080FF);
|
||||
assert.equal(toRgba(0x90, 0x90, 0x90), 0x909090FF);
|
||||
assert.equal(toRgba(0xa0, 0xa0, 0xa0), 0xa0a0a0FF);
|
||||
assert.equal(toRgba(0xb0, 0xb0, 0xb0), 0xb0b0b0FF);
|
||||
assert.equal(toRgba(0xc0, 0xc0, 0xc0), 0xc0c0c0FF);
|
||||
assert.equal(toRgba(0xd0, 0xd0, 0xd0), 0xd0d0d0FF);
|
||||
assert.equal(toRgba(0xe0, 0xe0, 0xe0), 0xe0e0e0FF);
|
||||
assert.equal(toRgba(0xf0, 0xf0, 0xf0), 0xf0f0f0FF);
|
||||
assert.equal(toRgba(0xff, 0xff, 0xff), 0xffffffFF);
|
||||
});
|
||||
it('should convert an rgba array to an rgba number', () => {
|
||||
assert.equal(toRgba(0x00, 0x00, 0x00, 0x00), 0x00000000);
|
||||
assert.equal(toRgba(0x10, 0x10, 0x10, 0x10), 0x10101010);
|
||||
assert.equal(toRgba(0x20, 0x20, 0x20, 0x20), 0x20202020);
|
||||
assert.equal(toRgba(0x30, 0x30, 0x30, 0x30), 0x30303030);
|
||||
assert.equal(toRgba(0x40, 0x40, 0x40, 0x40), 0x40404040);
|
||||
assert.equal(toRgba(0x50, 0x50, 0x50, 0x50), 0x50505050);
|
||||
assert.equal(toRgba(0x60, 0x60, 0x60, 0x60), 0x60606060);
|
||||
assert.equal(toRgba(0x70, 0x70, 0x70, 0x70), 0x70707070);
|
||||
assert.equal(toRgba(0x80, 0x80, 0x80, 0x80), 0x80808080);
|
||||
assert.equal(toRgba(0x90, 0x90, 0x90, 0x90), 0x90909090);
|
||||
assert.equal(toRgba(0xa0, 0xa0, 0xa0, 0xa0), 0xa0a0a0a0);
|
||||
assert.equal(toRgba(0xb0, 0xb0, 0xb0, 0xb0), 0xb0b0b0b0);
|
||||
assert.equal(toRgba(0xc0, 0xc0, 0xc0, 0xc0), 0xc0c0c0c0);
|
||||
assert.equal(toRgba(0xd0, 0xd0, 0xd0, 0xd0), 0xd0d0d0d0);
|
||||
assert.equal(toRgba(0xe0, 0xe0, 0xe0, 0xe0), 0xe0e0e0e0);
|
||||
assert.equal(toRgba(0xf0, 0xf0, 0xf0, 0xf0), 0xf0f0f0f0);
|
||||
assert.equal(toRgba(0xff, 0xff, 0xff, 0xff), 0xffffffff);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromRgba', () => {
|
||||
it('should convert an rgba number to an rgba array', () => {
|
||||
assert.deepEqual(fromRgba(0x00000000), [0x00, 0x00, 0x00, 0x00]);
|
||||
assert.deepEqual(fromRgba(0x10101010), [0x10, 0x10, 0x10, 0x10]);
|
||||
assert.deepEqual(fromRgba(0x20202020), [0x20, 0x20, 0x20, 0x20]);
|
||||
assert.deepEqual(fromRgba(0x30303030), [0x30, 0x30, 0x30, 0x30]);
|
||||
assert.deepEqual(fromRgba(0x40404040), [0x40, 0x40, 0x40, 0x40]);
|
||||
assert.deepEqual(fromRgba(0x50505050), [0x50, 0x50, 0x50, 0x50]);
|
||||
assert.deepEqual(fromRgba(0x60606060), [0x60, 0x60, 0x60, 0x60]);
|
||||
assert.deepEqual(fromRgba(0x70707070), [0x70, 0x70, 0x70, 0x70]);
|
||||
assert.deepEqual(fromRgba(0x80808080), [0x80, 0x80, 0x80, 0x80]);
|
||||
assert.deepEqual(fromRgba(0x90909090), [0x90, 0x90, 0x90, 0x90]);
|
||||
assert.deepEqual(fromRgba(0xa0a0a0a0), [0xa0, 0xa0, 0xa0, 0xa0]);
|
||||
assert.deepEqual(fromRgba(0xb0b0b0b0), [0xb0, 0xb0, 0xb0, 0xb0]);
|
||||
assert.deepEqual(fromRgba(0xc0c0c0c0), [0xc0, 0xc0, 0xc0, 0xc0]);
|
||||
assert.deepEqual(fromRgba(0xd0d0d0d0), [0xd0, 0xd0, 0xd0, 0xd0]);
|
||||
assert.deepEqual(fromRgba(0xe0e0e0e0), [0xe0, 0xe0, 0xe0, 0xe0]);
|
||||
assert.deepEqual(fromRgba(0xf0f0f0f0), [0xf0, 0xf0, 0xf0, 0xf0]);
|
||||
assert.deepEqual(fromRgba(0xffffffff), [0xff, 0xff, 0xff, 0xff]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('opaque', () => {
|
||||
it('should make the color opaque', () => {
|
||||
assert.deepEqual(opaque({ css: '#00000000', rgba: 0x00000000 }), { css: '#000000', rgba: 0x000000FF });
|
||||
assert.deepEqual(opaque({ css: '#10101010', rgba: 0x10101010 }), { css: '#101010', rgba: 0x101010FF });
|
||||
assert.deepEqual(opaque({ css: '#20202020', rgba: 0x20202020 }), { css: '#202020', rgba: 0x202020FF });
|
||||
assert.deepEqual(opaque({ css: '#30303030', rgba: 0x30303030 }), { css: '#303030', rgba: 0x303030FF });
|
||||
assert.deepEqual(opaque({ css: '#40404040', rgba: 0x40404040 }), { css: '#404040', rgba: 0x404040FF });
|
||||
assert.deepEqual(opaque({ css: '#50505050', rgba: 0x50505050 }), { css: '#505050', rgba: 0x505050FF });
|
||||
assert.deepEqual(opaque({ css: '#60606060', rgba: 0x60606060 }), { css: '#606060', rgba: 0x606060FF });
|
||||
assert.deepEqual(opaque({ css: '#70707070', rgba: 0x70707070 }), { css: '#707070', rgba: 0x707070FF });
|
||||
assert.deepEqual(opaque({ css: '#80808080', rgba: 0x80808080 }), { css: '#808080', rgba: 0x808080FF });
|
||||
assert.deepEqual(opaque({ css: '#90909090', rgba: 0x90909090 }), { css: '#909090', rgba: 0x909090FF });
|
||||
assert.deepEqual(opaque({ css: '#a0a0a0a0', rgba: 0xa0a0a0a0 }), { css: '#a0a0a0', rgba: 0xa0a0a0FF });
|
||||
assert.deepEqual(opaque({ css: '#b0b0b0b0', rgba: 0xb0b0b0b0 }), { css: '#b0b0b0', rgba: 0xb0b0b0FF });
|
||||
assert.deepEqual(opaque({ css: '#c0c0c0c0', rgba: 0xc0c0c0c0 }), { css: '#c0c0c0', rgba: 0xc0c0c0FF });
|
||||
assert.deepEqual(opaque({ css: '#d0d0d0d0', rgba: 0xd0d0d0d0 }), { css: '#d0d0d0', rgba: 0xd0d0d0FF });
|
||||
assert.deepEqual(opaque({ css: '#e0e0e0e0', rgba: 0xe0e0e0e0 }), { css: '#e0e0e0', rgba: 0xe0e0e0FF });
|
||||
assert.deepEqual(opaque({ css: '#f0f0f0f0', rgba: 0xf0f0f0f0 }), { css: '#f0f0f0', rgba: 0xf0f0f0FF });
|
||||
assert.deepEqual(opaque({ css: '#ffffffff', rgba: 0xffffffff }), { css: '#ffffff', rgba: 0xffffffFF });
|
||||
});
|
||||
});
|
||||
|
||||
describe('rgbRelativeLuminance', () => {
|
||||
it('should calculate the relative luminance of the color', () => {
|
||||
assert.equal(rgbRelativeLuminance(0x000000), 0);
|
||||
assert.equal(rgbRelativeLuminance(0x101010).toFixed(4), '0.0052');
|
||||
assert.equal(rgbRelativeLuminance(0x202020).toFixed(4), '0.0144');
|
||||
assert.equal(rgbRelativeLuminance(0x303030).toFixed(4), '0.0296');
|
||||
assert.equal(rgbRelativeLuminance(0x404040).toFixed(4), '0.0513');
|
||||
assert.equal(rgbRelativeLuminance(0x505050).toFixed(4), '0.0802');
|
||||
assert.equal(rgbRelativeLuminance(0x606060).toFixed(4), '0.1170');
|
||||
assert.equal(rgbRelativeLuminance(0x707070).toFixed(4), '0.1620');
|
||||
assert.equal(rgbRelativeLuminance(0x808080).toFixed(4), '0.2159');
|
||||
assert.equal(rgbRelativeLuminance(0x909090).toFixed(4), '0.2789');
|
||||
assert.equal(rgbRelativeLuminance(0xA0A0A0).toFixed(4), '0.3515');
|
||||
assert.equal(rgbRelativeLuminance(0xB0B0B0).toFixed(4), '0.4342');
|
||||
assert.equal(rgbRelativeLuminance(0xC0C0C0).toFixed(4), '0.5271');
|
||||
assert.equal(rgbRelativeLuminance(0xD0D0D0).toFixed(4), '0.6308');
|
||||
assert.equal(rgbRelativeLuminance(0xE0E0E0).toFixed(4), '0.7454');
|
||||
assert.equal(rgbRelativeLuminance(0xF0F0F0).toFixed(4), '0.8714');
|
||||
assert.equal(rgbRelativeLuminance(0xFFFFFF), 1);
|
||||
});
|
||||
});
|
||||
describe('contrastRatio', () => {
|
||||
it('should calculate the relative luminance of the color', () => {
|
||||
assert.equal(contrastRatio(0, 0), 1);
|
||||
@@ -212,56 +277,4 @@ describe('Color', () => {
|
||||
assert.equal(contrastRatio(1, 0), 21);
|
||||
});
|
||||
});
|
||||
describe('ensureContrastRatioRgba', () => {
|
||||
it('should return undefined if the color already meets the contrast ratio (black bg)', () => {
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 1), undefined);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 2), undefined);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 3), undefined);
|
||||
});
|
||||
it('should return a color that meets the contrast ratio (black bg)', () => {
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 4), 0x707070ff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 5), 0x7f7f7fff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 6), 0x8c8c8cff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 7), 0x989898ff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 8), 0xa3a3a3ff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 9), 0xadadadff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 10), 0xb6b6b6ff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 11), 0xbebebeff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 12), 0xc5c5c5ff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 13), 0xd1d1d1ff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 14), 0xd6d6d6ff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 15), 0xdbdbdbff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 16), 0xe3e3e3ff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 17), 0xe9e9e9ff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 18), 0xeeeeeeff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 19), 0xf4f4f4ff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 20), 0xfafafaff);
|
||||
assert.equal(ensureContrastRatioRgba(0x000000ff, 0x606060ff, 21), 0xffffffff);
|
||||
});
|
||||
it('should return undefined if the color already meets the contrast ratio (white bg)', () => {
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 1), undefined);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 2), undefined);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 3), undefined);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 4), undefined);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 5), undefined);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 6), undefined);
|
||||
});
|
||||
it('should return a color that meets the contrast ratio (white bg)', () => {
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 7), 0x565656ff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 8), 0x4d4d4dff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 9), 0x454545ff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 10), 0x3e3e3eff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 11), 0x373737ff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 12), 0x313131ff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 13), 0x313131ff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 14), 0x272727ff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 15), 0x232323ff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 16), 0x1f1f1fff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 17), 0x1b1b1bff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 18), 0x151515ff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 19), 0x101010ff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 20), 0x080808ff);
|
||||
assert.equal(ensureContrastRatioRgba(0xffffffff, 0x606060ff, 21), 0x000000ff);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+177
-152
@@ -5,33 +5,186 @@
|
||||
|
||||
import { IColor } from 'browser/Types';
|
||||
|
||||
export function blend(bg: IColor, fg: IColor): IColor {
|
||||
const a = (fg.rgba & 0xFF) / 255;
|
||||
if (a === 1) {
|
||||
return {
|
||||
css: fg.css,
|
||||
rgba: fg.rgba
|
||||
};
|
||||
/**
|
||||
* Helper functions where the source type is "channels" (individual color channels as numbers).
|
||||
*/
|
||||
export namespace channels {
|
||||
export function toCss(r: number, g: number, b: number, a?: number): string {
|
||||
if (a !== undefined) {
|
||||
return `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}${toPaddedHex(a)}`;
|
||||
}
|
||||
return `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}`;
|
||||
}
|
||||
|
||||
export function toRgba(r: number, g: number, b: number, a: number = 0xFF): number {
|
||||
// >>> 0 forces an unsigned int
|
||||
return (r << 24 | g << 16 | b << 8 | a) >>> 0;
|
||||
}
|
||||
const fgR = (fg.rgba >> 24) & 0xFF;
|
||||
const fgG = (fg.rgba >> 16) & 0xFF;
|
||||
const fgB = (fg.rgba >> 8) & 0xFF;
|
||||
const bgR = (bg.rgba >> 24) & 0xFF;
|
||||
const bgG = (bg.rgba >> 16) & 0xFF;
|
||||
const bgB = (bg.rgba >> 8) & 0xFF;
|
||||
const r = bgR + Math.round((fgR - bgR) * a);
|
||||
const g = bgG + Math.round((fgG - bgG) * a);
|
||||
const b = bgB + Math.round((fgB - bgB) * a);
|
||||
const css = toCss(r, g, b);
|
||||
const rgba = toRgba(r, g, b);
|
||||
return { css, rgba };
|
||||
}
|
||||
|
||||
export function fromCss(css: string): IColor {
|
||||
return {
|
||||
css,
|
||||
rgba: (parseInt(css.slice(1), 16) << 8 | 0xFF) >>> 0
|
||||
};
|
||||
/**
|
||||
* Helper functions where the source type is `IColor`.
|
||||
*/
|
||||
export namespace color {
|
||||
export function blend(bg: IColor, fg: IColor): IColor {
|
||||
const a = (fg.rgba & 0xFF) / 255;
|
||||
if (a === 1) {
|
||||
return {
|
||||
css: fg.css,
|
||||
rgba: fg.rgba
|
||||
};
|
||||
}
|
||||
const fgR = (fg.rgba >> 24) & 0xFF;
|
||||
const fgG = (fg.rgba >> 16) & 0xFF;
|
||||
const fgB = (fg.rgba >> 8) & 0xFF;
|
||||
const bgR = (bg.rgba >> 24) & 0xFF;
|
||||
const bgG = (bg.rgba >> 16) & 0xFF;
|
||||
const bgB = (bg.rgba >> 8) & 0xFF;
|
||||
const r = bgR + Math.round((fgR - bgR) * a);
|
||||
const g = bgG + Math.round((fgG - bgG) * a);
|
||||
const b = bgB + Math.round((fgB - bgB) * a);
|
||||
const css = channels.toCss(r, g, b);
|
||||
const rgba = channels.toRgba(r, g, b);
|
||||
return { css, rgba };
|
||||
}
|
||||
|
||||
export function ensureContrastRatio(bg: IColor, fg: IColor, ratio: number): IColor | undefined {
|
||||
const result = rgba.ensureContrastRatio(bg.rgba, fg.rgba, ratio);
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
return rgba.toColor(
|
||||
(result >> 24 & 0xFF),
|
||||
(result >> 16 & 0xFF),
|
||||
(result >> 8 & 0xFF)
|
||||
);
|
||||
}
|
||||
|
||||
export function opaque(color: IColor): IColor {
|
||||
const rgbaColor = (color.rgba | 0xFF) >>> 0;
|
||||
const [r, g, b] = rgba.toChannels(rgbaColor);
|
||||
return {
|
||||
css: channels.toCss(r, g, b),
|
||||
rgba: rgbaColor
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper functions where the source type is "css" (string: '#rgb', '#rgba', '#rrggbb', '#rrggbbaa').
|
||||
*/
|
||||
export namespace css {
|
||||
export function toColor(css: string): IColor {
|
||||
return {
|
||||
css,
|
||||
rgba: (parseInt(css.slice(1), 16) << 8 | 0xFF) >>> 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper functions where the source type is "rgb" (number: 0xrrggbb).
|
||||
*/
|
||||
export namespace rgb {
|
||||
/**
|
||||
* Gets the relative luminance of an RGB color, this is useful in determining the contrast ratio
|
||||
* between two colors.
|
||||
* @param rgb The color to use.
|
||||
* @see https://www.w3.org/TR/WCAG20/#relativeluminancedef
|
||||
*/
|
||||
export function relativeLuminance(rgb: number): number {
|
||||
return relativeLuminance2(
|
||||
(rgb >> 16) & 0xFF,
|
||||
(rgb >> 8 ) & 0xFF,
|
||||
(rgb ) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative luminance of an RGB color, this is useful in determining the contrast ratio
|
||||
* between two colors.
|
||||
* @param r The red channel (0x00 to 0xFF).
|
||||
* @param g The green channel (0x00 to 0xFF).
|
||||
* @param b The blue channel (0x00 to 0xFF).
|
||||
* @see https://www.w3.org/TR/WCAG20/#relativeluminancedef
|
||||
*/
|
||||
export function relativeLuminance2(r: number, g: number, b: number): number {
|
||||
const rs = r / 255;
|
||||
const gs = g / 255;
|
||||
const bs = b / 255;
|
||||
const rr = rs <= 0.03928 ? rs / 12.92 : Math.pow((rs + 0.055) / 1.055, 2.4);
|
||||
const rg = gs <= 0.03928 ? gs / 12.92 : Math.pow((gs + 0.055) / 1.055, 2.4);
|
||||
const rb = bs <= 0.03928 ? bs / 12.92 : Math.pow((bs + 0.055) / 1.055, 2.4);
|
||||
return rr * 0.2126 + rg * 0.7152 + rb * 0.0722;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper functions where the source type is "rgba" (number: 0xrrggbbaa).
|
||||
*/
|
||||
export namespace rgba {
|
||||
export function ensureContrastRatio(bgRgba: number, fgRgba: number, ratio: number): number | undefined {
|
||||
const bgL = rgb.relativeLuminance(bgRgba >> 8);
|
||||
const fgL = rgb.relativeLuminance(fgRgba >> 8);
|
||||
const cr = contrastRatio(bgL, fgL);
|
||||
if (cr < ratio) {
|
||||
if (fgL < bgL) {
|
||||
return reduceLuminance(bgRgba, fgRgba, ratio);
|
||||
}
|
||||
return increaseLuminance(bgRgba, fgRgba, ratio);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function reduceLuminance(bgRgba: number, fgRgba: number, ratio: number): number {
|
||||
// This is a naive but fast approach to reducing luminance as converting to
|
||||
// HSL and back is expensive
|
||||
const bgR = (bgRgba >> 24) & 0xFF;
|
||||
const bgG = (bgRgba >> 16) & 0xFF;
|
||||
const bgB = (bgRgba >> 8) & 0xFF;
|
||||
let fgR = (fgRgba >> 24) & 0xFF;
|
||||
let fgG = (fgRgba >> 16) & 0xFF;
|
||||
let fgB = (fgRgba >> 8) & 0xFF;
|
||||
let cr = contrastRatio(rgb.relativeLuminance2(fgR, fgB, fgG), rgb.relativeLuminance2(bgR, bgG, bgB));
|
||||
while (cr < ratio && (fgR > 0 || fgG > 0 || fgB > 0)) {
|
||||
// Reduce by 10% until the ratio is hit
|
||||
fgR -= Math.max(0, Math.ceil(fgR * 0.1));
|
||||
fgG -= Math.max(0, Math.ceil(fgG * 0.1));
|
||||
fgB -= Math.max(0, Math.ceil(fgB * 0.1));
|
||||
cr = contrastRatio(rgb.relativeLuminance2(fgR, fgB, fgG), rgb.relativeLuminance2(bgR, bgG, bgB));
|
||||
}
|
||||
return (fgR << 24 | fgG << 16 | fgB << 8 | 0xFF) >>> 0;
|
||||
}
|
||||
|
||||
export function increaseLuminance(bgRgba: number, fgRgba: number, ratio: number): number {
|
||||
// This is a naive but fast approach to increasing luminance as converting to
|
||||
// HSL and back is expensive
|
||||
const bgR = (bgRgba >> 24) & 0xFF;
|
||||
const bgG = (bgRgba >> 16) & 0xFF;
|
||||
const bgB = (bgRgba >> 8) & 0xFF;
|
||||
let fgR = (fgRgba >> 24) & 0xFF;
|
||||
let fgG = (fgRgba >> 16) & 0xFF;
|
||||
let fgB = (fgRgba >> 8) & 0xFF;
|
||||
let cr = contrastRatio(rgb.relativeLuminance2(fgR, fgB, fgG), rgb.relativeLuminance2(bgR, bgG, bgB));
|
||||
while (cr < ratio && (fgR < 0xFF || fgG < 0xFF || fgB < 0xFF)) {
|
||||
// Increase by 10% until the ratio is hit
|
||||
fgR = Math.min(0xFF, fgR + Math.ceil((255 - fgR) * 0.1));
|
||||
fgG = Math.min(0xFF, fgG + Math.ceil((255 - fgG) * 0.1));
|
||||
fgB = Math.min(0xFF, fgB + Math.ceil((255 - fgB) * 0.1));
|
||||
cr = contrastRatio(rgb.relativeLuminance2(fgR, fgB, fgG), rgb.relativeLuminance2(bgR, bgG, bgB));
|
||||
}
|
||||
return (fgR << 24 | fgG << 16 | fgB << 8 | 0xFF) >>> 0;
|
||||
}
|
||||
|
||||
export function toChannels(value: number): [number, number, number, number] {
|
||||
return [(value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF];
|
||||
}
|
||||
|
||||
export function toColor(r: number, g: number, b: number): IColor {
|
||||
return {
|
||||
css: channels.toCss(r, g, b),
|
||||
rgba: channels.toRgba(r, g, b)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function toPaddedHex(c: number): string {
|
||||
@@ -39,62 +192,6 @@ export function toPaddedHex(c: number): string {
|
||||
return s.length < 2 ? '0' + s : s;
|
||||
}
|
||||
|
||||
export function toCss(r: number, g: number, b: number, a?: number): string {
|
||||
if (a !== undefined) {
|
||||
return `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}${toPaddedHex(a)}`;
|
||||
}
|
||||
return `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}`;
|
||||
}
|
||||
|
||||
export function toRgba(r: number, g: number, b: number, a: number = 0xFF): number {
|
||||
// >>> 0 forces an unsigned int
|
||||
return (r << 24 | g << 16 | b << 8 | a) >>> 0;
|
||||
}
|
||||
|
||||
export function fromRgba(value: number): [number, number, number, number] {
|
||||
return [(value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF];
|
||||
}
|
||||
|
||||
export function opaque(color: IColor): IColor {
|
||||
const rgba = (color.rgba | 0xFF) >>> 0;
|
||||
const [r, g, b] = fromRgba(rgba);
|
||||
return {
|
||||
css: toCss(r, g, b),
|
||||
rgba
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative luminance of an RGB color, this is useful in determining the contrast ratio
|
||||
* between two colors.
|
||||
* @param rgb The color to use.
|
||||
* @see https://www.w3.org/TR/WCAG20/#relativeluminancedef
|
||||
*/
|
||||
export function rgbRelativeLuminance(rgb: number): number {
|
||||
return rgbRelativeLuminance2(
|
||||
(rgb >> 16) & 0xFF,
|
||||
(rgb >> 8 ) & 0xFF,
|
||||
(rgb ) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative luminance of an RGB color, this is useful in determining the contrast ratio
|
||||
* between two colors.
|
||||
* @param r The red channel (0x00 to 0xFF).
|
||||
* @param g The green channel (0x00 to 0xFF).
|
||||
* @param b The blue channel (0x00 to 0xFF).
|
||||
* @see https://www.w3.org/TR/WCAG20/#relativeluminancedef
|
||||
*/
|
||||
export function rgbRelativeLuminance2(r: number, g: number, b: number): number {
|
||||
const rs = r / 255;
|
||||
const gs = g / 255;
|
||||
const bs = b / 255;
|
||||
const rr = rs <= 0.03928 ? rs / 12.92 : Math.pow((rs + 0.055) / 1.055, 2.4);
|
||||
const rg = gs <= 0.03928 ? gs / 12.92 : Math.pow((gs + 0.055) / 1.055, 2.4);
|
||||
const rb = bs <= 0.03928 ? bs / 12.92 : Math.pow((bs + 0.055) / 1.055, 2.4);
|
||||
return rr * 0.2126 + rg * 0.7152 + rb * 0.0722;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the contrast ratio between two relative luminance values.
|
||||
* @param l1 The first relative luminance.
|
||||
@@ -107,75 +204,3 @@ export function contrastRatio(l1: number, l2: number): number {
|
||||
}
|
||||
return (l1 + 0.05) / (l2 + 0.05);
|
||||
}
|
||||
|
||||
export function rgbaToColor(r: number, g: number, b: number): IColor {
|
||||
return {
|
||||
css: toCss(r, g, b),
|
||||
rgba: toRgba(r, g, b)
|
||||
};
|
||||
}
|
||||
|
||||
export function ensureContrastRatioRgba(bgRgba: number, fgRgba: number, ratio: number): number | undefined {
|
||||
const bgL = rgbRelativeLuminance(bgRgba >> 8);
|
||||
const fgL = rgbRelativeLuminance(fgRgba >> 8);
|
||||
const cr = contrastRatio(bgL, fgL);
|
||||
if (cr < ratio) {
|
||||
if (fgL < bgL) {
|
||||
return reduceLuminance(bgRgba, fgRgba, ratio);
|
||||
}
|
||||
return increaseLuminance(bgRgba, fgRgba, ratio);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function ensureContrastRatio(bg: IColor, fg: IColor, ratio: number): IColor | undefined {
|
||||
const result = ensureContrastRatioRgba(bg.rgba, fg.rgba, ratio);
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
return rgbaToColor(
|
||||
(result >> 24 & 0xFF),
|
||||
(result >> 16 & 0xFF),
|
||||
(result >> 8 & 0xFF)
|
||||
);
|
||||
}
|
||||
|
||||
export function reduceLuminance(bgRgba: number, fgRgba: number, ratio: number): number {
|
||||
// This is a naive but fast approach to reducing luminance as converting to
|
||||
// HSL and back is expensive
|
||||
const bgR = (bgRgba >> 24) & 0xFF;
|
||||
const bgG = (bgRgba >> 16) & 0xFF;
|
||||
const bgB = (bgRgba >> 8) & 0xFF;
|
||||
let fgR = (fgRgba >> 24) & 0xFF;
|
||||
let fgG = (fgRgba >> 16) & 0xFF;
|
||||
let fgB = (fgRgba >> 8) & 0xFF;
|
||||
let cr = contrastRatio(rgbRelativeLuminance2(fgR, fgB, fgG), rgbRelativeLuminance2(bgR, bgG, bgB));
|
||||
while (cr < ratio && (fgR > 0 || fgG > 0 || fgB > 0)) {
|
||||
// Reduce by 10% until the ratio is hit
|
||||
fgR -= Math.max(0, Math.ceil(fgR * 0.1));
|
||||
fgG -= Math.max(0, Math.ceil(fgG * 0.1));
|
||||
fgB -= Math.max(0, Math.ceil(fgB * 0.1));
|
||||
cr = contrastRatio(rgbRelativeLuminance2(fgR, fgB, fgG), rgbRelativeLuminance2(bgR, bgG, bgB));
|
||||
}
|
||||
return (fgR << 24 | fgG << 16 | fgB << 8 | 0xFF) >>> 0;
|
||||
}
|
||||
|
||||
export function increaseLuminance(bgRgba: number, fgRgba: number, ratio: number): number {
|
||||
// This is a naive but fast approach to increasing luminance as converting to
|
||||
// HSL and back is expensive
|
||||
const bgR = (bgRgba >> 24) & 0xFF;
|
||||
const bgG = (bgRgba >> 16) & 0xFF;
|
||||
const bgB = (bgRgba >> 8) & 0xFF;
|
||||
let fgR = (fgRgba >> 24) & 0xFF;
|
||||
let fgG = (fgRgba >> 16) & 0xFF;
|
||||
let fgB = (fgRgba >> 8) & 0xFF;
|
||||
let cr = contrastRatio(rgbRelativeLuminance2(fgR, fgB, fgG), rgbRelativeLuminance2(bgR, bgG, bgB));
|
||||
while (cr < ratio && (fgR < 0xFF || fgG < 0xFF || fgB < 0xFF)) {
|
||||
// Increase by 10% until the ratio is hit
|
||||
fgR = Math.min(0xFF, fgR + Math.ceil((255 - fgR) * 0.1));
|
||||
fgG = Math.min(0xFF, fgG + Math.ceil((255 - fgG) * 0.1));
|
||||
fgB = Math.min(0xFF, fgB + Math.ceil((255 - fgB) * 0.1));
|
||||
cr = contrastRatio(rgbRelativeLuminance2(fgR, fgB, fgG), rgbRelativeLuminance2(bgR, bgG, bgB));
|
||||
}
|
||||
return (fgR << 24 | fgG << 16 | fgB << 8 | 0xFF) >>> 0;
|
||||
}
|
||||
|
||||
+37
-49
@@ -5,13 +5,13 @@
|
||||
|
||||
import { IColorManager, IColor, IColorSet, IColorContrastCache } from 'browser/Types';
|
||||
import { ITheme } from 'common/services/Services';
|
||||
import { fromCss, toCss, blend, toRgba, toPaddedHex } from 'browser/Color';
|
||||
import { channels, color, css } from 'browser/Color';
|
||||
import { ColorContrastCache } from 'browser/ColorContrastCache';
|
||||
|
||||
const DEFAULT_FOREGROUND = fromCss('#ffffff');
|
||||
const DEFAULT_BACKGROUND = fromCss('#000000');
|
||||
const DEFAULT_CURSOR = fromCss('#ffffff');
|
||||
const DEFAULT_CURSOR_ACCENT = fromCss('#000000');
|
||||
const DEFAULT_FOREGROUND = css.toColor('#ffffff');
|
||||
const DEFAULT_BACKGROUND = css.toColor('#000000');
|
||||
const DEFAULT_CURSOR = css.toColor('#ffffff');
|
||||
const DEFAULT_CURSOR_ACCENT = css.toColor('#000000');
|
||||
const DEFAULT_SELECTION = {
|
||||
css: 'rgba(255, 255, 255, 0.3)',
|
||||
rgba: 0xFFFFFF4D
|
||||
@@ -22,23 +22,23 @@ const DEFAULT_SELECTION = {
|
||||
export const DEFAULT_ANSI_COLORS = (() => {
|
||||
const colors = [
|
||||
// dark:
|
||||
fromCss('#2e3436'),
|
||||
fromCss('#cc0000'),
|
||||
fromCss('#4e9a06'),
|
||||
fromCss('#c4a000'),
|
||||
fromCss('#3465a4'),
|
||||
fromCss('#75507b'),
|
||||
fromCss('#06989a'),
|
||||
fromCss('#d3d7cf'),
|
||||
css.toColor('#2e3436'),
|
||||
css.toColor('#cc0000'),
|
||||
css.toColor('#4e9a06'),
|
||||
css.toColor('#c4a000'),
|
||||
css.toColor('#3465a4'),
|
||||
css.toColor('#75507b'),
|
||||
css.toColor('#06989a'),
|
||||
css.toColor('#d3d7cf'),
|
||||
// bright:
|
||||
fromCss('#555753'),
|
||||
fromCss('#ef2929'),
|
||||
fromCss('#8ae234'),
|
||||
fromCss('#fce94f'),
|
||||
fromCss('#729fcf'),
|
||||
fromCss('#ad7fa8'),
|
||||
fromCss('#34e2e2'),
|
||||
fromCss('#eeeeec')
|
||||
css.toColor('#555753'),
|
||||
css.toColor('#ef2929'),
|
||||
css.toColor('#8ae234'),
|
||||
css.toColor('#fce94f'),
|
||||
css.toColor('#729fcf'),
|
||||
css.toColor('#ad7fa8'),
|
||||
css.toColor('#34e2e2'),
|
||||
css.toColor('#eeeeec')
|
||||
];
|
||||
|
||||
// Fill in the remaining 240 ANSI colors.
|
||||
@@ -49,8 +49,8 @@ export const DEFAULT_ANSI_COLORS = (() => {
|
||||
const g = v[(i / 6) % 6 | 0];
|
||||
const b = v[i % 6];
|
||||
colors.push({
|
||||
css: toCss(r, g, b),
|
||||
rgba: toRgba(r, g, b)
|
||||
css: channels.toCss(r, g, b),
|
||||
rgba: channels.toRgba(r, g, b)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ export const DEFAULT_ANSI_COLORS = (() => {
|
||||
for (let i = 0; i < 24; i++) {
|
||||
const c = 8 + i * 10;
|
||||
colors.push({
|
||||
css: toCss(c, c, c),
|
||||
rgba: toRgba(c, c, c)
|
||||
css: channels.toCss(c, c, c),
|
||||
rgba: channels.toRgba(c, c, c)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export class ColorManager implements IColorManager {
|
||||
cursor: DEFAULT_CURSOR,
|
||||
cursorAccent: DEFAULT_CURSOR_ACCENT,
|
||||
selection: DEFAULT_SELECTION,
|
||||
selectionOpaque: blend(DEFAULT_BACKGROUND, DEFAULT_SELECTION),
|
||||
selectionOpaque: color.blend(DEFAULT_BACKGROUND, DEFAULT_SELECTION),
|
||||
ansi: DEFAULT_ANSI_COLORS.slice(),
|
||||
contrastCache: this._contrastCache
|
||||
};
|
||||
@@ -116,7 +116,7 @@ export class ColorManager implements IColorManager {
|
||||
this.colors.cursor = this._parseColor(theme.cursor, DEFAULT_CURSOR, true);
|
||||
this.colors.cursorAccent = this._parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT, true);
|
||||
this.colors.selection = this._parseColor(theme.selection, DEFAULT_SELECTION, true);
|
||||
this.colors.selectionOpaque = blend(this.colors.background, this.colors.selection);
|
||||
this.colors.selectionOpaque = color.blend(this.colors.background, this.colors.selection);
|
||||
this.colors.ansi[0] = this._parseColor(theme.black, DEFAULT_ANSI_COLORS[0]);
|
||||
this.colors.ansi[1] = this._parseColor(theme.red, DEFAULT_ANSI_COLORS[1]);
|
||||
this.colors.ansi[2] = this._parseColor(theme.green, DEFAULT_ANSI_COLORS[2]);
|
||||
@@ -184,35 +184,23 @@ export class ColorManager implements IColorManager {
|
||||
);
|
||||
return fallback;
|
||||
}
|
||||
let r: number;
|
||||
let g: number;
|
||||
let b: number;
|
||||
let a: number;
|
||||
let rgba: number;
|
||||
if (css.length === 5) {
|
||||
const num = parseInt(css.substr(1), 16);
|
||||
r = ((num >> 12) & 0xF) * 16;
|
||||
g = ((num >> 8) & 0xF) * 16;
|
||||
b = ((num >> 4) & 0xF) * 16;
|
||||
a = (num & 0xF) * 16;
|
||||
rgba = toRgba(r, g, b, a);
|
||||
} else {
|
||||
rgba = parseInt(css.substr(1), 16);
|
||||
r = (rgba >> 24) & 0xFF;
|
||||
g = (rgba >> 16) & 0xFF;
|
||||
b = (rgba >> 8) & 0xFF;
|
||||
a = (rgba ) & 0xFF;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color
|
||||
// the color value has alpha less than 1.0, and the string is the color value in the CSS rgba()
|
||||
const [r, g, b, a] = this._ctx.fillStyle.substring(5, this._ctx.fillStyle.length - 1).split(',').map(component => Number(component));
|
||||
const alpha = Math.round(a * 255);
|
||||
const rgba: number = channels.toRgba(r, g, b, alpha);
|
||||
return {
|
||||
rgba,
|
||||
css: toCss(r, g, b, a)
|
||||
css: channels.toCss(r, g, b, alpha)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
css,
|
||||
rgba: toRgba(data[0], data[1], data[2], data[3])
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color
|
||||
// if it has alpha equal to 1.0, then the string is a lowercase six-digit hex value, prefixed with a "#" character
|
||||
css: this._ctx.fillStyle,
|
||||
rgba: channels.toRgba(data[0], data[1], data[2], data[3])
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IColorSet, IColor } from 'browser/Types';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||
import { throwIfFalsy } from 'browser/renderer/RendererUtils';
|
||||
import { toCss, ensureContrastRatioRgba, opaque } from 'browser/Color';
|
||||
import { channels, color, rgba } from 'browser/Color';
|
||||
|
||||
export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
private _canvas: HTMLCanvasElement;
|
||||
@@ -325,7 +325,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
if (fgOverride) {
|
||||
this._ctx.fillStyle = fgOverride.css;
|
||||
} else if (cell.isBgDefault()) {
|
||||
this._ctx.fillStyle = opaque(this._colors.background).css;
|
||||
this._ctx.fillStyle = color.opaque(this._colors.background).css;
|
||||
} else if (cell.isBgRGB()) {
|
||||
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;
|
||||
} else {
|
||||
@@ -418,7 +418,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
|
||||
const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, isInverse);
|
||||
const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, isInverse, isBold);
|
||||
const result = ensureContrastRatioRgba(bgRgba, fgRgba, this._optionsService.options.minimumContrastRatio);
|
||||
const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._optionsService.options.minimumContrastRatio);
|
||||
|
||||
if (!result) {
|
||||
this._colors.contrastCache.setColor(cell.bg, cell.fg, null);
|
||||
@@ -426,7 +426,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
}
|
||||
|
||||
const color: IColor = {
|
||||
css: toCss(
|
||||
css: channels.toCss(
|
||||
(result >> 24) & 0xFF,
|
||||
(result >> 16) & 0xFF,
|
||||
(result >> 8) & 0xFF
|
||||
|
||||
@@ -11,7 +11,7 @@ import { LRUMap } from 'browser/renderer/atlas/LRUMap';
|
||||
import { isFirefox, isSafari } from 'common/Platform';
|
||||
import { IColor } from 'browser/Types';
|
||||
import { throwIfFalsy } from 'browser/renderer/RendererUtils';
|
||||
import { opaque } from 'browser/Color';
|
||||
import { color } from 'browser/Color';
|
||||
|
||||
// In practice we're probably never going to exhaust a texture this large. For debugging purposes,
|
||||
// however, it can be useful to set this to a really tiny value, to verify that LRU eviction works.
|
||||
@@ -223,7 +223,7 @@ export class DynamicCharAtlas extends BaseCharAtlas {
|
||||
|
||||
private _getForegroundColor(glyph: IGlyphIdentifier): IColor {
|
||||
if (glyph.fg === INVERTED_DEFAULT_COLOR) {
|
||||
return opaque(this._config.colors.background);
|
||||
return color.opaque(this._config.colors.background);
|
||||
} else if (glyph.fg < 256) {
|
||||
// 256 color support
|
||||
return this._getColorFromAnsiIndex(glyph.fg);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { IColorSet, ILinkifierEvent, ILinkifier } from 'browser/Types';
|
||||
import { ICharSizeService } from 'browser/services/Services';
|
||||
import { IOptionsService, IBufferService } from 'common/services/Services';
|
||||
import { EventEmitter, IEvent } from 'common/EventEmitter';
|
||||
import { opaque } from 'browser/Color';
|
||||
import { color } from 'browser/Color';
|
||||
|
||||
const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
|
||||
const ROW_CONTAINER_CLASS = 'xterm-rows';
|
||||
@@ -231,7 +231,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
`${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
|
||||
});
|
||||
styles +=
|
||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${opaque(this._colors.background).css}; }` +
|
||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(this._colors.background).css}; }` +
|
||||
`${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`;
|
||||
|
||||
this._themeStyleElement.innerHTML = styles;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
import { IBufferLine } from 'common/Types';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { MockOptionsService } from 'common/TestUtils.test';
|
||||
import { fromCss } from 'browser/Color';
|
||||
import { css } from 'browser/Color';
|
||||
|
||||
describe('DomRendererRowFactory', () => {
|
||||
let dom: jsdom.JSDOM;
|
||||
@@ -21,27 +21,27 @@ describe('DomRendererRowFactory', () => {
|
||||
beforeEach(() => {
|
||||
dom = new jsdom.JSDOM('');
|
||||
rowFactory = new DomRendererRowFactory(dom.window.document, new MockOptionsService({ drawBoldTextInBrightColors: true }), {
|
||||
background: fromCss('#010101'),
|
||||
foreground: fromCss('#020202'),
|
||||
background: css.toColor('#010101'),
|
||||
foreground: css.toColor('#020202'),
|
||||
ansi: [
|
||||
// dark:
|
||||
fromCss('#2e3436'),
|
||||
fromCss('#cc0000'),
|
||||
fromCss('#4e9a06'),
|
||||
fromCss('#c4a000'),
|
||||
fromCss('#3465a4'),
|
||||
fromCss('#75507b'),
|
||||
fromCss('#06989a'),
|
||||
fromCss('#d3d7cf'),
|
||||
css.toColor('#2e3436'),
|
||||
css.toColor('#cc0000'),
|
||||
css.toColor('#4e9a06'),
|
||||
css.toColor('#c4a000'),
|
||||
css.toColor('#3465a4'),
|
||||
css.toColor('#75507b'),
|
||||
css.toColor('#06989a'),
|
||||
css.toColor('#d3d7cf'),
|
||||
// bright:
|
||||
fromCss('#555753'),
|
||||
fromCss('#ef2929'),
|
||||
fromCss('#8ae234'),
|
||||
fromCss('#fce94f'),
|
||||
fromCss('#729fcf'),
|
||||
fromCss('#ad7fa8'),
|
||||
fromCss('#34e2e2'),
|
||||
fromCss('#eeeeec')
|
||||
css.toColor('#555753'),
|
||||
css.toColor('#ef2929'),
|
||||
css.toColor('#8ae234'),
|
||||
css.toColor('#fce94f'),
|
||||
css.toColor('#729fcf'),
|
||||
css.toColor('#ad7fa8'),
|
||||
css.toColor('#34e2e2'),
|
||||
css.toColor('#eeeeec')
|
||||
]
|
||||
} as any);
|
||||
lineData = createEmptyLineData(2);
|
||||
|
||||
@@ -8,7 +8,7 @@ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
||||
import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/Constants';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { IOptionsService } from 'common/services/Services';
|
||||
import { ensureContrastRatio, rgbaToColor } from 'browser/Color';
|
||||
import { color, rgba } from 'browser/Color';
|
||||
import { IColorSet, IColor } from 'browser/Types';
|
||||
|
||||
export const BOLD_CLASS = 'xterm-bold';
|
||||
@@ -133,7 +133,7 @@ export class DomRendererRowFactory {
|
||||
}
|
||||
break;
|
||||
case Attributes.CM_RGB:
|
||||
const color = rgbaToColor(
|
||||
const color = rgba.toColor(
|
||||
(fg >> 16) & 0xFF,
|
||||
(fg >> 8) & 0xFF,
|
||||
(fg ) & 0xFF
|
||||
@@ -182,7 +182,7 @@ export class DomRendererRowFactory {
|
||||
|
||||
// Calculate and store in cache
|
||||
if (adjustedColor === undefined) {
|
||||
adjustedColor = ensureContrastRatio(bg, fg, this._optionsService.options.minimumContrastRatio);
|
||||
adjustedColor = color.ensureContrastRatio(bg, fg, this._optionsService.options.minimumContrastRatio);
|
||||
this._colors.contrastCache.setColor(this._workCell.bg, this._workCell.fg, adjustedColor ?? null);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+3
-3
@@ -123,9 +123,9 @@ export interface IBufferLine {
|
||||
setCell(index: number, cell: ICellData): void;
|
||||
setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number): void;
|
||||
addCodepointToCell(index: number, codePoint: number): void;
|
||||
insertCells(pos: number, n: number, ch: ICellData): void;
|
||||
deleteCells(pos: number, n: number, fill: ICellData): void;
|
||||
replaceCells(start: number, end: number, fill: ICellData): void;
|
||||
insertCells(pos: number, n: number, ch: ICellData, eraseAttr?: IAttributeData): void;
|
||||
deleteCells(pos: number, n: number, fill: ICellData, eraseAttr?: IAttributeData): void;
|
||||
replaceCells(start: number, end: number, fill: ICellData, eraseAttr?: IAttributeData): void;
|
||||
resize(cols: number, fill: ICellData): void;
|
||||
fill(fillCellData: ICellData): void;
|
||||
copyFrom(line: IBufferLine): void;
|
||||
|
||||
@@ -366,4 +366,94 @@ describe('BufferLine', function(): void {
|
||||
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
});
|
||||
});
|
||||
describe('correct fullwidth handling', () => {
|
||||
function populate(line: BufferLine): void {
|
||||
const cell = CellData.fromCharData([1, '¥', 2, '¥'.charCodeAt(0)]);
|
||||
for (let i = 0; i < line.length; i += 2) {
|
||||
line.setCell(i, cell);
|
||||
}
|
||||
}
|
||||
it('insert - wide char at pos', () => {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.insertCells(9, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), '¥¥¥¥ a');
|
||||
line.insertCells(8, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), '¥¥¥¥a ');
|
||||
line.insertCells(1, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' a ¥¥¥a');
|
||||
});
|
||||
it('insert - wide char at end', () => {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.insertCells(0, 3, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaa¥¥¥ ');
|
||||
line.insertCells(4, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaa a ¥¥');
|
||||
line.insertCells(4, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaa aa ¥ ');
|
||||
});
|
||||
it('delete', () => {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.deleteCells(0, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' ¥¥¥¥a');
|
||||
line.deleteCells(5, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' ¥¥¥aaa');
|
||||
line.deleteCells(0, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' ¥¥aaaaa');
|
||||
});
|
||||
it('replace - start at 0', () => {
|
||||
let line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(0, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'a ¥¥¥¥');
|
||||
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(0, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'aa¥¥¥¥');
|
||||
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(0, 3, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaa ¥¥¥');
|
||||
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(0, 8, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaaaaaaa¥');
|
||||
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(0, 9, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaaaaaaaa ');
|
||||
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(0, 10, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaaaaaaaaa');
|
||||
});
|
||||
it('replace - start at 1', () => {
|
||||
let line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(1, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' a¥¥¥¥');
|
||||
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(1, 3, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' aa ¥¥¥');
|
||||
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(1, 4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' aaa¥¥¥');
|
||||
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(1, 8, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' aaaaaaa¥');
|
||||
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(1, 9, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' aaaaaaaa ');
|
||||
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
populate(line);
|
||||
line.replaceCells(1, 10, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' aaaaaaaaa');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { CharData, IBufferLine, ICellData } from 'common/Types';
|
||||
import { CharData, IBufferLine, ICellData, IAttributeData } from 'common/Types';
|
||||
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
||||
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
@@ -228,8 +228,14 @@ export class BufferLine implements IBufferLine {
|
||||
}
|
||||
}
|
||||
|
||||
public insertCells(pos: number, n: number, fillCellData: ICellData): void {
|
||||
public insertCells(pos: number, n: number, fillCellData: ICellData, eraseAttr?: IAttributeData): void {
|
||||
pos %= this.length;
|
||||
|
||||
// handle fullwidth at pos: reset cell one to the left if pos is second cell of a wide char
|
||||
if (pos && this.getWidth(pos - 1) === 2) {
|
||||
this.setCellFromCodePoint(pos - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
}
|
||||
|
||||
if (n < this.length - pos) {
|
||||
const cell = new CellData();
|
||||
for (let i = this.length - pos - n - 1; i >= 0; --i) {
|
||||
@@ -243,9 +249,14 @@ export class BufferLine implements IBufferLine {
|
||||
this.setCell(i, fillCellData);
|
||||
}
|
||||
}
|
||||
|
||||
// handle fullwidth at line end: reset last cell if it is first cell of a wide char
|
||||
if (this.getWidth(this.length - 1) === 2) {
|
||||
this.setCellFromCodePoint(this.length - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
}
|
||||
}
|
||||
|
||||
public deleteCells(pos: number, n: number, fillCellData: ICellData): void {
|
||||
public deleteCells(pos: number, n: number, fillCellData: ICellData, eraseAttr?: IAttributeData): void {
|
||||
pos %= this.length;
|
||||
if (n < this.length - pos) {
|
||||
const cell = new CellData();
|
||||
@@ -260,9 +271,28 @@ export class BufferLine implements IBufferLine {
|
||||
this.setCell(i, fillCellData);
|
||||
}
|
||||
}
|
||||
|
||||
// handle fullwidth at pos:
|
||||
// - reset pos-1 if wide char
|
||||
// - reset pos if width==0 (previous second cell of a wide char)
|
||||
if (pos && this.getWidth(pos - 1) === 2) {
|
||||
this.setCellFromCodePoint(pos - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
}
|
||||
if (this.getWidth(pos) === 0 && !this.hasContent(pos)) {
|
||||
this.setCellFromCodePoint(pos, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
}
|
||||
}
|
||||
|
||||
public replaceCells(start: number, end: number, fillCellData: ICellData): void {
|
||||
public replaceCells(start: number, end: number, fillCellData: ICellData, eraseAttr?: IAttributeData): void {
|
||||
// handle fullwidth at start: reset cell one to the left if start is second cell of a wide char
|
||||
if (start && this.getWidth(start - 1) === 2) {
|
||||
this.setCellFromCodePoint(start - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
}
|
||||
// handle fullwidth at last cell + 1: reset to empty cell if it is second part of a wide char
|
||||
if (end < this.length && this.getWidth(end - 1) === 2) {
|
||||
this.setCellFromCodePoint(end, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
}
|
||||
|
||||
while (start < end && start < this.length) {
|
||||
this.setCell(start++, fillCellData);
|
||||
}
|
||||
|
||||
+20
-5
@@ -77,10 +77,13 @@ export class Terminal implements ITerminalApi {
|
||||
public deregisterCharacterJoiner(joinerId: number): void {
|
||||
this._core.deregisterCharacterJoiner(joinerId);
|
||||
}
|
||||
public addMarker(cursorYOffset: number): IMarker {
|
||||
public registerMarker(cursorYOffset: number): IMarker {
|
||||
this._verifyIntegers(cursorYOffset);
|
||||
return this._core.addMarker(cursorYOffset);
|
||||
}
|
||||
public addMarker(cursorYOffset: number): IMarker {
|
||||
return this.registerMarker(cursorYOffset);
|
||||
}
|
||||
public hasSelection(): boolean {
|
||||
return this._core.hasSelection();
|
||||
}
|
||||
@@ -230,18 +233,30 @@ class BufferCellApiView implements IBufferCellApi {
|
||||
class ParserApi implements IParser {
|
||||
constructor(private _core: ITerminal) {}
|
||||
|
||||
public addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
|
||||
public registerCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
|
||||
return this._core.addCsiHandler(id, (params: IParams) => callback(params.toArray()));
|
||||
}
|
||||
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable {
|
||||
public addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
|
||||
return this.registerCsiHandler(id, callback);
|
||||
}
|
||||
public registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable {
|
||||
return this._core.addDcsHandler(id, (data: string, params: IParams) => callback(data, params.toArray()));
|
||||
}
|
||||
public addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
|
||||
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable {
|
||||
return this.registerDcsHandler(id, callback);
|
||||
}
|
||||
public registerEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
|
||||
return this._core.addEscHandler(id, handler);
|
||||
}
|
||||
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
public addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
|
||||
return this.registerEscHandler(id, handler);
|
||||
}
|
||||
public registerOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
return this._core.addOscHandler(ident, callback);
|
||||
}
|
||||
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
return this.registerOscHandler(ident, callback);
|
||||
}
|
||||
}
|
||||
|
||||
class UnicodeApi implements IUnicodeHandling {
|
||||
|
||||
Vendored
+25
@@ -623,6 +623,11 @@ declare module 'xterm' {
|
||||
* alt buffer is active, undefined is returned.
|
||||
* @param cursorYOffset The y position offset of the marker from the cursor.
|
||||
*/
|
||||
registerMarker(cursorYOffset: number): IMarker;
|
||||
|
||||
/**
|
||||
* @deprecated use `registerMarker` instead.
|
||||
*/
|
||||
addMarker(cursorYOffset: number): IMarker;
|
||||
|
||||
/**
|
||||
@@ -1089,6 +1094,11 @@ declare module 'xterm' {
|
||||
* The most recently added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
registerCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* @deprecated use `registerMarker` instead.
|
||||
*/
|
||||
addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
@@ -1108,6 +1118,11 @@ declare module 'xterm' {
|
||||
* The most recently added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* @deprecated use `registerMarker` instead.
|
||||
*/
|
||||
addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
@@ -1121,6 +1136,11 @@ declare module 'xterm' {
|
||||
* The most recently added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
registerEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* @deprecated use `registerMarker` instead.
|
||||
*/
|
||||
addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
@@ -1139,6 +1159,11 @@ declare module 'xterm' {
|
||||
* The most recently added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
registerOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* @deprecated use `registerMarker` instead.
|
||||
*/
|
||||
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user