bn-lightweight-charts 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl

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.
@@ -1,2472 +0,0 @@
1
- var Lib = (function (exports, lightweightCharts) {
2
- 'use strict';
3
-
4
- const paneStyleDefault = {
5
- backgroundColor: 'rgb(18,24,38)',
6
- hoverBackgroundColor: '#3c434c',
7
- clickBackgroundColor: '#50565E',
8
- activeBackgroundColor: 'rgba(0, 122, 255, 0.7)',
9
- mutedBackgroundColor: 'rgba(0, 122, 255, 0.3)',
10
- borderColor: '#3C434C',
11
- color: '#d8d9db',
12
- activeColor: '#ececed',
13
- };
14
- function globalParamInit() {
15
- window.pane = {
16
- ...paneStyleDefault,
17
- };
18
- window.containerDiv = document.getElementById("container") || document.createElement('div');
19
- window.setCursor = (type) => {
20
- if (type)
21
- window.cursor = type;
22
- document.body.style.cursor = window.cursor;
23
- };
24
- window.cursor = 'default';
25
- window.textBoxFocused = false;
26
- }
27
- const setCursor = (type) => {
28
- if (type)
29
- window.cursor = type;
30
- document.body.style.cursor = window.cursor;
31
- };
32
- function htmlToElement(html) {
33
- let template = document.createElement('template');
34
- html = html.trim(); // Never return a text node of whitespace as the result
35
- template.innerHTML = html;
36
- const el = template.content.firstChild;
37
- if (!el)
38
- throw new Error("Invalid HTML passed to htmlToElement");
39
- return el;
40
- }
41
- // export interface SeriesHandler {
42
- // type: string;
43
- // series: ISeriesApi<SeriesType>;
44
- // markers: SeriesMarker<"">[],
45
- // horizontal_lines: HorizontalLine[],
46
- // name?: string,
47
- // precision: number,
48
- // }
49
-
50
- class Legend {
51
- handler;
52
- div;
53
- seriesContainer;
54
- ohlcEnabled = false;
55
- percentEnabled = false;
56
- linesEnabled = false;
57
- colorBasedOnCandle = false;
58
- text;
59
- candle;
60
- _lines = [];
61
- _lines_grp = {};
62
- constructor(handler) {
63
- this.legendHandler = this.legendHandler.bind(this);
64
- this.handler = handler;
65
- this.ohlcEnabled = false;
66
- this.percentEnabled = false;
67
- this.linesEnabled = false;
68
- this.colorBasedOnCandle = false;
69
- this.div = document.createElement('div');
70
- this.div.classList.add("legend");
71
- this.div.style.maxWidth = `${(handler.scale.width * 100) - 8}vw`;
72
- this.div.style.display = 'none';
73
- const seriesWrapper = document.createElement('div');
74
- seriesWrapper.style.display = 'flex';
75
- seriesWrapper.style.flexDirection = 'row';
76
- this.seriesContainer = document.createElement("div");
77
- this.seriesContainer.classList.add("series-container");
78
- this.text = document.createElement('span');
79
- this.text.style.lineHeight = '1.8';
80
- this.candle = document.createElement('div');
81
- seriesWrapper.appendChild(this.seriesContainer);
82
- this.div.appendChild(this.text);
83
- this.div.appendChild(this.candle);
84
- this.div.appendChild(seriesWrapper);
85
- handler.div.appendChild(this.div);
86
- // this.makeSeriesRows(handler);
87
- handler.chart.subscribeCrosshairMove(this.legendHandler);
88
- }
89
- toJSON() {
90
- // Exclude the chart attribute from serialization
91
- const { _lines, handler, ...serialized } = this;
92
- return serialized;
93
- }
94
- // makeSeriesRows(handler: Handler) {
95
- // if (this.linesEnabled) handler._seriesList.forEach(s => this.makeSeriesRow(s))
96
- // }
97
- makeSeriesRow(name, series, paneIndex) {
98
- const strokeColor = '#FFF';
99
- let openEye = `
100
- <path style="fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke:${strokeColor};stroke-opacity:1;stroke-miterlimit:4;" d="M 21.998437 12 C 21.998437 12 18.998437 18 12 18 C 5.001562 18 2.001562 12 2.001562 12 C 2.001562 12 5.001562 6 12 6 C 18.998437 6 21.998437 12 21.998437 12 Z M 21.998437 12 " transform="matrix(0.833333,0,0,0.833333,0,0)"/>
101
- <path style="fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke:${strokeColor};stroke-opacity:1;stroke-miterlimit:4;" d="M 15 12 C 15 13.654687 13.654687 15 12 15 C 10.345312 15 9 13.654687 9 12 C 9 10.345312 10.345312 9 12 9 C 13.654687 9 15 10.345312 15 12 Z M 15 12 " transform="matrix(0.833333,0,0,0.833333,0,0)"/>\`
102
- `;
103
- let closedEye = `
104
- <path style="fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke:${strokeColor};stroke-opacity:1;stroke-miterlimit:4;" d="M 20.001562 9 C 20.001562 9 19.678125 9.665625 18.998437 10.514062 M 12 14.001562 C 10.392187 14.001562 9.046875 13.589062 7.95 12.998437 M 12 14.001562 C 13.607812 14.001562 14.953125 13.589062 16.05 12.998437 M 12 14.001562 L 12 17.498437 M 3.998437 9 C 3.998437 9 4.354687 9.735937 5.104687 10.645312 M 7.95 12.998437 L 5.001562 15.998437 M 7.95 12.998437 C 6.689062 12.328125 5.751562 11.423437 5.104687 10.645312 M 16.05 12.998437 L 18.501562 15.998437 M 16.05 12.998437 C 17.38125 12.290625 18.351562 11.320312 18.998437 10.514062 M 5.104687 10.645312 L 2.001562 12 M 18.998437 10.514062 L 21.998437 12 " transform="matrix(0.833333,0,0,0.833333,0,0)"/>
105
- `;
106
- let row = document.createElement('div');
107
- row.style.display = 'flex';
108
- row.style.alignItems = 'center';
109
- let div = document.createElement('div');
110
- let toggle = document.createElement('div');
111
- toggle.classList.add('legend-toggle-switch');
112
- let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
113
- svg.setAttribute("width", "22");
114
- svg.setAttribute("height", "16");
115
- let group = document.createElementNS("http://www.w3.org/2000/svg", "g");
116
- group.innerHTML = openEye;
117
- let on = true;
118
- toggle.addEventListener('click', () => {
119
- if (on) {
120
- on = false;
121
- group.innerHTML = closedEye;
122
- series.applyOptions({
123
- visible: false
124
- });
125
- }
126
- else {
127
- on = true;
128
- series.applyOptions({
129
- visible: true
130
- });
131
- group.innerHTML = openEye;
132
- }
133
- });
134
- svg.appendChild(group);
135
- toggle.appendChild(svg);
136
- row.appendChild(div);
137
- row.appendChild(toggle);
138
- this.seriesContainer.appendChild(row);
139
- const color = series.options().baseLineColor;
140
- this._lines.push({
141
- name: name,
142
- paneIndex: paneIndex,
143
- div: div,
144
- row: row,
145
- toggle: toggle,
146
- series: series,
147
- solid: color.startsWith('rgba') ? color.replace(/[^,]+(?=\))/, '1') : color
148
- });
149
- this._lines.sort((a, b) => a.paneIndex - b.paneIndex);
150
- this._lines_grp = this._lines.reduce((acc, item) => {
151
- if (!acc[item.paneIndex]) {
152
- acc[item.paneIndex] = [];
153
- }
154
- acc[item.paneIndex].push(item);
155
- return acc;
156
- }, {});
157
- this.seriesContainer.innerHTML = '';
158
- for (const k in this._lines_grp) {
159
- for (const l of this._lines_grp[k]) {
160
- this.seriesContainer.appendChild(l.row);
161
- }
162
- this.seriesContainer.appendChild(htmlToElement("<br>"));
163
- }
164
- }
165
- legendItemFormat(num, decimal) {
166
- return num.toFixed(decimal).toString().padStart(8, ' ');
167
- }
168
- shorthandFormat(num) {
169
- const absNum = Math.abs(num);
170
- if (absNum >= 1000000) {
171
- return (num / 1000000).toFixed(1) + 'M';
172
- }
173
- else if (absNum >= 1000) {
174
- return (num / 1000).toFixed(1) + 'K';
175
- }
176
- return num.toString().padStart(8, ' ');
177
- }
178
- legendHandler(param, usingPoint = false) {
179
- if (!this.ohlcEnabled && !this.linesEnabled && !this.percentEnabled)
180
- return;
181
- const options = this.handler.series.options();
182
- if (!param.time) {
183
- this.candle.style.color = 'transparent';
184
- this.candle.innerHTML = this.candle.innerHTML.replace(options['upColor'], '').replace(options['downColor'], '');
185
- return;
186
- }
187
- let data;
188
- let logical = null;
189
- if (usingPoint) {
190
- const timeScale = this.handler.chart.timeScale();
191
- let coordinate = timeScale.timeToCoordinate(param.time);
192
- if (coordinate)
193
- logical = timeScale.coordinateToLogical(coordinate.valueOf());
194
- if (logical)
195
- data = this.handler.series.dataByIndex(logical.valueOf());
196
- }
197
- else {
198
- data = param.seriesData.get(this.handler.series);
199
- }
200
- this.candle.style.color = '';
201
- let str = '<span style="line-height: 1.8;">';
202
- if (data) {
203
- if (this.ohlcEnabled) {
204
- str += `O ${this.legendItemFormat(data.open, this.handler.precision)} `;
205
- str += `| H ${this.legendItemFormat(data.high, this.handler.precision)} `;
206
- str += `| L ${this.legendItemFormat(data.low, this.handler.precision)} `;
207
- str += `| C ${this.legendItemFormat(data.close, this.handler.precision)} `;
208
- }
209
- if (this.handler.volumeSeries) {
210
- let volumeData;
211
- if (logical) {
212
- volumeData = this.handler.volumeSeries.dataByIndex(logical);
213
- }
214
- else {
215
- volumeData = param.seriesData.get(this.handler.volumeSeries);
216
- }
217
- if (volumeData) {
218
- str += this.ohlcEnabled ? `| V ${this.shorthandFormat(volumeData.value)}` : '';
219
- }
220
- }
221
- if (this.percentEnabled) {
222
- let percentMove = ((data.close - data.open) / data.open) * 100;
223
- let color = percentMove > 0 ? options["upColor"] : options["downColor"];
224
- let percentStr = `${percentMove >= 0 ? "+" : ""}${percentMove.toFixed(2)} %`;
225
- if (this.colorBasedOnCandle) {
226
- str += `| <span style="color: ${color};">${percentStr}</span>`;
227
- }
228
- else {
229
- str += "| " + percentStr;
230
- }
231
- }
232
- }
233
- this.candle.innerHTML = str + '</span>';
234
- this._lines.forEach((e) => {
235
- if (!this.linesEnabled) {
236
- e.row.style.display = 'none';
237
- return;
238
- }
239
- e.row.style.display = 'flex';
240
- let data;
241
- if (usingPoint && logical) {
242
- data = e.series.dataByIndex(logical);
243
- }
244
- else {
245
- data = param.seriesData.get(e.series);
246
- }
247
- if (!data?.value)
248
- return;
249
- let price;
250
- if (e.series.seriesType() == 'Histogram') {
251
- price = this.shorthandFormat(data.value);
252
- }
253
- else {
254
- const format = e.series.options().priceFormat;
255
- price = this.legendItemFormat(data.value, format.precision); // couldn't this just be line.options().precision?
256
- }
257
- e.div.innerHTML = `<span style="color: ${e.solid};">▨</span> ${e.name} : ${price}`;
258
- });
259
- }
260
- }
261
-
262
- function ensureDefined(value) {
263
- if (value === undefined) {
264
- throw new Error('Value is undefined');
265
- }
266
- return value;
267
- }
268
-
269
- //* PluginBase is a useful base to build a plugin upon which
270
- //* already handles creating getters for the chart and series,
271
- //* and provides a requestUpdate method.
272
- class PluginBase {
273
- _chart = undefined;
274
- _series = undefined;
275
- requestUpdate() {
276
- if (this._requestUpdate)
277
- this._requestUpdate();
278
- }
279
- _requestUpdate;
280
- attached({ chart, series, requestUpdate, }) {
281
- this._chart = chart;
282
- this._series = series;
283
- this._series.subscribeDataChanged(this._fireDataUpdated);
284
- this._requestUpdate = requestUpdate;
285
- this.requestUpdate();
286
- }
287
- detached() {
288
- this._chart = undefined;
289
- this._series = undefined;
290
- this._requestUpdate = undefined;
291
- }
292
- get chart() {
293
- return ensureDefined(this._chart);
294
- }
295
- get series() {
296
- return ensureDefined(this._series);
297
- }
298
- _fireDataUpdated(scope) {
299
- if (this.dataUpdated) {
300
- this.dataUpdated(scope);
301
- }
302
- }
303
- }
304
-
305
- const defaultOptions = {
306
- lineColor: '#1E80F0',
307
- lineStyle: lightweightCharts.LineStyle.Solid,
308
- width: 4,
309
- };
310
-
311
- var InteractionState;
312
- (function (InteractionState) {
313
- InteractionState[InteractionState["NONE"] = 0] = "NONE";
314
- InteractionState[InteractionState["HOVERING"] = 1] = "HOVERING";
315
- InteractionState[InteractionState["DRAGGING"] = 2] = "DRAGGING";
316
- InteractionState[InteractionState["DRAGGINGP1"] = 3] = "DRAGGINGP1";
317
- InteractionState[InteractionState["DRAGGINGP2"] = 4] = "DRAGGINGP2";
318
- InteractionState[InteractionState["DRAGGINGP3"] = 5] = "DRAGGINGP3";
319
- InteractionState[InteractionState["DRAGGINGP4"] = 6] = "DRAGGINGP4";
320
- })(InteractionState || (InteractionState = {}));
321
- class Drawing extends PluginBase {
322
- _paneViews = [];
323
- _options;
324
- _points = [];
325
- _state = InteractionState.NONE;
326
- _startDragPoint = null;
327
- _latestHoverPoint = null;
328
- static _mouseIsDown = false;
329
- static hoveredObject = null;
330
- static lastHoveredObject = null;
331
- _listeners = [];
332
- constructor(options) {
333
- super();
334
- this._options = {
335
- ...defaultOptions,
336
- ...options,
337
- };
338
- }
339
- updateAllViews() {
340
- this._paneViews.forEach(pw => pw.update());
341
- }
342
- paneViews() {
343
- return this._paneViews;
344
- }
345
- applyOptions(options) {
346
- this._options = {
347
- ...this._options,
348
- ...options,
349
- };
350
- this.requestUpdate();
351
- }
352
- updatePoints(...points) {
353
- for (let i = 0; i < this.points.length; i++) {
354
- if (points[i] == null)
355
- continue;
356
- this.points[i] = points[i];
357
- }
358
- this.requestUpdate();
359
- }
360
- detach() {
361
- this._options.lineColor = 'transparent';
362
- this.requestUpdate();
363
- this.series.detachPrimitive(this);
364
- for (const s of this._listeners) {
365
- document.body.removeEventListener(s.name, s.listener);
366
- }
367
- }
368
- get points() {
369
- return this._points;
370
- }
371
- _subscribe(name, listener) {
372
- document.body.addEventListener(name, listener);
373
- this._listeners.push({ name: name, listener: listener });
374
- }
375
- _unsubscribe(name, callback) {
376
- document.body.removeEventListener(name, callback);
377
- const toRemove = this._listeners.find((x) => x.name === name && x.listener === callback);
378
- this._listeners.splice(this._listeners.indexOf(toRemove), 1);
379
- }
380
- _handleHoverInteraction(param) {
381
- this._latestHoverPoint = param.point;
382
- if (Drawing._mouseIsDown) {
383
- this._handleDragInteraction(param);
384
- }
385
- else {
386
- if (this._mouseIsOverDrawing(param)) {
387
- if (this._state != InteractionState.NONE)
388
- return;
389
- this._moveToState(InteractionState.HOVERING);
390
- Drawing.hoveredObject = Drawing.lastHoveredObject = this;
391
- }
392
- else {
393
- if (this._state == InteractionState.NONE)
394
- return;
395
- this._moveToState(InteractionState.NONE);
396
- if (Drawing.hoveredObject === this)
397
- Drawing.hoveredObject = null;
398
- }
399
- }
400
- }
401
- static _eventToPoint(param, series) {
402
- if (!series || !param.point || !param.logical)
403
- return null;
404
- const barPrice = series.coordinateToPrice(param.point.y);
405
- if (barPrice == null)
406
- return null;
407
- return {
408
- time: param.time || null,
409
- logical: param.logical,
410
- price: barPrice.valueOf(),
411
- };
412
- }
413
- static _getDiff(p1, p2) {
414
- const diff = {
415
- logical: p1.logical - p2.logical,
416
- price: p1.price - p2.price,
417
- };
418
- return diff;
419
- }
420
- _addDiffToPoint(point, logicalDiff, priceDiff) {
421
- if (!point)
422
- return;
423
- point.logical = point.logical + logicalDiff;
424
- point.price = point.price + priceDiff;
425
- point.time = this.series.dataByIndex(point.logical)?.time || null;
426
- }
427
- _handleMouseDownInteraction = () => {
428
- // if (Drawing._mouseIsDown) return;
429
- Drawing._mouseIsDown = true;
430
- this._onMouseDown();
431
- };
432
- _handleMouseUpInteraction = () => {
433
- // if (!Drawing._mouseIsDown) return;
434
- Drawing._mouseIsDown = false;
435
- this._moveToState(InteractionState.HOVERING);
436
- };
437
- _handleDragInteraction(param) {
438
- if (this._state != InteractionState.DRAGGING &&
439
- this._state != InteractionState.DRAGGINGP1 &&
440
- this._state != InteractionState.DRAGGINGP2 &&
441
- this._state != InteractionState.DRAGGINGP3 &&
442
- this._state != InteractionState.DRAGGINGP4) {
443
- return;
444
- }
445
- const mousePoint = Drawing._eventToPoint(param, this.series);
446
- if (!mousePoint)
447
- return;
448
- this._startDragPoint = this._startDragPoint || mousePoint;
449
- const diff = Drawing._getDiff(mousePoint, this._startDragPoint);
450
- this._onDrag(diff);
451
- this.requestUpdate();
452
- this._startDragPoint = mousePoint;
453
- }
454
- }
455
-
456
- class DrawingPaneRenderer {
457
- _options;
458
- constructor(options) {
459
- this._options = options;
460
- }
461
- }
462
- class TwoPointDrawingPaneRenderer extends DrawingPaneRenderer {
463
- _p1;
464
- _p2;
465
- _hovered;
466
- constructor(p1, p2, options, hovered) {
467
- super(options);
468
- this._p1 = p1;
469
- this._p2 = p2;
470
- this._hovered = hovered;
471
- }
472
- _getScaledCoordinates(scope) {
473
- if (this._p1.x === null || this._p1.y === null ||
474
- this._p2.x === null || this._p2.y === null)
475
- return null;
476
- return {
477
- x1: Math.round(this._p1.x * scope.horizontalPixelRatio),
478
- y1: Math.round(this._p1.y * scope.verticalPixelRatio),
479
- x2: Math.round(this._p2.x * scope.horizontalPixelRatio),
480
- y2: Math.round(this._p2.y * scope.verticalPixelRatio),
481
- };
482
- }
483
- // _drawTextLabel(scope: BitmapCoordinatesRenderingScope, text: string, x: number, y: number, left: boolean) {
484
- // scope.context.font = '24px Arial';
485
- // scope.context.beginPath();
486
- // const offset = 5 * scope.horizontalPixelRatio;
487
- // const textWidth = scope.context.measureText(text);
488
- // const leftAdjustment = left ? textWidth.width + offset * 4 : 0;
489
- // scope.context.fillStyle = this._options.labelBackgroundColor;
490
- // scope.context.roundRect(x + offset - leftAdjustment, y - 24, textWidth.width + offset * 2, 24 + offset, 5);
491
- // scope.context.fill();
492
- // scope.context.beginPath();
493
- // scope.context.fillStyle = this._options.labelTextColor;
494
- // scope.context.fillText(text, x + offset * 2 - leftAdjustment, y);
495
- // }
496
- _drawEndCircle(scope, x, y) {
497
- const radius = 9;
498
- scope.context.fillStyle = '#000';
499
- scope.context.beginPath();
500
- scope.context.arc(x, y, radius, 0, 2 * Math.PI);
501
- scope.context.stroke();
502
- scope.context.fill();
503
- // scope.context.strokeStyle = this._options.lineColor;
504
- }
505
- }
506
-
507
- function setLineStyle(ctx, style) {
508
- const dashPatterns = {
509
- [lightweightCharts.LineStyle.Solid]: [],
510
- [lightweightCharts.LineStyle.Dotted]: [ctx.lineWidth, ctx.lineWidth],
511
- [lightweightCharts.LineStyle.Dashed]: [2 * ctx.lineWidth, 2 * ctx.lineWidth],
512
- [lightweightCharts.LineStyle.LargeDashed]: [6 * ctx.lineWidth, 6 * ctx.lineWidth],
513
- [lightweightCharts.LineStyle.SparseDotted]: [ctx.lineWidth, 4 * ctx.lineWidth],
514
- };
515
- const dashPattern = dashPatterns[style];
516
- ctx.setLineDash(dashPattern);
517
- }
518
-
519
- class HorizontalLinePaneRenderer extends DrawingPaneRenderer {
520
- _point = { x: null, y: null };
521
- constructor(point, options) {
522
- super(options);
523
- this._point = point;
524
- }
525
- draw(target) {
526
- target.useBitmapCoordinateSpace(scope => {
527
- if (this._point.y == null)
528
- return;
529
- const ctx = scope.context;
530
- const scaledY = Math.round(this._point.y * scope.verticalPixelRatio);
531
- const scaledX = this._point.x ? this._point.x * scope.horizontalPixelRatio : 0;
532
- ctx.lineWidth = this._options.width;
533
- ctx.strokeStyle = this._options.lineColor;
534
- setLineStyle(ctx, this._options.lineStyle);
535
- ctx.beginPath();
536
- ctx.moveTo(scaledX, scaledY);
537
- ctx.lineTo(scope.bitmapSize.width, scaledY);
538
- ctx.stroke();
539
- });
540
- }
541
- }
542
-
543
- class DrawingPaneView {
544
- _source;
545
- constructor(source) {
546
- this._source = source;
547
- }
548
- }
549
- class TwoPointDrawingPaneView extends DrawingPaneView {
550
- _p1 = { x: null, y: null };
551
- _p2 = { x: null, y: null };
552
- _source;
553
- constructor(source) {
554
- super(source);
555
- this._source = source;
556
- }
557
- update() {
558
- if (!this._source.p1 || !this._source.p2)
559
- return;
560
- const series = this._source.series;
561
- const y1 = series.priceToCoordinate(this._source.p1.price);
562
- const y2 = series.priceToCoordinate(this._source.p2.price);
563
- const x1 = this._getX(this._source.p1);
564
- const x2 = this._getX(this._source.p2);
565
- this._p1 = { x: x1, y: y1 };
566
- this._p2 = { x: x2, y: y2 };
567
- if (!x1 || !x2 || !y1 || !y2)
568
- return;
569
- }
570
- _getX(p) {
571
- const timeScale = this._source.chart.timeScale();
572
- return timeScale.logicalToCoordinate(p.logical);
573
- }
574
- }
575
-
576
- class HorizontalLinePaneView extends DrawingPaneView {
577
- _source;
578
- _point = { x: null, y: null };
579
- constructor(source) {
580
- super(source);
581
- this._source = source;
582
- }
583
- update() {
584
- const point = this._source._point;
585
- const timeScale = this._source.chart.timeScale();
586
- const series = this._source.series;
587
- if (this._source._type == "RayLine") {
588
- this._point.x = point.time ? timeScale.timeToCoordinate(point.time) : timeScale.logicalToCoordinate(point.logical);
589
- }
590
- this._point.y = series.priceToCoordinate(point.price);
591
- }
592
- renderer() {
593
- return new HorizontalLinePaneRenderer(this._point, this._source._options);
594
- }
595
- }
596
-
597
- class HorizontalLineAxisView {
598
- _source;
599
- _y = null;
600
- _price = null;
601
- constructor(source) {
602
- this._source = source;
603
- }
604
- update() {
605
- if (!this._source.series || !this._source._point)
606
- return;
607
- this._y = this._source.series.priceToCoordinate(this._source._point.price);
608
- const priceFormat = this._source.series.options().priceFormat;
609
- const precision = priceFormat.precision;
610
- this._price = this._source._point.price.toFixed(precision).toString();
611
- }
612
- visible() {
613
- return true;
614
- }
615
- tickVisible() {
616
- return true;
617
- }
618
- coordinate() {
619
- return this._y ?? 0;
620
- }
621
- text() {
622
- return this._price || '';
623
- }
624
- textColor() {
625
- return 'white';
626
- }
627
- backColor() {
628
- return this._source._options.lineColor;
629
- }
630
- }
631
-
632
- class HorizontalLine extends Drawing {
633
- _type = 'HorizontalLine';
634
- _paneViews;
635
- _point;
636
- _callbackName;
637
- _priceAxisViews;
638
- _startDragPoint = null;
639
- constructor(point, options, callbackName = null) {
640
- super(options);
641
- this._point = point;
642
- this._point.time = null; // time is null for horizontal lines
643
- this._paneViews = [new HorizontalLinePaneView(this)];
644
- this._priceAxisViews = [new HorizontalLineAxisView(this)];
645
- this._callbackName = callbackName;
646
- }
647
- get points() {
648
- return [this._point];
649
- }
650
- updatePoints(...points) {
651
- for (const p of points)
652
- if (p)
653
- this._point.price = p.price;
654
- this.requestUpdate();
655
- }
656
- updateAllViews() {
657
- this._paneViews.forEach((pw) => pw.update());
658
- this._priceAxisViews.forEach((tw) => tw.update());
659
- }
660
- priceAxisViews() {
661
- return this._priceAxisViews;
662
- }
663
- _moveToState(state) {
664
- switch (state) {
665
- case InteractionState.NONE:
666
- document.body.style.cursor = "default";
667
- this._unsubscribe("mousedown", this._handleMouseDownInteraction);
668
- break;
669
- case InteractionState.HOVERING:
670
- document.body.style.cursor = "pointer";
671
- this._unsubscribe("mouseup", this._childHandleMouseUpInteraction);
672
- this._subscribe("mousedown", this._handleMouseDownInteraction);
673
- this.chart.applyOptions({ handleScroll: true });
674
- break;
675
- case InteractionState.DRAGGING:
676
- document.body.style.cursor = "grabbing";
677
- this._subscribe("mouseup", this._childHandleMouseUpInteraction);
678
- this.chart.applyOptions({ handleScroll: false });
679
- break;
680
- }
681
- this._state = state;
682
- }
683
- _onDrag(diff) {
684
- this._addDiffToPoint(this._point, 0, diff.price);
685
- this.requestUpdate();
686
- }
687
- _mouseIsOverDrawing(param, tolerance = 4) {
688
- if (!param.point)
689
- return false;
690
- const y = this.series.priceToCoordinate(this._point.price);
691
- if (!y)
692
- return false;
693
- return (Math.abs(y - param.point.y) < tolerance);
694
- }
695
- _onMouseDown() {
696
- this._startDragPoint = null;
697
- const hoverPoint = this._latestHoverPoint;
698
- if (!hoverPoint)
699
- return;
700
- return this._moveToState(InteractionState.DRAGGING);
701
- }
702
- _childHandleMouseUpInteraction = () => {
703
- this._handleMouseUpInteraction();
704
- if (!this._callbackName)
705
- return;
706
- window.callbackFunction(`${this._callbackName}_~_${this._point.price.toFixed(8)}`);
707
- };
708
- }
709
-
710
- class DrawingTool {
711
- _chart;
712
- _series;
713
- _finishDrawingCallback = null;
714
- _drawings = [];
715
- _activeDrawing = null;
716
- _isDrawing = false;
717
- _drawingType = null;
718
- constructor(chart, series, finishDrawingCallback = null) {
719
- this._chart = chart;
720
- this._series = series;
721
- this._finishDrawingCallback = finishDrawingCallback;
722
- this._chart.subscribeClick(this._clickHandler);
723
- this._chart.subscribeCrosshairMove(this._moveHandler);
724
- }
725
- _clickHandler = (param) => this._onClick(param);
726
- _moveHandler = (param) => this._onMouseMove(param);
727
- beginDrawing(DrawingType) {
728
- this._drawingType = DrawingType;
729
- this._isDrawing = true;
730
- }
731
- stopDrawing() {
732
- this._isDrawing = false;
733
- this._activeDrawing = null;
734
- }
735
- get drawings() {
736
- return this._drawings;
737
- }
738
- addNewDrawing(drawing) {
739
- this._series.attachPrimitive(drawing);
740
- this._drawings.push(drawing);
741
- }
742
- delete(d) {
743
- if (d == null)
744
- return;
745
- const idx = this._drawings.indexOf(d);
746
- if (idx == -1)
747
- return;
748
- this._drawings.splice(idx, 1);
749
- d.detach();
750
- }
751
- clearDrawings() {
752
- for (const d of this._drawings)
753
- d.detach();
754
- this._drawings = [];
755
- }
756
- repositionOnTime() {
757
- for (const drawing of this.drawings) {
758
- const newPoints = [];
759
- for (const point of drawing.points) {
760
- if (!point) {
761
- newPoints.push(point);
762
- continue;
763
- }
764
- const logical = point.time ? this._chart.timeScale()
765
- .coordinateToLogical(this._chart.timeScale().timeToCoordinate(point.time) || 0) : point.logical;
766
- newPoints.push({
767
- time: point.time,
768
- logical: logical,
769
- price: point.price,
770
- });
771
- }
772
- drawing.updatePoints(...newPoints);
773
- }
774
- }
775
- _onClick(param) {
776
- if (!this._isDrawing)
777
- return;
778
- const point = Drawing._eventToPoint(param, this._series);
779
- if (!point)
780
- return;
781
- if (this._activeDrawing == null) {
782
- if (this._drawingType == null)
783
- return;
784
- this._activeDrawing = new this._drawingType(point, point);
785
- this._series.attachPrimitive(this._activeDrawing);
786
- if (this._drawingType == HorizontalLine)
787
- this._onClick(param);
788
- }
789
- else {
790
- this._drawings.push(this._activeDrawing);
791
- this.stopDrawing();
792
- if (!this._finishDrawingCallback)
793
- return;
794
- this._finishDrawingCallback();
795
- }
796
- }
797
- _onMouseMove(param) {
798
- if (!param)
799
- return;
800
- for (const t of this._drawings)
801
- t._handleHoverInteraction(param);
802
- if (!this._isDrawing || !this._activeDrawing)
803
- return;
804
- const point = Drawing._eventToPoint(param, this._series);
805
- if (!point)
806
- return;
807
- this._activeDrawing.updatePoints(null, point);
808
- // this._activeDrawing.setSecondPoint(point);
809
- }
810
- }
811
-
812
- class TrendLinePaneRenderer extends TwoPointDrawingPaneRenderer {
813
- constructor(p1, p2, options, hovered) {
814
- super(p1, p2, options, hovered);
815
- }
816
- draw(target) {
817
- target.useBitmapCoordinateSpace(scope => {
818
- if (this._p1.x === null ||
819
- this._p1.y === null ||
820
- this._p2.x === null ||
821
- this._p2.y === null)
822
- return;
823
- const ctx = scope.context;
824
- const scaled = this._getScaledCoordinates(scope);
825
- if (!scaled)
826
- return;
827
- ctx.lineWidth = this._options.width;
828
- ctx.strokeStyle = this._options.lineColor;
829
- setLineStyle(ctx, this._options.lineStyle);
830
- ctx.beginPath();
831
- ctx.moveTo(scaled.x1, scaled.y1);
832
- ctx.lineTo(scaled.x2, scaled.y2);
833
- ctx.stroke();
834
- // this._drawTextLabel(scope, this._text1, x1Scaled, y1Scaled, true);
835
- // this._drawTextLabel(scope, this._text2, x2Scaled, y2Scaled, false);
836
- if (!this._hovered)
837
- return;
838
- this._drawEndCircle(scope, scaled.x1, scaled.y1);
839
- this._drawEndCircle(scope, scaled.x2, scaled.y2);
840
- });
841
- }
842
- }
843
-
844
- class TrendLinePaneView extends TwoPointDrawingPaneView {
845
- constructor(source) {
846
- super(source);
847
- }
848
- renderer() {
849
- return new TrendLinePaneRenderer(this._p1, this._p2, this._source._options, this._source.hovered);
850
- }
851
- }
852
-
853
- class TwoPointDrawing extends Drawing {
854
- _paneViews = [];
855
- _hovered = false;
856
- constructor(p1, p2, options) {
857
- super();
858
- this.points.push(p1);
859
- this.points.push(p2);
860
- this._options = {
861
- ...defaultOptions,
862
- ...options,
863
- };
864
- }
865
- setFirstPoint(point) {
866
- this.updatePoints(point);
867
- }
868
- setSecondPoint(point) {
869
- this.updatePoints(null, point);
870
- }
871
- get p1() { return this.points[0]; }
872
- get p2() { return this.points[1]; }
873
- get hovered() { return this._hovered; }
874
- }
875
-
876
- class TrendLine extends TwoPointDrawing {
877
- _type = "TrendLine";
878
- constructor(p1, p2, options) {
879
- super(p1, p2, options);
880
- this._paneViews = [new TrendLinePaneView(this)];
881
- }
882
- _moveToState(state) {
883
- switch (state) {
884
- case InteractionState.NONE:
885
- document.body.style.cursor = "default";
886
- this._hovered = false;
887
- this.requestUpdate();
888
- this._unsubscribe("mousedown", this._handleMouseDownInteraction);
889
- break;
890
- case InteractionState.HOVERING:
891
- document.body.style.cursor = "pointer";
892
- this._hovered = true;
893
- this.requestUpdate();
894
- this._subscribe("mousedown", this._handleMouseDownInteraction);
895
- this._unsubscribe("mouseup", this._handleMouseDownInteraction);
896
- this.chart.applyOptions({ handleScroll: true });
897
- break;
898
- case InteractionState.DRAGGINGP1:
899
- case InteractionState.DRAGGINGP2:
900
- case InteractionState.DRAGGING:
901
- document.body.style.cursor = "grabbing";
902
- this._subscribe("mouseup", this._handleMouseUpInteraction);
903
- this.chart.applyOptions({ handleScroll: false });
904
- break;
905
- }
906
- this._state = state;
907
- }
908
- _onDrag(diff) {
909
- if (this._state == InteractionState.DRAGGING || this._state == InteractionState.DRAGGINGP1) {
910
- this._addDiffToPoint(this.p1, diff.logical, diff.price);
911
- }
912
- if (this._state == InteractionState.DRAGGING || this._state == InteractionState.DRAGGINGP2) {
913
- this._addDiffToPoint(this.p2, diff.logical, diff.price);
914
- }
915
- }
916
- _onMouseDown() {
917
- this._startDragPoint = null;
918
- const hoverPoint = this._latestHoverPoint;
919
- if (!hoverPoint)
920
- return;
921
- const p1 = this._paneViews[0]._p1;
922
- const p2 = this._paneViews[0]._p2;
923
- if (!p1.x || !p2.x || !p1.y || !p2.y)
924
- return this._moveToState(InteractionState.DRAGGING);
925
- const tolerance = 10;
926
- if (Math.abs(hoverPoint.x - p1.x) < tolerance && Math.abs(hoverPoint.y - p1.y) < tolerance) {
927
- this._moveToState(InteractionState.DRAGGINGP1);
928
- }
929
- else if (Math.abs(hoverPoint.x - p2.x) < tolerance && Math.abs(hoverPoint.y - p2.y) < tolerance) {
930
- this._moveToState(InteractionState.DRAGGINGP2);
931
- }
932
- else {
933
- this._moveToState(InteractionState.DRAGGING);
934
- }
935
- }
936
- _mouseIsOverDrawing(param, tolerance = 4) {
937
- if (!param.point)
938
- return false;
939
- const x1 = this._paneViews[0]._p1.x;
940
- const y1 = this._paneViews[0]._p1.y;
941
- const x2 = this._paneViews[0]._p2.x;
942
- const y2 = this._paneViews[0]._p2.y;
943
- if (!x1 || !x2 || !y1 || !y2)
944
- return false;
945
- const mouseX = param.point.x;
946
- const mouseY = param.point.y;
947
- if (mouseX <= Math.min(x1, x2) - tolerance ||
948
- mouseX >= Math.max(x1, x2) + tolerance) {
949
- return false;
950
- }
951
- const distance = Math.abs((y2 - y1) * mouseX - (x2 - x1) * mouseY + x2 * y1 - y2 * x1) / Math.sqrt((y2 - y1) ** 2 + (x2 - x1) ** 2);
952
- return distance <= tolerance;
953
- }
954
- }
955
-
956
- class BoxPaneRenderer extends TwoPointDrawingPaneRenderer {
957
- constructor(p1, p2, options, showCircles) {
958
- super(p1, p2, options, showCircles);
959
- }
960
- draw(target) {
961
- target.useBitmapCoordinateSpace(scope => {
962
- const ctx = scope.context;
963
- const scaled = this._getScaledCoordinates(scope);
964
- if (!scaled)
965
- return;
966
- ctx.lineWidth = this._options.width;
967
- ctx.strokeStyle = this._options.lineColor;
968
- setLineStyle(ctx, this._options.lineStyle);
969
- ctx.fillStyle = this._options.fillColor;
970
- const mainX = Math.min(scaled.x1, scaled.x2);
971
- const mainY = Math.min(scaled.y1, scaled.y2);
972
- const width = Math.abs(scaled.x1 - scaled.x2);
973
- const height = Math.abs(scaled.y1 - scaled.y2);
974
- ctx.strokeRect(mainX, mainY, width, height);
975
- ctx.fillRect(mainX, mainY, width, height);
976
- if (!this._hovered)
977
- return;
978
- this._drawEndCircle(scope, mainX, mainY);
979
- this._drawEndCircle(scope, mainX + width, mainY);
980
- this._drawEndCircle(scope, mainX + width, mainY + height);
981
- this._drawEndCircle(scope, mainX, mainY + height);
982
- });
983
- }
984
- }
985
-
986
- class BoxPaneView extends TwoPointDrawingPaneView {
987
- constructor(source) {
988
- super(source);
989
- }
990
- renderer() {
991
- return new BoxPaneRenderer(this._p1, this._p2, this._source._options, this._source.hovered);
992
- }
993
- }
994
-
995
- const defaultBoxOptions = {
996
- fillEnabled: true,
997
- fillColor: 'rgba(255, 255, 255, 0.2)',
998
- ...defaultOptions
999
- };
1000
- class Box extends TwoPointDrawing {
1001
- _type = "Box";
1002
- constructor(p1, p2, options) {
1003
- super(p1, p2, options);
1004
- this._options = {
1005
- ...defaultBoxOptions,
1006
- ...options,
1007
- };
1008
- this._paneViews = [new BoxPaneView(this)];
1009
- }
1010
- // autoscaleInfo(startTimePoint: Logical, endTimePoint: Logical): AutoscaleInfo | null {
1011
- // const p1Index = this._pointIndex(this._p1);
1012
- // const p2Index = this._pointIndex(this._p2);
1013
- // if (p1Index === null || p2Index === null) return null;
1014
- // if (endTimePoint < p1Index || startTimePoint > p2Index) return null;
1015
- // return {
1016
- // priceRange: {
1017
- // minValue: this._minPrice,
1018
- // maxValue: this._maxPrice,
1019
- // },
1020
- // };
1021
- // }
1022
- _moveToState(state) {
1023
- switch (state) {
1024
- case InteractionState.NONE:
1025
- document.body.style.cursor = "default";
1026
- this._hovered = false;
1027
- this._unsubscribe("mousedown", this._handleMouseDownInteraction);
1028
- break;
1029
- case InteractionState.HOVERING:
1030
- document.body.style.cursor = "pointer";
1031
- this._hovered = true;
1032
- this._unsubscribe("mouseup", this._handleMouseUpInteraction);
1033
- this._subscribe("mousedown", this._handleMouseDownInteraction);
1034
- this.chart.applyOptions({ handleScroll: true });
1035
- break;
1036
- case InteractionState.DRAGGINGP1:
1037
- case InteractionState.DRAGGINGP2:
1038
- case InteractionState.DRAGGINGP3:
1039
- case InteractionState.DRAGGINGP4:
1040
- case InteractionState.DRAGGING:
1041
- document.body.style.cursor = "grabbing";
1042
- document.body.addEventListener("mouseup", this._handleMouseUpInteraction);
1043
- this._subscribe("mouseup", this._handleMouseUpInteraction);
1044
- this.chart.applyOptions({ handleScroll: false });
1045
- break;
1046
- }
1047
- this._state = state;
1048
- }
1049
- _onDrag(diff) {
1050
- if (this._state == InteractionState.DRAGGING || this._state == InteractionState.DRAGGINGP1) {
1051
- this._addDiffToPoint(this.p1, diff.logical, diff.price);
1052
- }
1053
- if (this._state == InteractionState.DRAGGING || this._state == InteractionState.DRAGGINGP2) {
1054
- this._addDiffToPoint(this.p2, diff.logical, diff.price);
1055
- }
1056
- if (this._state != InteractionState.DRAGGING) {
1057
- if (this._state == InteractionState.DRAGGINGP3) {
1058
- this._addDiffToPoint(this.p1, diff.logical, 0);
1059
- this._addDiffToPoint(this.p2, 0, diff.price);
1060
- }
1061
- if (this._state == InteractionState.DRAGGINGP4) {
1062
- this._addDiffToPoint(this.p1, 0, diff.price);
1063
- this._addDiffToPoint(this.p2, diff.logical, 0);
1064
- }
1065
- }
1066
- }
1067
- _onMouseDown() {
1068
- this._startDragPoint = null;
1069
- const hoverPoint = this._latestHoverPoint;
1070
- const p1 = this._paneViews[0]._p1;
1071
- const p2 = this._paneViews[0]._p2;
1072
- if (!p1.x || !p2.x || !p1.y || !p2.y)
1073
- return this._moveToState(InteractionState.DRAGGING);
1074
- const tolerance = 10;
1075
- if (Math.abs(hoverPoint.x - p1.x) < tolerance && Math.abs(hoverPoint.y - p1.y) < tolerance) {
1076
- this._moveToState(InteractionState.DRAGGINGP1);
1077
- }
1078
- else if (Math.abs(hoverPoint.x - p2.x) < tolerance && Math.abs(hoverPoint.y - p2.y) < tolerance) {
1079
- this._moveToState(InteractionState.DRAGGINGP2);
1080
- }
1081
- else if (Math.abs(hoverPoint.x - p1.x) < tolerance && Math.abs(hoverPoint.y - p2.y) < tolerance) {
1082
- this._moveToState(InteractionState.DRAGGINGP3);
1083
- }
1084
- else if (Math.abs(hoverPoint.x - p2.x) < tolerance && Math.abs(hoverPoint.y - p1.y) < tolerance) {
1085
- this._moveToState(InteractionState.DRAGGINGP4);
1086
- }
1087
- else {
1088
- this._moveToState(InteractionState.DRAGGING);
1089
- }
1090
- }
1091
- _mouseIsOverDrawing(param, tolerance = 4) {
1092
- if (!param.point)
1093
- return false;
1094
- const x1 = this._paneViews[0]._p1.x;
1095
- const y1 = this._paneViews[0]._p1.y;
1096
- const x2 = this._paneViews[0]._p2.x;
1097
- const y2 = this._paneViews[0]._p2.y;
1098
- if (!x1 || !x2 || !y1 || !y2)
1099
- return false;
1100
- const mouseX = param.point.x;
1101
- const mouseY = param.point.y;
1102
- const mainX = Math.min(x1, x2);
1103
- const mainY = Math.min(y1, y2);
1104
- const width = Math.abs(x1 - x2);
1105
- const height = Math.abs(y1 - y2);
1106
- const halfTolerance = tolerance / 2;
1107
- return mouseX > mainX - halfTolerance && mouseX < mainX + width + halfTolerance &&
1108
- mouseY > mainY - halfTolerance && mouseY < mainY + height + halfTolerance;
1109
- }
1110
- }
1111
-
1112
- class ColorPicker {
1113
- colorOption;
1114
- static colors = [
1115
- '#EBB0B0', '#E9CEA1', '#E5DF80', '#ADEB97', '#A3C3EA', '#D8BDED',
1116
- '#E15F5D', '#E1B45F', '#E2D947', '#4BE940', '#639AE1', '#D7A0E8',
1117
- '#E42C2A', '#E49D30', '#E7D827', '#3CFF0A', '#3275E4', '#B06CE3',
1118
- '#F3000D', '#EE9A14', '#F1DA13', '#2DFC0F', '#1562EE', '#BB00EF',
1119
- '#B50911', '#E3860E', '#D2BD11', '#48DE0E', '#1455B4', '#6E009F',
1120
- '#7C1713', '#B76B12', '#8D7A13', '#479C12', '#165579', '#51007E',
1121
- ];
1122
- _div;
1123
- saveDrawings;
1124
- opacity = 0;
1125
- _opacitySlider;
1126
- _opacityLabel;
1127
- rgba;
1128
- constructor(saveDrawings, colorOption) {
1129
- this.colorOption = colorOption;
1130
- this.saveDrawings = saveDrawings;
1131
- this._div = document.createElement('div');
1132
- this._div.classList.add('color-picker');
1133
- let colorPicker = document.createElement('div');
1134
- colorPicker.style.margin = '10px';
1135
- colorPicker.style.display = 'flex';
1136
- colorPicker.style.flexWrap = 'wrap';
1137
- ColorPicker.colors.forEach((color) => colorPicker.appendChild(this.makeColorBox(color)));
1138
- let separator = document.createElement('div');
1139
- separator.style.backgroundColor = window.pane.borderColor;
1140
- separator.style.height = '1px';
1141
- separator.style.width = '130px';
1142
- let opacity = document.createElement('div');
1143
- opacity.style.margin = '10px';
1144
- let opacityText = document.createElement('div');
1145
- opacityText.style.color = 'lightgray';
1146
- opacityText.style.fontSize = '12px';
1147
- opacityText.innerText = 'Opacity';
1148
- this._opacityLabel = document.createElement('div');
1149
- this._opacityLabel.style.color = 'lightgray';
1150
- this._opacityLabel.style.fontSize = '12px';
1151
- this._opacitySlider = document.createElement('input');
1152
- this._opacitySlider.type = 'range';
1153
- this._opacitySlider.value = (this.opacity * 100).toString();
1154
- this._opacityLabel.innerText = this._opacitySlider.value + '%';
1155
- this._opacitySlider.oninput = () => {
1156
- this._opacityLabel.innerText = this._opacitySlider.value + '%';
1157
- this.opacity = parseInt(this._opacitySlider.value) / 100;
1158
- this.updateColor();
1159
- };
1160
- opacity.appendChild(opacityText);
1161
- opacity.appendChild(this._opacitySlider);
1162
- opacity.appendChild(this._opacityLabel);
1163
- this._div.appendChild(colorPicker);
1164
- this._div.appendChild(separator);
1165
- this._div.appendChild(opacity);
1166
- window.containerDiv.appendChild(this._div);
1167
- }
1168
- _updateOpacitySlider() {
1169
- this._opacitySlider.value = (this.opacity * 100).toString();
1170
- this._opacityLabel.innerText = this._opacitySlider.value + '%';
1171
- }
1172
- makeColorBox(color) {
1173
- const box = document.createElement('div');
1174
- box.style.width = '18px';
1175
- box.style.height = '18px';
1176
- box.style.borderRadius = '3px';
1177
- box.style.margin = '3px';
1178
- box.style.boxSizing = 'border-box';
1179
- box.style.backgroundColor = color;
1180
- box.addEventListener('mouseover', () => box.style.border = '2px solid lightgray');
1181
- box.addEventListener('mouseout', () => box.style.border = 'none');
1182
- const rgba = ColorPicker.extractRGBA(color);
1183
- box.addEventListener('click', () => {
1184
- this.rgba = rgba;
1185
- this.updateColor();
1186
- });
1187
- return box;
1188
- }
1189
- static extractRGBA(anyColor) {
1190
- const dummyElem = document.createElement('div');
1191
- dummyElem.style.color = anyColor;
1192
- document.body.appendChild(dummyElem);
1193
- const computedColor = getComputedStyle(dummyElem).color;
1194
- document.body.removeChild(dummyElem);
1195
- const rgb = computedColor.match(/\d+/g)?.map(Number);
1196
- if (!rgb)
1197
- return [];
1198
- let isRgba = computedColor.includes('rgba');
1199
- let opacity = isRgba ? parseFloat(computedColor.split(',')[3]) : 1;
1200
- return [rgb[0], rgb[1], rgb[2], opacity];
1201
- }
1202
- updateColor() {
1203
- if (!Drawing.lastHoveredObject || !this.rgba)
1204
- return;
1205
- const oColor = `rgba(${this.rgba[0]}, ${this.rgba[1]}, ${this.rgba[2]}, ${this.opacity})`;
1206
- Drawing.lastHoveredObject.applyOptions({ [this.colorOption]: oColor });
1207
- this.saveDrawings();
1208
- }
1209
- openMenu(rect) {
1210
- if (!Drawing.lastHoveredObject)
1211
- return;
1212
- this.rgba = ColorPicker.extractRGBA(Drawing.lastHoveredObject._options[this.colorOption]);
1213
- this.opacity = this.rgba[3];
1214
- this._updateOpacitySlider();
1215
- this._div.style.top = (rect.top - 30) + 'px';
1216
- this._div.style.left = rect.right + 'px';
1217
- this._div.style.display = 'flex';
1218
- setTimeout(() => document.addEventListener('mousedown', (event) => {
1219
- if (!this._div.contains(event.target)) {
1220
- this.closeMenu();
1221
- }
1222
- }), 10);
1223
- }
1224
- closeMenu() {
1225
- document.body.removeEventListener('click', this.closeMenu);
1226
- this._div.style.display = 'none';
1227
- }
1228
- }
1229
-
1230
- class StylePicker {
1231
- static _styles = [
1232
- { name: 'Solid', var: lightweightCharts.LineStyle.Solid },
1233
- { name: 'Dotted', var: lightweightCharts.LineStyle.Dotted },
1234
- { name: 'Dashed', var: lightweightCharts.LineStyle.Dashed },
1235
- { name: 'Large Dashed', var: lightweightCharts.LineStyle.LargeDashed },
1236
- { name: 'Sparse Dotted', var: lightweightCharts.LineStyle.SparseDotted },
1237
- ];
1238
- _div;
1239
- _saveDrawings;
1240
- constructor(saveDrawings) {
1241
- this._saveDrawings = saveDrawings;
1242
- this._div = document.createElement('div');
1243
- this._div.classList.add('context-menu');
1244
- StylePicker._styles.forEach((style) => {
1245
- this._div.appendChild(this._makeTextBox(style.name, style.var));
1246
- });
1247
- window.containerDiv.appendChild(this._div);
1248
- }
1249
- _makeTextBox(text, style) {
1250
- const item = document.createElement('span');
1251
- item.classList.add('context-menu-item');
1252
- item.innerText = text;
1253
- item.addEventListener('click', () => {
1254
- Drawing.lastHoveredObject?.applyOptions({ lineStyle: style });
1255
- this._saveDrawings();
1256
- });
1257
- return item;
1258
- }
1259
- openMenu(rect) {
1260
- this._div.style.top = (rect.top - 30) + 'px';
1261
- this._div.style.left = rect.right + 'px';
1262
- this._div.style.display = 'block';
1263
- setTimeout(() => document.addEventListener('mousedown', (event) => {
1264
- if (!this._div.contains(event.target)) {
1265
- this.closeMenu();
1266
- }
1267
- }), 10);
1268
- }
1269
- closeMenu() {
1270
- document.removeEventListener('click', this.closeMenu);
1271
- this._div.style.display = 'none';
1272
- }
1273
- }
1274
-
1275
- function camelToTitle(inputString) {
1276
- const result = [];
1277
- for (const c of inputString) {
1278
- if (result.length == 0) {
1279
- result.push(c.toUpperCase());
1280
- }
1281
- else if (c == c.toUpperCase()) {
1282
- result.push(' ' + c);
1283
- }
1284
- else
1285
- result.push(c);
1286
- }
1287
- return result.join('');
1288
- }
1289
- class ContextMenu {
1290
- saveDrawings;
1291
- drawingTool;
1292
- div;
1293
- hoverItem;
1294
- items = [];
1295
- constructor(saveDrawings, drawingTool) {
1296
- this.saveDrawings = saveDrawings;
1297
- this.drawingTool = drawingTool;
1298
- this._onRightClick = this._onRightClick.bind(this);
1299
- this.div = document.createElement('div');
1300
- this.div.classList.add('context-menu');
1301
- document.body.appendChild(this.div);
1302
- this.hoverItem = null;
1303
- document.body.addEventListener('contextmenu', this._onRightClick);
1304
- }
1305
- _handleClick = (ev) => this._onClick(ev);
1306
- _onClick(ev) {
1307
- if (!ev.target)
1308
- return;
1309
- if (!this.div.contains(ev.target)) {
1310
- this.div.style.display = 'none';
1311
- document.body.removeEventListener('click', this._handleClick);
1312
- }
1313
- }
1314
- _onRightClick(ev) {
1315
- if (!Drawing.hoveredObject)
1316
- return;
1317
- for (const item of this.items) {
1318
- this.div.removeChild(item);
1319
- }
1320
- this.items = [];
1321
- for (const optionName of Object.keys(Drawing.hoveredObject._options)) {
1322
- let subMenu;
1323
- if (optionName.toLowerCase().includes('color')) {
1324
- subMenu = new ColorPicker(this.saveDrawings, optionName);
1325
- }
1326
- else if (optionName === 'lineStyle') {
1327
- subMenu = new StylePicker(this.saveDrawings);
1328
- }
1329
- else
1330
- continue;
1331
- let onClick = (rect) => subMenu.openMenu(rect);
1332
- this.menuItem(camelToTitle(optionName), onClick, () => {
1333
- document.removeEventListener('click', subMenu.closeMenu);
1334
- subMenu._div.style.display = 'none';
1335
- });
1336
- }
1337
- let onClickDelete = () => this.drawingTool.delete(Drawing.lastHoveredObject);
1338
- this.separator();
1339
- this.menuItem('Delete Drawing', onClickDelete);
1340
- // const colorPicker = new ColorPicker(this.saveDrawings)
1341
- // const stylePicker = new StylePicker(this.saveDrawings)
1342
- // let onClickDelete = () => this._drawingTool.delete(Drawing.lastHoveredObject);
1343
- // let onClickColor = (rect: DOMRect) => colorPicker.openMenu(rect)
1344
- // let onClickStyle = (rect: DOMRect) => stylePicker.openMenu(rect)
1345
- // contextMenu.menuItem('Color Picker', onClickColor, () => {
1346
- // document.removeEventListener('click', colorPicker.closeMenu)
1347
- // colorPicker._div.style.display = 'none'
1348
- // })
1349
- // contextMenu.menuItem('Style', onClickStyle, () => {
1350
- // document.removeEventListener('click', stylePicker.closeMenu)
1351
- // stylePicker._div.style.display = 'none'
1352
- // })
1353
- // contextMenu.separator()
1354
- // contextMenu.menuItem('Delete Drawing', onClickDelete)
1355
- ev.preventDefault();
1356
- this.div.style.left = ev.clientX + 'px';
1357
- this.div.style.top = ev.clientY + 'px';
1358
- this.div.style.display = 'block';
1359
- document.body.addEventListener('click', this._handleClick);
1360
- }
1361
- menuItem(text, action, hover = null) {
1362
- const item = document.createElement('span');
1363
- item.classList.add('context-menu-item');
1364
- this.div.appendChild(item);
1365
- const elem = document.createElement('span');
1366
- elem.innerText = text;
1367
- elem.style.pointerEvents = 'none';
1368
- item.appendChild(elem);
1369
- if (hover) {
1370
- let arrow = document.createElement('span');
1371
- arrow.innerText = `►`;
1372
- arrow.style.fontSize = '8px';
1373
- arrow.style.pointerEvents = 'none';
1374
- item.appendChild(arrow);
1375
- }
1376
- item.addEventListener('mouseover', () => {
1377
- if (this.hoverItem && this.hoverItem.closeAction)
1378
- this.hoverItem.closeAction();
1379
- this.hoverItem = { elem: elem, action: action, closeAction: hover };
1380
- });
1381
- if (!hover)
1382
- item.addEventListener('click', (event) => { action(event); this.div.style.display = 'none'; });
1383
- else {
1384
- let timeout;
1385
- item.addEventListener('mouseover', () => timeout = setTimeout(() => action(item.getBoundingClientRect()), 100));
1386
- item.addEventListener('mouseout', () => clearTimeout(timeout));
1387
- }
1388
- this.items.push(item);
1389
- }
1390
- separator() {
1391
- const separator = document.createElement('div');
1392
- separator.style.width = '90%';
1393
- separator.style.height = '1px';
1394
- separator.style.margin = '3px 0px';
1395
- separator.style.backgroundColor = window.pane.borderColor;
1396
- this.div.appendChild(separator);
1397
- this.items.push(separator);
1398
- }
1399
- }
1400
-
1401
- class RayLine extends HorizontalLine {
1402
- _type = 'RayLine';
1403
- constructor(point, options) {
1404
- super({ ...point }, options);
1405
- this._point.time = point.time;
1406
- }
1407
- updatePoints(...points) {
1408
- for (const p of points)
1409
- if (p)
1410
- this._point = p;
1411
- this.requestUpdate();
1412
- }
1413
- _onDrag(diff) {
1414
- this._addDiffToPoint(this._point, diff.logical, diff.price);
1415
- this.requestUpdate();
1416
- }
1417
- _mouseIsOverDrawing(param, tolerance = 4) {
1418
- if (!param.point)
1419
- return false;
1420
- const y = this.series.priceToCoordinate(this._point.price);
1421
- const x = this._point.time ? this.chart.timeScale().timeToCoordinate(this._point.time) : null;
1422
- if (!y || !x)
1423
- return false;
1424
- return (Math.abs(y - param.point.y) < tolerance && param.point.x > x - tolerance);
1425
- }
1426
- }
1427
-
1428
- class VerticalLinePaneRenderer extends DrawingPaneRenderer {
1429
- _point = { x: null, y: null };
1430
- constructor(point, options) {
1431
- super(options);
1432
- this._point = point;
1433
- }
1434
- draw(target) {
1435
- target.useBitmapCoordinateSpace(scope => {
1436
- if (this._point.x == null)
1437
- return;
1438
- const ctx = scope.context;
1439
- const scaledX = this._point.x * scope.horizontalPixelRatio;
1440
- ctx.lineWidth = this._options.width;
1441
- ctx.strokeStyle = this._options.lineColor;
1442
- setLineStyle(ctx, this._options.lineStyle);
1443
- ctx.beginPath();
1444
- ctx.moveTo(scaledX, 0);
1445
- ctx.lineTo(scaledX, scope.bitmapSize.height);
1446
- ctx.stroke();
1447
- });
1448
- }
1449
- }
1450
-
1451
- class VerticalLinePaneView extends DrawingPaneView {
1452
- _source;
1453
- _point = { x: null, y: null };
1454
- constructor(source) {
1455
- super(source);
1456
- this._source = source;
1457
- }
1458
- update() {
1459
- const point = this._source._point;
1460
- const timeScale = this._source.chart.timeScale();
1461
- const series = this._source.series;
1462
- this._point.x = point.time ? timeScale.timeToCoordinate(point.time) : timeScale.logicalToCoordinate(point.logical);
1463
- this._point.y = series.priceToCoordinate(point.price);
1464
- }
1465
- renderer() {
1466
- return new VerticalLinePaneRenderer(this._point, this._source._options);
1467
- }
1468
- }
1469
-
1470
- class VerticalLineTimeAxisView {
1471
- _source;
1472
- _x = null;
1473
- constructor(source) {
1474
- this._source = source;
1475
- }
1476
- update() {
1477
- if (!this._source.chart || !this._source._point)
1478
- return;
1479
- const point = this._source._point;
1480
- const timeScale = this._source.chart.timeScale();
1481
- this._x = point.time ? timeScale.timeToCoordinate(point.time) : timeScale.logicalToCoordinate(point.logical);
1482
- }
1483
- visible() {
1484
- return true;
1485
- }
1486
- tickVisible() {
1487
- return true;
1488
- }
1489
- coordinate() {
1490
- return this._x ?? 0;
1491
- }
1492
- text() {
1493
- return '';
1494
- }
1495
- textColor() {
1496
- return "white";
1497
- }
1498
- backColor() {
1499
- return this._source._options.lineColor;
1500
- }
1501
- }
1502
-
1503
- class VerticalLine extends Drawing {
1504
- _type = 'VerticalLine';
1505
- _paneViews;
1506
- _timeAxisViews;
1507
- _point;
1508
- _callbackName;
1509
- _startDragPoint = null;
1510
- constructor(point, options, callbackName = null) {
1511
- super(options);
1512
- this._point = point;
1513
- this._paneViews = [new VerticalLinePaneView(this)];
1514
- this._callbackName = callbackName;
1515
- this._timeAxisViews = [new VerticalLineTimeAxisView(this)];
1516
- }
1517
- updateAllViews() {
1518
- this._paneViews.forEach(pw => pw.update());
1519
- this._timeAxisViews.forEach(tw => tw.update());
1520
- }
1521
- timeAxisViews() {
1522
- return this._timeAxisViews;
1523
- }
1524
- updatePoints(...points) {
1525
- for (const p of points) {
1526
- if (!p)
1527
- continue;
1528
- if (!p.time && p.logical) {
1529
- p.time = this.series.dataByIndex(p.logical)?.time || null;
1530
- }
1531
- this._point = p;
1532
- }
1533
- this.requestUpdate();
1534
- }
1535
- get points() {
1536
- return [this._point];
1537
- }
1538
- _moveToState(state) {
1539
- switch (state) {
1540
- case InteractionState.NONE:
1541
- document.body.style.cursor = "default";
1542
- this._unsubscribe("mousedown", this._handleMouseDownInteraction);
1543
- break;
1544
- case InteractionState.HOVERING:
1545
- document.body.style.cursor = "pointer";
1546
- this._unsubscribe("mouseup", this._childHandleMouseUpInteraction);
1547
- this._subscribe("mousedown", this._handleMouseDownInteraction);
1548
- this.chart.applyOptions({ handleScroll: true });
1549
- break;
1550
- case InteractionState.DRAGGING:
1551
- document.body.style.cursor = "grabbing";
1552
- this._subscribe("mouseup", this._childHandleMouseUpInteraction);
1553
- this.chart.applyOptions({ handleScroll: false });
1554
- break;
1555
- }
1556
- this._state = state;
1557
- }
1558
- _onDrag(diff) {
1559
- this._addDiffToPoint(this._point, diff.logical, 0);
1560
- this.requestUpdate();
1561
- }
1562
- _mouseIsOverDrawing(param, tolerance = 4) {
1563
- if (!param.point)
1564
- return false;
1565
- const timeScale = this.chart.timeScale();
1566
- let x;
1567
- if (this._point.time) {
1568
- x = timeScale.timeToCoordinate(this._point.time);
1569
- }
1570
- else {
1571
- x = timeScale.logicalToCoordinate(this._point.logical);
1572
- }
1573
- if (!x)
1574
- return false;
1575
- return (Math.abs(x - param.point.x) < tolerance);
1576
- }
1577
- _onMouseDown() {
1578
- this._startDragPoint = null;
1579
- const hoverPoint = this._latestHoverPoint;
1580
- if (!hoverPoint)
1581
- return;
1582
- return this._moveToState(InteractionState.DRAGGING);
1583
- }
1584
- _childHandleMouseUpInteraction = () => {
1585
- this._handleMouseUpInteraction();
1586
- if (!this._callbackName)
1587
- return;
1588
- window.callbackFunction(`${this._callbackName}_~_${this._point.price.toFixed(8)}`);
1589
- };
1590
- }
1591
-
1592
- class ToolBox {
1593
- static TREND_SVG = '<rect x="3.84" y="13.67" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -5.9847 14.4482)" width="21.21" height="1.56"/><path d="M23,3.17L20.17,6L23,8.83L25.83,6L23,3.17z M23,7.41L21.59,6L23,4.59L24.41,6L23,7.41z"/><path d="M6,20.17L3.17,23L6,25.83L8.83,23L6,20.17z M6,24.41L4.59,23L6,21.59L7.41,23L6,24.41z"/>';
1594
- static HORZ_SVG = '<rect x="4" y="14" width="9" height="1"/><rect x="16" y="14" width="9" height="1"/><path d="M11.67,14.5l2.83,2.83l2.83-2.83l-2.83-2.83L11.67,14.5z M15.91,14.5l-1.41,1.41l-1.41-1.41l1.41-1.41L15.91,14.5z"/>';
1595
- static RAY_SVG = '<rect x="8" y="14" width="17" height="1"/><path d="M3.67,14.5l2.83,2.83l2.83-2.83L6.5,11.67L3.67,14.5z M7.91,14.5L6.5,15.91L5.09,14.5l1.41-1.41L7.91,14.5z"/>';
1596
- static BOX_SVG = '<rect x="8" y="6" width="12" height="1"/><rect x="9" y="22" width="11" height="1"/><path d="M3.67,6.5L6.5,9.33L9.33,6.5L6.5,3.67L3.67,6.5z M7.91,6.5L6.5,7.91L5.09,6.5L6.5,5.09L7.91,6.5z"/><path d="M19.67,6.5l2.83,2.83l2.83-2.83L22.5,3.67L19.67,6.5z M23.91,6.5L22.5,7.91L21.09,6.5l1.41-1.41L23.91,6.5z"/><path d="M19.67,22.5l2.83,2.83l2.83-2.83l-2.83-2.83L19.67,22.5z M23.91,22.5l-1.41,1.41l-1.41-1.41l1.41-1.41L23.91,22.5z"/><path d="M3.67,22.5l2.83,2.83l2.83-2.83L6.5,19.67L3.67,22.5z M7.91,22.5L6.5,23.91L5.09,22.5l1.41-1.41L7.91,22.5z"/><rect x="22" y="9" width="1" height="11"/><rect x="6" y="9" width="1" height="11"/>';
1597
- static VERT_SVG = ToolBox.RAY_SVG;
1598
- div;
1599
- activeIcon = null;
1600
- buttons = [];
1601
- _commandFunctions;
1602
- _handlerID;
1603
- _drawingTool;
1604
- constructor(handlerID, chart, series, commandFunctions) {
1605
- this._handlerID = handlerID;
1606
- this._commandFunctions = commandFunctions;
1607
- this._drawingTool = new DrawingTool(chart, series, () => this.removeActiveAndSave());
1608
- this.div = this._makeToolBox();
1609
- new ContextMenu(this.saveDrawings, this._drawingTool);
1610
- commandFunctions.push((event) => {
1611
- if ((event.metaKey || event.ctrlKey) && event.code === 'KeyZ') {
1612
- const drawingToDelete = this._drawingTool.drawings.pop();
1613
- if (drawingToDelete)
1614
- this._drawingTool.delete(drawingToDelete);
1615
- return true;
1616
- }
1617
- return false;
1618
- });
1619
- }
1620
- toJSON() {
1621
- // Exclude the chart attribute from serialization
1622
- const { ...serialized } = this;
1623
- return serialized;
1624
- }
1625
- _makeToolBox() {
1626
- let div = document.createElement('div');
1627
- div.classList.add('toolbox');
1628
- this.buttons.push(this._makeToolBoxElement(TrendLine, 'KeyT', ToolBox.TREND_SVG));
1629
- this.buttons.push(this._makeToolBoxElement(HorizontalLine, 'KeyH', ToolBox.HORZ_SVG));
1630
- this.buttons.push(this._makeToolBoxElement(RayLine, 'KeyR', ToolBox.RAY_SVG));
1631
- this.buttons.push(this._makeToolBoxElement(Box, 'KeyB', ToolBox.BOX_SVG));
1632
- this.buttons.push(this._makeToolBoxElement(VerticalLine, 'KeyV', ToolBox.VERT_SVG, true));
1633
- for (const button of this.buttons) {
1634
- div.appendChild(button);
1635
- }
1636
- return div;
1637
- }
1638
- _makeToolBoxElement(DrawingType, keyCmd, paths, rotate = false) {
1639
- const elem = document.createElement('div');
1640
- elem.classList.add("toolbox-button");
1641
- const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1642
- svg.setAttribute("width", "29");
1643
- svg.setAttribute("height", "29");
1644
- const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
1645
- group.innerHTML = paths;
1646
- group.setAttribute("fill", window.pane.color);
1647
- svg.appendChild(group);
1648
- elem.appendChild(svg);
1649
- const icon = { div: elem, group: group, type: DrawingType };
1650
- elem.addEventListener('click', () => this._onIconClick(icon));
1651
- this._commandFunctions.push((event) => {
1652
- if (this._handlerID !== window.handlerInFocus)
1653
- return false;
1654
- if (event.altKey && event.code === keyCmd) {
1655
- event.preventDefault();
1656
- this._onIconClick(icon);
1657
- return true;
1658
- }
1659
- return false;
1660
- });
1661
- if (rotate == true) {
1662
- svg.style.transform = 'rotate(90deg)';
1663
- svg.style.transformBox = 'fill-box';
1664
- svg.style.transformOrigin = 'center';
1665
- }
1666
- return elem;
1667
- }
1668
- _onIconClick(icon) {
1669
- if (this.activeIcon) {
1670
- this.activeIcon.div.classList.remove('active-toolbox-button');
1671
- window.setCursor('crosshair');
1672
- this._drawingTool?.stopDrawing();
1673
- if (this.activeIcon === icon) {
1674
- this.activeIcon = null;
1675
- return;
1676
- }
1677
- }
1678
- this.activeIcon = icon;
1679
- this.activeIcon.div.classList.add('active-toolbox-button');
1680
- window.setCursor('crosshair');
1681
- this._drawingTool?.beginDrawing(this.activeIcon.type);
1682
- }
1683
- removeActiveAndSave = () => {
1684
- window.setCursor('default');
1685
- if (this.activeIcon)
1686
- this.activeIcon.div.classList.remove('active-toolbox-button');
1687
- this.activeIcon = null;
1688
- this.saveDrawings();
1689
- };
1690
- addNewDrawing(d) {
1691
- this._drawingTool.addNewDrawing(d);
1692
- }
1693
- clearDrawings() {
1694
- this._drawingTool.clearDrawings();
1695
- }
1696
- saveDrawings = () => {
1697
- const drawingMeta = [];
1698
- for (const d of this._drawingTool.drawings) {
1699
- drawingMeta.push({
1700
- type: d._type,
1701
- points: d.points,
1702
- options: d._options
1703
- });
1704
- }
1705
- const string = JSON.stringify(drawingMeta);
1706
- window.callbackFunction(`save_drawings${this._handlerID}_~_${string}`);
1707
- };
1708
- loadDrawings(drawings) {
1709
- drawings.forEach((d) => {
1710
- switch (d.type) {
1711
- case "Box":
1712
- this._drawingTool.addNewDrawing(new Box(d.points[0], d.points[1], d.options));
1713
- break;
1714
- case "TrendLine":
1715
- this._drawingTool.addNewDrawing(new TrendLine(d.points[0], d.points[1], d.options));
1716
- break;
1717
- case "HorizontalLine":
1718
- this._drawingTool.addNewDrawing(new HorizontalLine(d.points[0], d.options));
1719
- break;
1720
- case "RayLine":
1721
- this._drawingTool.addNewDrawing(new RayLine(d.points[0], d.options));
1722
- break;
1723
- case "VerticalLine":
1724
- this._drawingTool.addNewDrawing(new VerticalLine(d.points[0], d.options));
1725
- break;
1726
- }
1727
- });
1728
- }
1729
- }
1730
-
1731
- class Menu {
1732
- makeButton;
1733
- callbackName;
1734
- div;
1735
- isOpen = false;
1736
- widget;
1737
- constructor(makeButton, callbackName, items, activeItem, separator, align) {
1738
- this.makeButton = makeButton;
1739
- this.callbackName = callbackName;
1740
- this.div = document.createElement('div');
1741
- this.div.classList.add('topbar-menu');
1742
- this.widget = this.makeButton(activeItem + ' ↓', null, separator, true, align);
1743
- this.updateMenuItems(items);
1744
- this.widget.elem.addEventListener('click', () => {
1745
- this.isOpen = !this.isOpen;
1746
- if (!this.isOpen) {
1747
- this.div.style.display = 'none';
1748
- return;
1749
- }
1750
- let rect = this.widget.elem.getBoundingClientRect();
1751
- this.div.style.display = 'flex';
1752
- this.div.style.flexDirection = 'column';
1753
- let center = rect.x + (rect.width / 2);
1754
- this.div.style.left = center - (this.div.clientWidth / 2) + 'px';
1755
- this.div.style.top = rect.y + rect.height + 'px';
1756
- });
1757
- document.body.appendChild(this.div);
1758
- }
1759
- updateMenuItems(items) {
1760
- this.div.innerHTML = '';
1761
- items.forEach(text => {
1762
- let button = this.makeButton(text, null, false, false);
1763
- button.elem.addEventListener('click', () => {
1764
- this._clickHandler(button.elem.innerText);
1765
- });
1766
- button.elem.style.margin = '4px 4px';
1767
- button.elem.style.padding = '2px 2px';
1768
- this.div.appendChild(button.elem);
1769
- });
1770
- this.widget.elem.innerText = items[0] + ' ↓';
1771
- }
1772
- _clickHandler(name) {
1773
- this.widget.elem.innerText = name + ' ↓';
1774
- window.callbackFunction(`${this.callbackName}_~_${name}`);
1775
- this.div.style.display = 'none';
1776
- this.isOpen = false;
1777
- }
1778
- }
1779
-
1780
- class TopBar {
1781
- _handler;
1782
- _div;
1783
- left;
1784
- right;
1785
- constructor(handler) {
1786
- this._handler = handler;
1787
- this._div = document.createElement('div');
1788
- this._div.classList.add('topbar');
1789
- const createTopBarContainer = (justification) => {
1790
- const div = document.createElement('div');
1791
- div.classList.add('topbar-container');
1792
- div.style.justifyContent = justification;
1793
- this._div.appendChild(div);
1794
- return div;
1795
- };
1796
- this.left = createTopBarContainer('flex-start');
1797
- this.right = createTopBarContainer('flex-end');
1798
- }
1799
- makeSwitcher(items, defaultItem, callbackName, align = 'left') {
1800
- const switcherElement = document.createElement('div');
1801
- switcherElement.style.margin = '4px 12px';
1802
- let activeItemEl;
1803
- const createAndReturnSwitcherButton = (itemName) => {
1804
- const button = document.createElement('button');
1805
- button.classList.add('topbar-button');
1806
- button.classList.add('switcher-button');
1807
- button.style.margin = '0px 2px';
1808
- button.innerText = itemName;
1809
- if (itemName == defaultItem) {
1810
- activeItemEl = button;
1811
- button.classList.add('active-switcher-button');
1812
- }
1813
- const buttonWidth = TopBar.getClientWidth(button);
1814
- button.style.minWidth = buttonWidth + 1 + 'px';
1815
- button.addEventListener('click', () => widget.onItemClicked(button));
1816
- switcherElement.appendChild(button);
1817
- return button;
1818
- };
1819
- const widget = {
1820
- elem: switcherElement,
1821
- callbackName: callbackName,
1822
- intervalElements: items.map(createAndReturnSwitcherButton),
1823
- onItemClicked: (item) => {
1824
- if (item == activeItemEl)
1825
- return;
1826
- activeItemEl.classList.remove('active-switcher-button');
1827
- item.classList.add('active-switcher-button');
1828
- activeItemEl = item;
1829
- window.callbackFunction(`${widget.callbackName}_~_${item.innerText}`);
1830
- }
1831
- };
1832
- this.appendWidget(switcherElement, align, true);
1833
- return widget;
1834
- }
1835
- makeTextBoxWidget(text, align = 'left', callbackName = null) {
1836
- if (callbackName) {
1837
- const textBox = document.createElement('input');
1838
- textBox.classList.add('topbar-textbox-input');
1839
- textBox.value = text;
1840
- textBox.style.width = `${(textBox.value.length + 2)}ch`;
1841
- textBox.addEventListener('focus', () => {
1842
- window.textBoxFocused = true;
1843
- });
1844
- textBox.addEventListener('input', (e) => {
1845
- e.preventDefault();
1846
- textBox.style.width = `${(textBox.value.length + 2)}ch`;
1847
- });
1848
- textBox.addEventListener('keydown', (e) => {
1849
- if (e.key == 'Enter') {
1850
- e.preventDefault();
1851
- textBox.blur();
1852
- }
1853
- });
1854
- textBox.addEventListener('blur', () => {
1855
- window.callbackFunction(`${callbackName}_~_${textBox.value}`);
1856
- window.textBoxFocused = false;
1857
- });
1858
- this.appendWidget(textBox, align, true);
1859
- return textBox;
1860
- }
1861
- else {
1862
- const textBox = document.createElement('div');
1863
- textBox.classList.add('topbar-textbox');
1864
- textBox.innerText = text;
1865
- this.appendWidget(textBox, align, true);
1866
- return textBox;
1867
- }
1868
- }
1869
- makeMenu(items, activeItem, separator, callbackName, align) {
1870
- return new Menu(this.makeButton.bind(this), callbackName, items, activeItem, separator, align);
1871
- }
1872
- makeButton(defaultText, callbackName, separator, append = true, align = 'left', toggle = false) {
1873
- let button = document.createElement('button');
1874
- button.classList.add('topbar-button');
1875
- // button.style.color = window.pane.color
1876
- button.innerText = defaultText;
1877
- document.body.appendChild(button);
1878
- button.style.minWidth = button.clientWidth + 1 + 'px';
1879
- document.body.removeChild(button);
1880
- let widget = {
1881
- elem: button,
1882
- callbackName: callbackName
1883
- };
1884
- if (callbackName) {
1885
- let handler;
1886
- if (toggle) {
1887
- let state = false;
1888
- handler = () => {
1889
- state = !state;
1890
- window.callbackFunction(`${widget.callbackName}_~_${state}`);
1891
- button.style.backgroundColor = state ? 'var(--active-bg-color)' : '';
1892
- button.style.color = state ? 'var(--active-color)' : '';
1893
- };
1894
- }
1895
- else {
1896
- handler = () => window.callbackFunction(`${widget.callbackName}_~_${button.innerText}`);
1897
- }
1898
- button.addEventListener('click', handler);
1899
- }
1900
- if (append)
1901
- this.appendWidget(button, align, separator);
1902
- return widget;
1903
- }
1904
- makeSeparator(align = 'left') {
1905
- const separator = document.createElement('div');
1906
- separator.classList.add('topbar-seperator');
1907
- const div = align == 'left' ? this.left : this.right;
1908
- div.appendChild(separator);
1909
- }
1910
- appendWidget(widget, align, separator) {
1911
- const div = align == 'left' ? this.left : this.right;
1912
- if (separator) {
1913
- if (align == 'left')
1914
- div.appendChild(widget);
1915
- this.makeSeparator(align);
1916
- if (align == 'right')
1917
- div.appendChild(widget);
1918
- }
1919
- else
1920
- div.appendChild(widget);
1921
- this._handler.reSize();
1922
- }
1923
- static getClientWidth(element) {
1924
- document.body.appendChild(element);
1925
- const width = element.clientWidth;
1926
- document.body.removeChild(element);
1927
- return width;
1928
- }
1929
- }
1930
-
1931
- globalParamInit();
1932
- class Handler {
1933
- id;
1934
- commandFunctions = [];
1935
- wrapper;
1936
- div;
1937
- chart;
1938
- scale;
1939
- precision = 2;
1940
- series;
1941
- volumeSeries;
1942
- legend;
1943
- _topBar;
1944
- toolBox;
1945
- spinner;
1946
- _seriesList = [];
1947
- resize_hdr_height = 8;
1948
- watermark;
1949
- seriesMarkers;
1950
- // TODO find a better solution rather than the 'position' parameter
1951
- constructor(chartId, innerWidth, innerHeight, position, autoSize, paneIndex = 0) {
1952
- this.reSize = this.reSize.bind(this);
1953
- this.id = chartId;
1954
- this.scale = {
1955
- width: innerWidth,
1956
- height: innerHeight,
1957
- };
1958
- this.wrapper = document.createElement('div');
1959
- this.wrapper.classList.add("handler");
1960
- this.wrapper.style.float = position;
1961
- this.div = document.createElement('div');
1962
- this.div.style.position = 'relative';
1963
- this.wrapper.appendChild(this.div);
1964
- window.containerDiv.append(this.wrapper);
1965
- // --- add this block to enable mouse‐drag height resizing ---
1966
- const handle = document.createElement('div');
1967
- handle.classList.add('resize-handle');
1968
- this.wrapper.appendChild(handle);
1969
- let startY, startHeight;
1970
- const onMouseMove = (e) => {
1971
- const delta = e.clientY - startY;
1972
- const newH = Math.max(50, startHeight + delta); // min height 50px
1973
- this.wrapper.style.height = `${newH}px`;
1974
- // Resize the chart canvas accordingly:
1975
- this.chart.resize(this.wrapper.offsetWidth, newH - this.resize_hdr_height);
1976
- };
1977
- const onMouseUp = () => {
1978
- document.removeEventListener('mousemove', onMouseMove);
1979
- document.removeEventListener('mouseup', onMouseUp);
1980
- };
1981
- handle.addEventListener('mousedown', (e) => {
1982
- // prevent selecting text, etc.
1983
- e.preventDefault();
1984
- startY = e.clientY;
1985
- startHeight = this.wrapper.getBoundingClientRect().height;
1986
- document.addEventListener('mousemove', onMouseMove);
1987
- document.addEventListener('mouseup', onMouseUp);
1988
- });
1989
- this.chart = this._createChart();
1990
- this.series = this.createCandlestickSeries(paneIndex);
1991
- this.volumeSeries = this.createVolumeSeries(paneIndex);
1992
- this.seriesMarkers = lightweightCharts.createSeriesMarkers(this.series, []);
1993
- this.legend = new Legend(this);
1994
- document.addEventListener('keydown', (event) => {
1995
- for (let i = 0; i < this.commandFunctions.length; i++) {
1996
- if (this.commandFunctions[i](event))
1997
- break;
1998
- }
1999
- });
2000
- window.handlerInFocus = this.id;
2001
- this.wrapper.addEventListener('mouseover', () => window.handlerInFocus = this.id);
2002
- this.reSize();
2003
- if (!autoSize)
2004
- return;
2005
- window.addEventListener('resize', () => this.reSize());
2006
- }
2007
- reSize() {
2008
- let topBarOffset = this.scale.height !== 0 ? this._topBar?._div.offsetHeight || 0 : 0;
2009
- if (this.scale.height >= 0) {
2010
- this.chart.resize(window.innerWidth * this.scale.width, (window.innerHeight * this.scale.height) - topBarOffset - this.resize_hdr_height);
2011
- this.wrapper.style.width = `${100 * this.scale.width}%`;
2012
- this.wrapper.style.height = `${100 * this.scale.height}%`;
2013
- }
2014
- else {
2015
- var chart_height = Math.ceil(Math.abs(this.scale.height));
2016
- this.chart.resize(window.containerDiv.offsetWidth * this.scale.width, chart_height - topBarOffset - this.resize_hdr_height);
2017
- this.wrapper.style.width = `${100 * this.scale.width}%`;
2018
- this.wrapper.style.height = `${chart_height}px`;
2019
- }
2020
- // TODO definitely a better way to do this
2021
- if (this.scale.height === 0 || this.scale.width === 0) {
2022
- // if (this.legend.div.style.display == 'flex') this.legend.div.style.display = 'none'
2023
- if (this.toolBox) {
2024
- this.toolBox.div.style.display = 'none';
2025
- }
2026
- }
2027
- else {
2028
- // this.legend.div.style.display = 'flex'
2029
- if (this.toolBox) {
2030
- this.toolBox.div.style.display = 'flex';
2031
- }
2032
- }
2033
- }
2034
- _createChart() {
2035
- return lightweightCharts.createChart(this.div, {
2036
- width: window.containerDiv.offsetWidth * this.scale.width,
2037
- height: this.scale.height < 0 ? Math.ceil(Math.abs(this.scale.height)) : window.innerHeight * this.scale.height,
2038
- layout: {
2039
- textColor: window.pane.color,
2040
- background: {
2041
- color: 'rgb(18,24,38)',
2042
- type: lightweightCharts.ColorType.Solid,
2043
- },
2044
- fontSize: 12,
2045
- panes: {
2046
- separatorColor: 'lightgrey',
2047
- separatorHoverColor: "rgba(255, 0, 0, 0.4)",
2048
- enableResize: true,
2049
- },
2050
- },
2051
- rightPriceScale: {
2052
- scaleMargins: { top: 0.3, bottom: 0.25 },
2053
- },
2054
- timeScale: { timeVisible: true, secondsVisible: false },
2055
- crosshair: {
2056
- mode: lightweightCharts.CrosshairMode.Normal,
2057
- vertLine: {
2058
- labelBackgroundColor: 'rgb(46, 46, 46)',
2059
- },
2060
- horzLine: {
2061
- labelBackgroundColor: 'rgb(55, 55, 55)',
2062
- },
2063
- },
2064
- grid: {
2065
- vertLines: { color: '#444', style: 1 },
2066
- horzLines: { color: '#444', style: 1 },
2067
- },
2068
- handleScroll: { vertTouchDrag: true },
2069
- });
2070
- }
2071
- createCandlestickSeries(paneIndex) {
2072
- const up = 'rgba(39, 157, 130, 100)';
2073
- const down = 'rgba(200, 97, 100, 100)';
2074
- const candleSeries = this.chart.addSeries(lightweightCharts.CandlestickSeries, {
2075
- upColor: up, borderUpColor: up, wickUpColor: up,
2076
- downColor: down, borderDownColor: down, wickDownColor: down
2077
- }, paneIndex);
2078
- candleSeries.priceScale().applyOptions({
2079
- scaleMargins: { top: 0.2, bottom: 0.2 },
2080
- });
2081
- return candleSeries;
2082
- }
2083
- createVolumeSeries(paneIndex) {
2084
- const volumeSeries = this.chart.addSeries(lightweightCharts.HistogramSeries, {
2085
- color: '#26a69a',
2086
- priceFormat: { type: 'volume' },
2087
- priceScaleId: 'volume_scale',
2088
- }, paneIndex);
2089
- volumeSeries.priceScale().applyOptions({
2090
- scaleMargins: { top: 0.8, bottom: 0 },
2091
- });
2092
- return volumeSeries;
2093
- }
2094
- createLineSeries(name, options, paneIndex = 0) {
2095
- const line = this.chart.addSeries(lightweightCharts.LineSeries, { ...options }, paneIndex);
2096
- this._seriesList.push(line);
2097
- this.legend.makeSeriesRow(name, line, paneIndex);
2098
- return {
2099
- name: name,
2100
- series: line,
2101
- };
2102
- }
2103
- createHistogramSeries(name, options, paneIndex = 0) {
2104
- const line = this.chart.addSeries(lightweightCharts.HistogramSeries, { ...options }, paneIndex);
2105
- this._seriesList.push(line);
2106
- this.legend.makeSeriesRow(name, line, paneIndex);
2107
- return {
2108
- name: name,
2109
- series: line,
2110
- };
2111
- }
2112
- createToolBox() {
2113
- this.toolBox = new ToolBox(this.id, this.chart, this.series, this.commandFunctions);
2114
- this.div.appendChild(this.toolBox.div);
2115
- }
2116
- createTopBar() {
2117
- this._topBar = new TopBar(this);
2118
- this.wrapper.prepend(this._topBar._div);
2119
- return this._topBar;
2120
- }
2121
- toJSON() {
2122
- // Exclude the chart attribute from serialization
2123
- const { chart, ...serialized } = this;
2124
- return serialized;
2125
- }
2126
- static syncChartsAll(handlers, crosshairOnly = false) {
2127
- // 1) Crosshair
2128
- handlers.forEach((source) => {
2129
- source.chart.subscribeCrosshairMove((param) => {
2130
- handlers.forEach((target) => {
2131
- if (target === source)
2132
- return;
2133
- if (!param.time) {
2134
- target.chart.clearCrosshairPosition();
2135
- return;
2136
- }
2137
- // get the point from the source series (for legend update)
2138
- const point = param.seriesData.get(source.series) || null;
2139
- // set the crosshair on the target chart
2140
- target.chart.setCrosshairPosition(0, param.time, target.series);
2141
- // update the legend on the target
2142
- if (point) {
2143
- target.legend.legendHandler(point, true);
2144
- }
2145
- });
2146
- });
2147
- });
2148
- if (crosshairOnly)
2149
- return;
2150
- // 2) Visible range synchronization
2151
- handlers.forEach((source) => {
2152
- source.chart.timeScale().subscribeVisibleLogicalRangeChange((range) => {
2153
- handlers.forEach((target) => {
2154
- if (target === source || !range)
2155
- return;
2156
- target.chart.timeScale().setVisibleLogicalRange(range);
2157
- });
2158
- });
2159
- });
2160
- }
2161
- static syncCharts(childChart, parentChart, crosshairOnly = false) {
2162
- function crosshairHandler(chart, point, param) {
2163
- if (!param.time) {
2164
- chart.chart.clearCrosshairPosition();
2165
- return;
2166
- }
2167
- chart.chart.setCrosshairPosition(0, param.time, chart.series);
2168
- if (point)
2169
- chart.legend.legendHandler(point, true);
2170
- }
2171
- function getPoint(series, param) {
2172
- if (!param.time)
2173
- return null;
2174
- return param.seriesData.get(series) || null;
2175
- }
2176
- const childTimeScale = childChart.chart.timeScale();
2177
- const parentTimeScale = parentChart.chart.timeScale();
2178
- const setChildRange = (timeRange) => {
2179
- if (timeRange)
2180
- childTimeScale.setVisibleLogicalRange(timeRange);
2181
- };
2182
- const setParentRange = (timeRange) => {
2183
- if (timeRange)
2184
- parentTimeScale.setVisibleLogicalRange(timeRange);
2185
- };
2186
- const setParentCrosshair = (param) => {
2187
- crosshairHandler(parentChart, getPoint(childChart.series, param), param);
2188
- };
2189
- const setChildCrosshair = (param) => {
2190
- crosshairHandler(childChart, getPoint(parentChart.series, param), param);
2191
- };
2192
- parentChart.chart.subscribeCrosshairMove(setChildCrosshair);
2193
- childChart.chart.subscribeCrosshairMove(setParentCrosshair);
2194
- if (crosshairOnly)
2195
- return;
2196
- childChart.chart.timeScale().subscribeVisibleLogicalRangeChange(setParentRange);
2197
- parentChart.chart.timeScale().subscribeVisibleLogicalRangeChange(setChildRange);
2198
- }
2199
- static makeSearchBox(chart) {
2200
- const searchWindow = document.createElement('div');
2201
- searchWindow.classList.add('searchbox');
2202
- searchWindow.style.display = 'none';
2203
- const magnifyingGlass = document.createElement('div');
2204
- magnifyingGlass.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1"><path style="fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke:lightgray;stroke-opacity:1;stroke-miterlimit:4;" d="M 15 15 L 21 21 M 10 17 C 6.132812 17 3 13.867188 3 10 C 3 6.132812 6.132812 3 10 3 C 13.867188 3 17 6.132812 17 10 C 17 13.867188 13.867188 17 10 17 Z M 10 17 "/></svg>`;
2205
- const sBox = document.createElement('input');
2206
- sBox.type = 'text';
2207
- searchWindow.appendChild(magnifyingGlass);
2208
- searchWindow.appendChild(sBox);
2209
- chart.div.appendChild(searchWindow);
2210
- chart.commandFunctions.push((event) => {
2211
- if (window.handlerInFocus !== chart.id || window.textBoxFocused)
2212
- return false;
2213
- if (searchWindow.style.display === 'none') {
2214
- if (/^[a-zA-Z0-9]$/.test(event.key)) {
2215
- searchWindow.style.display = 'flex';
2216
- sBox.focus();
2217
- return true;
2218
- }
2219
- else
2220
- return false;
2221
- }
2222
- else if (event.key === 'Enter' || event.key === 'Escape') {
2223
- if (event.key === 'Enter')
2224
- window.callbackFunction(`search${chart.id}_~_${sBox.value}`);
2225
- searchWindow.style.display = 'none';
2226
- sBox.value = '';
2227
- return true;
2228
- }
2229
- else
2230
- return false;
2231
- });
2232
- sBox.addEventListener('input', () => sBox.value = sBox.value.toUpperCase());
2233
- return {
2234
- window: searchWindow,
2235
- box: sBox,
2236
- };
2237
- }
2238
- static makeSpinner(chart) {
2239
- chart.spinner = document.createElement('div');
2240
- chart.spinner.classList.add('spinner');
2241
- chart.wrapper.appendChild(chart.spinner);
2242
- // TODO below can be css (animate)
2243
- let rotation = 0;
2244
- const speed = 10;
2245
- function animateSpinner() {
2246
- if (!chart.spinner)
2247
- return;
2248
- rotation += speed;
2249
- chart.spinner.style.transform = `translate(-50%, -50%) rotate(${rotation}deg)`;
2250
- requestAnimationFrame(animateSpinner);
2251
- }
2252
- animateSpinner();
2253
- }
2254
- static _styleMap = {
2255
- '--bg-color': 'backgroundColor',
2256
- '--hover-bg-color': 'hoverBackgroundColor',
2257
- '--click-bg-color': 'clickBackgroundColor',
2258
- '--active-bg-color': 'activeBackgroundColor',
2259
- '--muted-bg-color': 'mutedBackgroundColor',
2260
- '--border-color': 'borderColor',
2261
- '--color': 'color',
2262
- '--active-color': 'activeColor',
2263
- };
2264
- static setRootStyles(styles) {
2265
- const rootStyle = document.documentElement.style;
2266
- for (const [property, valueKey] of Object.entries(this._styleMap)) {
2267
- rootStyle.setProperty(property, styles[valueKey]);
2268
- }
2269
- }
2270
- createWatermark(text, fontSize, color) {
2271
- if (!this.watermark) {
2272
- this.watermark = lightweightCharts.createTextWatermark(this.chart.panes()[0], {
2273
- horzAlign: 'center',
2274
- vertAlign: 'center',
2275
- lines: [{
2276
- text: text,
2277
- color: color,
2278
- fontSize: fontSize,
2279
- }],
2280
- });
2281
- return;
2282
- }
2283
- this.watermark.applyOptions({
2284
- lines: [{
2285
- text: text,
2286
- color: color,
2287
- fontSize: fontSize,
2288
- }]
2289
- });
2290
- }
2291
- }
2292
-
2293
- class Table {
2294
- _div;
2295
- callbackName;
2296
- borderColor;
2297
- borderWidth;
2298
- table;
2299
- rows = {};
2300
- headings;
2301
- widths;
2302
- alignments;
2303
- footer;
2304
- header;
2305
- constructor(width, height, headings, widths, alignments, position, draggable = false, tableBackgroundColor, borderColor, borderWidth, textColors, backgroundColors) {
2306
- this._div = document.createElement('div');
2307
- this.callbackName = null;
2308
- this.borderColor = borderColor;
2309
- this.borderWidth = borderWidth;
2310
- if (draggable) {
2311
- this._div.style.position = 'absolute';
2312
- this._div.style.cursor = 'move';
2313
- }
2314
- else {
2315
- this._div.style.position = 'relative';
2316
- this._div.style.float = position;
2317
- }
2318
- this._div.style.zIndex = '2000';
2319
- this.reSize(width, height);
2320
- this._div.style.display = 'flex';
2321
- this._div.style.flexDirection = 'column';
2322
- // this._div.style.justifyContent = 'space-between'
2323
- this._div.style.borderRadius = '5px';
2324
- this._div.style.color = 'white';
2325
- this._div.style.fontSize = '12px';
2326
- this._div.style.fontVariantNumeric = 'tabular-nums';
2327
- this.table = document.createElement('table');
2328
- this.table.style.width = '100%';
2329
- this.table.style.borderCollapse = 'collapse';
2330
- this._div.style.overflow = 'hidden';
2331
- this.headings = headings;
2332
- this.widths = widths.map((width) => `${width * 100}%`);
2333
- this.alignments = alignments;
2334
- let head = this.table.createTHead();
2335
- let row = head.insertRow();
2336
- for (let i = 0; i < this.headings.length; i++) {
2337
- let th = document.createElement('th');
2338
- th.textContent = this.headings[i];
2339
- th.style.width = this.widths[i];
2340
- th.style.letterSpacing = '0.03rem';
2341
- th.style.padding = '0.2rem 0px';
2342
- th.style.fontWeight = '500';
2343
- th.style.textAlign = 'center';
2344
- if (i !== 0)
2345
- th.style.borderLeft = borderWidth + 'px solid ' + borderColor;
2346
- th.style.position = 'sticky';
2347
- th.style.top = '0';
2348
- th.style.backgroundColor = backgroundColors.length > 0 ? backgroundColors[i] : tableBackgroundColor;
2349
- th.style.color = textColors[i];
2350
- row.appendChild(th);
2351
- }
2352
- let overflowWrapper = document.createElement('div');
2353
- overflowWrapper.style.overflowY = 'auto';
2354
- overflowWrapper.style.overflowX = 'hidden';
2355
- overflowWrapper.style.backgroundColor = tableBackgroundColor;
2356
- overflowWrapper.appendChild(this.table);
2357
- this._div.appendChild(overflowWrapper);
2358
- window.containerDiv.appendChild(this._div);
2359
- if (!draggable)
2360
- return;
2361
- let offsetX, offsetY;
2362
- let onMouseDown = (event) => {
2363
- offsetX = event.clientX - this._div.offsetLeft;
2364
- offsetY = event.clientY - this._div.offsetTop;
2365
- document.addEventListener('mousemove', onMouseMove);
2366
- document.addEventListener('mouseup', onMouseUp);
2367
- };
2368
- let onMouseMove = (event) => {
2369
- this._div.style.left = (event.clientX - offsetX) + 'px';
2370
- this._div.style.top = (event.clientY - offsetY) + 'px';
2371
- };
2372
- let onMouseUp = () => {
2373
- // Remove the event listeners for dragging
2374
- document.removeEventListener('mousemove', onMouseMove);
2375
- document.removeEventListener('mouseup', onMouseUp);
2376
- };
2377
- this._div.addEventListener('mousedown', onMouseDown);
2378
- }
2379
- divToButton(div, callbackString) {
2380
- div.addEventListener('mouseover', () => div.style.backgroundColor = 'rgba(60, 60, 60, 0.6)');
2381
- div.addEventListener('mouseout', () => div.style.backgroundColor = 'transparent');
2382
- div.addEventListener('mousedown', () => div.style.backgroundColor = 'rgba(60, 60, 60)');
2383
- div.addEventListener('click', () => window.callbackFunction(callbackString));
2384
- div.addEventListener('mouseup', () => div.style.backgroundColor = 'rgba(60, 60, 60, 0.6)');
2385
- }
2386
- newRow(id, returnClickedCell = false) {
2387
- let row = this.table.insertRow();
2388
- row.style.cursor = 'default';
2389
- for (let i = 0; i < this.headings.length; i++) {
2390
- let cell = row.insertCell();
2391
- cell.style.width = this.widths[i];
2392
- cell.style.textAlign = this.alignments[i];
2393
- cell.style.border = this.borderWidth + 'px solid ' + this.borderColor;
2394
- if (returnClickedCell) {
2395
- this.divToButton(cell, `${this.callbackName}_~_${id};;;${this.headings[i]}`);
2396
- }
2397
- }
2398
- if (!returnClickedCell) {
2399
- this.divToButton(row, `${this.callbackName}_~_${id}`);
2400
- }
2401
- this.rows[id] = row;
2402
- }
2403
- deleteRow(id) {
2404
- this.table.deleteRow(this.rows[id].rowIndex);
2405
- delete this.rows[id];
2406
- }
2407
- clearRows() {
2408
- let numRows = Object.keys(this.rows).length;
2409
- for (let i = 0; i < numRows; i++)
2410
- this.table.deleteRow(-1);
2411
- this.rows = {};
2412
- }
2413
- _getCell(rowId, column) {
2414
- return this.rows[rowId].cells[this.headings.indexOf(column)];
2415
- }
2416
- updateCell(rowId, column, val) {
2417
- this._getCell(rowId, column).textContent = val;
2418
- }
2419
- styleCell(rowId, column, styleAttribute, value) {
2420
- const style = this._getCell(rowId, column).style;
2421
- style[styleAttribute] = value;
2422
- }
2423
- makeSection(id, type, numBoxes, func = false) {
2424
- let section = document.createElement('div');
2425
- section.style.display = 'flex';
2426
- section.style.width = '100%';
2427
- section.style.padding = '3px 0px';
2428
- section.style.backgroundColor = 'rgb(30, 30, 30)';
2429
- type === 'footer' ? this._div.appendChild(section) : this._div.prepend(section);
2430
- const textBoxes = [];
2431
- for (let i = 0; i < numBoxes; i++) {
2432
- let textBox = document.createElement('div');
2433
- section.appendChild(textBox);
2434
- textBox.style.flex = '1';
2435
- textBox.style.textAlign = 'center';
2436
- if (func) {
2437
- this.divToButton(textBox, `${id}_~_${i}`);
2438
- textBox.style.borderRadius = '2px';
2439
- }
2440
- textBoxes.push(textBox);
2441
- }
2442
- if (type === 'footer') {
2443
- this.footer = textBoxes;
2444
- }
2445
- else {
2446
- this.header = textBoxes;
2447
- }
2448
- }
2449
- reSize(width, height) {
2450
- this._div.style.width = width <= 1 ? width * 100 + '%' : width + 'px';
2451
- this._div.style.height = height <= 1 ? height * 100 + '%' : height + 'px';
2452
- }
2453
- }
2454
-
2455
- exports.Box = Box;
2456
- exports.Handler = Handler;
2457
- exports.HorizontalLine = HorizontalLine;
2458
- exports.Legend = Legend;
2459
- exports.RayLine = RayLine;
2460
- exports.Table = Table;
2461
- exports.ToolBox = ToolBox;
2462
- exports.TopBar = TopBar;
2463
- exports.TrendLine = TrendLine;
2464
- exports.VerticalLine = VerticalLine;
2465
- exports.globalParamInit = globalParamInit;
2466
- exports.htmlToElement = htmlToElement;
2467
- exports.paneStyleDefault = paneStyleDefault;
2468
- exports.setCursor = setCursor;
2469
-
2470
- return exports;
2471
-
2472
- })({}, LightweightCharts);