lightweight-charts
Version:
Financial lightweight charts built with HTML5 canvas
1,204 lines (1,178 loc) • 512 kB
JavaScript
/*!
* @license
* TradingView Lightweight Charts v3.5.0
* Copyright (c) 2020 TradingView, Inc.
* Licensed under Apache License 2.0 https://www.apache.org/licenses/LICENSE-2.0
*/
import { bindToDevicePixelRatio } from 'fancy-canvas/coordinate-space';
var LineType;
(function (LineType) {
LineType[LineType["Simple"] = 0] = "Simple";
LineType[LineType["WithSteps"] = 1] = "WithSteps";
})(LineType || (LineType = {}));
var LineStyle;
(function (LineStyle) {
LineStyle[LineStyle["Solid"] = 0] = "Solid";
LineStyle[LineStyle["Dotted"] = 1] = "Dotted";
LineStyle[LineStyle["Dashed"] = 2] = "Dashed";
LineStyle[LineStyle["LargeDashed"] = 3] = "LargeDashed";
LineStyle[LineStyle["SparseDotted"] = 4] = "SparseDotted";
})(LineStyle || (LineStyle = {}));
function setLineStyle(ctx, style) {
var _a;
var dashPatterns = (_a = {},
_a[0 /* Solid */] = [],
_a[1 /* Dotted */] = [ctx.lineWidth, ctx.lineWidth],
_a[2 /* Dashed */] = [2 * ctx.lineWidth, 2 * ctx.lineWidth],
_a[3 /* LargeDashed */] = [6 * ctx.lineWidth, 6 * ctx.lineWidth],
_a[4 /* SparseDotted */] = [ctx.lineWidth, 4 * ctx.lineWidth],
_a);
var dashPattern = dashPatterns[style];
ctx.setLineDash(dashPattern);
}
function drawHorizontalLine(ctx, y, left, right) {
ctx.beginPath();
var correction = (ctx.lineWidth % 2) ? 0.5 : 0;
ctx.moveTo(left, y + correction);
ctx.lineTo(right, y + correction);
ctx.stroke();
}
function drawVerticalLine(ctx, x, top, bottom) {
ctx.beginPath();
var correction = (ctx.lineWidth % 2) ? 0.5 : 0;
ctx.moveTo(x + correction, top);
ctx.lineTo(x + correction, bottom);
ctx.stroke();
}
function strokeInPixel(ctx, drawFunction) {
ctx.save();
if (ctx.lineWidth % 2) {
ctx.translate(0.5, 0.5);
}
drawFunction();
ctx.restore();
}
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function __spreadArray(to, from) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
to[j] = from[i];
return to;
}
/**
* Checks an assertion. Throws if the assertion is failed.
*
* @param condition - Result of the assertion evaluation
* @param message - Text to include in the exception message
*/
function assert(condition, message) {
if (!condition) {
throw new Error('Assertion failed' + (message ? ': ' + message : ''));
}
}
function ensureDefined(value) {
if (value === undefined) {
throw new Error('Value is undefined');
}
return value;
}
function ensureNotNull(value) {
if (value === null) {
throw new Error('Value is null');
}
return value;
}
function ensure(value) {
return ensureNotNull(ensureDefined(value));
}
/**
* Compile time check for never
*/
function ensureNever(value) { }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function merge(dst) {
var sources = [];
for (var _i = 1; _i < arguments.length; _i++) {
sources[_i - 1] = arguments[_i];
}
for (var _a = 0, sources_1 = sources; _a < sources_1.length; _a++) {
var src = sources_1[_a];
// eslint-disable-next-line no-restricted-syntax
for (var i in src) {
if (src[i] === undefined) {
continue;
}
if ('object' !== typeof src[i] || dst[i] === undefined) {
dst[i] = src[i];
}
else {
merge(dst[i], src[i]);
}
}
}
return dst;
}
function isNumber(value) {
return (typeof value === 'number') && (isFinite(value));
}
function isInteger(value) {
return (typeof value === 'number') && ((value % 1) === 0);
}
function isString(value) {
return typeof value === 'string';
}
function isBoolean(value) {
return typeof value === 'boolean';
}
function clone(object) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
var o = object;
if (!o || 'object' !== typeof o) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return o;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
var c;
if (Array.isArray(o)) {
c = [];
}
else {
c = {};
}
var p;
var v;
// eslint-disable-next-line no-restricted-syntax
for (p in o) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call,no-prototype-builtins
if (o.hasOwnProperty(p)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
v = o[p];
if (v && 'object' === typeof v) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
c[p] = clone(v);
}
else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
c[p] = v;
}
}
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return c;
}
function notNull(t) {
return t !== null;
}
function undefinedIfNull(t) {
return (t === null) ? undefined : t;
}
var CompositeRenderer = /** @class */ (function () {
function CompositeRenderer() {
this._private__renderers = [];
}
CompositeRenderer.prototype._internal_setRenderers = function (renderers) {
this._private__renderers = renderers;
};
CompositeRenderer.prototype._internal_draw = function (ctx, pixelRatio, isHovered, hitTestData) {
this._private__renderers.forEach(function (r) {
ctx.save();
r._internal_draw(ctx, pixelRatio, isHovered, hitTestData);
ctx.restore();
});
};
return CompositeRenderer;
}());
var ScaledRenderer = /** @class */ (function () {
function ScaledRenderer() {
}
ScaledRenderer.prototype._internal_draw = function (ctx, pixelRatio, isHovered, hitTestData) {
ctx.save();
// actually we must be sure that this scaling applied only once at the same time
// currently ScaledRenderer could be only nodes renderer (not top-level renderers like CompositeRenderer or something)
// so this "constraint" is fulfilled for now
ctx.scale(pixelRatio, pixelRatio);
this._internal__drawImpl(ctx, isHovered, hitTestData);
ctx.restore();
};
ScaledRenderer.prototype._internal_drawBackground = function (ctx, pixelRatio, isHovered, hitTestData) {
ctx.save();
// actually we must be sure that this scaling applied only once at the same time
// currently ScaledRenderer could be only nodes renderer (not top-level renderers like CompositeRenderer or something)
// so this "constraint" is fulfilled for now
ctx.scale(pixelRatio, pixelRatio);
this._internal__drawBackgroundImpl(ctx, isHovered, hitTestData);
ctx.restore();
};
ScaledRenderer.prototype._internal__drawBackgroundImpl = function (ctx, isHovered, hitTestData) { };
return ScaledRenderer;
}());
var PaneRendererMarks = /** @class */ (function (_super) {
__extends(PaneRendererMarks, _super);
function PaneRendererMarks() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this._internal__data = null;
return _this;
}
PaneRendererMarks.prototype._internal_setData = function (data) {
this._internal__data = data;
};
PaneRendererMarks.prototype._internal__drawImpl = function (ctx) {
if (this._internal__data === null || this._internal__data._internal_visibleRange === null) {
return;
}
var visibleRange = this._internal__data._internal_visibleRange;
var data = this._internal__data;
var draw = function (radius) {
ctx.beginPath();
for (var i = visibleRange.to - 1; i >= visibleRange.from; --i) {
var point = data._internal_items[i];
ctx.moveTo(point._internal_x, point._internal_y);
ctx.arc(point._internal_x, point._internal_y, radius, 0, Math.PI * 2);
}
ctx.fill();
};
ctx.fillStyle = data._internal_backColor;
draw(data._internal_radius + 2);
ctx.fillStyle = data._internal_lineColor;
draw(data._internal_radius);
};
return PaneRendererMarks;
}(ScaledRenderer));
function createEmptyMarkerData(chartOptions) {
return {
_internal_items: [{
_internal_x: 0,
_internal_y: 0,
_internal_time: 0,
_internal_price: 0,
}],
_internal_lineColor: '',
_internal_backColor: chartOptions.layout.backgroundColor,
_internal_radius: 0,
_internal_visibleRange: null,
};
}
var rangeForSinglePoint = { from: 0, to: 1 };
var CrosshairMarksPaneView = /** @class */ (function () {
function CrosshairMarksPaneView(chartModel, crosshair) {
this._private__compositeRenderer = new CompositeRenderer();
this._private__markersRenderers = [];
this._private__markersData = [];
this._private__invalidated = true;
this._private__chartModel = chartModel;
this._private__crosshair = crosshair;
this._private__compositeRenderer._internal_setRenderers(this._private__markersRenderers);
}
CrosshairMarksPaneView.prototype._internal_update = function (updateType) {
var _this = this;
var serieses = this._private__chartModel._internal_serieses();
if (serieses.length !== this._private__markersRenderers.length) {
this._private__markersData = serieses.map(function () { return createEmptyMarkerData(_this._private__chartModel._internal_options()); });
this._private__markersRenderers = this._private__markersData.map(function (data) {
var res = new PaneRendererMarks();
res._internal_setData(data);
return res;
});
this._private__compositeRenderer._internal_setRenderers(this._private__markersRenderers);
}
this._private__invalidated = true;
};
CrosshairMarksPaneView.prototype._internal_renderer = function (height, width, addAnchors) {
if (this._private__invalidated) {
this._private__updateImpl();
this._private__invalidated = false;
}
return this._private__compositeRenderer;
};
CrosshairMarksPaneView.prototype._private__updateImpl = function () {
var _this = this;
var serieses = this._private__chartModel._internal_serieses();
var timePointIndex = this._private__crosshair._internal_appliedIndex();
var timeScale = this._private__chartModel._internal_timeScale();
serieses.forEach(function (s, index) {
var data = _this._private__markersData[index];
var seriesData = s._internal_markerDataAtIndex(timePointIndex);
if (seriesData === null || !s._internal_visible()) {
data._internal_visibleRange = null;
return;
}
var firstValue = ensureNotNull(s._internal_firstValue());
data._internal_lineColor = seriesData._internal_backgroundColor;
data._internal_backColor = seriesData._internal_borderColor;
data._internal_radius = seriesData._internal_radius;
data._internal_items[0]._internal_price = seriesData._internal_price;
data._internal_items[0]._internal_y = s._internal_priceScale()._internal_priceToCoordinate(seriesData._internal_price, firstValue._internal_value);
data._internal_items[0]._internal_time = timePointIndex;
data._internal_items[0]._internal_x = timeScale._internal_indexToCoordinate(timePointIndex);
data._internal_visibleRange = rangeForSinglePoint;
});
};
return CrosshairMarksPaneView;
}());
var CrosshairRenderer = /** @class */ (function () {
function CrosshairRenderer(data) {
this._private__data = data;
}
CrosshairRenderer.prototype._internal_draw = function (ctx, pixelRatio, isHovered, hitTestData) {
if (this._private__data === null) {
return;
}
var vertLinesVisible = this._private__data._internal_vertLine._internal_visible;
var horzLinesVisible = this._private__data._internal_horzLine._internal_visible;
if (!vertLinesVisible && !horzLinesVisible) {
return;
}
ctx.save();
var x = Math.round(this._private__data._internal_x * pixelRatio);
var y = Math.round(this._private__data._internal_y * pixelRatio);
var w = Math.ceil(this._private__data._internal_w * pixelRatio);
var h = Math.ceil(this._private__data._internal_h * pixelRatio);
ctx.lineCap = 'butt';
if (vertLinesVisible && x >= 0) {
ctx.lineWidth = Math.floor(this._private__data._internal_vertLine._internal_lineWidth * pixelRatio);
ctx.strokeStyle = this._private__data._internal_vertLine._internal_color;
ctx.fillStyle = this._private__data._internal_vertLine._internal_color;
setLineStyle(ctx, this._private__data._internal_vertLine._internal_lineStyle);
drawVerticalLine(ctx, x, 0, h);
}
if (horzLinesVisible && y >= 0) {
ctx.lineWidth = Math.floor(this._private__data._internal_horzLine._internal_lineWidth * pixelRatio);
ctx.strokeStyle = this._private__data._internal_horzLine._internal_color;
ctx.fillStyle = this._private__data._internal_horzLine._internal_color;
setLineStyle(ctx, this._private__data._internal_horzLine._internal_lineStyle);
drawHorizontalLine(ctx, y, 0, w);
}
ctx.restore();
};
return CrosshairRenderer;
}());
var CrosshairPaneView = /** @class */ (function () {
function CrosshairPaneView(source) {
this._private__invalidated = true;
this._private__rendererData = {
_internal_vertLine: {
_internal_lineWidth: 1,
_internal_lineStyle: 0,
_internal_color: '',
_internal_visible: false,
},
_internal_horzLine: {
_internal_lineWidth: 1,
_internal_lineStyle: 0,
_internal_color: '',
_internal_visible: false,
},
_internal_w: 0,
_internal_h: 0,
_internal_x: 0,
_internal_y: 0,
};
this._private__renderer = new CrosshairRenderer(this._private__rendererData);
this._private__source = source;
}
CrosshairPaneView.prototype._internal_update = function () {
this._private__invalidated = true;
};
CrosshairPaneView.prototype._internal_renderer = function (height, width) {
if (this._private__invalidated) {
this._private__updateImpl();
this._private__invalidated = false;
}
return this._private__renderer;
};
CrosshairPaneView.prototype._private__updateImpl = function () {
var visible = this._private__source._internal_visible();
var pane = ensureNotNull(this._private__source._internal_pane());
var crosshairOptions = pane._internal_model()._internal_options().crosshair;
var data = this._private__rendererData;
data._internal_horzLine._internal_visible = visible && this._private__source._internal_horzLineVisible(pane);
data._internal_vertLine._internal_visible = visible && this._private__source._internal_vertLineVisible();
data._internal_horzLine._internal_lineWidth = crosshairOptions.horzLine.width;
data._internal_horzLine._internal_lineStyle = crosshairOptions.horzLine.style;
data._internal_horzLine._internal_color = crosshairOptions.horzLine.color;
data._internal_vertLine._internal_lineWidth = crosshairOptions.vertLine.width;
data._internal_vertLine._internal_lineStyle = crosshairOptions.vertLine.style;
data._internal_vertLine._internal_color = crosshairOptions.vertLine.color;
data._internal_w = pane._internal_width();
data._internal_h = pane._internal_height();
data._internal_x = this._private__source._internal_appliedX();
data._internal_y = this._private__source._internal_appliedY();
};
return CrosshairPaneView;
}());
/** @public see https://developer.mozilla.org/en-US/docs/Web/CSS/color_value */
var namedColorRgbHexStrings = {
// The order of properties in this Record is not important for the internal logic.
// It's just GZIPped better when props follows this order.
// Please add new colors to the end of the record.
khaki: '#f0e68c',
azure: '#f0ffff',
aliceblue: '#f0f8ff',
ghostwhite: '#f8f8ff',
gold: '#ffd700',
goldenrod: '#daa520',
gainsboro: '#dcdcdc',
gray: '#808080',
green: '#008000',
honeydew: '#f0fff0',
floralwhite: '#fffaf0',
lightblue: '#add8e6',
lightcoral: '#f08080',
lemonchiffon: '#fffacd',
hotpink: '#ff69b4',
lightyellow: '#ffffe0',
greenyellow: '#adff2f',
lightgoldenrodyellow: '#fafad2',
limegreen: '#32cd32',
linen: '#faf0e6',
lightcyan: '#e0ffff',
magenta: '#f0f',
maroon: '#800000',
olive: '#808000',
orange: '#ffa500',
oldlace: '#fdf5e6',
mediumblue: '#0000cd',
transparent: '#0000',
lime: '#0f0',
lightpink: '#ffb6c1',
mistyrose: '#ffe4e1',
moccasin: '#ffe4b5',
midnightblue: '#191970',
orchid: '#da70d6',
mediumorchid: '#ba55d3',
mediumturquoise: '#48d1cc',
orangered: '#ff4500',
royalblue: '#4169e1',
powderblue: '#b0e0e6',
red: '#f00',
coral: '#ff7f50',
turquoise: '#40e0d0',
white: '#fff',
whitesmoke: '#f5f5f5',
wheat: '#f5deb3',
teal: '#008080',
steelblue: '#4682b4',
bisque: '#ffe4c4',
aquamarine: '#7fffd4',
aqua: '#0ff',
sienna: '#a0522d',
silver: '#c0c0c0',
springgreen: '#00ff7f',
antiquewhite: '#faebd7',
burlywood: '#deb887',
brown: '#a52a2a',
beige: '#f5f5dc',
chocolate: '#d2691e',
chartreuse: '#7fff00',
cornflowerblue: '#6495ed',
cornsilk: '#fff8dc',
crimson: '#dc143c',
cadetblue: '#5f9ea0',
tomato: '#ff6347',
fuchsia: '#f0f',
blue: '#00f',
salmon: '#fa8072',
blanchedalmond: '#ffebcd',
slateblue: '#6a5acd',
slategray: '#708090',
thistle: '#d8bfd8',
tan: '#d2b48c',
cyan: '#0ff',
darkblue: '#00008b',
darkcyan: '#008b8b',
darkgoldenrod: '#b8860b',
darkgray: '#a9a9a9',
blueviolet: '#8a2be2',
black: '#000',
darkmagenta: '#8b008b',
darkslateblue: '#483d8b',
darkkhaki: '#bdb76b',
darkorchid: '#9932cc',
darkorange: '#ff8c00',
darkgreen: '#006400',
darkred: '#8b0000',
dodgerblue: '#1e90ff',
darkslategray: '#2f4f4f',
dimgray: '#696969',
deepskyblue: '#00bfff',
firebrick: '#b22222',
forestgreen: '#228b22',
indigo: '#4b0082',
ivory: '#fffff0',
lavenderblush: '#fff0f5',
feldspar: '#d19275',
indianred: '#cd5c5c',
lightgreen: '#90ee90',
lightgrey: '#d3d3d3',
lightskyblue: '#87cefa',
lightslategray: '#789',
lightslateblue: '#8470ff',
snow: '#fffafa',
lightseagreen: '#20b2aa',
lightsalmon: '#ffa07a',
darksalmon: '#e9967a',
darkviolet: '#9400d3',
mediumpurple: '#9370d8',
mediumaquamarine: '#66cdaa',
skyblue: '#87ceeb',
lavender: '#e6e6fa',
lightsteelblue: '#b0c4de',
mediumvioletred: '#c71585',
mintcream: '#f5fffa',
navajowhite: '#ffdead',
navy: '#000080',
olivedrab: '#6b8e23',
palevioletred: '#d87093',
violetred: '#d02090',
yellow: '#ff0',
yellowgreen: '#9acd32',
lawngreen: '#7cfc00',
pink: '#ffc0cb',
paleturquoise: '#afeeee',
palegoldenrod: '#eee8aa',
darkolivegreen: '#556b2f',
darkseagreen: '#8fbc8f',
darkturquoise: '#00ced1',
peachpuff: '#ffdab9',
deeppink: '#ff1493',
violet: '#ee82ee',
palegreen: '#98fb98',
mediumseagreen: '#3cb371',
peru: '#cd853f',
saddlebrown: '#8b4513',
sandybrown: '#f4a460',
rosybrown: '#bc8f8f',
purple: '#800080',
seagreen: '#2e8b57',
seashell: '#fff5ee',
papayawhip: '#ffefd5',
mediumslateblue: '#7b68ee',
plum: '#dda0dd',
mediumspringgreen: '#00fa9a',
};
function normalizeRgbComponent(component) {
if (component < 0) {
return 0;
}
if (component > 255) {
return 255;
}
// NaN values are treated as 0
return (Math.round(component) || 0);
}
/**
* @example
* #fb0
* @example
* #f0f
* @example
* #f0fa
*/
var shortHexRe = /^#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/i;
/**
* @example
* #00ff00
* @example
* #336699
* @example
* #336699FA
*/
var hexRe = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i;
/**
* @example
* rgb(123, 234, 45)
* @example
* rgb(255,234,245)
*/
var rgbRe = /^rgb\(\s*(-?\d{1,10})\s*,\s*(-?\d{1,10})\s*,\s*(-?\d{1,10})\s*\)$/;
/**
* @example
* rgba(123, 234, 45, 1)
* @example
* rgba(255,234,245,0.1)
*/
var rgbaRe = /^rgba\(\s*(-?\d{1,10})\s*,\s*(-?\d{1,10})\s*,\s*(-?\d{1,10})\s*,\s*(-?[\d]{0,10}(?:\.\d+)?)\s*\)$/;
function colorStringToRgb(colorString) {
colorString = colorString.toLowerCase();
// eslint-disable-next-line no-restricted-syntax
if (colorString in namedColorRgbHexStrings) {
colorString = namedColorRgbHexStrings[colorString];
}
{
var matches = rgbaRe.exec(colorString) || rgbRe.exec(colorString);
if (matches) {
return [
normalizeRgbComponent(parseInt(matches[1], 10)),
normalizeRgbComponent(parseInt(matches[2], 10)),
normalizeRgbComponent(parseInt(matches[3], 10)),
];
}
}
{
var matches = hexRe.exec(colorString);
if (matches) {
return [
normalizeRgbComponent(parseInt(matches[1], 16)),
normalizeRgbComponent(parseInt(matches[2], 16)),
normalizeRgbComponent(parseInt(matches[3], 16)),
];
}
}
{
var matches = shortHexRe.exec(colorString);
if (matches) {
return [
normalizeRgbComponent(parseInt(matches[1], 16) * 0x11),
normalizeRgbComponent(parseInt(matches[2], 16) * 0x11),
normalizeRgbComponent(parseInt(matches[3], 16) * 0x11),
];
}
}
throw new Error("Cannot parse color: " + colorString);
}
function rgbToGrayscale(rgbValue) {
// Originally, the NTSC RGB to YUV formula
// perfected by @eugene-korobko's black magic
var redComponentGrayscaleWeight = 0.199;
var greenComponentGrayscaleWeight = 0.687;
var blueComponentGrayscaleWeight = 0.114;
return (redComponentGrayscaleWeight * rgbValue[0] +
greenComponentGrayscaleWeight * rgbValue[1] +
blueComponentGrayscaleWeight * rgbValue[2]);
}
function generateContrastColors(backgroundColor) {
var rgb = colorStringToRgb(backgroundColor);
return {
_internal_background: "rgb(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ")",
_internal_foreground: rgbToGrayscale(rgb) > 160 ? 'black' : 'white',
};
}
/**
* (x, y)
* O***********************|*****
* | border | ^
* | ***************** | |
* | | | | |
* | b | | b | h
* | o | | o | e
* | r | | r | i
* | d | | d | g
* | e | | e | h
* | r | | r | t
* | | | | |
* | ***************** | |
* | border | v
* |***********************|*****
* | |
* |<------- width ------->|
*
* Fills rectangle's inner border (so, all the filled area is limited by the [x, x + width]*[y, y + height] region)
*
* @param ctx context to draw on
* @param x left side of the target rectangle
* @param y top side of the target rectangle
* @param width width of the target rectangle
* @param height height of the target rectangle
* @param borderWidth width of border to fill, must be less than width and height of the target rectangle
*/
function fillRectInnerBorder(ctx, x, y, width, height, borderWidth) {
// horizontal (top and bottom) edges
ctx.fillRect(x + borderWidth, y, width - borderWidth * 2, borderWidth);
ctx.fillRect(x + borderWidth, y + height - borderWidth, width - borderWidth * 2, borderWidth);
// vertical (left and right) edges
ctx.fillRect(x, y, borderWidth, height);
ctx.fillRect(x + width - borderWidth, y, borderWidth, height);
}
function drawScaled(ctx, ratio, func) {
ctx.save();
ctx.scale(ratio, ratio);
func();
ctx.restore();
}
function clearRect(ctx, x, y, w, h, clearColor) {
ctx.save();
ctx.globalCompositeOperation = 'copy';
ctx.fillStyle = clearColor;
ctx.fillRect(x, y, w, h);
ctx.restore();
}
var PriceAxisViewRenderer = /** @class */ (function () {
function PriceAxisViewRenderer(data, commonData) {
this._internal_setData(data, commonData);
}
PriceAxisViewRenderer.prototype._internal_setData = function (data, commonData) {
this._private__data = data;
this._private__commonData = commonData;
};
PriceAxisViewRenderer.prototype._internal_draw = function (ctx, rendererOptions, textWidthCache, width, align, pixelRatio) {
if (!this._private__data._internal_visible) {
return;
}
ctx.font = rendererOptions._internal_font;
var tickSize = (this._private__data._internal_tickVisible || !this._private__data._internal_moveTextToInvisibleTick) ? rendererOptions._internal_tickLength : 0;
var horzBorder = rendererOptions._internal_borderSize;
var paddingTop = rendererOptions._internal_paddingTop;
var paddingBottom = rendererOptions._internal_paddingBottom;
var paddingInner = rendererOptions._internal_paddingInner;
var paddingOuter = rendererOptions._internal_paddingOuter;
var text = this._private__data._internal_text;
var textWidth = Math.ceil(textWidthCache._internal_measureText(ctx, text));
var baselineOffset = rendererOptions._internal_baselineOffset;
var totalHeight = rendererOptions._internal_fontSize + paddingTop + paddingBottom;
var halfHeigth = Math.ceil(totalHeight * 0.5);
var totalWidth = horzBorder + textWidth + paddingInner + paddingOuter + tickSize;
var yMid = this._private__commonData._internal_coordinate;
if (this._private__commonData._internal_fixedCoordinate) {
yMid = this._private__commonData._internal_fixedCoordinate;
}
yMid = Math.round(yMid);
var yTop = yMid - halfHeigth;
var yBottom = yTop + totalHeight;
var alignRight = align === 'right';
var xInside = alignRight ? width : 0;
var rightScaled = Math.ceil(width * pixelRatio);
var xOutside = xInside;
var xTick;
var xText;
ctx.fillStyle = this._private__commonData._internal_background;
ctx.lineWidth = 1;
ctx.lineCap = 'butt';
if (text) {
if (alignRight) {
// 2 1
//
// 6 5
//
// 3 4
xOutside = xInside - totalWidth;
xTick = xInside - tickSize;
xText = xOutside + paddingOuter;
}
else {
// 1 2
//
// 6 5
//
// 4 3
xOutside = xInside + totalWidth;
xTick = xInside + tickSize;
xText = xInside + horzBorder + tickSize + paddingInner;
}
var tickHeight = Math.max(1, Math.floor(pixelRatio));
var horzBorderScaled = Math.max(1, Math.floor(horzBorder * pixelRatio));
var xInsideScaled = alignRight ? rightScaled : 0;
var yTopScaled = Math.round(yTop * pixelRatio);
var xOutsideScaled = Math.round(xOutside * pixelRatio);
var yMidScaled = Math.round(yMid * pixelRatio) - Math.floor(pixelRatio * 0.5);
var yBottomScaled = yMidScaled + tickHeight + (yMidScaled - yTopScaled);
var xTickScaled = Math.round(xTick * pixelRatio);
ctx.save();
ctx.beginPath();
ctx.moveTo(xInsideScaled, yTopScaled);
ctx.lineTo(xOutsideScaled, yTopScaled);
ctx.lineTo(xOutsideScaled, yBottomScaled);
ctx.lineTo(xInsideScaled, yBottomScaled);
ctx.fill();
// draw border
ctx.fillStyle = this._private__data._internal_borderColor;
ctx.fillRect(alignRight ? rightScaled - horzBorderScaled : 0, yTopScaled, horzBorderScaled, yBottomScaled - yTopScaled);
if (this._private__data._internal_tickVisible) {
ctx.fillStyle = this._private__commonData._internal_color;
ctx.fillRect(xInsideScaled, yMidScaled, xTickScaled - xInsideScaled, tickHeight);
}
ctx.textAlign = 'left';
ctx.fillStyle = this._private__commonData._internal_color;
drawScaled(ctx, pixelRatio, function () {
ctx.fillText(text, xText, yBottom - paddingBottom - baselineOffset);
});
ctx.restore();
}
};
PriceAxisViewRenderer.prototype._internal_height = function (rendererOptions, useSecondLine) {
if (!this._private__data._internal_visible) {
return 0;
}
return rendererOptions._internal_fontSize + rendererOptions._internal_paddingTop + rendererOptions._internal_paddingBottom;
};
return PriceAxisViewRenderer;
}());
var PriceAxisView = /** @class */ (function () {
function PriceAxisView(ctor) {
this._private__commonRendererData = {
_internal_coordinate: 0,
_internal_color: '#FFF',
_internal_background: '#000',
};
this._private__axisRendererData = {
_internal_text: '',
_internal_visible: false,
_internal_tickVisible: true,
_internal_moveTextToInvisibleTick: false,
_internal_borderColor: '',
};
this._private__paneRendererData = {
_internal_text: '',
_internal_visible: false,
_internal_tickVisible: false,
_internal_moveTextToInvisibleTick: true,
_internal_borderColor: '',
};
this._private__invalidated = true;
this._private__axisRenderer = new (ctor || PriceAxisViewRenderer)(this._private__axisRendererData, this._private__commonRendererData);
this._private__paneRenderer = new (ctor || PriceAxisViewRenderer)(this._private__paneRendererData, this._private__commonRendererData);
}
PriceAxisView.prototype._internal_text = function () {
return this._private__axisRendererData._internal_text;
};
PriceAxisView.prototype._internal_coordinate = function () {
this._private__updateRendererDataIfNeeded();
return this._private__commonRendererData._internal_coordinate;
};
PriceAxisView.prototype._internal_update = function () {
this._private__invalidated = true;
};
PriceAxisView.prototype._internal_height = function (rendererOptions, useSecondLine) {
if (useSecondLine === void 0) { useSecondLine = false; }
return Math.max(this._private__axisRenderer._internal_height(rendererOptions, useSecondLine), this._private__paneRenderer._internal_height(rendererOptions, useSecondLine));
};
PriceAxisView.prototype._internal_getFixedCoordinate = function () {
return this._private__commonRendererData._internal_fixedCoordinate || 0;
};
PriceAxisView.prototype._internal_setFixedCoordinate = function (value) {
this._private__commonRendererData._internal_fixedCoordinate = value;
};
PriceAxisView.prototype._internal_isVisible = function () {
this._private__updateRendererDataIfNeeded();
return this._private__axisRendererData._internal_visible || this._private__paneRendererData._internal_visible;
};
PriceAxisView.prototype._internal_isAxisLabelVisible = function () {
this._private__updateRendererDataIfNeeded();
return this._private__axisRendererData._internal_visible;
};
PriceAxisView.prototype._internal_renderer = function (priceScale) {
this._private__updateRendererDataIfNeeded();
// force update tickVisible state from price scale options
// because we don't have and we can't have price axis in other methods
// (like paneRenderer or any other who call _updateRendererDataIfNeeded)
this._private__axisRendererData._internal_tickVisible = this._private__axisRendererData._internal_tickVisible && priceScale._internal_options().drawTicks;
this._private__paneRendererData._internal_tickVisible = this._private__paneRendererData._internal_tickVisible && priceScale._internal_options().drawTicks;
this._private__axisRenderer._internal_setData(this._private__axisRendererData, this._private__commonRendererData);
this._private__paneRenderer._internal_setData(this._private__paneRendererData, this._private__commonRendererData);
return this._private__axisRenderer;
};
PriceAxisView.prototype._internal_paneRenderer = function () {
this._private__updateRendererDataIfNeeded();
this._private__axisRenderer._internal_setData(this._private__axisRendererData, this._private__commonRendererData);
this._private__paneRenderer._internal_setData(this._private__paneRendererData, this._private__commonRendererData);
return this._private__paneRenderer;
};
PriceAxisView.prototype._private__updateRendererDataIfNeeded = function () {
if (this._private__invalidated) {
this._private__axisRendererData._internal_tickVisible = true;
this._private__paneRendererData._internal_tickVisible = false;
this._internal__updateRendererData(this._private__axisRendererData, this._private__paneRendererData, this._private__commonRendererData);
}
};
return PriceAxisView;
}());
var CrosshairPriceAxisView = /** @class */ (function (_super) {
__extends(CrosshairPriceAxisView, _super);
function CrosshairPriceAxisView(source, priceScale, valueProvider) {
var _this = _super.call(this) || this;
_this._private__source = source;
_this._private__priceScale = priceScale;
_this._private__valueProvider = valueProvider;
return _this;
}
CrosshairPriceAxisView.prototype._internal__updateRendererData = function (axisRendererData, paneRendererData, commonRendererData) {
axisRendererData._internal_visible = false;
var options = this._private__source._internal_options().horzLine;
if (!options.labelVisible) {
return;
}
var firstValue = this._private__priceScale._internal_firstValue();
if (!this._private__source._internal_visible() || this._private__priceScale._internal_isEmpty() || (firstValue === null)) {
return;
}
var colors = generateContrastColors(options.labelBackgroundColor);
commonRendererData._internal_background = colors._internal_background;
commonRendererData._internal_color = colors._internal_foreground;
var value = this._private__valueProvider(this._private__priceScale);
commonRendererData._internal_coordinate = value._internal_coordinate;
axisRendererData._internal_text = this._private__priceScale._internal_formatPrice(value._internal_price, firstValue);
axisRendererData._internal_visible = true;
};
return CrosshairPriceAxisView;
}(PriceAxisView));
var optimizationReplacementRe = /[1-9]/g;
var TimeAxisViewRenderer = /** @class */ (function () {
function TimeAxisViewRenderer() {
this._private__data = null;
}
TimeAxisViewRenderer.prototype._internal_setData = function (data) {
this._private__data = data;
};
TimeAxisViewRenderer.prototype._internal_draw = function (ctx, rendererOptions, pixelRatio) {
var _this = this;
if (this._private__data === null || this._private__data._internal_visible === false || this._private__data._internal_text.length === 0) {
return;
}
ctx.font = rendererOptions._internal_font;
var textWidth = Math.round(rendererOptions._internal_widthCache._internal_measureText(ctx, this._private__data._internal_text, optimizationReplacementRe));
if (textWidth <= 0) {
return;
}
ctx.save();
var horzMargin = rendererOptions._internal_paddingHorizontal;
var labelWidth = textWidth + 2 * horzMargin;
var labelWidthHalf = labelWidth / 2;
var timeScaleWidth = this._private__data._internal_width;
var coordinate = this._private__data._internal_coordinate;
var x1 = Math.floor(coordinate - labelWidthHalf) + 0.5;
if (x1 < 0) {
coordinate = coordinate + Math.abs(0 - x1);
x1 = Math.floor(coordinate - labelWidthHalf) + 0.5;
}
else if (x1 + labelWidth > timeScaleWidth) {
coordinate = coordinate - Math.abs(timeScaleWidth - (x1 + labelWidth));
x1 = Math.floor(coordinate - labelWidthHalf) + 0.5;
}
var x2 = x1 + labelWidth;
var y1 = 0;
var y2 = (y1 +
rendererOptions._internal_borderSize +
rendererOptions._internal_paddingTop +
rendererOptions._internal_fontSize +
rendererOptions._internal_paddingBottom);
ctx.fillStyle = this._private__data._internal_background;
var x1scaled = Math.round(x1 * pixelRatio);
var y1scaled = Math.round(y1 * pixelRatio);
var x2scaled = Math.round(x2 * pixelRatio);
var y2scaled = Math.round(y2 * pixelRatio);
ctx.fillRect(x1scaled, y1scaled, x2scaled - x1scaled, y2scaled - y1scaled);
var tickX = Math.round(this._private__data._internal_coordinate * pixelRatio);
var tickTop = y1scaled;
var tickBottom = Math.round((tickTop + rendererOptions._internal_borderSize + rendererOptions._internal_tickLength) * pixelRatio);
ctx.fillStyle = this._private__data._internal_color;
var tickWidth = Math.max(1, Math.floor(pixelRatio));
var tickOffset = Math.floor(pixelRatio * 0.5);
ctx.fillRect(tickX - tickOffset, tickTop, tickWidth, tickBottom - tickTop);
var yText = y2 - rendererOptions._internal_baselineOffset - rendererOptions._internal_paddingBottom;
ctx.textAlign = 'left';
ctx.fillStyle = this._private__data._internal_color;
drawScaled(ctx, pixelRatio, function () {
ctx.fillText(ensureNotNull(_this._private__data)._internal_text, x1 + horzMargin, yText);
});
ctx.restore();
};
return TimeAxisViewRenderer;
}());
var CrosshairTimeAxisView = /** @class */ (function () {
function CrosshairTimeAxisView(crosshair, model, valueProvider) {
this._private__invalidated = true;
this._private__renderer = new TimeAxisViewRenderer();
this._private__rendererData = {
_internal_visible: false,
_internal_background: '#4c525e',
_internal_color: 'white',
_internal_text: '',
_internal_width: 0,
_internal_coordinate: NaN,
};
this._private__crosshair = crosshair;
this._private__model = model;
this._private__valueProvider = valueProvider;
}
CrosshairTimeAxisView.prototype._internal_update = function () {
this._private__invalidated = true;
};
CrosshairTimeAxisView.prototype._internal_renderer = function () {
if (this._private__invalidated) {
this._private__updateImpl();
this._private__invalidated = false;
}
this._private__renderer._internal_setData(this._private__rendererData);
return this._private__renderer;
};
CrosshairTimeAxisView.prototype._private__updateImpl = function () {
var data = this._private__rendererData;
data._internal_visible = false;
var options = this._private__crosshair._internal_options().vertLine;
if (!options.labelVisible) {
return;
}
var timeScale = this._private__model._internal_timeScale();
if (timeScale._internal_isEmpty()) {
return;
}
var currentTime = timeScale._internal_indexToTime(this._private__crosshair._internal_appliedIndex());
data._internal_width = timeScale._internal_width();
var value = this._private__valueProvider();
if (!value._internal_time) {
return;
}
data._internal_coordinate = value._internal_coordinate;
data._internal_text = timeScale._internal_formatDateTime(ensureNotNull(currentTime));
data._internal_visible = true;
var colors = generateContrastColors(options.labelBackgroundColor);
data._internal_background = colors._internal_background;
data._internal_color = colors._internal_foreground;
};
return CrosshairTimeAxisView;
}());
var DataSource = /** @class */ (function () {
function DataSource() {
this._internal__priceScale = null;
this._private__zorder = 0;
}
DataSource.prototype._internal_zorder = function () {
return this._private__zorder;
};
DataSource.prototype._internal_setZorder = function (zorder) {
this._private__zorder = zorder;
};
DataSource.prototype._internal_priceScale = function () {
return this._internal__priceScale;
};
DataSource.prototype._internal_setPriceScale = function (priceScale) {
this._internal__priceScale = priceScale;
};
DataSource.prototype._internal_priceAxisViews = function (pane, priceScale) {
return [];
};
DataSource.prototype._internal_paneViews = function (pane) {
return [];
};
DataSource.prototype._internal_timeAxisViews = function () {
return [];
};
DataSource.prototype._internal_visible = function () {
return true;
};
return DataSource;
}());
/**
* Enum of possible crosshair behavior modes.
* Normal means that the crosshair always follows the pointer.
* Magnet means that the vertical line of the crosshair follows the pointer, while the horizontal line is placed on the corresponding series point.
*/
var CrosshairMode;
(function (CrosshairMode) {
CrosshairMode[CrosshairMode["Normal"] = 0] = "Normal";
CrosshairMode[CrosshairMode["Magnet"] = 1] = "Magnet";
})(CrosshairMode || (CrosshairMode = {}));
var Crosshair = /** @class */ (function (_super) {
__extends(Crosshair, _super);
function Crosshair(model, options) {
var _this = _super.call(this) || this;
_this._private__pane = null;
_this._private__price = NaN;
_this._private__index = 0;
_this._private__visible = true;
_this._private__priceAxisViews = new Map();
_this._private__subscribed = false;
_this._private__x = NaN;
_this._private__y = NaN;
_this._private__originX = NaN;
_this._private__originY = NaN;
_this._private__model = model;
_this._private__options = options;
_this._private__markersPaneView = new CrosshairMarksPaneView(model, _this);
var valuePriceProvider = function (rawPriceProvider, rawCoordinateProvider) {
return function (priceScale) {
var coordinate = rawCoordinateProvider();
var rawPrice = rawPriceProvider();
if (priceScale === ensureNotNull(_this._private__pane)._internal_defaultPriceScale()) {
// price must be defined
return { _internal_price: rawPrice, _internal_coordinate: coordinate };
}
else {
// always convert from coordinate
var firstValue = ensureNotNull(priceScale._internal_firstValue());
var price = priceScale._internal_coordinateToPrice(coordinate, firstValue);
return { _internal_price: price, _internal_coordinate: coordinate };
}
};
};
var valueTimeProvider = function (rawIndexProvider, rawCoordinateProvider) {
return function () {
return {
_internal_time: _this._private__model._internal_timeScale()._internal_indexToTime(rawIndexProvider()),
_internal_coordinate: rawCoordinateProvider(),
};
};
};
// for current position always return both price and coordinate
_this._private__currentPosPriceProvider = valuePriceProvider(function () { return _this._private__price; }, function () { return _this._private__y; });
var currentPosTimeProvider = valueTimeProvider(function () { return _this._private__index; }, function () { return _this._internal_appliedX(); });
_this._private__timeAxisView = new CrosshairTimeAxisView(_this, model, currentPosTimeProvider);
_this._private__paneView = new CrosshairPaneView(_this);
return _this;
}
Crosshair.prototype._internal_options = function () {
return this._private__options;
};
Crosshair.prototype._internal_saveOriginCoord = function (x, y) {
this._private__originX = x;
this._private__originY = y;
};
Crosshair.prototype._internal_clearOriginCoord = function () {
this._private__originX = NaN;
this._private__originY = NaN;
};
Crosshair.prototype._internal_originCoordX = function () {
return this._private__originX;
};
Crosshair.prototype._internal_originCoordY = function () {
return this._private__originY;
};
Crosshair.prototype._internal_setPosition = function (index, price, pane) {
if (!this._private__subscribed) {
this._private__subscribed = true;
}
this._private