@pipsend/charts 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/pipsend-charts.development.mjs +1001 -115
- package/dist/pipsend-charts.production.mjs +2 -2
- package/dist/pipsend-charts.standalone.development.js +1002 -114
- package/dist/pipsend-charts.standalone.development.mjs +1001 -115
- package/dist/pipsend-charts.standalone.production.js +2 -2
- package/dist/pipsend-charts.standalone.production.mjs +2 -2
- package/dist/typings.d.ts +334 -1
- package/package.json +83 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @license
|
|
3
|
-
* Pipsend Charts v2.
|
|
3
|
+
* Pipsend Charts v2.3.0-dev+202609220108
|
|
4
4
|
* Copyright (c) 2026 Pipsend
|
|
5
5
|
* Licensed under MIT License
|
|
6
6
|
* Built on TradingView Lightweight Charts™ (Apache 2.0)
|
|
@@ -14245,6 +14245,8 @@ function shapeSize(shape, originalSize) {
|
|
|
14245
14245
|
return size(originalSize, 0.8);
|
|
14246
14246
|
case 'square':
|
|
14247
14247
|
return size(originalSize, 0.7);
|
|
14248
|
+
case 'tag':
|
|
14249
|
+
return size(originalSize, 1);
|
|
14248
14250
|
}
|
|
14249
14251
|
}
|
|
14250
14252
|
function calculateShapeHeight(barSpacing) {
|
|
@@ -14354,6 +14356,85 @@ function hitTestSquare(centerX, centerY, size, x, y) {
|
|
|
14354
14356
|
y >= top && y <= top + squareSize;
|
|
14355
14357
|
}
|
|
14356
14358
|
|
|
14359
|
+
const TAIL_HALF_WIDTH_RATIO = 0.62;
|
|
14360
|
+
/**
|
|
14361
|
+
* Lays the pointer into the edge the path is already travelling along, so the
|
|
14362
|
+
* bubble and its tail are a single outline. Two subpaths would leave a stroked
|
|
14363
|
+
* seam where they meet.
|
|
14364
|
+
*/
|
|
14365
|
+
// eslint-disable-next-line max-params
|
|
14366
|
+
function tailPath(ctx, xCenter, edgeY, tailHeight, halfWidth, up) {
|
|
14367
|
+
// A tip with a small radius reads as drawn rather than as an artefact of the
|
|
14368
|
+
// rasteriser, which a bare triangle does at these sizes.
|
|
14369
|
+
const tipRadius = Math.min(halfWidth / 3, tailHeight / 3);
|
|
14370
|
+
const sign = up ? -1 : 1;
|
|
14371
|
+
const tipY = edgeY + sign * tailHeight;
|
|
14372
|
+
const shoulderY = tipY - sign * tipRadius;
|
|
14373
|
+
// The top edge is travelled left to right, the bottom edge right to left.
|
|
14374
|
+
const entry = up ? -halfWidth : halfWidth;
|
|
14375
|
+
ctx.lineTo(xCenter + entry, edgeY);
|
|
14376
|
+
ctx.lineTo(xCenter + (up ? -tipRadius : tipRadius), shoulderY);
|
|
14377
|
+
ctx.quadraticCurveTo(xCenter, tipY, xCenter + (up ? tipRadius : -tipRadius), shoulderY);
|
|
14378
|
+
ctx.lineTo(xCenter - entry, edgeY);
|
|
14379
|
+
}
|
|
14380
|
+
/**
|
|
14381
|
+
* Draws a rounded speech-bubble tag with an optional pointer towards its anchor.
|
|
14382
|
+
*/
|
|
14383
|
+
function drawTag(ctx, params) {
|
|
14384
|
+
const hpr = params._internal_horizontalPixelRatio;
|
|
14385
|
+
const vpr = params._internal_verticalPixelRatio;
|
|
14386
|
+
const left = Math.round((params._internal_x - params._internal_width / 2) * hpr);
|
|
14387
|
+
const right = Math.round((params._internal_x + params._internal_width / 2) * hpr);
|
|
14388
|
+
const top = Math.round((params._internal_centerY - params._internal_height / 2) * vpr);
|
|
14389
|
+
const bottom = Math.round((params._internal_centerY + params._internal_height / 2) * vpr);
|
|
14390
|
+
const xCenter = Math.round(params._internal_x * hpr);
|
|
14391
|
+
const radius = Math.min(params._internal_radius * hpr, (right - left) / 2, (bottom - top) / 2);
|
|
14392
|
+
const tailHeight = params._internal_tailHeight * vpr;
|
|
14393
|
+
const tailHalfWidth = Math.min(tailHeight * TAIL_HALF_WIDTH_RATIO, Math.max(0, (right - left) / 2 - radius));
|
|
14394
|
+
const hasTail = params._internal_tailDirection !== 'none' && tailHalfWidth > 0 && tailHeight > 0;
|
|
14395
|
+
ctx.save();
|
|
14396
|
+
ctx.beginPath();
|
|
14397
|
+
if (!hasTail) {
|
|
14398
|
+
drawRoundRect(ctx, left, top, right - left, bottom - top, [radius, radius, radius, radius]);
|
|
14399
|
+
}
|
|
14400
|
+
else {
|
|
14401
|
+
ctx.moveTo(left + radius, top);
|
|
14402
|
+
if (params._internal_tailDirection === 'up') {
|
|
14403
|
+
tailPath(ctx, xCenter, top, tailHeight, tailHalfWidth, true);
|
|
14404
|
+
}
|
|
14405
|
+
ctx.lineTo(right - radius, top);
|
|
14406
|
+
ctx.arcTo(right, top, right, top + radius, radius);
|
|
14407
|
+
ctx.lineTo(right, bottom - radius);
|
|
14408
|
+
ctx.arcTo(right, bottom, right - radius, bottom, radius);
|
|
14409
|
+
if (params._internal_tailDirection === 'down') {
|
|
14410
|
+
tailPath(ctx, xCenter, bottom, tailHeight, tailHalfWidth, false);
|
|
14411
|
+
}
|
|
14412
|
+
ctx.lineTo(left + radius, bottom);
|
|
14413
|
+
ctx.arcTo(left, bottom, left, bottom - radius, radius);
|
|
14414
|
+
ctx.lineTo(left, top + radius);
|
|
14415
|
+
ctx.arcTo(left, top, left + radius, top, radius);
|
|
14416
|
+
}
|
|
14417
|
+
ctx.closePath();
|
|
14418
|
+
ctx.fillStyle = params._internal_fillColor;
|
|
14419
|
+
ctx.fill();
|
|
14420
|
+
if (params._internal_borderColor !== undefined && params._internal_borderWidth > 0) {
|
|
14421
|
+
ctx.lineWidth = Math.max(1, Math.round(params._internal_borderWidth * hpr));
|
|
14422
|
+
ctx.strokeStyle = params._internal_borderColor;
|
|
14423
|
+
ctx.stroke();
|
|
14424
|
+
}
|
|
14425
|
+
ctx.restore();
|
|
14426
|
+
}
|
|
14427
|
+
function hitTestTag(centerX, centerY, tag, x, y) {
|
|
14428
|
+
if (tag._internal_width <= 0) {
|
|
14429
|
+
return false;
|
|
14430
|
+
}
|
|
14431
|
+
const halfHeight = tag._internal_height / 2;
|
|
14432
|
+
const top = tag._internal_tailDirection === 'up' ? centerY - halfHeight - tag._internal_tailHeight : centerY - halfHeight;
|
|
14433
|
+
const bottom = tag._internal_tailDirection === 'down' ? centerY + halfHeight + tag._internal_tailHeight : centerY + halfHeight;
|
|
14434
|
+
const left = centerX - tag._internal_width / 2;
|
|
14435
|
+
return x >= left && x <= left + tag._internal_width && y >= top && y <= bottom;
|
|
14436
|
+
}
|
|
14437
|
+
|
|
14357
14438
|
function drawText(ctx, text, x, y, horizontalPixelRatio, verticalPixelRatio) {
|
|
14358
14439
|
ctx.save();
|
|
14359
14440
|
ctx.scale(horizontalPixelRatio, verticalPixelRatio);
|
|
@@ -14370,9 +14451,11 @@ class SeriesMarkersRenderer {
|
|
|
14370
14451
|
constructor() {
|
|
14371
14452
|
this._private__data = null;
|
|
14372
14453
|
this._private__textWidthCache = new TextWidthCache();
|
|
14454
|
+
this._private__tagTextWidthCache = new TextWidthCache();
|
|
14373
14455
|
this._private__fontSize = -1;
|
|
14374
14456
|
this._private__fontFamily = '';
|
|
14375
14457
|
this._private__font = '';
|
|
14458
|
+
this._private__tagFont = '';
|
|
14376
14459
|
this._private__zOrder = 'normal';
|
|
14377
14460
|
}
|
|
14378
14461
|
_internal_setData(data) {
|
|
@@ -14383,7 +14466,11 @@ class SeriesMarkersRenderer {
|
|
|
14383
14466
|
this._private__fontSize = fontSize;
|
|
14384
14467
|
this._private__fontFamily = fontFamily;
|
|
14385
14468
|
this._private__font = makeFont(fontSize, fontFamily);
|
|
14469
|
+
// Tags carry a label rather than an annotation, so they get the heavier
|
|
14470
|
+
// weight — and their own cache, since widths differ per font.
|
|
14471
|
+
this._private__tagFont = makeFont(fontSize, fontFamily, '600');
|
|
14386
14472
|
this._private__textWidthCache._internal_reset();
|
|
14473
|
+
this._private__tagTextWidthCache._internal_reset();
|
|
14387
14474
|
}
|
|
14388
14475
|
this._private__zOrder = zOrder;
|
|
14389
14476
|
}
|
|
@@ -14423,14 +14510,21 @@ class SeriesMarkersRenderer {
|
|
|
14423
14510
|
return;
|
|
14424
14511
|
}
|
|
14425
14512
|
ctx.textBaseline = 'middle';
|
|
14426
|
-
ctx.font = this._private__font;
|
|
14427
14513
|
for (let index = this._private__data._internal_visibleRange.from; index < this._private__data._internal_visibleRange.to; index++) {
|
|
14428
14514
|
const item = this._private__data._internal_items[index];
|
|
14515
|
+
const isTag = item._internal_tag !== undefined;
|
|
14516
|
+
ctx.font = isTag ? this._private__tagFont : this._private__font;
|
|
14429
14517
|
if (item._internal_text !== undefined) {
|
|
14430
|
-
|
|
14518
|
+
const cache = isTag ? this._private__tagTextWidthCache : this._private__textWidthCache;
|
|
14519
|
+
item._internal_text._internal_width = cache._internal_measureText(ctx, item._internal_text._internal_content);
|
|
14431
14520
|
item._internal_text._internal_height = this._private__fontSize;
|
|
14432
14521
|
item._internal_text._internal_x = item._internal_x - item._internal_text._internal_width / 2;
|
|
14433
14522
|
}
|
|
14523
|
+
if (item._internal_tag !== undefined) {
|
|
14524
|
+
const textWidth = item._internal_text !== undefined ? item._internal_text._internal_width : 0;
|
|
14525
|
+
item._internal_tag._internal_width = Math.max(textWidth + item._internal_tag._internal_paddingX * 2, item._internal_tag._internal_height);
|
|
14526
|
+
item._internal_tag._internal_left = item._internal_x - item._internal_tag._internal_width / 2;
|
|
14527
|
+
}
|
|
14434
14528
|
drawItem(item, ctx, horizontalPixelRatio, verticalPixelRatio);
|
|
14435
14529
|
}
|
|
14436
14530
|
}
|
|
@@ -14445,12 +14539,40 @@ function bitmapShapeItemCoordinates(item, horizontalPixelRatio, verticalPixelRat
|
|
|
14445
14539
|
};
|
|
14446
14540
|
}
|
|
14447
14541
|
function drawItem(item, ctx, horizontalPixelRatio, verticalPixelRatio) {
|
|
14542
|
+
if (item._internal_tag !== undefined) {
|
|
14543
|
+
drawTagItem(item, item._internal_tag, ctx, horizontalPixelRatio, verticalPixelRatio);
|
|
14544
|
+
return;
|
|
14545
|
+
}
|
|
14448
14546
|
ctx.fillStyle = item._internal_color;
|
|
14449
14547
|
if (item._internal_text !== undefined) {
|
|
14450
14548
|
drawText(ctx, item._internal_text._internal_content, item._internal_text._internal_x, item._internal_text._internal_y, horizontalPixelRatio, verticalPixelRatio);
|
|
14451
14549
|
}
|
|
14452
14550
|
drawShape(item, ctx, bitmapShapeItemCoordinates(item, horizontalPixelRatio, verticalPixelRatio));
|
|
14453
14551
|
}
|
|
14552
|
+
// eslint-disable-next-line max-params
|
|
14553
|
+
function drawTagItem(item, tag, ctx, horizontalPixelRatio, verticalPixelRatio) {
|
|
14554
|
+
if (item._internal_size === 0) {
|
|
14555
|
+
return;
|
|
14556
|
+
}
|
|
14557
|
+
drawTag(ctx, {
|
|
14558
|
+
_internal_x: item._internal_x,
|
|
14559
|
+
_internal_centerY: item._internal_y,
|
|
14560
|
+
_internal_width: tag._internal_width,
|
|
14561
|
+
_internal_height: tag._internal_height,
|
|
14562
|
+
_internal_tailHeight: tag._internal_tailHeight,
|
|
14563
|
+
_internal_tailDirection: tag._internal_tailDirection,
|
|
14564
|
+
_internal_radius: tag._internal_radius,
|
|
14565
|
+
_internal_fillColor: item._internal_color,
|
|
14566
|
+
_internal_borderColor: item._internal_borderColor,
|
|
14567
|
+
_internal_borderWidth: item._internal_borderWidth ?? 1,
|
|
14568
|
+
_internal_horizontalPixelRatio: horizontalPixelRatio,
|
|
14569
|
+
_internal_verticalPixelRatio: verticalPixelRatio,
|
|
14570
|
+
});
|
|
14571
|
+
if (item._internal_text !== undefined) {
|
|
14572
|
+
ctx.fillStyle = item._internal_textColor ?? '#FFFFFF';
|
|
14573
|
+
drawText(ctx, item._internal_text._internal_content, item._internal_text._internal_x, item._internal_text._internal_y, horizontalPixelRatio, verticalPixelRatio);
|
|
14574
|
+
}
|
|
14575
|
+
}
|
|
14454
14576
|
function drawShape(item, ctx, coordinates) {
|
|
14455
14577
|
if (item._internal_size === 0) {
|
|
14456
14578
|
return;
|
|
@@ -14468,10 +14590,16 @@ function drawShape(item, ctx, coordinates) {
|
|
|
14468
14590
|
case 'square':
|
|
14469
14591
|
drawSquare(ctx, coordinates, item._internal_size);
|
|
14470
14592
|
return;
|
|
14593
|
+
case 'tag':
|
|
14594
|
+
// Handled by drawTagItem, which needs both pixel ratios and the text.
|
|
14595
|
+
return;
|
|
14471
14596
|
}
|
|
14472
14597
|
ensureNever(item._internal_shape);
|
|
14473
14598
|
}
|
|
14474
14599
|
function hitTestItem(item, x, y) {
|
|
14600
|
+
if (item._internal_tag !== undefined) {
|
|
14601
|
+
return item._internal_size !== 0 && hitTestTag(item._internal_x, item._internal_y, item._internal_tag, x, y);
|
|
14602
|
+
}
|
|
14475
14603
|
if (item._internal_text !== undefined && hitTestText(item._internal_text._internal_x, item._internal_text._internal_y, item._internal_text._internal_width, item._internal_text._internal_height, x, y)) {
|
|
14476
14604
|
return true;
|
|
14477
14605
|
}
|
|
@@ -14490,6 +14618,8 @@ function hitTestShape(item, x, y) {
|
|
|
14490
14618
|
return hitTestCircle(item._internal_x, item._internal_y, item._internal_size, x, y);
|
|
14491
14619
|
case 'square':
|
|
14492
14620
|
return hitTestSquare(item._internal_x, item._internal_y, item._internal_size, x, y);
|
|
14621
|
+
case 'tag':
|
|
14622
|
+
return false;
|
|
14493
14623
|
}
|
|
14494
14624
|
}
|
|
14495
14625
|
|
|
@@ -14522,15 +14652,71 @@ function getPrice(seriesData, marker, isInverted) {
|
|
|
14522
14652
|
}
|
|
14523
14653
|
return;
|
|
14524
14654
|
}
|
|
14655
|
+
/**
|
|
14656
|
+
* Lays out a `tag` marker: the bubble carries the text, so it is measured from
|
|
14657
|
+
* the font rather than from the bar spacing, and its pointer is aimed back at
|
|
14658
|
+
* the anchor price.
|
|
14659
|
+
*/
|
|
14660
|
+
// eslint-disable-next-line max-params
|
|
14661
|
+
function fillTagSizeAndY(rendererItem, marker, anchorY, offsets, textHeight, shapeMargin, sizeMultiplier) {
|
|
14662
|
+
const height = Math.round((textHeight + 5 /* Constants.TagPaddingY */ * 2) * sizeMultiplier);
|
|
14663
|
+
const tailHeight = Math.round(7 /* Constants.TagTailHeight */ * sizeMultiplier);
|
|
14664
|
+
const ignoreOffset = isPriceMarker(marker.position);
|
|
14665
|
+
const halfHeight = height / 2;
|
|
14666
|
+
let tailDirection = 'none';
|
|
14667
|
+
let centerY = anchorY;
|
|
14668
|
+
switch (marker.position) {
|
|
14669
|
+
case 'aboveBar':
|
|
14670
|
+
case 'atPriceTop': {
|
|
14671
|
+
const offset = ignoreOffset ? 0 : offsets._internal_aboveBar;
|
|
14672
|
+
tailDirection = 'down';
|
|
14673
|
+
centerY = (anchorY - offset - tailHeight - halfHeight);
|
|
14674
|
+
if (!ignoreOffset) {
|
|
14675
|
+
offsets._internal_aboveBar += height + tailHeight + shapeMargin;
|
|
14676
|
+
}
|
|
14677
|
+
break;
|
|
14678
|
+
}
|
|
14679
|
+
case 'belowBar':
|
|
14680
|
+
case 'atPriceBottom': {
|
|
14681
|
+
const offset = ignoreOffset ? 0 : offsets._internal_belowBar;
|
|
14682
|
+
tailDirection = 'up';
|
|
14683
|
+
centerY = (anchorY + offset + tailHeight + halfHeight);
|
|
14684
|
+
if (!ignoreOffset) {
|
|
14685
|
+
offsets._internal_belowBar += height + tailHeight + shapeMargin;
|
|
14686
|
+
}
|
|
14687
|
+
break;
|
|
14688
|
+
}
|
|
14689
|
+
}
|
|
14690
|
+
const tag = {
|
|
14691
|
+
_internal_height: height,
|
|
14692
|
+
_internal_tailHeight: tailHeight,
|
|
14693
|
+
_internal_tailDirection: tailDirection,
|
|
14694
|
+
_internal_radius: marker.borderRadius ?? 6 /* Constants.TagRadius */,
|
|
14695
|
+
_internal_paddingX: 9 /* Constants.TagPaddingX */ * sizeMultiplier,
|
|
14696
|
+
_internal_left: 0,
|
|
14697
|
+
_internal_width: 0,
|
|
14698
|
+
};
|
|
14699
|
+
rendererItem._internal_size = height;
|
|
14700
|
+
rendererItem._internal_y = centerY;
|
|
14701
|
+
rendererItem._internal_tag = tag;
|
|
14702
|
+
if (rendererItem._internal_text !== undefined) {
|
|
14703
|
+
rendererItem._internal_text._internal_y = centerY;
|
|
14704
|
+
}
|
|
14705
|
+
}
|
|
14525
14706
|
// eslint-disable-next-line max-params, complexity
|
|
14526
|
-
function fillSizeAndY(rendererItem, marker, seriesData, offsets, textHeight, shapeMargin, series, chart) {
|
|
14527
|
-
const price = getPrice(seriesData, marker,
|
|
14707
|
+
function fillSizeAndY(rendererItem, marker, seriesData, offsets, textHeight, shapeMargin, series, chart, invertScale) {
|
|
14708
|
+
const price = getPrice(seriesData, marker, invertScale);
|
|
14528
14709
|
if (price === undefined) {
|
|
14529
14710
|
return;
|
|
14530
14711
|
}
|
|
14531
14712
|
const ignoreOffset = isPriceMarker(marker.position);
|
|
14532
14713
|
const timeScale = chart.timeScale();
|
|
14533
14714
|
const sizeMultiplier = isNumber(marker.size) ? Math.max(marker.size, 0) : 1;
|
|
14715
|
+
if (marker.shape === 'tag') {
|
|
14716
|
+
const anchorY = ensureNotNull(series.priceToCoordinate(price));
|
|
14717
|
+
fillTagSizeAndY(rendererItem, marker, anchorY, offsets, textHeight, shapeMargin, sizeMultiplier);
|
|
14718
|
+
return;
|
|
14719
|
+
}
|
|
14534
14720
|
const shapeSize = calculateShapeHeight(timeScale.options().barSpacing) * sizeMultiplier;
|
|
14535
14721
|
const halfSize = shapeSize / 2;
|
|
14536
14722
|
rendererItem._internal_size = shapeSize;
|
|
@@ -14637,6 +14823,10 @@ class SeriesMarkersPaneView {
|
|
|
14637
14823
|
_internal_externalId: marker.id,
|
|
14638
14824
|
_internal_internalId: marker._internal_internalId,
|
|
14639
14825
|
_internal_text: undefined,
|
|
14826
|
+
_internal_textColor: marker.textColor,
|
|
14827
|
+
_internal_borderColor: marker.borderColor,
|
|
14828
|
+
_internal_borderWidth: marker.borderWidth,
|
|
14829
|
+
_internal_tag: undefined,
|
|
14640
14830
|
}));
|
|
14641
14831
|
this._private__dataInvalidated = false;
|
|
14642
14832
|
}
|
|
@@ -14647,13 +14837,22 @@ class SeriesMarkersPaneView {
|
|
|
14647
14837
|
return;
|
|
14648
14838
|
}
|
|
14649
14839
|
const visibleBarsRange = new RangeImpl(Math.floor(visibleBars.from), Math.ceil(visibleBars.to));
|
|
14650
|
-
const firstValue = this._private__series.
|
|
14840
|
+
const firstValue = this._private__series.dataByIndex(0, 1 /* MismatchDirection.NearestRight */);
|
|
14651
14841
|
if (firstValue === null) {
|
|
14652
14842
|
return;
|
|
14653
14843
|
}
|
|
14654
14844
|
if (this._private__data._internal_items.length === 0) {
|
|
14655
14845
|
return;
|
|
14656
14846
|
}
|
|
14847
|
+
// This runs inside the hit-test and paint loops, where a throw costs the
|
|
14848
|
+
// rest of the frame.
|
|
14849
|
+
let invertScale = false;
|
|
14850
|
+
try {
|
|
14851
|
+
invertScale = this._private__series.priceScale().options().invertScale;
|
|
14852
|
+
}
|
|
14853
|
+
catch (error) {
|
|
14854
|
+
invertScale = false;
|
|
14855
|
+
}
|
|
14657
14856
|
let prevTimeIndex = NaN;
|
|
14658
14857
|
const shapeMargin$1 = shapeMargin(timeScale.options().barSpacing);
|
|
14659
14858
|
const offsets = {
|
|
@@ -14684,7 +14883,7 @@ class SeriesMarkersPaneView {
|
|
|
14684
14883
|
if (dataAt === null) {
|
|
14685
14884
|
continue;
|
|
14686
14885
|
}
|
|
14687
|
-
fillSizeAndY(rendererItem, marker, dataAt, offsets, layoutOptions.fontSize, shapeMargin$1, this._private__series, this._private__chart);
|
|
14886
|
+
fillSizeAndY(rendererItem, marker, dataAt, offsets, layoutOptions.fontSize, shapeMargin$1, this._private__series, this._private__chart, invertScale);
|
|
14688
14887
|
}
|
|
14689
14888
|
this._private__invalidated = false;
|
|
14690
14889
|
}
|
|
@@ -14842,6 +15041,10 @@ class SeriesMarkersPrimitive {
|
|
|
14842
15041
|
text: marker.text,
|
|
14843
15042
|
size: marker.size,
|
|
14844
15043
|
price: marker.price,
|
|
15044
|
+
textColor: marker.textColor,
|
|
15045
|
+
borderColor: marker.borderColor,
|
|
15046
|
+
borderWidth: marker.borderWidth,
|
|
15047
|
+
borderRadius: marker.borderRadius,
|
|
14845
15048
|
_internal_originalTime: marker.time,
|
|
14846
15049
|
};
|
|
14847
15050
|
if (marker.position === 'atPriceTop' ||
|
|
@@ -19143,6 +19346,7 @@ class TradingLineController {
|
|
|
19143
19346
|
constructor(chart, element) {
|
|
19144
19347
|
this._private__lines = new Map();
|
|
19145
19348
|
this._private__hoveredId = null;
|
|
19349
|
+
this._private__hoveredPart = null;
|
|
19146
19350
|
this._private__dragging = null;
|
|
19147
19351
|
this._private__pointerId = null;
|
|
19148
19352
|
this._private__pressOrigin = null;
|
|
@@ -19200,8 +19404,12 @@ class TradingLineController {
|
|
|
19200
19404
|
if (this._private__dragging !== null) {
|
|
19201
19405
|
return;
|
|
19202
19406
|
}
|
|
19203
|
-
const
|
|
19407
|
+
const hit = this._private__splitHit(param.hoveredObjectId);
|
|
19408
|
+
const hovered = hit === null ? null : this._private__lines.get(hit._internal_id) ?? null;
|
|
19204
19409
|
this._private__setHovered(hovered ? hovered.id() : null);
|
|
19410
|
+
// The part is updated after the id, and separately from it: moving from a
|
|
19411
|
+
// label onto its close button keeps the same line hovered.
|
|
19412
|
+
this._private__setHoveredPart(hovered, hovered === null ? null : hit?._internal_part ?? null);
|
|
19205
19413
|
this._private__applyCursor();
|
|
19206
19414
|
}
|
|
19207
19415
|
_private__onPointerDown(event) {
|
|
@@ -19305,24 +19513,43 @@ class TradingLineController {
|
|
|
19305
19513
|
if (line === null) {
|
|
19306
19514
|
return null;
|
|
19307
19515
|
}
|
|
19308
|
-
//
|
|
19309
|
-
//
|
|
19310
|
-
//
|
|
19516
|
+
// The pointer event is the gesture's own position, so it is the source of
|
|
19517
|
+
// truth. The crosshair is only a mirror of it: it stops updating as soon
|
|
19518
|
+
// as the pointer leaves the pane canvas — over the price axis, past the
|
|
19519
|
+
// chart's edge — and a drag driven from it freezes there while the user
|
|
19520
|
+
// is still moving.
|
|
19521
|
+
const pane = this._private__chart.panes()[line.paneIndex()];
|
|
19522
|
+
const paneElement = pane ? pane.getHTMLElement() : null;
|
|
19523
|
+
if (paneElement) {
|
|
19524
|
+
return line.priceAt(event.clientY - paneElement.getBoundingClientRect().top);
|
|
19525
|
+
}
|
|
19311
19526
|
if (this._private__point !== null && this._private__paneIndex === line.paneIndex()) {
|
|
19312
19527
|
return line.priceAt(this._private__point._internal_y);
|
|
19313
19528
|
}
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19529
|
+
return null;
|
|
19530
|
+
}
|
|
19531
|
+
/**
|
|
19532
|
+
* Hit ids are either a bare line id or `id#part`, which is how a line with
|
|
19533
|
+
* several targets reports which one was hit.
|
|
19534
|
+
*/
|
|
19535
|
+
_private__splitHit(externalId) {
|
|
19536
|
+
if (typeof externalId !== 'string' || externalId.length === 0) {
|
|
19317
19537
|
return null;
|
|
19318
19538
|
}
|
|
19319
|
-
|
|
19539
|
+
const separator = externalId.indexOf('#');
|
|
19540
|
+
if (separator === -1) {
|
|
19541
|
+
return { _internal_id: externalId, _internal_part: null };
|
|
19542
|
+
}
|
|
19543
|
+
return { _internal_id: externalId.slice(0, separator), _internal_part: externalId.slice(separator + 1) };
|
|
19320
19544
|
}
|
|
19321
|
-
|
|
19322
|
-
if (
|
|
19323
|
-
return
|
|
19545
|
+
_private__setHoveredPart(line, part) {
|
|
19546
|
+
if (this._private__hoveredPart === part) {
|
|
19547
|
+
return;
|
|
19548
|
+
}
|
|
19549
|
+
this._private__hoveredPart = part;
|
|
19550
|
+
if (line && line.setHoveredPart) {
|
|
19551
|
+
line.setHoveredPart(part);
|
|
19324
19552
|
}
|
|
19325
|
-
return this._private__lines.get(externalId) ?? null;
|
|
19326
19553
|
}
|
|
19327
19554
|
_private__setHovered(id) {
|
|
19328
19555
|
if (this._private__hoveredId === id) {
|
|
@@ -19332,6 +19559,7 @@ class TradingLineController {
|
|
|
19332
19559
|
this._private__lines.get(this._private__hoveredId)?.setHovered(false);
|
|
19333
19560
|
}
|
|
19334
19561
|
this._private__hoveredId = id;
|
|
19562
|
+
this._private__hoveredPart = null;
|
|
19335
19563
|
if (id !== null) {
|
|
19336
19564
|
this._private__lines.get(id)?.setHovered(true);
|
|
19337
19565
|
}
|
|
@@ -19855,6 +20083,702 @@ function createInteractiveLineManager(chart, series) {
|
|
|
19855
20083
|
return new InteractiveLineManager(chart, series);
|
|
19856
20084
|
}
|
|
19857
20085
|
|
|
20086
|
+
const positionLineDefaultColors = {
|
|
20087
|
+
entry: '#2962FF',
|
|
20088
|
+
takeProfit: '#26A17B',
|
|
20089
|
+
stopLoss: '#D9A23B',
|
|
20090
|
+
};
|
|
20091
|
+
const positionLineThemeDefaults = {
|
|
20092
|
+
badgeTextColor: '#FFFFFF',
|
|
20093
|
+
borderRadius: 4,
|
|
20094
|
+
borderWidth: 1,
|
|
20095
|
+
paddingX: 8,
|
|
20096
|
+
paddingY: 6,
|
|
20097
|
+
backgroundOpacity: 1,
|
|
20098
|
+
};
|
|
20099
|
+
const positionLineLevels = ['takeProfit', 'entry', 'stopLoss'];
|
|
20100
|
+
|
|
20101
|
+
class PositionLineRenderer {
|
|
20102
|
+
constructor() {
|
|
20103
|
+
this._private__data = null;
|
|
20104
|
+
this._private__layouts = [];
|
|
20105
|
+
this._private__connectorX = 0;
|
|
20106
|
+
this._private__font = '';
|
|
20107
|
+
this._private__colorParser = new ColorParser([]);
|
|
20108
|
+
this._private__textWidthCache = new TextWidthCache(50);
|
|
20109
|
+
}
|
|
20110
|
+
_internal_setData(data) {
|
|
20111
|
+
this._private__data = data;
|
|
20112
|
+
}
|
|
20113
|
+
_internal_hitTest(x, y) {
|
|
20114
|
+
// The close button sits on top of the label, which sits on top of the
|
|
20115
|
+
// line, so the narrowest target is tested first.
|
|
20116
|
+
for (const layout of this._private__layouts) {
|
|
20117
|
+
if (layout._internal_closable && x >= layout._internal_closeLeft && x <= layout._internal_closeRight &&
|
|
20118
|
+
Math.abs(y - layout._internal_y) <= layout._internal_height / 2) {
|
|
20119
|
+
return { _internal_level: layout._internal_level, _internal_part: 'close' };
|
|
20120
|
+
}
|
|
20121
|
+
}
|
|
20122
|
+
for (const layout of this._private__layouts) {
|
|
20123
|
+
if (x >= layout._internal_pillLeft && x <= layout._internal_pillRight && Math.abs(y - layout._internal_y) <= layout._internal_height / 2) {
|
|
20124
|
+
return { _internal_level: layout._internal_level, _internal_part: 'body' };
|
|
20125
|
+
}
|
|
20126
|
+
}
|
|
20127
|
+
for (const layout of this._private__layouts) {
|
|
20128
|
+
if (Math.abs(y - layout._internal_y) <= 8 /* Constants.LineHitTolerance */) {
|
|
20129
|
+
return { _internal_level: layout._internal_level, _internal_part: 'line' };
|
|
20130
|
+
}
|
|
20131
|
+
}
|
|
20132
|
+
return null;
|
|
20133
|
+
}
|
|
20134
|
+
draw(target) {
|
|
20135
|
+
target.useBitmapCoordinateSpace((scope) => this._private__drawImpl(scope));
|
|
20136
|
+
}
|
|
20137
|
+
_private__drawImpl(scope) {
|
|
20138
|
+
const data = this._private__data;
|
|
20139
|
+
this._private__layouts = [];
|
|
20140
|
+
if (data === null || data._internal_levels.length === 0) {
|
|
20141
|
+
return;
|
|
20142
|
+
}
|
|
20143
|
+
const { context: ctx, mediaSize } = scope;
|
|
20144
|
+
ctx.save();
|
|
20145
|
+
if (this._private__font !== data._internal_font) {
|
|
20146
|
+
this._private__font = data._internal_font;
|
|
20147
|
+
this._private__textWidthCache._internal_reset();
|
|
20148
|
+
}
|
|
20149
|
+
ctx.font = data._internal_font;
|
|
20150
|
+
ctx.textBaseline = 'middle';
|
|
20151
|
+
this._private__connectorX = Math.max(0, mediaSize.width - data._internal_rightOffset);
|
|
20152
|
+
const lineEnd = data._internal_connectorVisible ? this._private__connectorX : mediaSize.width;
|
|
20153
|
+
for (const level of data._internal_levels) {
|
|
20154
|
+
this._private__layouts.push(this._private__drawLevel(scope, data, level, lineEnd));
|
|
20155
|
+
}
|
|
20156
|
+
if (data._internal_connectorVisible && data._internal_levels.length > 1) {
|
|
20157
|
+
this._private__drawConnector(scope, data);
|
|
20158
|
+
}
|
|
20159
|
+
ctx.restore();
|
|
20160
|
+
}
|
|
20161
|
+
// eslint-disable-next-line max-params
|
|
20162
|
+
_private__drawLevel(scope, data, level, lineEnd) {
|
|
20163
|
+
const { context: ctx, horizontalPixelRatio: hpr, verticalPixelRatio: vpr } = scope;
|
|
20164
|
+
const height = data._internal_fontSize + data._internal_paddingY * 2;
|
|
20165
|
+
const badgeWidth = level._internal_badge.length === 0
|
|
20166
|
+
? 0
|
|
20167
|
+
: Math.max(this._private__textWidthCache._internal_measureText(ctx, level._internal_badge) + data._internal_paddingX * 2, height * 0.85 /* Constants.BadgeMinWidthRatio */);
|
|
20168
|
+
const labelWidth = this._private__textWidthCache._internal_measureText(ctx, level._internal_text) + data._internal_paddingX * 2;
|
|
20169
|
+
const closeWidth = level._internal_closable ? height : 0;
|
|
20170
|
+
const pillWidth = badgeWidth + labelWidth + closeWidth;
|
|
20171
|
+
const pillRight = Math.max(pillWidth, lineEnd - 8 /* Constants.LabelGap */);
|
|
20172
|
+
const pillLeft = pillRight - pillWidth;
|
|
20173
|
+
const top = level._internal_y - height / 2;
|
|
20174
|
+
this._private__drawLineSegments(scope, level, pillLeft, pillRight, lineEnd);
|
|
20175
|
+
this._private__drawPill(scope, data, level, { _internal_left: pillLeft, _internal_top: top, _internal_width: pillWidth, _internal_height: height });
|
|
20176
|
+
let cellLeft = pillLeft;
|
|
20177
|
+
if (badgeWidth > 0) {
|
|
20178
|
+
this._private__drawBadge(scope, data, level, { _internal_left: cellLeft, _internal_top: top, _internal_width: badgeWidth, _internal_height: height });
|
|
20179
|
+
cellLeft += badgeWidth;
|
|
20180
|
+
this._private__drawSeparator(ctx, level._internal_color, cellLeft, top, height, hpr, vpr);
|
|
20181
|
+
}
|
|
20182
|
+
ctx.save();
|
|
20183
|
+
ctx.scale(hpr, vpr);
|
|
20184
|
+
ctx.textAlign = 'center';
|
|
20185
|
+
ctx.fillStyle = data._internal_textColor ?? level._internal_color;
|
|
20186
|
+
ctx.fillText(level._internal_text, cellLeft + labelWidth / 2, level._internal_y);
|
|
20187
|
+
ctx.restore();
|
|
20188
|
+
cellLeft += labelWidth;
|
|
20189
|
+
if (level._internal_closable) {
|
|
20190
|
+
this._private__drawSeparator(ctx, level._internal_color, cellLeft, top, height, hpr, vpr);
|
|
20191
|
+
this._private__drawCloseButton(scope, data, level, { _internal_left: cellLeft, _internal_top: top, _internal_width: closeWidth, _internal_height: height });
|
|
20192
|
+
}
|
|
20193
|
+
return {
|
|
20194
|
+
_internal_level: level._internal_level,
|
|
20195
|
+
_internal_y: level._internal_y,
|
|
20196
|
+
_internal_height: height,
|
|
20197
|
+
_internal_pillLeft: pillLeft,
|
|
20198
|
+
_internal_pillRight: pillRight,
|
|
20199
|
+
_internal_closeLeft: cellLeft,
|
|
20200
|
+
_internal_closeRight: cellLeft + closeWidth,
|
|
20201
|
+
_internal_closable: level._internal_closable,
|
|
20202
|
+
};
|
|
20203
|
+
}
|
|
20204
|
+
// eslint-disable-next-line max-params
|
|
20205
|
+
_private__drawLineSegments(scope, level, pillLeft, pillRight, lineEnd) {
|
|
20206
|
+
const { context: ctx, horizontalPixelRatio: hpr, verticalPixelRatio: vpr } = scope;
|
|
20207
|
+
const lineWidth = Math.max(1, Math.round(level._internal_lineWidth * vpr));
|
|
20208
|
+
const y = Math.round(level._internal_y * vpr) - Math.floor(lineWidth / 2);
|
|
20209
|
+
ctx.save();
|
|
20210
|
+
ctx.strokeStyle = level._internal_color;
|
|
20211
|
+
ctx.lineWidth = lineWidth;
|
|
20212
|
+
setLineStyle(ctx, level._internal_lineStyle);
|
|
20213
|
+
ctx.beginPath();
|
|
20214
|
+
ctx.moveTo(0, y);
|
|
20215
|
+
ctx.lineTo(Math.round(Math.max(0, pillLeft - 8 /* Constants.LabelGap */) * hpr), y);
|
|
20216
|
+
if (lineEnd > pillRight + 8 /* Constants.LabelGap */) {
|
|
20217
|
+
ctx.moveTo(Math.round((pillRight + 8 /* Constants.LabelGap */) * hpr), y);
|
|
20218
|
+
ctx.lineTo(Math.round(lineEnd * hpr), y);
|
|
20219
|
+
}
|
|
20220
|
+
ctx.stroke();
|
|
20221
|
+
ctx.restore();
|
|
20222
|
+
}
|
|
20223
|
+
// eslint-disable-next-line max-params
|
|
20224
|
+
_private__drawPill(scope, data, level, box) {
|
|
20225
|
+
const { context: ctx, horizontalPixelRatio: hpr, verticalPixelRatio: vpr } = scope;
|
|
20226
|
+
const hovered = data._internal_hoveredLevel === level._internal_level;
|
|
20227
|
+
const borderWidth = Math.max(1, Math.round((hovered ? data._internal_borderWidth + 1 : data._internal_borderWidth) * hpr));
|
|
20228
|
+
ctx.save();
|
|
20229
|
+
ctx.setLineDash([]);
|
|
20230
|
+
bitmapRoundRect(ctx, box, data._internal_borderRadius, hpr, vpr);
|
|
20231
|
+
ctx.fillStyle = this._private__colorParser._internal_applyAlpha(data._internal_background, data._internal_backgroundOpacity);
|
|
20232
|
+
ctx.fill();
|
|
20233
|
+
ctx.lineWidth = borderWidth;
|
|
20234
|
+
ctx.strokeStyle = level._internal_color;
|
|
20235
|
+
ctx.stroke();
|
|
20236
|
+
ctx.restore();
|
|
20237
|
+
}
|
|
20238
|
+
// eslint-disable-next-line max-params
|
|
20239
|
+
_private__drawBadge(scope, data, level, box) {
|
|
20240
|
+
const { context: ctx, horizontalPixelRatio: hpr, verticalPixelRatio: vpr } = scope;
|
|
20241
|
+
if (level._internal_badgeFilled) {
|
|
20242
|
+
// The badge shares the label's left corners, so its fill has to stop
|
|
20243
|
+
// short of the border instead of squaring it off.
|
|
20244
|
+
const inset = data._internal_borderWidth;
|
|
20245
|
+
ctx.save();
|
|
20246
|
+
bitmapRoundRect(ctx, { _internal_left: box._internal_left + inset, _internal_top: box._internal_top + inset, _internal_width: box._internal_width - inset, _internal_height: box._internal_height - inset * 2 }, Math.max(0, data._internal_borderRadius - inset), hpr, vpr, 'left');
|
|
20247
|
+
ctx.fillStyle = level._internal_color;
|
|
20248
|
+
ctx.fill();
|
|
20249
|
+
ctx.restore();
|
|
20250
|
+
}
|
|
20251
|
+
ctx.save();
|
|
20252
|
+
ctx.scale(hpr, vpr);
|
|
20253
|
+
ctx.textAlign = 'center';
|
|
20254
|
+
ctx.fillStyle = level._internal_badgeFilled ? data._internal_badgeTextColor : level._internal_color;
|
|
20255
|
+
ctx.fillText(level._internal_badge, box._internal_left + box._internal_width / 2, box._internal_top + box._internal_height / 2);
|
|
20256
|
+
ctx.restore();
|
|
20257
|
+
}
|
|
20258
|
+
// eslint-disable-next-line max-params
|
|
20259
|
+
_private__drawSeparator(ctx, color, x, top, height, hpr, vpr) {
|
|
20260
|
+
const width = Math.max(1, Math.floor(hpr));
|
|
20261
|
+
ctx.save();
|
|
20262
|
+
ctx.setLineDash([]);
|
|
20263
|
+
ctx.fillStyle = this._private__colorParser._internal_applyAlpha(color, 0.4 /* Constants.SeparatorAlpha */);
|
|
20264
|
+
ctx.fillRect(Math.round(x * hpr), Math.round(top * vpr), width, Math.round(height * vpr));
|
|
20265
|
+
ctx.restore();
|
|
20266
|
+
}
|
|
20267
|
+
// eslint-disable-next-line max-params
|
|
20268
|
+
_private__drawCloseButton(scope, data, level, box) {
|
|
20269
|
+
const { context: ctx, horizontalPixelRatio: hpr, verticalPixelRatio: vpr } = scope;
|
|
20270
|
+
const hovered = data._internal_hoveredLevel === level._internal_level && data._internal_hoveredPart === 'close';
|
|
20271
|
+
if (hovered) {
|
|
20272
|
+
ctx.save();
|
|
20273
|
+
const inset = data._internal_borderWidth;
|
|
20274
|
+
bitmapRoundRect(ctx, { _internal_left: box._internal_left, _internal_top: box._internal_top + inset, _internal_width: box._internal_width - inset, _internal_height: box._internal_height - inset * 2 }, Math.max(0, data._internal_borderRadius - inset), hpr, vpr, 'right');
|
|
20275
|
+
ctx.fillStyle = this._private__colorParser._internal_applyAlpha(level._internal_color, 0.25);
|
|
20276
|
+
ctx.fill();
|
|
20277
|
+
ctx.restore();
|
|
20278
|
+
}
|
|
20279
|
+
const centerX = (box._internal_left + box._internal_width / 2) * hpr;
|
|
20280
|
+
const centerY = (box._internal_top + box._internal_height / 2) * vpr;
|
|
20281
|
+
const arm = (7 /* Constants.CloseGlyphSize */ / 2) * hpr;
|
|
20282
|
+
ctx.save();
|
|
20283
|
+
ctx.setLineDash([]);
|
|
20284
|
+
ctx.lineCap = 'round';
|
|
20285
|
+
ctx.lineWidth = Math.max(1, Math.round(1.4 * hpr));
|
|
20286
|
+
ctx.strokeStyle = level._internal_color;
|
|
20287
|
+
ctx.beginPath();
|
|
20288
|
+
ctx.moveTo(centerX - arm, centerY - arm);
|
|
20289
|
+
ctx.lineTo(centerX + arm, centerY + arm);
|
|
20290
|
+
ctx.moveTo(centerX + arm, centerY - arm);
|
|
20291
|
+
ctx.lineTo(centerX - arm, centerY + arm);
|
|
20292
|
+
ctx.stroke();
|
|
20293
|
+
ctx.restore();
|
|
20294
|
+
}
|
|
20295
|
+
_private__drawConnector(scope, data) {
|
|
20296
|
+
const { context: ctx, horizontalPixelRatio: hpr, verticalPixelRatio: vpr } = scope;
|
|
20297
|
+
const entry = data._internal_levels.find((level) => level._internal_level === 'entry') ?? data._internal_levels[0];
|
|
20298
|
+
const ys = data._internal_levels.map((level) => level._internal_y);
|
|
20299
|
+
const x = Math.round(this._private__connectorX * hpr);
|
|
20300
|
+
ctx.save();
|
|
20301
|
+
ctx.setLineDash([]);
|
|
20302
|
+
ctx.strokeStyle = entry._internal_color;
|
|
20303
|
+
ctx.lineWidth = Math.max(1, Math.floor(hpr));
|
|
20304
|
+
ctx.beginPath();
|
|
20305
|
+
ctx.moveTo(x, Math.round(Math.min(...ys) * vpr));
|
|
20306
|
+
ctx.lineTo(x, Math.round(Math.max(...ys) * vpr));
|
|
20307
|
+
ctx.stroke();
|
|
20308
|
+
for (const level of data._internal_levels) {
|
|
20309
|
+
ctx.beginPath();
|
|
20310
|
+
ctx.arc(x, Math.round(level._internal_y * vpr), 4.5 /* Constants.HandleRadius */ * hpr, 0, 2 * Math.PI);
|
|
20311
|
+
ctx.fillStyle = level._internal_level === 'entry'
|
|
20312
|
+
? entry._internal_color
|
|
20313
|
+
: this._private__colorParser._internal_applyAlpha(data._internal_background, data._internal_backgroundOpacity);
|
|
20314
|
+
ctx.fill();
|
|
20315
|
+
ctx.lineWidth = Math.max(1, Math.round(1.5 * hpr));
|
|
20316
|
+
ctx.strokeStyle = level._internal_color;
|
|
20317
|
+
ctx.stroke();
|
|
20318
|
+
}
|
|
20319
|
+
ctx.restore();
|
|
20320
|
+
}
|
|
20321
|
+
}
|
|
20322
|
+
// eslint-disable-next-line max-params
|
|
20323
|
+
function bitmapRoundRect(ctx, box, radius, hpr, vpr, side = 'all') {
|
|
20324
|
+
const left = Math.round(box._internal_left * hpr);
|
|
20325
|
+
const top = Math.round(box._internal_top * vpr);
|
|
20326
|
+
const width = Math.round(box._internal_width * hpr);
|
|
20327
|
+
const height = Math.round(box._internal_height * vpr);
|
|
20328
|
+
const r = Math.min(radius * hpr, width / 2, height / 2);
|
|
20329
|
+
const radii = side === 'left'
|
|
20330
|
+
? [r, 0, 0, r]
|
|
20331
|
+
: side === 'right'
|
|
20332
|
+
? [0, r, r, 0]
|
|
20333
|
+
: [r, r, r, r];
|
|
20334
|
+
drawRoundRect(ctx, left, top, width, height, radii);
|
|
20335
|
+
}
|
|
20336
|
+
|
|
20337
|
+
class PositionLinePaneView {
|
|
20338
|
+
constructor() {
|
|
20339
|
+
this._private__renderer = new PositionLineRenderer();
|
|
20340
|
+
this._private__data = null;
|
|
20341
|
+
}
|
|
20342
|
+
_internal_update(data) {
|
|
20343
|
+
this._private__data = data;
|
|
20344
|
+
}
|
|
20345
|
+
renderer() {
|
|
20346
|
+
if (this._private__data === null) {
|
|
20347
|
+
return null;
|
|
20348
|
+
}
|
|
20349
|
+
this._private__renderer._internal_setData(this._private__data);
|
|
20350
|
+
return this._private__renderer;
|
|
20351
|
+
}
|
|
20352
|
+
zOrder() {
|
|
20353
|
+
return 'top';
|
|
20354
|
+
}
|
|
20355
|
+
_internal_hitTest(x, y) {
|
|
20356
|
+
return this._private__data === null ? null : this._private__renderer._internal_hitTest(x, y);
|
|
20357
|
+
}
|
|
20358
|
+
}
|
|
20359
|
+
|
|
20360
|
+
class PositionLineAxisView {
|
|
20361
|
+
constructor(source) {
|
|
20362
|
+
this._private__source = source;
|
|
20363
|
+
}
|
|
20364
|
+
coordinate() {
|
|
20365
|
+
// A hidden label is pushed far off the axis so it cannot reserve a gap
|
|
20366
|
+
// between the labels that are shown.
|
|
20367
|
+
return this._private__label()?._internal_coordinate ?? -1e3;
|
|
20368
|
+
}
|
|
20369
|
+
text() {
|
|
20370
|
+
return this._private__label()?._internal_text ?? '';
|
|
20371
|
+
}
|
|
20372
|
+
textColor() {
|
|
20373
|
+
return this._private__label()?._internal_textColor ?? '#FFFFFF';
|
|
20374
|
+
}
|
|
20375
|
+
backColor() {
|
|
20376
|
+
return this._private__label()?._internal_color ?? '#000000';
|
|
20377
|
+
}
|
|
20378
|
+
visible() {
|
|
20379
|
+
return this._private__label() !== null;
|
|
20380
|
+
}
|
|
20381
|
+
_private__label() {
|
|
20382
|
+
return this._private__source();
|
|
20383
|
+
}
|
|
20384
|
+
}
|
|
20385
|
+
|
|
20386
|
+
let nextPositionId = 0;
|
|
20387
|
+
const DEFAULT_RIGHT_OFFSET = 56;
|
|
20388
|
+
/**
|
|
20389
|
+
* A group of trading levels — entry, take profit and stop loss — drawn as one
|
|
20390
|
+
* unit: a line per level, a label carrying size and P&L, and a connector that
|
|
20391
|
+
* ties the three together.
|
|
20392
|
+
*
|
|
20393
|
+
* Input is not handled here. Every trading line on a chart shares a single
|
|
20394
|
+
* TradingLineController, which is what guarantees that one gesture moves one
|
|
20395
|
+
* level, and that a position line and a plain draggable price line on the same
|
|
20396
|
+
* chart do not fight over the pointer.
|
|
20397
|
+
*/
|
|
20398
|
+
class PositionLine {
|
|
20399
|
+
constructor(chart, series, options) {
|
|
20400
|
+
this._private__paneView = new PositionLinePaneView();
|
|
20401
|
+
this._private__colorParser = new ColorParser([]);
|
|
20402
|
+
this._private__attached = false;
|
|
20403
|
+
this._private__hoveredLevel = null;
|
|
20404
|
+
this._private__hoveredPart = null;
|
|
20405
|
+
this._private__dragLevel = null;
|
|
20406
|
+
this._private__priceBeforeDrag = null;
|
|
20407
|
+
this._private__chart = chart;
|
|
20408
|
+
this._private__series = series;
|
|
20409
|
+
this._private__id = `pipsend-position-line-${nextPositionId++}`;
|
|
20410
|
+
this._private__options = options;
|
|
20411
|
+
this._private__theme = { ...positionLineThemeDefaults, ...options.theme };
|
|
20412
|
+
this._private__paneViews = [this._private__paneView];
|
|
20413
|
+
this._private__axisViews = positionLineLevels.map((level) => new PositionLineAxisView(() => this._private__axisLabel(level)));
|
|
20414
|
+
this._private__series.attachPrimitive(this);
|
|
20415
|
+
acquireController(this._private__chart)?._internal_register(this);
|
|
20416
|
+
}
|
|
20417
|
+
// ---- ISeriesPrimitive ----
|
|
20418
|
+
attached(param) {
|
|
20419
|
+
this._private__requestUpdate = param.requestUpdate;
|
|
20420
|
+
this._private__attached = true;
|
|
20421
|
+
this.requestUpdate();
|
|
20422
|
+
}
|
|
20423
|
+
detached() {
|
|
20424
|
+
this._private__attached = false;
|
|
20425
|
+
this._private__requestUpdate = undefined;
|
|
20426
|
+
}
|
|
20427
|
+
paneViews() {
|
|
20428
|
+
return this._private__paneViews;
|
|
20429
|
+
}
|
|
20430
|
+
priceAxisViews() {
|
|
20431
|
+
return this._private__axisViews;
|
|
20432
|
+
}
|
|
20433
|
+
updateAllViews() {
|
|
20434
|
+
this._private__paneView._internal_update(this._private__buildRendererData());
|
|
20435
|
+
}
|
|
20436
|
+
hitTest(x, y) {
|
|
20437
|
+
const hit = this._private__paneView._internal_hitTest(x, y);
|
|
20438
|
+
if (hit === null) {
|
|
20439
|
+
return null;
|
|
20440
|
+
}
|
|
20441
|
+
const level = this._private__levelOptions(hit._internal_level);
|
|
20442
|
+
if (level === null) {
|
|
20443
|
+
return null;
|
|
20444
|
+
}
|
|
20445
|
+
return {
|
|
20446
|
+
externalId: `${this._private__id}#${hit._internal_level}:${hit._internal_part}`,
|
|
20447
|
+
zOrder: 'top',
|
|
20448
|
+
cursorStyle: hit._internal_part === 'close'
|
|
20449
|
+
? 'pointer'
|
|
20450
|
+
: (level.draggable ?? true) ? 'ns-resize' : undefined,
|
|
20451
|
+
};
|
|
20452
|
+
}
|
|
20453
|
+
// ---- ControllableLine ----
|
|
20454
|
+
id() {
|
|
20455
|
+
return this._private__id;
|
|
20456
|
+
}
|
|
20457
|
+
paneIndex() {
|
|
20458
|
+
try {
|
|
20459
|
+
return this._private__series.getPane().paneIndex();
|
|
20460
|
+
}
|
|
20461
|
+
catch (error) {
|
|
20462
|
+
// The engine asserts the series belongs to a pane, and that lookup can
|
|
20463
|
+
// come back empty while panes are being rebuilt. The main pane is the
|
|
20464
|
+
// right guess and keeps the gesture alive instead of throwing mid-drag.
|
|
20465
|
+
return 0;
|
|
20466
|
+
}
|
|
20467
|
+
}
|
|
20468
|
+
setHoveredPart(part) {
|
|
20469
|
+
const [level, name] = part === null ? [null, null] : part.split(':');
|
|
20470
|
+
const nextLevel = level ?? null;
|
|
20471
|
+
const nextPart = name ?? null;
|
|
20472
|
+
if (this._private__hoveredLevel === nextLevel && this._private__hoveredPart === nextPart) {
|
|
20473
|
+
return;
|
|
20474
|
+
}
|
|
20475
|
+
this._private__hoveredLevel = nextLevel;
|
|
20476
|
+
this._private__hoveredPart = nextPart;
|
|
20477
|
+
this.requestUpdate();
|
|
20478
|
+
}
|
|
20479
|
+
setHovered(hovered) {
|
|
20480
|
+
if (!hovered) {
|
|
20481
|
+
this.setHoveredPart(null);
|
|
20482
|
+
}
|
|
20483
|
+
}
|
|
20484
|
+
isDraggable() {
|
|
20485
|
+
if (this._private__hoveredLevel === null || this._private__hoveredPart === 'close') {
|
|
20486
|
+
return false;
|
|
20487
|
+
}
|
|
20488
|
+
return this._private__levelOptions(this._private__hoveredLevel)?.draggable ?? true;
|
|
20489
|
+
}
|
|
20490
|
+
isClickable() {
|
|
20491
|
+
if (this._private__hoveredPart === 'close') {
|
|
20492
|
+
return true;
|
|
20493
|
+
}
|
|
20494
|
+
return this._private__options.onClick !== undefined;
|
|
20495
|
+
}
|
|
20496
|
+
priceAt(paneY) {
|
|
20497
|
+
const price = this._private__series.coordinateToPrice(paneY);
|
|
20498
|
+
return price === null ? null : price;
|
|
20499
|
+
}
|
|
20500
|
+
beginDrag() {
|
|
20501
|
+
this._private__dragLevel = this._private__hoveredLevel;
|
|
20502
|
+
if (this._private__dragLevel === null) {
|
|
20503
|
+
return;
|
|
20504
|
+
}
|
|
20505
|
+
const price = this._private__levelOptions(this._private__dragLevel)?.price ?? null;
|
|
20506
|
+
this._private__priceBeforeDrag = price;
|
|
20507
|
+
if (price !== null && this._private__options.onDragStart) {
|
|
20508
|
+
this._private__options.onDragStart(this._private__dragLevel, price);
|
|
20509
|
+
}
|
|
20510
|
+
}
|
|
20511
|
+
dragTo(price) {
|
|
20512
|
+
if (this._private__dragLevel === null) {
|
|
20513
|
+
return;
|
|
20514
|
+
}
|
|
20515
|
+
this._private__applyPrice(this._private__dragLevel, price);
|
|
20516
|
+
if (this._private__options.onDragMove) {
|
|
20517
|
+
this._private__options.onDragMove(this._private__dragLevel, price);
|
|
20518
|
+
}
|
|
20519
|
+
}
|
|
20520
|
+
commitDrag() {
|
|
20521
|
+
const level = this._private__dragLevel;
|
|
20522
|
+
this._private__dragLevel = null;
|
|
20523
|
+
this._private__priceBeforeDrag = null;
|
|
20524
|
+
if (level === null) {
|
|
20525
|
+
return;
|
|
20526
|
+
}
|
|
20527
|
+
const price = this._private__levelOptions(level)?.price;
|
|
20528
|
+
if (price !== undefined && this._private__options.onDragEnd) {
|
|
20529
|
+
this._private__options.onDragEnd(level, price);
|
|
20530
|
+
}
|
|
20531
|
+
}
|
|
20532
|
+
cancelDrag() {
|
|
20533
|
+
const level = this._private__dragLevel;
|
|
20534
|
+
const original = this._private__priceBeforeDrag;
|
|
20535
|
+
this._private__dragLevel = null;
|
|
20536
|
+
this._private__priceBeforeDrag = null;
|
|
20537
|
+
if (level === null) {
|
|
20538
|
+
return;
|
|
20539
|
+
}
|
|
20540
|
+
// Reported even when the price did not move, so a caller that pauses work
|
|
20541
|
+
// on `onDragStart` always gets the matching end of the gesture.
|
|
20542
|
+
const restored = original !== null && original !== this._private__levelOptions(level)?.price;
|
|
20543
|
+
if (restored) {
|
|
20544
|
+
this._private__applyPrice(level, original);
|
|
20545
|
+
if (this._private__options.onDragMove) {
|
|
20546
|
+
this._private__options.onDragMove(level, original);
|
|
20547
|
+
}
|
|
20548
|
+
}
|
|
20549
|
+
if (this._private__options.onDragCancel) {
|
|
20550
|
+
this._private__options.onDragCancel(level, this._private__levelOptions(level)?.price ?? 0);
|
|
20551
|
+
}
|
|
20552
|
+
}
|
|
20553
|
+
click() {
|
|
20554
|
+
const level = this._private__hoveredLevel;
|
|
20555
|
+
if (level === null) {
|
|
20556
|
+
return;
|
|
20557
|
+
}
|
|
20558
|
+
if (this._private__hoveredPart === 'close') {
|
|
20559
|
+
if (this._private__options.onClose) {
|
|
20560
|
+
this._private__options.onClose(level);
|
|
20561
|
+
}
|
|
20562
|
+
return;
|
|
20563
|
+
}
|
|
20564
|
+
const price = this._private__levelOptions(level)?.price;
|
|
20565
|
+
if (price !== undefined && this._private__options.onClick) {
|
|
20566
|
+
this._private__options.onClick(level, price);
|
|
20567
|
+
}
|
|
20568
|
+
}
|
|
20569
|
+
// ---- public API ----
|
|
20570
|
+
/**
|
|
20571
|
+
* Updates the group. Any subset of options can be passed; the levels given
|
|
20572
|
+
* are merged into the current ones, and a level set to `undefined` is
|
|
20573
|
+
* removed.
|
|
20574
|
+
*/
|
|
20575
|
+
applyOptions(options) {
|
|
20576
|
+
// A level under an active gesture owns its price. Callers feeding the
|
|
20577
|
+
// group from a socket re-send the old price several times a second, and
|
|
20578
|
+
// letting that through would pull the level out from under the cursor.
|
|
20579
|
+
const dragged = this._private__dragLevel;
|
|
20580
|
+
const draggedPrice = dragged === null ? undefined : this._private__levelOptions(dragged)?.price;
|
|
20581
|
+
const merged = { ...this._private__options, ...options };
|
|
20582
|
+
merged.entry = { ...this._private__options.entry, ...options.entry };
|
|
20583
|
+
merged.takeProfit = mergeLevel(this._private__options.takeProfit, options, 'takeProfit');
|
|
20584
|
+
merged.stopLoss = mergeLevel(this._private__options.stopLoss, options, 'stopLoss');
|
|
20585
|
+
this._private__options = merged;
|
|
20586
|
+
if (dragged !== null && draggedPrice !== undefined) {
|
|
20587
|
+
const level = this._private__levelOptions(dragged);
|
|
20588
|
+
if (level !== null) {
|
|
20589
|
+
level.price = draggedPrice;
|
|
20590
|
+
}
|
|
20591
|
+
}
|
|
20592
|
+
if (options.theme) {
|
|
20593
|
+
// Merged onto what is already applied, so two partial theme updates do
|
|
20594
|
+
// not undo each other.
|
|
20595
|
+
this._private__theme = { ...this._private__theme, ...options.theme };
|
|
20596
|
+
}
|
|
20597
|
+
this.requestUpdate();
|
|
20598
|
+
}
|
|
20599
|
+
/**
|
|
20600
|
+
* Moves one level to a new price, recomputing its text when the level has a
|
|
20601
|
+
* `formatText`.
|
|
20602
|
+
*
|
|
20603
|
+
* Ignored for a level the user is currently dragging — the gesture owns the
|
|
20604
|
+
* price until it reports back through
|
|
20605
|
+
* {@link PositionLineOptions.onDragEnd} or
|
|
20606
|
+
* {@link PositionLineOptions.onDragCancel}.
|
|
20607
|
+
*/
|
|
20608
|
+
setPrice(level, price) {
|
|
20609
|
+
if (this._private__dragLevel === level) {
|
|
20610
|
+
return;
|
|
20611
|
+
}
|
|
20612
|
+
this._private__applyPrice(level, price);
|
|
20613
|
+
}
|
|
20614
|
+
/**
|
|
20615
|
+
* Replaces one level's label text. Use this for text that changes on its own
|
|
20616
|
+
* — a ticking P&L on a fixed entry — rather than with the price.
|
|
20617
|
+
*/
|
|
20618
|
+
setText(level, text) {
|
|
20619
|
+
const options = this._private__levelOptions(level);
|
|
20620
|
+
if (options === null) {
|
|
20621
|
+
return;
|
|
20622
|
+
}
|
|
20623
|
+
options.text = text;
|
|
20624
|
+
this.requestUpdate();
|
|
20625
|
+
}
|
|
20626
|
+
/**
|
|
20627
|
+
* The current price of each level that is present.
|
|
20628
|
+
*/
|
|
20629
|
+
prices() {
|
|
20630
|
+
const result = {};
|
|
20631
|
+
for (const level of positionLineLevels) {
|
|
20632
|
+
const options = this._private__levelOptions(level);
|
|
20633
|
+
if (options !== null) {
|
|
20634
|
+
result[level] = options.price;
|
|
20635
|
+
}
|
|
20636
|
+
}
|
|
20637
|
+
return result;
|
|
20638
|
+
}
|
|
20639
|
+
options() {
|
|
20640
|
+
return this._private__options;
|
|
20641
|
+
}
|
|
20642
|
+
/**
|
|
20643
|
+
* Removes the group from the chart and releases its share of the input
|
|
20644
|
+
* controller.
|
|
20645
|
+
*/
|
|
20646
|
+
remove() {
|
|
20647
|
+
releaseController(this._private__chart, this);
|
|
20648
|
+
this._private__series.detachPrimitive(this);
|
|
20649
|
+
}
|
|
20650
|
+
requestUpdate() {
|
|
20651
|
+
if (this._private__requestUpdate) {
|
|
20652
|
+
this._private__requestUpdate();
|
|
20653
|
+
}
|
|
20654
|
+
}
|
|
20655
|
+
/** Reads back the label the price axis should draw for a level. */
|
|
20656
|
+
_private__axisLabel(level) {
|
|
20657
|
+
const options = this._private__levelOptions(level);
|
|
20658
|
+
if (options === null || !isFinite(options.price) ||
|
|
20659
|
+
this._private__options.visible === false || options.axisLabelVisible === false) {
|
|
20660
|
+
return null;
|
|
20661
|
+
}
|
|
20662
|
+
const y = this._private__series.priceToCoordinate(options.price);
|
|
20663
|
+
if (y === null) {
|
|
20664
|
+
return null;
|
|
20665
|
+
}
|
|
20666
|
+
const color = options.color ?? positionLineDefaultColors[level];
|
|
20667
|
+
return {
|
|
20668
|
+
_internal_coordinate: y,
|
|
20669
|
+
_internal_text: this._private__series.priceFormatter().format(options.price),
|
|
20670
|
+
_internal_color: color,
|
|
20671
|
+
_internal_textColor: this._private__colorParser._internal_generateContrastColors(color)._internal_foreground,
|
|
20672
|
+
};
|
|
20673
|
+
}
|
|
20674
|
+
_private__levelOptions(level) {
|
|
20675
|
+
const options = level === 'entry' ? this._private__options.entry : this._private__options[level];
|
|
20676
|
+
return options ?? null;
|
|
20677
|
+
}
|
|
20678
|
+
_private__applyPrice(level, price) {
|
|
20679
|
+
const options = this._private__levelOptions(level);
|
|
20680
|
+
if (options === null) {
|
|
20681
|
+
return;
|
|
20682
|
+
}
|
|
20683
|
+
options.price = price;
|
|
20684
|
+
if (options.formatText) {
|
|
20685
|
+
options.text = options.formatText(price);
|
|
20686
|
+
}
|
|
20687
|
+
this.requestUpdate();
|
|
20688
|
+
}
|
|
20689
|
+
_private__buildRendererData() {
|
|
20690
|
+
if (!this._private__attached || this._private__options.visible === false) {
|
|
20691
|
+
return null;
|
|
20692
|
+
}
|
|
20693
|
+
const levels = [];
|
|
20694
|
+
for (const level of positionLineLevels) {
|
|
20695
|
+
const options = this._private__levelOptions(level);
|
|
20696
|
+
// A level added through a patch can be missing its price until the
|
|
20697
|
+
// caller supplies one; drawing it would put it at NaN.
|
|
20698
|
+
if (options === null || !isFinite(options.price)) {
|
|
20699
|
+
continue;
|
|
20700
|
+
}
|
|
20701
|
+
const y = this._private__series.priceToCoordinate(options.price);
|
|
20702
|
+
if (y === null) {
|
|
20703
|
+
continue;
|
|
20704
|
+
}
|
|
20705
|
+
const isEntry = level === 'entry';
|
|
20706
|
+
levels.push({
|
|
20707
|
+
_internal_level: level,
|
|
20708
|
+
_internal_y: y,
|
|
20709
|
+
_internal_color: options.color ?? positionLineDefaultColors[level],
|
|
20710
|
+
_internal_text: options.formatText ? options.formatText(options.price) : options.text ?? '',
|
|
20711
|
+
_internal_badge: options.badge ?? '',
|
|
20712
|
+
_internal_badgeFilled: options.badgeFilled ?? isEntry,
|
|
20713
|
+
_internal_lineWidth: options.lineWidth ?? 1,
|
|
20714
|
+
_internal_lineStyle: options.lineStyle ?? (isEntry ? 0 /* LineStyle.Solid */ : 2 /* LineStyle.Dashed */),
|
|
20715
|
+
_internal_closable: options.closable ?? true,
|
|
20716
|
+
_internal_draggable: options.draggable ?? true,
|
|
20717
|
+
});
|
|
20718
|
+
}
|
|
20719
|
+
if (levels.length === 0) {
|
|
20720
|
+
return null;
|
|
20721
|
+
}
|
|
20722
|
+
const layout = this._private__chart.options()['layout'];
|
|
20723
|
+
const fontSize = this._private__theme.fontSize ?? layout.fontSize;
|
|
20724
|
+
return {
|
|
20725
|
+
_internal_levels: levels,
|
|
20726
|
+
_internal_background: this._private__theme.background ?? resolveBackgroundColor(layout.background),
|
|
20727
|
+
_internal_textColor: this._private__theme.textColor,
|
|
20728
|
+
_internal_badgeTextColor: this._private__theme.badgeTextColor,
|
|
20729
|
+
_internal_borderRadius: this._private__theme.borderRadius,
|
|
20730
|
+
_internal_borderWidth: this._private__theme.borderWidth,
|
|
20731
|
+
_internal_paddingX: this._private__theme.paddingX,
|
|
20732
|
+
_internal_paddingY: this._private__theme.paddingY,
|
|
20733
|
+
_internal_backgroundOpacity: this._private__theme.backgroundOpacity,
|
|
20734
|
+
_internal_font: makeFont(fontSize, this._private__theme.fontFamily ?? layout.fontFamily),
|
|
20735
|
+
_internal_fontSize: fontSize,
|
|
20736
|
+
_internal_connectorVisible: this._private__options.connectorVisible !== false && levels.length > 1,
|
|
20737
|
+
_internal_rightOffset: this._private__options.rightOffset ?? DEFAULT_RIGHT_OFFSET,
|
|
20738
|
+
_internal_hoveredLevel: this._private__hoveredLevel,
|
|
20739
|
+
_internal_hoveredPart: this._private__hoveredPart,
|
|
20740
|
+
};
|
|
20741
|
+
}
|
|
20742
|
+
}
|
|
20743
|
+
function mergeLevel(current, options, key) {
|
|
20744
|
+
if (!(key in options)) {
|
|
20745
|
+
return current;
|
|
20746
|
+
}
|
|
20747
|
+
const next = options[key];
|
|
20748
|
+
return next === undefined ? undefined : { ...current, ...next };
|
|
20749
|
+
}
|
|
20750
|
+
/**
|
|
20751
|
+
* Reads the chart's own background so the label sits on a solid surface. The
|
|
20752
|
+
* parameter is typed as the public {@link Background}: the production build
|
|
20753
|
+
* renames the members of structural types it does not see in the public API,
|
|
20754
|
+
* and an anonymous `{ color: string }` here resolved to nothing at runtime.
|
|
20755
|
+
*/
|
|
20756
|
+
function resolveBackgroundColor(background) {
|
|
20757
|
+
return background.type === "solid" /* ColorType.Solid */ ? background.color : background.topColor;
|
|
20758
|
+
}
|
|
20759
|
+
/**
|
|
20760
|
+
* Creates a position line group: entry, take profit and stop loss drawn as one
|
|
20761
|
+
* interactive unit.
|
|
20762
|
+
*
|
|
20763
|
+
* @param series - The series the levels are priced against.
|
|
20764
|
+
* @param chart - The chart the series belongs to.
|
|
20765
|
+
* @param options - The levels and their style.
|
|
20766
|
+
*
|
|
20767
|
+
* @example
|
|
20768
|
+
* ```js
|
|
20769
|
+
* const position = createPositionLine(candleSeries, chart, {
|
|
20770
|
+
* entry: { price: 4306.79, badge: '1', formatText: () => '+ 2.32 USD' },
|
|
20771
|
+
* takeProfit: { price: 4349.58, badge: '1', text: '+ 42.79 USD' },
|
|
20772
|
+
* stopLoss: { price: 4266.06, badge: '1', text: '- 40.73 USD' },
|
|
20773
|
+
* onClose: (level) => console.log('close', level),
|
|
20774
|
+
* onDragEnd: (level, price) => console.log(level, 'moved to', price),
|
|
20775
|
+
* });
|
|
20776
|
+
* ```
|
|
20777
|
+
*/
|
|
20778
|
+
function createPositionLine(series, chart, options) {
|
|
20779
|
+
return new PositionLine(chart, series, options);
|
|
20780
|
+
}
|
|
20781
|
+
|
|
19858
20782
|
/** Hit parts that drag the whole object rather than a single handle. */
|
|
19859
20783
|
const WHOLE_OBJECT_PARTS = new Set([
|
|
19860
20784
|
'line',
|
|
@@ -20734,13 +21658,6 @@ class TrendLineTool extends DrawingToolBase {
|
|
|
20734
21658
|
// Clean up references
|
|
20735
21659
|
this._private__primitive = null;
|
|
20736
21660
|
this._private__paneView = null;
|
|
20737
|
-
// Force chart update by triggering a resize
|
|
20738
|
-
const chartElement = this._chart.chartElement?.();
|
|
20739
|
-
if (chartElement) {
|
|
20740
|
-
const currentWidth = chartElement.clientWidth;
|
|
20741
|
-
const currentHeight = chartElement.clientHeight;
|
|
20742
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
20743
|
-
}
|
|
20744
21661
|
}
|
|
20745
21662
|
if (this._private__trendOptions.onRemove) {
|
|
20746
21663
|
this._private__trendOptions.onRemove();
|
|
@@ -21013,12 +21930,6 @@ class HorizontalLineTool extends DrawingToolBase {
|
|
|
21013
21930
|
this._series.detachPrimitive(this._private__primitive);
|
|
21014
21931
|
this._private__primitive = null;
|
|
21015
21932
|
this._private__paneView = null;
|
|
21016
|
-
const chartElement = this._chart.chartElement?.();
|
|
21017
|
-
if (chartElement) {
|
|
21018
|
-
const currentWidth = chartElement.clientWidth;
|
|
21019
|
-
const currentHeight = chartElement.clientHeight;
|
|
21020
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
21021
|
-
}
|
|
21022
21933
|
}
|
|
21023
21934
|
if (this._private__hLineOptions.onRemove) {
|
|
21024
21935
|
this._private__hLineOptions.onRemove();
|
|
@@ -22857,12 +23768,6 @@ class VerticalLineTool extends DrawingToolBase {
|
|
|
22857
23768
|
this._series.detachPrimitive(this._private__primitive);
|
|
22858
23769
|
this._private__primitive = null;
|
|
22859
23770
|
this._private__paneView = null;
|
|
22860
|
-
const chartElement = this._chart.chartElement?.();
|
|
22861
|
-
if (chartElement) {
|
|
22862
|
-
const currentWidth = chartElement.clientWidth;
|
|
22863
|
-
const currentHeight = chartElement.clientHeight;
|
|
22864
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
22865
|
-
}
|
|
22866
23771
|
}
|
|
22867
23772
|
if (this._private__vLineOptions.onRemove) {
|
|
22868
23773
|
this._private__vLineOptions.onRemove();
|
|
@@ -23515,12 +24420,6 @@ class FibonacciTool extends DrawingToolBase {
|
|
|
23515
24420
|
this._series.detachPrimitive(this._private__primitive);
|
|
23516
24421
|
this._private__primitive = null;
|
|
23517
24422
|
this._private__paneView = null;
|
|
23518
|
-
const chartElement = this._chart.chartElement?.();
|
|
23519
|
-
if (chartElement) {
|
|
23520
|
-
const currentWidth = chartElement.clientWidth;
|
|
23521
|
-
const currentHeight = chartElement.clientHeight;
|
|
23522
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
23523
|
-
}
|
|
23524
24423
|
}
|
|
23525
24424
|
if (this._private__drawingManager) {
|
|
23526
24425
|
this._private__drawingManager._internal_destroy();
|
|
@@ -23536,12 +24435,6 @@ class FibonacciTool extends DrawingToolBase {
|
|
|
23536
24435
|
this._series.detachPrimitive(this._private__primitive);
|
|
23537
24436
|
this._private__primitive = null;
|
|
23538
24437
|
this._private__paneView = null;
|
|
23539
|
-
const chartElement = this._chart.chartElement?.();
|
|
23540
|
-
if (chartElement) {
|
|
23541
|
-
const currentWidth = chartElement.clientWidth;
|
|
23542
|
-
const currentHeight = chartElement.clientHeight;
|
|
23543
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
23544
|
-
}
|
|
23545
24438
|
}
|
|
23546
24439
|
if (this._private__fibOptions.onRemove) {
|
|
23547
24440
|
this._private__fibOptions.onRemove();
|
|
@@ -23969,12 +24862,6 @@ class FibonacciExtensionTool extends DrawingToolBase {
|
|
|
23969
24862
|
this._series.detachPrimitive(this._private__primitive);
|
|
23970
24863
|
this._private__primitive = null;
|
|
23971
24864
|
this._private__paneView = null;
|
|
23972
|
-
const chartElement = this._chart.chartElement?.();
|
|
23973
|
-
if (chartElement) {
|
|
23974
|
-
const currentWidth = chartElement.clientWidth;
|
|
23975
|
-
const currentHeight = chartElement.clientHeight;
|
|
23976
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
23977
|
-
}
|
|
23978
24865
|
}
|
|
23979
24866
|
}
|
|
23980
24867
|
}
|
|
@@ -24416,12 +25303,6 @@ class FibonacciFanTool extends DrawingToolBase {
|
|
|
24416
25303
|
this._series.detachPrimitive(this._private__primitive);
|
|
24417
25304
|
this._private__primitive = null;
|
|
24418
25305
|
this._private__paneView = null;
|
|
24419
|
-
const chartElement = this._chart.chartElement?.();
|
|
24420
|
-
if (chartElement) {
|
|
24421
|
-
const currentWidth = chartElement.clientWidth;
|
|
24422
|
-
const currentHeight = chartElement.clientHeight;
|
|
24423
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
24424
|
-
}
|
|
24425
25306
|
}
|
|
24426
25307
|
if (this._private__fanOptions.onRemove) {
|
|
24427
25308
|
this._private__fanOptions.onRemove();
|
|
@@ -24962,12 +25843,6 @@ class RectangleTool extends DrawingToolBase {
|
|
|
24962
25843
|
this._series.detachPrimitive(this._private__primitive);
|
|
24963
25844
|
this._private__primitive = null;
|
|
24964
25845
|
this._private__paneView = null;
|
|
24965
|
-
const chartElement = this._chart.chartElement?.();
|
|
24966
|
-
if (chartElement) {
|
|
24967
|
-
const currentWidth = chartElement.clientWidth;
|
|
24968
|
-
const currentHeight = chartElement.clientHeight;
|
|
24969
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
24970
|
-
}
|
|
24971
25846
|
}
|
|
24972
25847
|
if (this._private__rectOptions.onRemove) {
|
|
24973
25848
|
this._private__rectOptions.onRemove();
|
|
@@ -25428,12 +26303,6 @@ class CircleTool extends DrawingToolBase {
|
|
|
25428
26303
|
this._series.detachPrimitive(this._private__primitive);
|
|
25429
26304
|
this._private__primitive = null;
|
|
25430
26305
|
this._private__paneView = null;
|
|
25431
|
-
const chartElement = this._chart.chartElement?.();
|
|
25432
|
-
if (chartElement) {
|
|
25433
|
-
const currentWidth = chartElement.clientWidth;
|
|
25434
|
-
const currentHeight = chartElement.clientHeight;
|
|
25435
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
25436
|
-
}
|
|
25437
26306
|
}
|
|
25438
26307
|
if (this._private__circleOptions.onRemove) {
|
|
25439
26308
|
this._private__circleOptions.onRemove();
|
|
@@ -25983,12 +26852,6 @@ class ArrowTool extends DrawingToolBase {
|
|
|
25983
26852
|
this._series.detachPrimitive(this._private__primitive);
|
|
25984
26853
|
this._private__primitive = null;
|
|
25985
26854
|
this._private__paneView = null;
|
|
25986
|
-
const chartElement = this._chart.chartElement?.();
|
|
25987
|
-
if (chartElement) {
|
|
25988
|
-
const currentWidth = chartElement.clientWidth;
|
|
25989
|
-
const currentHeight = chartElement.clientHeight;
|
|
25990
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
25991
|
-
}
|
|
25992
26855
|
}
|
|
25993
26856
|
if (this._private__arrowOptions.onRemove) {
|
|
25994
26857
|
this._private__arrowOptions.onRemove();
|
|
@@ -26849,12 +27712,6 @@ class TextTool extends DrawingToolBase {
|
|
|
26849
27712
|
this._series.detachPrimitive(this._private__primitive);
|
|
26850
27713
|
this._private__primitive = null;
|
|
26851
27714
|
this._private__paneView = null;
|
|
26852
|
-
const chartElement = this._chart.chartElement?.();
|
|
26853
|
-
if (chartElement) {
|
|
26854
|
-
const currentWidth = chartElement.clientWidth;
|
|
26855
|
-
const currentHeight = chartElement.clientHeight;
|
|
26856
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
26857
|
-
}
|
|
26858
27715
|
}
|
|
26859
27716
|
if (this._private__textOptions.onRemove) {
|
|
26860
27717
|
this._private__textOptions.onRemove();
|
|
@@ -27437,12 +28294,6 @@ class RulerTool extends DrawingToolBase {
|
|
|
27437
28294
|
this._series.detachPrimitive(this._private__primitive);
|
|
27438
28295
|
this._private__primitive = null;
|
|
27439
28296
|
this._private__paneView = null;
|
|
27440
|
-
const chartElement = this._chart.chartElement?.();
|
|
27441
|
-
if (chartElement) {
|
|
27442
|
-
const currentWidth = chartElement.clientWidth;
|
|
27443
|
-
const currentHeight = chartElement.clientHeight;
|
|
27444
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
27445
|
-
}
|
|
27446
28297
|
}
|
|
27447
28298
|
if (this._private__drawingManager) {
|
|
27448
28299
|
this._private__drawingManager._internal_destroy();
|
|
@@ -28298,12 +29149,6 @@ class PositionTool extends DrawingToolBase {
|
|
|
28298
29149
|
this._series.detachPrimitive(this._private__primitive);
|
|
28299
29150
|
this._private__primitive = null;
|
|
28300
29151
|
this._private__paneView = null;
|
|
28301
|
-
const chartElement = this._chart.chartElement?.();
|
|
28302
|
-
if (chartElement) {
|
|
28303
|
-
const currentWidth = chartElement.clientWidth;
|
|
28304
|
-
const currentHeight = chartElement.clientHeight;
|
|
28305
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
28306
|
-
}
|
|
28307
29152
|
}
|
|
28308
29153
|
if (this._private__positionOptions.onRemove) {
|
|
28309
29154
|
this._private__positionOptions.onRemove();
|
|
@@ -28646,12 +29491,6 @@ class GanttTool extends DrawingToolBase {
|
|
|
28646
29491
|
this._series.detachPrimitive(this._private__primitive);
|
|
28647
29492
|
this._private__primitive = null;
|
|
28648
29493
|
this._private__paneView = null;
|
|
28649
|
-
const chartElement = this._chart.chartElement?.();
|
|
28650
|
-
if (chartElement) {
|
|
28651
|
-
const currentWidth = chartElement.clientWidth;
|
|
28652
|
-
const currentHeight = chartElement.clientHeight;
|
|
28653
|
-
this._chart.resize(currentWidth, currentHeight);
|
|
28654
|
-
}
|
|
28655
29494
|
}
|
|
28656
29495
|
if (this._private__ganttOptions.onRemove) {
|
|
28657
29496
|
this._private__ganttOptions.onRemove();
|
|
@@ -28662,6 +29501,8 @@ function createGanttTool(context, options) {
|
|
|
28662
29501
|
return new GanttTool(context, options);
|
|
28663
29502
|
}
|
|
28664
29503
|
|
|
29504
|
+
/** Undo steps kept, each a full snapshot of every drawing. */
|
|
29505
|
+
const UNDO_HISTORY_LIMIT = 100;
|
|
28665
29506
|
/**
|
|
28666
29507
|
* Central manager for all drawing tools - TradingView style
|
|
28667
29508
|
* Handles multiple tools, selection, deletion, and keyboard shortcuts
|
|
@@ -28684,6 +29525,8 @@ class DrawingToolsManager {
|
|
|
28684
29525
|
this._private__pointerDownHandler = null;
|
|
28685
29526
|
this._private__pointerUpHandler = null;
|
|
28686
29527
|
this._private__pointerCancelHandler = null;
|
|
29528
|
+
this._private__lostCaptureHandler = null;
|
|
29529
|
+
this._private__windowPointerUpHandler = null;
|
|
28687
29530
|
this._private__pointerId = null;
|
|
28688
29531
|
/** A press that grabbed a tool already selected it; its click must not undo that. */
|
|
28689
29532
|
this._private__gestureHandled = false;
|
|
@@ -28849,6 +29692,9 @@ class DrawingToolsManager {
|
|
|
28849
29692
|
return;
|
|
28850
29693
|
}
|
|
28851
29694
|
this._private__past.push(this._private__committed);
|
|
29695
|
+
if (this._private__past.length > UNDO_HISTORY_LIMIT) {
|
|
29696
|
+
this._private__past.shift();
|
|
29697
|
+
}
|
|
28852
29698
|
this._private__future = [];
|
|
28853
29699
|
this._private__committed = this.serializeAll();
|
|
28854
29700
|
}
|
|
@@ -29228,8 +30074,12 @@ class DrawingToolsManager {
|
|
|
29228
30074
|
this._private__completionCheck = null;
|
|
29229
30075
|
}
|
|
29230
30076
|
this._private__drawingCompleted._internal_destroy();
|
|
30077
|
+
this._private__selectionChanged._internal_destroy();
|
|
29231
30078
|
this.clearAll();
|
|
29232
30079
|
this._private__detachEventHandlers();
|
|
30080
|
+
this._private__past = [];
|
|
30081
|
+
this._private__future = [];
|
|
30082
|
+
this._private__committed = [];
|
|
29233
30083
|
}
|
|
29234
30084
|
_private__attachEventHandlers() {
|
|
29235
30085
|
// Click handler for selection
|
|
@@ -29249,9 +30099,15 @@ class DrawingToolsManager {
|
|
|
29249
30099
|
this._private__pointerDownHandler = (event) => this._private__handlePointerDown(event);
|
|
29250
30100
|
this._private__pointerUpHandler = (event) => this._private__handlePointerUp(event);
|
|
29251
30101
|
this._private__pointerCancelHandler = (event) => this._private__handlePointerUp(event);
|
|
30102
|
+
this._private__lostCaptureHandler = (event) => this._private__handleLostPointerCapture(event);
|
|
29252
30103
|
chartElement.addEventListener('pointerdown', this._private__pointerDownHandler);
|
|
29253
30104
|
chartElement.addEventListener('pointerup', this._private__pointerUpHandler);
|
|
29254
30105
|
chartElement.addEventListener('pointercancel', this._private__pointerCancelHandler);
|
|
30106
|
+
chartElement.addEventListener('lostpointercapture', this._private__lostCaptureHandler);
|
|
30107
|
+
}
|
|
30108
|
+
if (isRunningOnClientSide) {
|
|
30109
|
+
this._private__windowPointerUpHandler = (event) => this._private__handlePointerUp(event);
|
|
30110
|
+
window.addEventListener('pointerup', this._private__windowPointerUpHandler);
|
|
29255
30111
|
}
|
|
29256
30112
|
// Keyboard handler for shortcuts
|
|
29257
30113
|
if (this._private__options.enableKeyboardShortcuts) {
|
|
@@ -29284,6 +30140,14 @@ class DrawingToolsManager {
|
|
|
29284
30140
|
chartElement.removeEventListener('pointercancel', this._private__pointerCancelHandler);
|
|
29285
30141
|
this._private__pointerCancelHandler = null;
|
|
29286
30142
|
}
|
|
30143
|
+
if (this._private__lostCaptureHandler) {
|
|
30144
|
+
chartElement.removeEventListener('lostpointercapture', this._private__lostCaptureHandler);
|
|
30145
|
+
this._private__lostCaptureHandler = null;
|
|
30146
|
+
}
|
|
30147
|
+
}
|
|
30148
|
+
if (this._private__windowPointerUpHandler) {
|
|
30149
|
+
window.removeEventListener('pointerup', this._private__windowPointerUpHandler);
|
|
30150
|
+
this._private__windowPointerUpHandler = null;
|
|
29287
30151
|
}
|
|
29288
30152
|
if (this._private__keyboardHandler) {
|
|
29289
30153
|
document.removeEventListener('keydown', this._private__keyboardHandler);
|
|
@@ -29409,7 +30273,12 @@ class DrawingToolsManager {
|
|
|
29409
30273
|
this._private__gestureHandled = true;
|
|
29410
30274
|
this._private__draggedToolId = toolId;
|
|
29411
30275
|
this._private__pointerId = event.pointerId;
|
|
29412
|
-
|
|
30276
|
+
try {
|
|
30277
|
+
chartElement.setPointerCapture(event.pointerId);
|
|
30278
|
+
}
|
|
30279
|
+
catch (error) {
|
|
30280
|
+
this._private__pointerId = null;
|
|
30281
|
+
}
|
|
29413
30282
|
tool.startDrag(endpoint, anchor);
|
|
29414
30283
|
// Panning is stopped by disabling it for the gesture, not by preventing
|
|
29415
30284
|
// the event: preventDefault on pointerdown suppresses the compatibility
|
|
@@ -29423,17 +30292,32 @@ class DrawingToolsManager {
|
|
|
29423
30292
|
if (event && this._private__pointerId !== null && event.pointerId !== this._private__pointerId) {
|
|
29424
30293
|
return;
|
|
29425
30294
|
}
|
|
29426
|
-
const
|
|
29427
|
-
|
|
29428
|
-
|
|
29429
|
-
}
|
|
29430
|
-
this._private__releasePointer();
|
|
29431
|
-
this._private__enableChartInteractions();
|
|
30295
|
+
const draggedToolId = this._private__draggedToolId;
|
|
30296
|
+
// Cleared first: anything below can throw, and a half-finished release
|
|
30297
|
+
// would leave the manager dragging forever.
|
|
29432
30298
|
this._private__isDragging = false;
|
|
29433
30299
|
this._private__draggedToolId = null;
|
|
30300
|
+
try {
|
|
30301
|
+
const tool = this._private__tools.get(draggedToolId);
|
|
30302
|
+
if (tool) {
|
|
30303
|
+
tool.stopDrag();
|
|
30304
|
+
}
|
|
30305
|
+
}
|
|
30306
|
+
finally {
|
|
30307
|
+
this._private__releasePointer();
|
|
30308
|
+
this._private__enableChartInteractions();
|
|
30309
|
+
}
|
|
29434
30310
|
// A finished drag is one step, not one per frame
|
|
29435
30311
|
this.recordChange();
|
|
29436
30312
|
}
|
|
30313
|
+
/** Ends the drag when capture is lost without a pointerup ever arriving. */
|
|
30314
|
+
_private__handleLostPointerCapture(event) {
|
|
30315
|
+
if (this._private__pointerId !== null && event.pointerId !== this._private__pointerId) {
|
|
30316
|
+
return;
|
|
30317
|
+
}
|
|
30318
|
+
this._private__pointerId = null;
|
|
30319
|
+
this._private__handlePointerUp();
|
|
30320
|
+
}
|
|
29437
30321
|
/**
|
|
29438
30322
|
* Pane element of a tool's series. The engine asserts the series has a pane,
|
|
29439
30323
|
* so the lookup is guarded.
|
|
@@ -29484,9 +30368,11 @@ class DrawingToolsManager {
|
|
|
29484
30368
|
return;
|
|
29485
30369
|
}
|
|
29486
30370
|
const options = this._private__chart.options();
|
|
30371
|
+
// `options()` returns the live object and `applyOptions` merges into it
|
|
30372
|
+
// in place, so a snapshot by reference restores all-false.
|
|
29487
30373
|
this._private__interactionsBeforeDrag = {
|
|
29488
|
-
_internal_handleScroll: options.handleScroll,
|
|
29489
|
-
_internal_handleScale: options.handleScale,
|
|
30374
|
+
_internal_handleScroll: clone(options.handleScroll),
|
|
30375
|
+
_internal_handleScale: clone(options.handleScale),
|
|
29490
30376
|
};
|
|
29491
30377
|
this._private__chart.applyOptions({
|
|
29492
30378
|
handleScroll: false,
|
|
@@ -29572,7 +30458,7 @@ const customSeriesDefaultOptions = {
|
|
|
29572
30458
|
* Returns the current version as a string. For example `'1.0.0'`.
|
|
29573
30459
|
*/
|
|
29574
30460
|
function version() {
|
|
29575
|
-
return "2.
|
|
30461
|
+
return "2.3.0-dev+202609220108";
|
|
29576
30462
|
}
|
|
29577
30463
|
|
|
29578
|
-
export { areaSeries as AreaSeries, ArrowTool, barSeries as BarSeries, baselineSeries as BaselineSeries, BrushTool, candlestickSeries as CandlestickSeries, CircleTool, ColorType, CrossLineTool, CrosshairMode, DEFAULT_SESSIONS, DraggablePriceLine, DrawingToolsManager, EXTENDED_FIBONACCI_RATIOS, FibonacciCalculator, FibonacciExtensionTool, FibonacciFanTool, FibonacciTool, GanttTool, histogramSeries as HistogramSeries, HorizontalLineTool, InteractiveLineManager, LastPriceAnimationMode, lineSeries as LineSeries, LineStyle, LineType, MismatchDirection, ParallelChannelTool, PitchforkTool, PolygonTool, PositionCalculator, PositionMarkersManager, PositionTool, PriceLineSource, PriceScaleMode, RectangleTool, RulerCalculator, RulerTool, STANDARD_FIBONACCI_RATIOS, TextTool, TickMarkType, TrackingModeExitMode, TradingSessionsIndicator, TrendLineTool, VerticalLineTool, VolumeProfileIndicator, applyADX, applyATR, applyBollingerBands, applyCCI, applyDonchianChannels, applyEMA, applyHeikinAshi, applyIchimoku, applyKeltnerChannels, applyMACD, applyMFI, applyOBV, applyParabolicSAR, applyPivotPoints, applyRSI, applySMA, applyStochastic, applySuperTrend, applyVWAP, applyVolume, applyWMA, applyWilliamsR, calculateHeikinAshi, calculatePipConfig, coordinateFromTime, createArrowTool, createBrushTool, createChart, createChartEx, createCircleTool, createCrossLineTool, createFibonacciExtensionTool, createFibonacciFanTool, createFibonacciTool, createGanttTool, createHorizontalLineTool, createImageWatermark, createInteractiveLineManager, createOptionsChart, createParallelChannelTool, createPitchforkTool, createPolygonTool, createPositionMarkers, createPositionTool, createRectangleTool, createRulerTool, createSeriesMarkers, createTextTool, createTextWatermark, createTradingLine, createTradingSessionsIndicator, createTrendLineTool, createUpDownMarkers, createVerticalLineTool, createVolumeProfileIndicator, createYieldCurveChart, customSeriesDefaultOptions, defaultHorzScaleBehavior, getPipConfig, hideSeriesPaint, isBusinessDay, isUTCTimestamp, setSeriesVolumes, setVolumeProfileData, timeFromCoordinate, version };
|
|
30464
|
+
export { areaSeries as AreaSeries, ArrowTool, barSeries as BarSeries, baselineSeries as BaselineSeries, BrushTool, candlestickSeries as CandlestickSeries, CircleTool, ColorType, CrossLineTool, CrosshairMode, DEFAULT_SESSIONS, DraggablePriceLine, DrawingToolsManager, EXTENDED_FIBONACCI_RATIOS, FibonacciCalculator, FibonacciExtensionTool, FibonacciFanTool, FibonacciTool, GanttTool, histogramSeries as HistogramSeries, HorizontalLineTool, InteractiveLineManager, LastPriceAnimationMode, lineSeries as LineSeries, LineStyle, LineType, MismatchDirection, ParallelChannelTool, PitchforkTool, PolygonTool, PositionCalculator, PositionLine, PositionMarkersManager, PositionTool, PriceLineSource, PriceScaleMode, RectangleTool, RulerCalculator, RulerTool, STANDARD_FIBONACCI_RATIOS, TextTool, TickMarkType, TrackingModeExitMode, TradingSessionsIndicator, TrendLineTool, VerticalLineTool, VolumeProfileIndicator, applyADX, applyATR, applyBollingerBands, applyCCI, applyDonchianChannels, applyEMA, applyHeikinAshi, applyIchimoku, applyKeltnerChannels, applyMACD, applyMFI, applyOBV, applyParabolicSAR, applyPivotPoints, applyRSI, applySMA, applyStochastic, applySuperTrend, applyVWAP, applyVolume, applyWMA, applyWilliamsR, calculateHeikinAshi, calculatePipConfig, coordinateFromTime, createArrowTool, createBrushTool, createChart, createChartEx, createCircleTool, createCrossLineTool, createFibonacciExtensionTool, createFibonacciFanTool, createFibonacciTool, createGanttTool, createHorizontalLineTool, createImageWatermark, createInteractiveLineManager, createOptionsChart, createParallelChannelTool, createPitchforkTool, createPolygonTool, createPositionLine, createPositionMarkers, createPositionTool, createRectangleTool, createRulerTool, createSeriesMarkers, createTextTool, createTextWatermark, createTradingLine, createTradingSessionsIndicator, createTrendLineTool, createUpDownMarkers, createVerticalLineTool, createVolumeProfileIndicator, createYieldCurveChart, customSeriesDefaultOptions, defaultHorzScaleBehavior, getPipConfig, hideSeriesPaint, isBusinessDay, isUTCTimestamp, setSeriesVolumes, setVolumeProfileData, timeFromCoordinate, version };
|