hyperprop-charting-library 0.1.53 → 0.1.55
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/dist/hyperprop-charting-library.cjs +302 -4
- package/dist/hyperprop-charting-library.d.ts +35 -1
- package/dist/hyperprop-charting-library.js +302 -4
- package/dist/index.cjs +302 -4
- package/dist/index.d.cts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.js +302 -4
- package/docs/API.md +43 -0
- package/docs/RECIPES.md +28 -0
- package/package.json +1 -1
|
@@ -190,7 +190,8 @@ var DEFAULT_OPTIONS = {
|
|
|
190
190
|
},
|
|
191
191
|
labels: DEFAULT_LABELS_OPTIONS,
|
|
192
192
|
dashPatterns: DEFAULT_DASH_PATTERNS,
|
|
193
|
-
indicators: []
|
|
193
|
+
indicators: [],
|
|
194
|
+
drawings: []
|
|
194
195
|
};
|
|
195
196
|
var mergeChartOptions = (baseOptions, options = {}) => ({
|
|
196
197
|
...baseOptions,
|
|
@@ -881,6 +882,7 @@ function createChart(element, options = {}) {
|
|
|
881
882
|
let generatedPriceLineId = 1;
|
|
882
883
|
let generatedOrderLineId = 1;
|
|
883
884
|
let generatedIndicatorId = 1;
|
|
885
|
+
let generatedDrawingId = 1;
|
|
884
886
|
const indicatorRegistry = /* @__PURE__ */ new Map();
|
|
885
887
|
for (const indicator of BUILTIN_INDICATORS) {
|
|
886
888
|
indicatorRegistry.set(indicator.id, indicator);
|
|
@@ -903,7 +905,41 @@ function createChart(element, options = {}) {
|
|
|
903
905
|
}
|
|
904
906
|
};
|
|
905
907
|
};
|
|
908
|
+
const normalizeDrawingState = (drawing) => ({
|
|
909
|
+
id: drawing.id ?? `drawing-${generatedDrawingId++}`,
|
|
910
|
+
type: drawing.type,
|
|
911
|
+
points: drawing.points.map((point) => ({
|
|
912
|
+
index: Number(point.index) || 0,
|
|
913
|
+
price: Number(point.price) || 0,
|
|
914
|
+
...point.time ? { time: point.time } : {}
|
|
915
|
+
})),
|
|
916
|
+
visible: drawing.visible ?? true,
|
|
917
|
+
color: drawing.color ?? "#94a3b8",
|
|
918
|
+
style: drawing.style ?? "dotted",
|
|
919
|
+
width: Math.max(1, Number(drawing.width) || 1),
|
|
920
|
+
locked: drawing.locked ?? false,
|
|
921
|
+
...drawing.label === void 0 ? {} : { label: drawing.label }
|
|
922
|
+
});
|
|
923
|
+
const serializeDrawing = (drawing) => ({
|
|
924
|
+
id: drawing.id,
|
|
925
|
+
type: drawing.type,
|
|
926
|
+
points: drawing.points.map((point) => ({ ...point })),
|
|
927
|
+
visible: drawing.visible,
|
|
928
|
+
color: drawing.color,
|
|
929
|
+
style: drawing.style,
|
|
930
|
+
width: drawing.width,
|
|
931
|
+
locked: drawing.locked,
|
|
932
|
+
...drawing.label === void 0 ? {} : { label: drawing.label }
|
|
933
|
+
});
|
|
906
934
|
let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
|
|
935
|
+
let drawings = (options.drawings ?? []).map((drawing) => normalizeDrawingState(drawing));
|
|
936
|
+
let activeDrawingTool = null;
|
|
937
|
+
let draftDrawing = null;
|
|
938
|
+
let drawingsChangeHandler = null;
|
|
939
|
+
let drawingSelectHandler = null;
|
|
940
|
+
const emitDrawingsChange = () => {
|
|
941
|
+
drawingsChangeHandler?.(drawings.map((drawing) => serializeDrawing(drawing)));
|
|
942
|
+
};
|
|
907
943
|
const orderWidgetWidthById = /* @__PURE__ */ new Map();
|
|
908
944
|
const orderPriceTagWidthById = /* @__PURE__ */ new Map();
|
|
909
945
|
let xCenter = 0;
|
|
@@ -1957,6 +1993,55 @@ function createChart(element, options = {}) {
|
|
|
1957
1993
|
const yFromPrice = (price) => {
|
|
1958
1994
|
return chartBottom - (price - yMin) / yRange * chartHeight;
|
|
1959
1995
|
};
|
|
1996
|
+
const xFromDrawingPoint = (point) => chartLeft + (point.index + 0.5 - xStart) / xSpan * chartWidth;
|
|
1997
|
+
const drawDrawingHandle = (x, y, color) => {
|
|
1998
|
+
ctx.save();
|
|
1999
|
+
ctx.setLineDash([]);
|
|
2000
|
+
ctx.fillStyle = mergedOptions.backgroundColor;
|
|
2001
|
+
ctx.strokeStyle = color;
|
|
2002
|
+
ctx.lineWidth = 2;
|
|
2003
|
+
ctx.beginPath();
|
|
2004
|
+
ctx.arc(x, y, 5, 0, Math.PI * 2);
|
|
2005
|
+
ctx.fill();
|
|
2006
|
+
ctx.stroke();
|
|
2007
|
+
ctx.restore();
|
|
2008
|
+
};
|
|
2009
|
+
const drawDrawing = (drawing, draft = false) => {
|
|
2010
|
+
if (!drawing.visible) {
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
ctx.save();
|
|
2014
|
+
ctx.strokeStyle = drawing.color;
|
|
2015
|
+
ctx.lineWidth = drawing.width;
|
|
2016
|
+
ctx.globalAlpha = draft ? 0.72 : 1;
|
|
2017
|
+
applyDashPattern(drawing.style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2018
|
+
if (drawing.type === "horizontal-line") {
|
|
2019
|
+
const point = drawing.points[0];
|
|
2020
|
+
if (point) {
|
|
2021
|
+
const y = clamp(yFromPrice(point.price), chartTop + 1, chartBottom - 1);
|
|
2022
|
+
ctx.beginPath();
|
|
2023
|
+
ctx.moveTo(crisp(chartLeft), crisp(y));
|
|
2024
|
+
ctx.lineTo(crisp(chartRight), crisp(y));
|
|
2025
|
+
ctx.stroke();
|
|
2026
|
+
}
|
|
2027
|
+
} else if (drawing.type === "trendline") {
|
|
2028
|
+
const first = drawing.points[0];
|
|
2029
|
+
const second = drawing.points[1];
|
|
2030
|
+
if (first && second) {
|
|
2031
|
+
const firstX = xFromDrawingPoint(first);
|
|
2032
|
+
const firstY = yFromPrice(first.price);
|
|
2033
|
+
const secondX = xFromDrawingPoint(second);
|
|
2034
|
+
const secondY = yFromPrice(second.price);
|
|
2035
|
+
ctx.beginPath();
|
|
2036
|
+
ctx.moveTo(firstX, firstY);
|
|
2037
|
+
ctx.lineTo(secondX, secondY);
|
|
2038
|
+
ctx.stroke();
|
|
2039
|
+
drawDrawingHandle(firstX, firstY, drawing.color);
|
|
2040
|
+
drawDrawingHandle(secondX, secondY, drawing.color);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
ctx.restore();
|
|
2044
|
+
};
|
|
1960
2045
|
if (watermark.visible && watermark.imageSrc.trim().length > 0) {
|
|
1961
2046
|
ensureWatermarkImage(watermark.imageSrc.trim());
|
|
1962
2047
|
if (watermarkImageReady && watermarkImage) {
|
|
@@ -2128,6 +2213,10 @@ function createChart(element, options = {}) {
|
|
|
2128
2213
|
);
|
|
2129
2214
|
});
|
|
2130
2215
|
}
|
|
2216
|
+
drawings.forEach((drawing) => drawDrawing(drawing));
|
|
2217
|
+
if (draftDrawing) {
|
|
2218
|
+
drawDrawing(draftDrawing, true);
|
|
2219
|
+
}
|
|
2131
2220
|
const crosshair = { ...DEFAULT_CROSSHAIR_OPTIONS, ...mergedOptions.crosshair ?? {} };
|
|
2132
2221
|
if (crosshair.visible && crosshairPoint) {
|
|
2133
2222
|
const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
|
|
@@ -2517,8 +2606,14 @@ function createChart(element, options = {}) {
|
|
|
2517
2606
|
drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
|
|
2518
2607
|
}
|
|
2519
2608
|
if (labels.visible && (labels.showIndicatorNames || labels.showIndicatorValues)) {
|
|
2520
|
-
const
|
|
2521
|
-
|
|
2609
|
+
const isLegendInputValue = (value) => {
|
|
2610
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
2611
|
+
return true;
|
|
2612
|
+
}
|
|
2613
|
+
return typeof value === "string" && !/^#(?:[0-9a-fA-F]{3}){1,2}$/.test(value.trim());
|
|
2614
|
+
};
|
|
2615
|
+
const labelEntries = activeOverlayIndicators.map(({ indicator, plugin }) => {
|
|
2616
|
+
const inputValues = Object.entries(indicator.inputs).filter(([, value]) => isLegendInputValue(value)).slice(0, 2).map(([, value]) => String(value));
|
|
2522
2617
|
if (labels.showIndicatorNames && labels.showIndicatorValues && inputValues.length > 0) {
|
|
2523
2618
|
return `${plugin.name} ${inputValues.join(" ")}`;
|
|
2524
2619
|
}
|
|
@@ -2958,6 +3053,66 @@ function createChart(element, options = {}) {
|
|
|
2958
3053
|
const ratio = clamp((drawState.chartBottom - y) / drawState.chartHeight, 0, 1);
|
|
2959
3054
|
return drawState.yMin + ratio * (drawState.yMax - drawState.yMin);
|
|
2960
3055
|
};
|
|
3056
|
+
const drawingPointFromCanvas = (x, y) => {
|
|
3057
|
+
if (!drawState) {
|
|
3058
|
+
return null;
|
|
3059
|
+
}
|
|
3060
|
+
const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
3061
|
+
const index = drawState.xStart + ratio * drawState.xSpan - 0.5;
|
|
3062
|
+
const nearestIndex = Math.round(index);
|
|
3063
|
+
const time = getTimeForIndex(nearestIndex);
|
|
3064
|
+
return {
|
|
3065
|
+
index,
|
|
3066
|
+
price: roundToPricePrecision(priceFromCanvasY(y)),
|
|
3067
|
+
...time ? { time: time.toISOString() } : {}
|
|
3068
|
+
};
|
|
3069
|
+
};
|
|
3070
|
+
const canvasXFromDrawingPoint = (point) => {
|
|
3071
|
+
if (!drawState) return 0;
|
|
3072
|
+
return drawState.chartLeft + (point.index + 0.5 - drawState.xStart) / drawState.xSpan * drawState.chartWidth;
|
|
3073
|
+
};
|
|
3074
|
+
const canvasYFromDrawingPrice = (price) => {
|
|
3075
|
+
if (!drawState) return 0;
|
|
3076
|
+
const range = drawState.yMax - drawState.yMin || 1;
|
|
3077
|
+
return drawState.chartBottom - (price - drawState.yMin) / range * drawState.chartHeight;
|
|
3078
|
+
};
|
|
3079
|
+
const distanceToSegment = (x, y, x1, y1, x2, y2) => {
|
|
3080
|
+
const dx = x2 - x1;
|
|
3081
|
+
const dy = y2 - y1;
|
|
3082
|
+
const lengthSq = dx * dx + dy * dy;
|
|
3083
|
+
if (lengthSq === 0) return Math.hypot(x - x1, y - y1);
|
|
3084
|
+
const t = clamp(((x - x1) * dx + (y - y1) * dy) / lengthSq, 0, 1);
|
|
3085
|
+
return Math.hypot(x - (x1 + t * dx), y - (y1 + t * dy));
|
|
3086
|
+
};
|
|
3087
|
+
const getDrawingHit = (x, y) => {
|
|
3088
|
+
for (let index = drawings.length - 1; index >= 0; index -= 1) {
|
|
3089
|
+
const drawing = drawings[index];
|
|
3090
|
+
if (!drawing?.visible) continue;
|
|
3091
|
+
if (drawing.type === "horizontal-line") {
|
|
3092
|
+
const point = drawing.points[0];
|
|
3093
|
+
if (!point) continue;
|
|
3094
|
+
const lineY = canvasYFromDrawingPrice(point.price);
|
|
3095
|
+
if (Math.abs(y - lineY) <= 7) {
|
|
3096
|
+
return { drawing, target: "line" };
|
|
3097
|
+
}
|
|
3098
|
+
} else if (drawing.type === "trendline") {
|
|
3099
|
+
const first = drawing.points[0];
|
|
3100
|
+
const second = drawing.points[1];
|
|
3101
|
+
if (!first || !second) continue;
|
|
3102
|
+
const x1 = canvasXFromDrawingPoint(first);
|
|
3103
|
+
const y1 = canvasYFromDrawingPrice(first.price);
|
|
3104
|
+
const x2 = canvasXFromDrawingPoint(second);
|
|
3105
|
+
const y2 = canvasYFromDrawingPrice(second.price);
|
|
3106
|
+
if (Math.hypot(x - x1, y - y1) <= 8 || Math.hypot(x - x2, y - y2) <= 8) {
|
|
3107
|
+
return { drawing, target: "handle" };
|
|
3108
|
+
}
|
|
3109
|
+
if (distanceToSegment(x, y, x1, y1, x2, y2) <= 6) {
|
|
3110
|
+
return { drawing, target: "line" };
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
return null;
|
|
3115
|
+
};
|
|
2961
3116
|
const indexFromCanvasX = (x) => {
|
|
2962
3117
|
if (!drawState) {
|
|
2963
3118
|
return null;
|
|
@@ -3010,6 +3165,53 @@ function createChart(element, options = {}) {
|
|
|
3010
3165
|
}
|
|
3011
3166
|
return "outside";
|
|
3012
3167
|
};
|
|
3168
|
+
const handleDrawingToolPointerDown = (x, y) => {
|
|
3169
|
+
if (!activeDrawingTool || !drawState) {
|
|
3170
|
+
return false;
|
|
3171
|
+
}
|
|
3172
|
+
const point = drawingPointFromCanvas(x, y);
|
|
3173
|
+
if (!point) {
|
|
3174
|
+
return false;
|
|
3175
|
+
}
|
|
3176
|
+
if (activeDrawingTool === "horizontal-line") {
|
|
3177
|
+
drawings.push(
|
|
3178
|
+
normalizeDrawingState({
|
|
3179
|
+
type: "horizontal-line",
|
|
3180
|
+
points: [point],
|
|
3181
|
+
color: "#38bdf8",
|
|
3182
|
+
style: "dotted",
|
|
3183
|
+
width: 1
|
|
3184
|
+
})
|
|
3185
|
+
);
|
|
3186
|
+
emitDrawingsChange();
|
|
3187
|
+
draw();
|
|
3188
|
+
return true;
|
|
3189
|
+
}
|
|
3190
|
+
if (activeDrawingTool === "trendline") {
|
|
3191
|
+
if (draftDrawing?.type === "trendline") {
|
|
3192
|
+
const completed = normalizeDrawingState({
|
|
3193
|
+
...serializeDrawing(draftDrawing),
|
|
3194
|
+
points: [draftDrawing.points[0], point]
|
|
3195
|
+
});
|
|
3196
|
+
drawings.push(completed);
|
|
3197
|
+
draftDrawing = null;
|
|
3198
|
+
activeDrawingTool = null;
|
|
3199
|
+
emitDrawingsChange();
|
|
3200
|
+
draw();
|
|
3201
|
+
return true;
|
|
3202
|
+
}
|
|
3203
|
+
draftDrawing = normalizeDrawingState({
|
|
3204
|
+
type: "trendline",
|
|
3205
|
+
points: [point, point],
|
|
3206
|
+
color: "#2563eb",
|
|
3207
|
+
style: "solid",
|
|
3208
|
+
width: 2
|
|
3209
|
+
});
|
|
3210
|
+
draw();
|
|
3211
|
+
return true;
|
|
3212
|
+
}
|
|
3213
|
+
return false;
|
|
3214
|
+
};
|
|
3013
3215
|
let isDragging = false;
|
|
3014
3216
|
let dragMode = null;
|
|
3015
3217
|
let lastPointerX = 0;
|
|
@@ -3135,6 +3337,25 @@ function createChart(element, options = {}) {
|
|
|
3135
3337
|
if (region === "outside") {
|
|
3136
3338
|
return;
|
|
3137
3339
|
}
|
|
3340
|
+
if (region === "plot" && !activeDrawingTool) {
|
|
3341
|
+
const drawingHit = getDrawingHit(point.x, point.y);
|
|
3342
|
+
if (drawingHit) {
|
|
3343
|
+
drawingSelectHandler?.({
|
|
3344
|
+
drawing: serializeDrawing(drawingHit.drawing),
|
|
3345
|
+
target: drawingHit.target,
|
|
3346
|
+
x: point.x,
|
|
3347
|
+
y: point.y
|
|
3348
|
+
});
|
|
3349
|
+
setCrosshairPoint(null);
|
|
3350
|
+
canvas.style.cursor = "pointer";
|
|
3351
|
+
return;
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
if (region === "plot" && handleDrawingToolPointerDown(point.x, point.y)) {
|
|
3355
|
+
setCrosshairPoint(null);
|
|
3356
|
+
canvas.style.cursor = "crosshair";
|
|
3357
|
+
return;
|
|
3358
|
+
}
|
|
3138
3359
|
isDragging = true;
|
|
3139
3360
|
dragMode = region;
|
|
3140
3361
|
activePointerId = event.pointerId;
|
|
@@ -3166,6 +3387,18 @@ function createChart(element, options = {}) {
|
|
|
3166
3387
|
pointerDownInfo.moved = true;
|
|
3167
3388
|
}
|
|
3168
3389
|
}
|
|
3390
|
+
if (draftDrawing && activeDrawingTool === "trendline") {
|
|
3391
|
+
const nextPoint = drawingPointFromCanvas(point.x, point.y);
|
|
3392
|
+
if (nextPoint) {
|
|
3393
|
+
draftDrawing = {
|
|
3394
|
+
...draftDrawing,
|
|
3395
|
+
points: [draftDrawing.points[0], nextPoint]
|
|
3396
|
+
};
|
|
3397
|
+
canvas.style.cursor = "crosshair";
|
|
3398
|
+
draw();
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3169
3402
|
if (orderDragState) {
|
|
3170
3403
|
if (activePointerId !== null && event.pointerId !== activePointerId) {
|
|
3171
3404
|
return;
|
|
@@ -3236,9 +3469,14 @@ function createChart(element, options = {}) {
|
|
|
3236
3469
|
setCrosshairPoint(null);
|
|
3237
3470
|
return;
|
|
3238
3471
|
}
|
|
3472
|
+
if (!activeDrawingTool && getDrawingHit(point.x, point.y)) {
|
|
3473
|
+
canvas.style.cursor = "pointer";
|
|
3474
|
+
setCrosshairPoint(null);
|
|
3475
|
+
return;
|
|
3476
|
+
}
|
|
3239
3477
|
const hoverRegion = getHitRegion(point.x, point.y);
|
|
3240
3478
|
if (hoverRegion === "plot") {
|
|
3241
|
-
canvas.style.cursor = doubleClickEnabled ? "default" : "crosshair";
|
|
3479
|
+
canvas.style.cursor = activeDrawingTool ? "crosshair" : doubleClickEnabled ? "default" : "crosshair";
|
|
3242
3480
|
setCrosshairPoint(point);
|
|
3243
3481
|
emitCrosshairMove(point.x, point.y, "plot");
|
|
3244
3482
|
} else if (hoverRegion === "x-axis") {
|
|
@@ -3637,6 +3875,56 @@ function createChart(element, options = {}) {
|
|
|
3637
3875
|
indicators = indicators.filter((indicator) => indicator.id !== id);
|
|
3638
3876
|
draw();
|
|
3639
3877
|
};
|
|
3878
|
+
const setActiveDrawingTool = (tool) => {
|
|
3879
|
+
activeDrawingTool = tool;
|
|
3880
|
+
draftDrawing = null;
|
|
3881
|
+
canvas.style.cursor = tool ? "crosshair" : "default";
|
|
3882
|
+
draw();
|
|
3883
|
+
};
|
|
3884
|
+
const getActiveDrawingTool = () => activeDrawingTool;
|
|
3885
|
+
const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
|
|
3886
|
+
const setDrawings = (nextDrawings) => {
|
|
3887
|
+
drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
|
|
3888
|
+
draftDrawing = null;
|
|
3889
|
+
emitDrawingsChange();
|
|
3890
|
+
draw();
|
|
3891
|
+
};
|
|
3892
|
+
const addDrawing = (drawing) => {
|
|
3893
|
+
const next = normalizeDrawingState(drawing);
|
|
3894
|
+
drawings.push(next);
|
|
3895
|
+
emitDrawingsChange();
|
|
3896
|
+
draw();
|
|
3897
|
+
return next.id;
|
|
3898
|
+
};
|
|
3899
|
+
const updateDrawing = (id, patch) => {
|
|
3900
|
+
drawings = drawings.map(
|
|
3901
|
+
(drawing) => drawing.id === id ? normalizeDrawingState({
|
|
3902
|
+
...serializeDrawing(drawing),
|
|
3903
|
+
...patch,
|
|
3904
|
+
id,
|
|
3905
|
+
points: patch.points ?? drawing.points
|
|
3906
|
+
}) : drawing
|
|
3907
|
+
);
|
|
3908
|
+
emitDrawingsChange();
|
|
3909
|
+
draw();
|
|
3910
|
+
};
|
|
3911
|
+
const removeDrawing = (id) => {
|
|
3912
|
+
drawings = drawings.filter((drawing) => drawing.id !== id);
|
|
3913
|
+
emitDrawingsChange();
|
|
3914
|
+
draw();
|
|
3915
|
+
};
|
|
3916
|
+
const clearDrawings = () => {
|
|
3917
|
+
drawings = [];
|
|
3918
|
+
draftDrawing = null;
|
|
3919
|
+
emitDrawingsChange();
|
|
3920
|
+
draw();
|
|
3921
|
+
};
|
|
3922
|
+
const onDrawingsChange = (handler) => {
|
|
3923
|
+
drawingsChangeHandler = handler;
|
|
3924
|
+
};
|
|
3925
|
+
const onDrawingSelect = (handler) => {
|
|
3926
|
+
drawingSelectHandler = handler;
|
|
3927
|
+
};
|
|
3640
3928
|
const destroy = () => {
|
|
3641
3929
|
if (smoothingRafId !== null) {
|
|
3642
3930
|
cancelAnimationFrame(smoothingRafId);
|
|
@@ -3680,6 +3968,16 @@ function createChart(element, options = {}) {
|
|
|
3680
3968
|
getViewport,
|
|
3681
3969
|
setViewport,
|
|
3682
3970
|
onViewportChange,
|
|
3971
|
+
setActiveDrawingTool,
|
|
3972
|
+
getActiveDrawingTool,
|
|
3973
|
+
setDrawings,
|
|
3974
|
+
getDrawings,
|
|
3975
|
+
addDrawing,
|
|
3976
|
+
updateDrawing,
|
|
3977
|
+
removeDrawing,
|
|
3978
|
+
clearDrawings,
|
|
3979
|
+
onDrawingsChange,
|
|
3980
|
+
onDrawingSelect,
|
|
3683
3981
|
setDoubleClickEnabled,
|
|
3684
3982
|
setDoubleClickAction,
|
|
3685
3983
|
registerIndicator,
|