diff --git a/lightweight_charts/abstract.py b/lightweight_charts/abstract.py index 60bdbd2..d5156d2 100644 --- a/lightweight_charts/abstract.py +++ b/lightweight_charts/abstract.py @@ -7,11 +7,12 @@ import pandas as pd from .table import Table from .toolbox import ToolBox +from .drawings import HorizontalLine, TwoPointDrawing, VerticalSpan from .topbar import TopBar from .util import ( - IDGen, as_enum, jbool, Pane, Events, TIME, NUM, FLOAT, - LINE_STYLE, MARKER_POSITION, MARKER_SHAPE, CROSSHAIR_MODE, PRICE_SCALE_MODE, js_json, - marker_position, marker_shape, js_data, + Pane, Events, IDGen, as_enum, jbool, js_json, TIME, NUM, FLOAT, + LINE_STYLE, MARKER_POSITION, MARKER_SHAPE, CROSSHAIR_MODE, + PRICE_SCALE_MODE, marker_position, marker_shape, js_data, ) current_dir = os.path.dirname(os.path.abspath(__file__)) @@ -43,8 +44,16 @@ class Window: if self.loaded: return self.loaded = True - [self.run_script(script) for script in self.scripts] - [self.run_script(script) for script in self.final_scripts] + + # TODO this wont work for anything which isnt pywebview :( put it in the chart class ? + while not self.run_script_and_get('document.readyState == "complete"'): + continue # scary, but works + + initial_script = '' + self.scripts.extend(self.final_scripts) + for script in self.scripts: + initial_script += f'\n{script}' + self.script_func(initial_script) def run_script(self, script: str, run_last: bool = False): """ @@ -54,53 +63,60 @@ class Window: raise AttributeError("script_func has not been set") if self.loaded: self.script_func(script) - return - self.scripts.append(script) if not run_last else self.final_scripts.append(script) + elif run_last: + self.final_scripts.append(script) + else: + self.scripts.append(script) def run_script_and_get(self, script: str): self.run_script(f'_~_~RETURN~_~_{script}') return self._return_q.get() def create_table( - self, - width: NUM, - height: NUM, - headings: tuple, - widths: Optional[tuple] = None, - alignments: Optional[tuple] = None, - position: FLOAT = 'left', - draggable: bool = False, - background_color: str = '#121417', - border_color: str = 'rgb(70, 70, 70)', - border_width: int = 1, - heading_text_colors: Optional[tuple] = None, - heading_background_colors: Optional[tuple] = None, - return_clicked_cells: bool = False, - func: Optional[Callable] = None + self, + width: NUM, + height: NUM, + headings: tuple, + widths: Optional[tuple] = None, + alignments: Optional[tuple] = None, + position: FLOAT = 'left', + draggable: bool = False, + background_color: str = '#121417', + border_color: str = 'rgb(70, 70, 70)', + border_width: int = 1, + heading_text_colors: Optional[tuple] = None, + heading_background_colors: Optional[tuple] = None, + return_clicked_cells: bool = False, + func: Optional[Callable] = None ) -> 'Table': - return Table(self, width, height, headings, widths, alignments, position, draggable, - background_color, border_color, border_width, heading_text_colors, - heading_background_colors, return_clicked_cells, func) + return Table(*locals().values()) def create_subchart( - self, - position: FLOAT = 'left', - width: float = 0.5, - height: float = 0.5, - sync_id: Optional[str] = None, - scale_candles_only: bool = False, - sync_crosshairs_only: bool = False, - toolbox: bool = False + self, + position: FLOAT = 'left', + width: float = 0.5, + height: float = 0.5, + sync_id: Optional[str] = None, + scale_candles_only: bool = False, + sync_crosshairs_only: bool = False, + toolbox: bool = False ) -> 'AbstractChart': - subchart = AbstractChart(self, width, height, scale_candles_only, toolbox, position=position) + subchart = AbstractChart( + self, + width, + height, + scale_candles_only, + toolbox, + position=position + ) if not sync_id: return subchart self.run_script(f''' - Handler.syncCharts({subchart.id}, {sync_id}, {jbool(sync_crosshairs_only)}) - // TODO this should be in syncCharts - {subchart.id}.chart.timeScale().setVisibleLogicalRange( - {sync_id}.chart.timeScale().getVisibleLogicalRange() - ) + Handler.syncCharts( + {subchart.id}, + {sync_id}, + {jbool(sync_crosshairs_only)} + ) ''', run_last=True) return subchart #TODO test func below with polygon and others @@ -377,95 +393,6 @@ class SeriesCommon(Pane): end_time = self._single_datetime_format(end_time) if end_time else None return VerticalSpan(self, start_time, end_time, color) -# TODO drawings should be in a seperate folder, and inherbit a abstract Drawing class -class HorizontalLine(Pane): - def __init__(self, chart, price, color, width, style, text, axis_label_visible, func): - super().__init__(chart.win) - self.price = price - self.run_script(f''' - - {self.id} = new HorizontalLine( - {{price: {price}}}, - {{ - lineColor: '{color}', - lineStyle: {as_enum(style, LINE_STYLE)}, - }}, - callbackName={f"'{self.id}'" if func else 'null'} - ) - {chart.id}.series.attachPrimitive({self.id}) - ''') - # {self.id} = new HorizontalLine( - # {chart.id}, '{self.id}', {price}, '{color}', {width}, - # {as_enum(style, LINE_STYLE)}, {jbool(axis_label_visible)}, '{text}' - # )''') - if not func: - return - - def wrapper(p): - self.price = float(p) - func(chart, self) - - async def wrapper_async(p): - self.price = float(p) - await func(chart, self) - - self.win.handlers[self.id] = wrapper_async if asyncio.iscoroutinefunction(func) else wrapper - self.run_script(f'{chart.id}.toolBox?.addNewDrawing({self.id})') - - def update(self, price: float): - """ - Moves the horizontal line to the given price. - """ - self.run_script(f'{self.id}.updatePoints({{price: {price}}})') - # self.run_script(f'{self.id}.updatePrice({price})') - self.price = price - - def label(self, text: str): # TODO - self.run_script(f'{self.id}.updateLabel("{text}")') - - def delete(self): # TODO test all methods - """ - Irreversibly deletes the horizontal line. - """ - self.run_script(f'{self.id}.detach()') - - -class VerticalSpan(Pane): - def __init__(self, series: 'SeriesCommon', start_time: Union[TIME, tuple, list], end_time: Optional[TIME] = None, - color: str = 'rgba(252, 219, 3, 0.2)'): - self._chart = series._chart - super().__init__(self._chart.win) - start_time, end_time = pd.to_datetime(start_time), pd.to_datetime(end_time) - self.run_script(f''' - {self.id} = {self._chart.id}.chart.addHistogramSeries({{ - color: '{color}', - priceFormat: {{type: 'volume'}}, - priceScaleId: 'vertical_line', - lastValueVisible: false, - priceLineVisible: false, - }}) - {self.id}.priceScale('').applyOptions({{ - scaleMargins: {{top: 0, bottom: 0}} - }}) - ''') - if end_time is None: - if isinstance(start_time, pd.DatetimeIndex): - data = [{'time': time.timestamp(), 'value': 1} for time in start_time] - else: - data = [{'time': start_time.timestamp(), 'value': 1}] - self.run_script(f'{self.id}.setData({data})') - else: - self.run_script(f''' - {self.id}.setData(calculateTrendLine( - {start_time.timestamp()}, 1, {end_time.timestamp()}, 1, {series.id})) - ''') - - def delete(self): - """ - Irreversibly deletes the vertical span. - """ - self.run_script(f'{self._chart.id}.chart.removeSeries({self.id})') - class Line(SeriesCommon): def __init__(self, chart, name, color, style, width, price_line, price_label, crosshair_marker=True): @@ -494,20 +421,20 @@ class Line(SeriesCommon): ) null''') - def _set_trend(self, start_time, start_value, end_time, end_value, ray=False, round=False): - if round: - start_time = self._single_datetime_format(start_time) - end_time = self._single_datetime_format(end_time) - else: - start_time, end_time = pd.to_datetime((start_time, end_time)).astype('int64') // 10 ** 9 + # def _set_trend(self, start_time, start_value, end_time, end_value, ray=False, round=False): + # if round: + # start_time = self._single_datetime_format(start_time) + # end_time = self._single_datetime_format(end_time) + # else: + # start_time, end_time = pd.to_datetime((start_time, end_time)).astype('int64') // 10 ** 9 - self.run_script(f''' - {self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: false}}) - {self.id}.series.setData( - calculateTrendLine({start_time}, {start_value}, {end_time}, {end_value}, - {self._chart.id}, {jbool(ray)})) - {self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: true}}) - ''') + # self.run_script(f''' + # {self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: false}}) + # {self.id}.series.setData( + # calculateTrendLine({start_time}, {start_value}, {end_time}, {end_value}, + # {self._chart.id}, {jbool(ray)})) + # {self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: true}}) + # ''') def delete(self): """ @@ -576,11 +503,11 @@ class Candlestick(SeriesCommon): # self.run_script(f'{self.id}.makeCandlestickSeries()') - def set(self, df: Optional[pd.DataFrame] = None, render_drawings=False): + def set(self, df: Optional[pd.DataFrame] = None, keep_drawings=False): """ Sets the initial data for the chart.\n :param df: columns: date/time, open, high, low, close, volume (if volume enabled). - :param render_drawings: Re-renders any drawings made through the toolbox. Otherwise, they will be deleted. + :param keep_drawings: keeps any drawings made through the toolbox. Otherwise, they will be deleted. """ if df is None or df.empty: self.run_script(f'{self.id}.series.setData([])') @@ -592,9 +519,8 @@ class Candlestick(SeriesCommon): self._last_bar = df.iloc[-1] self.run_script(f'{self.id}.series.setData({js_data(df)})') - # TODO are we not using renderdrawings then? - # toolbox_action = 'clearDrawings' if not render_drawings else 'renderDrawings' - # self.run_script(f"if ({self._chart.id}.toolBox) {self._chart.id}.toolBox.{toolbox_action}()") + # TODO keep drawings doesnt do anything + self.run_script(f"if ({self._chart.id}.toolBox) {self._chart.id}.toolBox.clearDrawings()") if 'volume' not in df: return volume = df.drop(columns=['open', 'high', 'low', 'close']).rename(columns={'volume': 'value'}) @@ -612,7 +538,7 @@ class Candlestick(SeriesCommon): {self.id}.chart.priceScale("right").applyOptions({{autoScale: true}}) ''') - def update(self, series: pd.Series, render_drawings=False, _from_tick=False): + def update(self, series: pd.Series, _from_tick=False): """ Updates the data from a bar; if series['time'] is the same time as the last bar, the last bar will be overwritten.\n @@ -661,11 +587,21 @@ class Candlestick(SeriesCommon): self.update(bar, _from_tick=True) def price_scale( - self, auto_scale: bool = True, mode: PRICE_SCALE_MODE = 'normal', invert_scale: bool = False, - align_labels: bool = True, scale_margin_top: float = 0.2, scale_margin_bottom: float = 0.2, - border_visible: bool = False, border_color: Optional[str] = None, text_color: Optional[str] = None, - entire_text_only: bool = False, visible: bool = True, ticks_visible: bool = False, minimum_width: int = 0 - ): + self, + auto_scale: bool = True, + mode: PRICE_SCALE_MODE = 'normal', + invert_scale: bool = False, + align_labels: bool = True, + scale_margin_top: float = 0.2, + scale_margin_bottom: float = 0.2, + border_visible: bool = False, + border_color: Optional[str] = None, + text_color: Optional[str] = None, + entire_text_only: bool = False, + visible: bool = True, + ticks_visible: bool = False, + minimum_width: int = 0 + ): self.run_script(f''' {self.id}.series.priceScale().applyOptions({{ autoScale: {jbool(auto_scale)}, @@ -776,18 +712,41 @@ class AbstractChart(Candlestick, Pane): """ return self._lines.copy() - def trend_line(self, start_time: TIME, start_value: NUM, end_time: TIME, end_value: NUM, - round: bool = False, color: str = '#1E80F0', width: int = 2, - style: LINE_STYLE = 'solid', - ) -> Line: - line = Line(self, '', color, style, width, False, False, False) - line._set_trend(start_time, start_value, end_time, end_value, round=round) - return line + def trend_line( + self, + start_time: TIME, + start_value: NUM, + end_time: TIME, + end_value: NUM, + round: bool = False, + color: str = '#1E80F0', + width: int = 2, + style: LINE_STYLE = 'solid', + ) -> TwoPointDrawing: + return TwoPointDrawing("TrendLine", *locals().values()) - def ray_line(self, start_time: TIME, value: NUM, round: bool = False, - color: str = '#1E80F0', width: int = 2, - style: LINE_STYLE = 'solid' - ) -> Line: + def box( + self, + start_time: TIME, + start_value: NUM, + end_time: TIME, + end_value: NUM, + round: bool = False, + color: str = '#1E80F0', + width: int = 2, + style: LINE_STYLE = 'solid', + ) -> TwoPointDrawing: + return TwoPointDrawing("Box", *locals().values()) + + def ray_line( + self, + start_time: TIME, + value: NUM, + round: bool = False, + color: str = '#1E80F0', + width: int = 2, + style: LINE_STYLE = 'solid' + ) -> Line: line = Line(self, '', color, style, width, False, False, False) line._set_trend(start_time, value, start_time, value, ray=True, round=round) return line @@ -851,11 +810,20 @@ class AbstractChart(Candlestick, Pane): }} }})""") - def crosshair(self, mode: CROSSHAIR_MODE = 'normal', vert_visible: bool = True, - vert_width: int = 1, vert_color: Optional[str] = None, vert_style: LINE_STYLE = 'large_dashed', - vert_label_background_color: str = 'rgb(46, 46, 46)', horz_visible: bool = True, - horz_width: int = 1, horz_color: Optional[str] = None, horz_style: LINE_STYLE = 'large_dashed', - horz_label_background_color: str = 'rgb(55, 55, 55)'): + def crosshair( + self, + mode: CROSSHAIR_MODE = 'normal', + vert_visible: bool = True, + vert_width: int = 1, + vert_color: Optional[str] = None, + vert_style: LINE_STYLE = 'large_dashed', + vert_label_background_color: str = 'rgb(46, 46, 46)', + horz_visible: bool = True, + horz_width: int = 1, + horz_color: Optional[str] = None, + horz_style: LINE_STYLE = 'large_dashed', + horz_label_background_color: str = 'rgb(55, 55, 55)' + ): """ Crosshair formatting for its vertical and horizontal axes. """ @@ -877,7 +845,8 @@ class AbstractChart(Candlestick, Pane): style: {as_enum(horz_style, LINE_STYLE)}, labelBackgroundColor: "{horz_label_background_color}" }} - }}}})''') + }} + }})''') def watermark(self, text: str, font_size: int = 44, color: str = 'rgba(180, 180, 200, 0.5)'): """ @@ -950,25 +919,25 @@ class AbstractChart(Candlestick, Pane): self.win.handlers[f'{modifier_key, keys}'] = func def create_table( - self, - width: NUM, - height: NUM, - headings: tuple, - widths: Optional[tuple] = None, - alignments: Optional[tuple] = None, - position: FLOAT = 'left', - draggable: bool = False, - background_color: str = '#121417', - border_color: str = 'rgb(70, 70, 70)', - border_width: int = 1, - heading_text_colors: Optional[tuple] = None, - heading_background_colors: Optional[tuple] = None, - return_clicked_cells: bool = False, - func: Optional[Callable] = None + self, + width: NUM, + height: NUM, + headings: tuple, + widths: Optional[tuple] = None, + alignments: Optional[tuple] = None, + position: FLOAT = 'left', + draggable: bool = False, + background_color: str = '#121417', + border_color: str = 'rgb(70, 70, 70)', + border_width: int = 1, + heading_text_colors: Optional[tuple] = None, + heading_background_colors: Optional[tuple] = None, + return_clicked_cells: bool = False, + func: Optional[Callable] = None ) -> Table: - return self.win.create_table(width, height, headings, widths, alignments, position, draggable, - background_color, border_color, border_width, heading_text_colors, - heading_background_colors, return_clicked_cells, func) + args = locals() + del args['self'] + return self.win.create_table(*args.values()) def screenshot(self) -> bytes: """ @@ -984,5 +953,6 @@ class AbstractChart(Candlestick, Pane): toolbox: bool = False) -> 'AbstractChart': if sync is True: sync = self.id - return self.win.create_subchart(position, width, height, sync, - scale_candles_only, sync_crosshairs_only, toolbox) + args = locals() + del args['self'] + return self.win.create_subchart(*args.values()) diff --git a/lightweight_charts/chart.py b/lightweight_charts/chart.py index 671f535..305534c 100644 --- a/lightweight_charts/chart.py +++ b/lightweight_charts/chart.py @@ -81,7 +81,6 @@ class PyWV: window.show() elif arg == 'hide': window.hide() - # TODO make sure setup.py requires latest pywebview now else: try: if '_~_~RETURN~_~_' in arg: diff --git a/lightweight_charts/drawings.py b/lightweight_charts/drawings.py new file mode 100644 index 0000000..63ec384 --- /dev/null +++ b/lightweight_charts/drawings.py @@ -0,0 +1,167 @@ +import asyncio +import json +import pandas as pd + +from typing import Union, Optional + +from lightweight_charts.util import js_json + +from .util import NUM, Pane, as_enum, LINE_STYLE, TIME + + +class Drawing(Pane): + def __init__(self, chart, color, width, style, func=None): + super().__init__(chart.win) + self.chart = chart + + def update(self, *points): + js_json_string = f'JSON.parse({json.dumps(points)})' + self.run_script(f'{self.id}.updatePoints(...{js_json_string})') + + def delete(self): + """ + Irreversibly deletes the drawing. + """ + self.run_script(f''' + if ({self.chart.id}.toolBox) {self.chart.id}.toolBox.delete({self.id}) + else {self.id}.detach() + ''') + +class TwoPointDrawing(Drawing): + def __init__( + self, + drawing_type, + chart, + start_time: TIME, + start_value: NUM, + end_time: TIME, + end_value: NUM, + round: bool, + color, + width, + style, + func=None + ): + super().__init__(chart, color, width, style, func) + + + def make_js_point(time, price): + return js_json({"time": time, "price": price}) + + self.run_script(f''' + {self.id} = new {drawing_type}( + {make_js_point(start_time, start_value)}, + {make_js_point(end_time, end_value)}, + {{ + lineColor: '{color}', + lineStyle: {as_enum(style, LINE_STYLE)}, + }} + ) + {chart.id}.series.attachPrimitive({self.id}) + ''') + + +class HorizontalLine(Drawing): + def __init__(self, chart, price, color, width, style, text, axis_label_visible, func): + super().__init__(chart, color, width, style, func) + self.price = price + self.run_script(f''' + + {self.id} = new HorizontalLine( + {{price: {price}}}, + {{ + lineColor: '{color}', + lineStyle: {as_enum(style, LINE_STYLE)}, + }}, + callbackName={f"'{self.id}'" if func else 'null'} + ) + {chart.id}.series.attachPrimitive({self.id}) + ''') + if not func: + return + + def wrapper(p): + self.price = float(p) + func(chart, self) + + async def wrapper_async(p): + self.price = float(p) + await func(chart, self) + + self.win.handlers[self.id] = wrapper_async if asyncio.iscoroutinefunction(func) else wrapper + self.run_script(f'{chart.id}.toolBox?.addNewDrawing({self.id})') + + def update(self, price: float): + """ + Moves the horizontal line to the given price. + """ + self.run_script(f'{self.id}.updatePoints({{price: {price}}})') + # self.run_script(f'{self.id}.updatePrice({price})') + self.price = price + + def label(self, text: str): # TODO + self.run_script(f'{self.id}.updateLabel("{text}")') + + + +class VerticalLine(Drawing): + def __init__(self, chart, time, color, width, style, text, axis_label_visible, func): + super().__init__(chart, color, width, style, func) + self.time = time + self.run_script(f''' + + {self.id} = new HorizontalLine( + {{time: {time}}}, + {{ + lineColor: '{color}', + lineStyle: {as_enum(style, LINE_STYLE)}, + }}, + callbackName={f"'{self.id}'" if func else 'null'} + ) + {chart.id}.series.attachPrimitive({self.id}) + ''') + + def update(self, time: TIME): + self.run_script(f'{self.id}.updatePoints({{time: {time}}})') + # self.run_script(f'{self.id}.updatePrice({price})') + self.price = price + + def label(self, text: str): # TODO + self.run_script(f'{self.id}.updateLabel("{text}")') + + +class VerticalSpan(Pane): + def __init__(self, series: 'SeriesCommon', start_time: Union[TIME, tuple, list], end_time: Optional[TIME] = None, + color: str = 'rgba(252, 219, 3, 0.2)'): + self._chart = series._chart + super().__init__(self._chart.win) + start_time, end_time = pd.to_datetime(start_time), pd.to_datetime(end_time) + self.run_script(f''' + {self.id} = {self._chart.id}.chart.addHistogramSeries({{ + color: '{color}', + priceFormat: {{type: 'volume'}}, + priceScaleId: 'vertical_line', + lastValueVisible: false, + priceLineVisible: false, + }}) + {self.id}.priceScale('').applyOptions({{ + scaleMargins: {{top: 0, bottom: 0}} + }}) + ''') + if end_time is None: + if isinstance(start_time, pd.DatetimeIndex): + data = [{'time': time.timestamp(), 'value': 1} for time in start_time] + else: + data = [{'time': start_time.timestamp(), 'value': 1}] + self.run_script(f'{self.id}.setData({data})') + else: + self.run_script(f''' + {self.id}.setData(calculateTrendLine( + {start_time.timestamp()}, 1, {end_time.timestamp()}, 1, {series.id})) + ''') + + def delete(self): + """ + Irreversibly deletes the vertical span. + """ + self.run_script(f'{self._chart.id}.chart.removeSeries({self.id})') diff --git a/lightweight_charts/js/bundle.js b/lightweight_charts/js/bundle.js index 4361326..010c921 100644 --- a/lightweight_charts/js/bundle.js +++ b/lightweight_charts/js/bundle.js @@ -1 +1 @@ -import{LineStyle as t,isUTCTimestamp as e,isBusinessDay as i,createChart as s,ColorType as n,CrosshairMode as o}from"lightweight-charts";function r(t){if(void 0===t)throw new Error("Value is undefined");return t}class a{_chart=void 0;_series=void 0;requestUpdate(){this._requestUpdate&&this._requestUpdate()}_requestUpdate;attached({chart:t,series:e,requestUpdate:i}){this._chart=t,this._series=e,this._series.subscribeDataChanged(this._fireDataUpdated),this._requestUpdate=i,this.requestUpdate()}detached(){this._chart=void 0,this._series=void 0,this._requestUpdate=void 0}get chart(){return r(this._chart)}get series(){return r(this._series)}_fireDataUpdated(t){this.dataUpdated&&this.dataUpdated(t)}}const l={lineColor:"rgb(255, 255, 255)",lineStyle:t.Solid,width:4,showLabels:!0,showCircles:!1};function d(t){if(e(t))return 1e3*t;if(i(t))return new Date(t.year,t.month,t.day).valueOf();const[s,n,o]=t.split("-").map(parseInt);return new Date(s,n,o).valueOf()}var h;!function(t){t[t.NONE=0]="NONE",t[t.HOVERING=1]="HOVERING",t[t.DRAGGING=2]="DRAGGING",t[t.DRAGGINGP1=3]="DRAGGINGP1",t[t.DRAGGINGP2=4]="DRAGGINGP2",t[t.DRAGGINGP3=5]="DRAGGINGP3",t[t.DRAGGINGP4=6]="DRAGGINGP4"}(h||(h={}));class c extends a{_paneViews=[];_options;_state=h.NONE;_startDragPoint=null;_latestHoverPoint=null;static _mouseIsDown=!1;static hoveredObject=null;static lastHoveredObject=null;_listeners=[];constructor(t){super(),this._options={...l,...t}}updateAllViews(){this._paneViews.forEach((t=>t.update()))}paneViews(){return this._paneViews}applyOptions(t){this._options={...this._options,...t},this.requestUpdate()}detach(){this._options.lineColor="transparent",this.requestUpdate(),this.series.detachPrimitive(this);for(const t of this._listeners)document.body.removeEventListener(t.name,t.listener)}_subscribe(t,e){document.body.addEventListener(t,e),this._listeners.push({name:t,listener:e})}_unsubscribe(t,e){document.body.removeEventListener(t,e);const i=this._listeners.find((i=>i.name===t&&i.listener===e));this._listeners.splice(this._listeners.indexOf(i),1)}_handleHoverInteraction(t){if(this._latestHoverPoint=t.point,c._mouseIsDown)this._handleDragInteraction(t);else if(this._mouseIsOverDrawing(t)){if(this._state!=h.NONE)return;this._moveToState(h.HOVERING),c.hoveredObject=c.lastHoveredObject=this}else{if(this._state==h.NONE)return;this._moveToState(h.NONE),c.hoveredObject===this&&(c.hoveredObject=null)}}static _eventToPoint(t,e){if(!e||!t.point||!t.logical)return null;const i=e.coordinateToPrice(t.point.y);return null==i?null:{time:t.time||null,logical:t.logical,price:i.valueOf()}}static _getDiff(t,e){const i={time:null,logical:t.logical-e.logical,price:t.price-e.price};return t.time&&e.time&&(i.time=d(t.time)-d(e.time)),i}static _addDiffToPoint(t,e,i,s){null!=e&&null!=t.time?t.time=(d(t.time)+e)/1e3:t.time=null,t.logical=t.logical+i,t.price=t.price+s}_handleMouseDownInteraction=()=>{c._mouseIsDown||(c._mouseIsDown=!0,this._onMouseDown())};_handleMouseUpInteraction=()=>{c._mouseIsDown&&(c._mouseIsDown=!1,this._moveToState(h.HOVERING))};_handleDragInteraction(t){if(this._state!=h.DRAGGING&&this._state!=h.DRAGGINGP1&&this._state!=h.DRAGGINGP2&&this._state!=h.DRAGGINGP3&&this._state!=h.DRAGGINGP4)return;const e=c._eventToPoint(t,this.series);if(!e)return;this._startDragPoint=this._startDragPoint||e;const i=c._getDiff(e,this._startDragPoint);this._onDrag(i),this.requestUpdate(),this._startDragPoint=e}}class p{_options;constructor(t){this._options=t}}class u extends p{_p1;_p2;_text1;_text2;constructor(t,e,i,s,n){super(n),this._p1=t,this._p2=e,this._text1=i,this._text2=s}_getScaledCoordinates(t){return null===this._p1.x||null===this._p1.y||null===this._p2.x||null===this._p2.y?null:{x1:Math.round(this._p1.x*t.horizontalPixelRatio),y1:Math.round(this._p1.y*t.verticalPixelRatio),x2:Math.round(this._p2.x*t.horizontalPixelRatio),y2:Math.round(this._p2.y*t.verticalPixelRatio)}}_drawEndCircle(t,e,i){t.context.fillStyle="#000",t.context.beginPath(),t.context.arc(e,i,9,0,2*Math.PI),t.context.stroke(),t.context.fill()}}class _ extends p{_point={x:null,y:null};constructor(t,e){super(e),this._point=t}draw(t){t.useBitmapCoordinateSpace((t=>{if(null==this._point.y)return;const e=t.context,i=Math.round(this._point.y*t.verticalPixelRatio),s=this._point.x?this._point.x*t.horizontalPixelRatio:0;e.lineWidth=this._options.width,e.strokeStyle=this._options.lineColor,e.beginPath(),e.moveTo(s,i),e.lineTo(t.bitmapSize.width,i),e.stroke()}))}}class m{_source;constructor(t){this._source=t}}class v extends m{_p1={x:null,y:null};_p2={x:null,y:null};_source;constructor(t){super(t),this._source=t}update(){const t=this._source.series,e=t.priceToCoordinate(this._source._p1.price),i=t.priceToCoordinate(this._source._p2.price),s=this._getX(this._source._p1),n=this._getX(this._source._p2);this._p1={x:s,y:e},this._p2={x:n,y:i}}_getX(t){const e=this._source.chart.timeScale();return t.time?e.timeToCoordinate(t.time):e.logicalToCoordinate(t.logical)}}class g extends m{_source;_point={x:null,y:null};constructor(t){super(t),this._source=t}update(){const t=this._source._point,e=this._source.chart.timeScale(),i=this._source.series;this._point.x=t.time?e.timeToCoordinate(t.time):null,this._point.y=i.priceToCoordinate(t.price)}renderer(){return new _(this._point,this._source._options)}}class w extends c{_type="HorizontalLine";_paneViews;_point;_callbackName;_startDragPoint=null;constructor(t,e,i=null){super(e),this._point=t,this._point.time=null,this._paneViews=[new g(this)],this._callbackName=i}updatePoints(...t){for(const e of t)e&&(this._point.price=e.price);this.requestUpdate()}_moveToState(t){switch(t){case h.NONE:document.body.style.cursor="default",this._unsubscribe("mousedown",this._handleMouseDownInteraction);break;case h.HOVERING:document.body.style.cursor="pointer",this._unsubscribe("mouseup",this._childHandleMouseUpInteraction),this._subscribe("mousedown",this._handleMouseDownInteraction),this.chart.applyOptions({handleScroll:!0});break;case h.DRAGGING:document.body.style.cursor="grabbing",this._subscribe("mouseup",this._childHandleMouseUpInteraction),this.chart.applyOptions({handleScroll:!1})}this._state=t}_onDrag(t){c._addDiffToPoint(this._point,0,0,t.price),this.requestUpdate()}_mouseIsOverDrawing(t,e=4){if(!t.point)return!1;const i=this.series.priceToCoordinate(this._point.price);return!!i&&Math.abs(i-t.point.y){this._handleMouseUpInteraction(),this._callbackName&&(console.log(window.callbackFunction),window.callbackFunction(`${this._callbackName}_~_${this._point.price.toFixed(8)}`))}}class y{_div;callbackName;borderColor;borderWidth;table;rows={};headings;widths;alignments;footer;header;constructor(t,e,i,s,n,o,r=!1,a,l,d,h,c){this._div=document.createElement("div"),this.callbackName=null,this.borderColor=l,this.borderWidth=d,r?(this._div.style.position="absolute",this._div.style.cursor="move"):(this._div.style.position="relative",this._div.style.float=o),this._div.style.zIndex="2000",this.reSize(t,e),this._div.style.display="flex",this._div.style.flexDirection="column",this._div.style.borderRadius="5px",this._div.style.color="white",this._div.style.fontSize="12px",this._div.style.fontVariantNumeric="tabular-nums",this.table=document.createElement("table"),this.table.style.width="100%",this.table.style.borderCollapse="collapse",this._div.style.overflow="hidden",this.headings=i,this.widths=s.map((t=>100*t+"%")),this.alignments=n;let p=this.table.createTHead().insertRow();for(let t=0;t0?c[t]:a,e.style.color=h[t],p.appendChild(e)}let u,_,m=document.createElement("div");if(m.style.overflowY="auto",m.style.overflowX="hidden",m.style.backgroundColor=a,m.appendChild(this.table),this._div.appendChild(m),window.containerDiv.appendChild(this._div),!r)return;let v=t=>{this._div.style.left=t.clientX-u+"px",this._div.style.top=t.clientY-_+"px"},g=()=>{document.removeEventListener("mousemove",v),document.removeEventListener("mouseup",g)};this._div.addEventListener("mousedown",(t=>{u=t.clientX-this._div.offsetLeft,_=t.clientY-this._div.offsetTop,document.addEventListener("mousemove",v),document.addEventListener("mouseup",g)}))}divToButton(t,e){t.addEventListener("mouseover",(()=>t.style.backgroundColor="rgba(60, 60, 60, 0.6)")),t.addEventListener("mouseout",(()=>t.style.backgroundColor="transparent")),t.addEventListener("mousedown",(()=>t.style.backgroundColor="rgba(60, 60, 60)")),t.addEventListener("click",(()=>window.callbackFunction(e))),t.addEventListener("mouseup",(()=>t.style.backgroundColor="rgba(60, 60, 60, 0.6)"))}newRow(t,e=!1){let i=this.table.insertRow();i.style.cursor="default";for(let s=0;s\n \`\n `,n=`\n \n `,o=document.createElement("div");o.style.display="flex",o.style.alignItems="center";let r=document.createElement("div"),a=document.createElement("div");a.classList.add("legend-toggle-switch");let l=document.createElementNS("http://www.w3.org/2000/svg","svg");l.setAttribute("width","22"),l.setAttribute("height","16");let d=document.createElementNS("http://www.w3.org/2000/svg","g");d.innerHTML=s;let h=!0;a.addEventListener("click",(()=>{h?(h=!1,d.innerHTML=n,e.applyOptions({visible:!1})):(h=!0,e.applyOptions({visible:!0}),d.innerHTML=s)})),l.appendChild(d),a.appendChild(l),o.appendChild(r),o.appendChild(a),this.div.appendChild(o);const c=e.options().baseLineColor;this._lines.push({name:t,div:r,row:o,toggle:a,series:e,solid:c.startsWith("rgba")?c.replace(/[^,]+(?=\))/,"1"):c})}legendItemFormat(t,e){return t.toFixed(e).toString().padStart(8," ")}shorthandFormat(t){const e=Math.abs(t);return e>=1e6?(t/1e6).toFixed(1)+"M":e>=1e3?(t/1e3).toFixed(1)+"K":t.toString().padStart(8," ")}legendHandler(t,e=!1){if(!this.ohlcEnabled&&!this.linesEnabled&&!this.percentEnabled)return;const i=this.handler.series.options();if(!t.time)return this.candle.style.color="transparent",void(this.candle.innerHTML=this.candle.innerHTML.replace(i.upColor,"").replace(i.downColor,""));let s,n=null;if(e){const e=this.handler.chart.timeScale();let i=e.timeToCoordinate(t.time);i&&(n=e.coordinateToLogical(i.valueOf())),n&&(s=this.handler.series.dataByIndex(n.valueOf()))}else s=t.seriesData.get(this.handler.series);this.candle.style.color="";let o='';if(s){if(this.ohlcEnabled&&(o+=`O ${this.legendItemFormat(s.open,this.handler.precision)} `,o+=`| H ${this.legendItemFormat(s.high,this.handler.precision)} `,o+=`| L ${this.legendItemFormat(s.low,this.handler.precision)} `,o+=`| C ${this.legendItemFormat(s.close,this.handler.precision)} `),this.percentEnabled){let t=(s.close-s.open)/s.open*100,e=t>0?i.upColor:i.downColor,n=`${t>=0?"+":""}${t.toFixed(2)} %`;this.colorBasedOnCandle?o+=`| ${n}`:o+="| "+n}if(this.handler.volumeSeries){let e;e=n?this.handler.volumeSeries.dataByIndex(n):t.seriesData.get(this.handler.volumeSeries),e&&(o+=this.ohlcEnabled?`
V ${this.shorthandFormat(e.value)}`:"")}}this.candle.innerHTML=o+"
",this._lines.forEach((i=>{if(!this.linesEnabled)return void(i.row.style.display="none");let s,o;if(i.row.style.display="flex",s=e&&n?i.series.dataByIndex(n):t.seriesData.get(i.series),"Histogram"==i.series.seriesType())o=this.shorthandFormat(s.value);else{const t=i.series.options().priceFormat;o=this.legendItemFormat(s.value,t.precision)}i.div.innerHTML=` ${i.name} : ${o}`}))}}class C{_chart;_series;_finishDrawingCallback=null;_drawings=[];_activeDrawing=null;_isDrawing=!1;_drawingType=null;constructor(t,e,i=null){this._chart=t,this._series=e,this._finishDrawingCallback=i,this._chart.subscribeClick(this._clickHandler),this._chart.subscribeCrosshairMove(this._moveHandler)}_clickHandler=t=>this._onClick(t);_moveHandler=t=>this._onMouseMove(t);beginDrawing(t){this._drawingType=t,this._isDrawing=!0}stopDrawing(){this._isDrawing=!1,this._activeDrawing=null}get drawings(){return this._drawings}addNewDrawing(t){this._series.attachPrimitive(t),this._drawings.push(t)}delete(t){if(null==t)return;const e=this._drawings.indexOf(t);-1!=e&&(this._drawings.splice(e,1),t.detach())}clearDrawings(){for(const t of this._drawings)t.detach();this._drawings=[]}_onClick(t){if(!this._isDrawing)return;const e=c._eventToPoint(t,this._series);if(e)if(null==this._activeDrawing){if(null==this._drawingType)return;this._activeDrawing=new this._drawingType(e,e),this._series.attachPrimitive(this._activeDrawing)}else{if(this._drawings.push(this._activeDrawing),this.stopDrawing(),!this._finishDrawingCallback)return;this._finishDrawingCallback()}}_onMouseMove(t){if(!t)return;for(const e of this._drawings)e._handleHoverInteraction(t);if(!this._isDrawing||!this._activeDrawing)return;const e=c._eventToPoint(t,this._series);e&&this._activeDrawing.updatePoints(null,e)}}class f extends u{constructor(t,e,i,s,n){super(t,e,i,s,n)}draw(t){t.useBitmapCoordinateSpace((t=>{if(null===this._p1.x||null===this._p1.y||null===this._p2.x||null===this._p2.y)return;const e=t.context,i=this._getScaledCoordinates(t);i&&(e.lineWidth=this._options.width,e.strokeStyle=this._options.lineColor,e.beginPath(),e.moveTo(i.x1,i.y1),e.lineTo(i.x2,i.y2),e.stroke(),this._options.showCircles&&(this._drawEndCircle(t,i.x1,i.y1),this._drawEndCircle(t,i.x2,i.y2)))}))}}class D extends v{constructor(t){super(t)}renderer(){return new f(this._p1,this._p2,""+this._source._p1.price.toFixed(1),""+this._source._p2.price.toFixed(1),this._source._options)}}class E extends c{_p1;_p2;_paneViews=[];constructor(t,e,i){super(),this._p1=t,this._p2=e,this._options={...l,...i}}setFirstPoint(t){this.updatePoints(t)}setSecondPoint(t){this.updatePoints(null,t)}updatePoints(...t){this._p1=t[0]||this._p1,this._p2=t[1]||this._p2,this.requestUpdate()}}class k extends E{_type="TrendLine";constructor(t,e,i){super(t,e,i),this._paneViews=[new D(this)]}_moveToState(t){switch(t){case h.NONE:document.body.style.cursor="default",this._options.showCircles=!1,this.requestUpdate(),this._unsubscribe("mousedown",this._handleMouseDownInteraction);break;case h.HOVERING:document.body.style.cursor="pointer",this._options.showCircles=!0,this.requestUpdate(),this._subscribe("mousedown",this._handleMouseDownInteraction),this._unsubscribe("mouseup",this._handleMouseDownInteraction),this.chart.applyOptions({handleScroll:!0});break;case h.DRAGGINGP1:case h.DRAGGINGP2:case h.DRAGGING:document.body.style.cursor="grabbing",this._subscribe("mouseup",this._handleMouseUpInteraction),this.chart.applyOptions({handleScroll:!1})}this._state=t}_onDrag(t){this._state!=h.DRAGGING&&this._state!=h.DRAGGINGP1||c._addDiffToPoint(this._p1,t.time,t.logical,t.price),this._state!=h.DRAGGING&&this._state!=h.DRAGGINGP2||c._addDiffToPoint(this._p2,t.time,t.logical,t.price)}_onMouseDown(){this._startDragPoint=null;const t=this._latestHoverPoint;if(!t)return;const e=this._paneViews[0]._p1,i=this._paneViews[0]._p2;if(!(e.x&&i.x&&e.y&&i.y))return this._moveToState(h.DRAGGING);Math.abs(t.x-e.x)<10&&Math.abs(t.y-e.y)<10?this._moveToState(h.DRAGGINGP1):Math.abs(t.x-i.x)<10&&Math.abs(t.y-i.y)<10?this._moveToState(h.DRAGGINGP2):this._moveToState(h.DRAGGING)}_mouseIsOverDrawing(t,e=4){if(!t.point)return!1;const i=this._paneViews[0]._p1.x,s=this._paneViews[0]._p1.y,n=this._paneViews[0]._p2.x,o=this._paneViews[0]._p2.y;if(!(i&&n&&s&&o))return!1;const r=t.point.x,a=t.point.y;if(r<=Math.min(i,n)-e||r>=Math.max(i,n)+e)return!1;return Math.abs((o-s)*r-(n-i)*a+n*s-o*i)/Math.sqrt((o-s)**2+(n-i)**2)<=e}}class L extends u{constructor(t,e,i,s,n){super(t,e,i,s,n)}draw(t){t.useBitmapCoordinateSpace((t=>{const e=t.context,i=this._getScaledCoordinates(t);if(!i)return;e.lineWidth=this._options.width,e.strokeStyle=this._options.lineColor,e.fillStyle=this._options.fillColor;const s=Math.min(i.x1,i.x2),n=Math.min(i.y1,i.y2),o=Math.abs(i.x1-i.x2),r=Math.abs(i.y1-i.y2);e.strokeRect(s,n,o,r),e.fillRect(s,n,o,r),this._options.showCircles&&(this._drawEndCircle(t,s,n),this._drawEndCircle(t,s+o,n),this._drawEndCircle(t,s+o,n+r),this._drawEndCircle(t,s,n+r))}))}}class G extends v{constructor(t){super(t)}renderer(){return new L(this._p1,this._p2,""+this._source._p1.price.toFixed(1),""+this._source._p2.price.toFixed(1),this._source._options)}}const S={fillEnabled:!0,fillColor:"rgba(255, 255, 255, 0.2)",...l};class I extends E{_type="Box";constructor(t,e,i){super(t,e,i),this._options={...this._options,...S},this._paneViews=[new G(this)]}_moveToState(t){switch(t){case h.NONE:document.body.style.cursor="default",this.applyOptions({showCircles:!1}),this._unsubscribe("mousedown",this._handleMouseDownInteraction);break;case h.HOVERING:document.body.style.cursor="pointer",this.applyOptions({showCircles:!0}),this._unsubscribe("mouseup",this._handleMouseUpInteraction),this._subscribe("mousedown",this._handleMouseDownInteraction),this.chart.applyOptions({handleScroll:!0});break;case h.DRAGGINGP1:case h.DRAGGINGP2:case h.DRAGGINGP3:case h.DRAGGINGP4:case h.DRAGGING:document.body.style.cursor="grabbing",document.body.addEventListener("mouseup",this._handleMouseUpInteraction),this._subscribe("mouseup",this._handleMouseUpInteraction),this.chart.applyOptions({handleScroll:!1})}this._state=t}_onDrag(t){this._state!=h.DRAGGING&&this._state!=h.DRAGGINGP1||c._addDiffToPoint(this._p1,t.time,t.logical,t.price),this._state!=h.DRAGGING&&this._state!=h.DRAGGINGP2||c._addDiffToPoint(this._p2,t.time,t.logical,t.price),this._state!=h.DRAGGING&&(this._state==h.DRAGGINGP3&&(c._addDiffToPoint(this._p1,t.time,t.logical,0),c._addDiffToPoint(this._p2,0,0,t.price)),this._state==h.DRAGGINGP4&&(c._addDiffToPoint(this._p1,0,0,t.price),c._addDiffToPoint(this._p2,t.time,t.logical,0)))}_onMouseDown(){this._startDragPoint=null;const t=this._latestHoverPoint,e=this._paneViews[0]._p1,i=this._paneViews[0]._p2;if(!(e.x&&i.x&&e.y&&i.y))return this._moveToState(h.DRAGGING);const s=10;Math.abs(t.x-e.x)l-p&&rd-p&&athis._onClick(t);_onClick(t){t.target&&(this.div.contains(t.target)||(this.div.style.display="none",document.body.removeEventListener("click",this._handleClick)))}_onRightClick(t){c.hoveredObject&&(t.preventDefault(),this.div.style.left=t.clientX+"px",this.div.style.top=t.clientY+"px",this.div.style.display="block",document.body.addEventListener("click",this._handleClick))}menuItem(t,e,i=null){const s=document.createElement("span");s.classList.add("context-menu-item"),this.div.appendChild(s);const n=document.createElement("span");if(n.innerText=t,n.style.pointerEvents="none",s.appendChild(n),i){let t=document.createElement("span");t.innerText="►",t.style.fontSize="8px",t.style.pointerEvents="none",s.appendChild(t)}if(s.addEventListener("mouseover",(()=>{this.hoverItem&&this.hoverItem.closeAction&&this.hoverItem.closeAction(),this.hoverItem={elem:n,action:e,closeAction:i}})),i){let t;s.addEventListener("mouseover",(()=>t=setTimeout((()=>e(s.getBoundingClientRect())),100))),s.addEventListener("mouseout",(()=>clearTimeout(t)))}else s.addEventListener("click",(t=>{e(t),this.div.style.display="none"}))}separator(){const t=document.createElement("div");t.style.width="90%",t.style.height="1px",t.style.margin="3px 0px",t.style.backgroundColor=window.pane.borderColor,this.div.appendChild(t)}}class T{static _styles=[{name:"Solid",var:t.Solid},{name:"Dotted",var:t.Dotted},{name:"Dashed",var:t.Dashed},{name:"Large Dashed",var:t.LargeDashed},{name:"Sparse Dotted",var:t.SparseDotted}];_div;_saveDrawings;constructor(t){this._saveDrawings=t,this._div=document.createElement("div"),this._div.classList.add("context-menu"),T._styles.forEach((t=>{this._div.appendChild(this._makeTextBox(t.name,t.var))})),window.containerDiv.appendChild(this._div)}_makeTextBox(t,e){const i=document.createElement("span");return i.classList.add("context-menu-item"),i.innerText=t,i.addEventListener("click",(()=>{c.lastHoveredObject?.applyOptions({lineStyle:e}),this._saveDrawings()})),i}openMenu(t){this._div.style.top=t.top-30+"px",this._div.style.left=t.right+"px",this._div.style.display="block",setTimeout((()=>document.addEventListener("mousedown",(t=>{this._div.contains(t.target)||this.closeMenu()}))),10)}closeMenu(){document.removeEventListener("click",this.closeMenu),this._div.style.display="none"}}class N{static colors=["#EBB0B0","#E9CEA1","#E5DF80","#ADEB97","#A3C3EA","#D8BDED","#E15F5D","#E1B45F","#E2D947","#4BE940","#639AE1","#D7A0E8","#E42C2A","#E49D30","#E7D827","#3CFF0A","#3275E4","#B06CE3","#F3000D","#EE9A14","#F1DA13","#2DFC0F","#1562EE","#BB00EF","#B50911","#E3860E","#D2BD11","#48DE0E","#1455B4","#6E009F","#7C1713","#B76B12","#8D7A13","#479C12","#165579","#51007E"];_div;saveDrawings;opacity=0;_opacitySlider;_opacityLabel;rgba;constructor(t){this.saveDrawings=t,this._div=document.createElement("div"),this._div.classList.add("color-picker");let e=document.createElement("div");e.style.margin="10px",e.style.display="flex",e.style.flexWrap="wrap",N.colors.forEach((t=>e.appendChild(this.makeColorBox(t))));let i=document.createElement("div");i.style.backgroundColor=window.pane.borderColor,i.style.height="1px",i.style.width="130px";let s=document.createElement("div");s.style.margin="10px";let n=document.createElement("div");n.style.color="lightgray",n.style.fontSize="12px",n.innerText="Opacity",this._opacityLabel=document.createElement("div"),this._opacityLabel.style.color="lightgray",this._opacityLabel.style.fontSize="12px",this._opacitySlider=document.createElement("input"),this._opacitySlider.type="range",this._opacitySlider.value=(100*this.opacity).toString(),this._opacityLabel.innerText=this._opacitySlider.value+"%",this._opacitySlider.oninput=()=>{this._opacityLabel.innerText=this._opacitySlider.value+"%",this.opacity=parseInt(this._opacitySlider.value)/100,this.updateColor()},s.appendChild(n),s.appendChild(this._opacitySlider),s.appendChild(this._opacityLabel),this._div.appendChild(e),this._div.appendChild(i),this._div.appendChild(s),window.containerDiv.appendChild(this._div)}_updateOpacitySlider(){this._opacitySlider.value=(100*this.opacity).toString(),this._opacityLabel.innerText=this._opacitySlider.value+"%"}makeColorBox(t){const e=document.createElement("div");e.style.width="18px",e.style.height="18px",e.style.borderRadius="3px",e.style.margin="3px",e.style.boxSizing="border-box",e.style.backgroundColor=t,e.addEventListener("mouseover",(()=>e.style.border="2px solid lightgray")),e.addEventListener("mouseout",(()=>e.style.border="none"));const i=N.extractRGBA(t);return e.addEventListener("click",(()=>{this.rgba=i,this.updateColor()})),e}static extractRGBA(t){const e=document.createElement("div");e.style.color=t,document.body.appendChild(e);const i=getComputedStyle(e).color;document.body.removeChild(e);const s=i.match(/\d+/g)?.map(Number);if(!s)return[];let n=i.includes("rgba")?parseFloat(i.split(",")[3]):1;return[s[0],s[1],s[2],n]}updateColor(){if(!c.lastHoveredObject||!this.rgba)return;const t=`rgba(${this.rgba[0]}, ${this.rgba[1]}, ${this.rgba[2]}, ${this.opacity})`;c.lastHoveredObject.applyOptions({lineColor:t}),this.saveDrawings()}openMenu(t){c.lastHoveredObject&&(this.rgba=N.extractRGBA(c.lastHoveredObject._options.lineColor),this.opacity=this.rgba[3],this._updateOpacitySlider(),this._div.style.top=t.top-30+"px",this._div.style.left=t.right+"px",this._div.style.display="flex",setTimeout((()=>document.addEventListener("mousedown",(t=>{this._div.contains(t.target)||this.closeMenu()}))),10))}closeMenu(){document.body.removeEventListener("click",this.closeMenu),this._div.style.display="none"}}class R extends w{_type="RayLine";constructor(t,e){super(t,e),this._point.time=t.time}updatePoints(...t){for(const e of t)e&&(this._point=e);this.requestUpdate()}_onDrag(t){c._addDiffToPoint(this._point,t.time,t.logical,t.price),this.requestUpdate()}_mouseIsOverDrawing(t,e=4){if(!t.point)return!1;const i=this.series.priceToCoordinate(this._point.price),s=this._point.time?this.chart.timeScale().timeToCoordinate(this._point.time):null;return!(!i||!s)&&(Math.abs(i-t.point.y)s-e)}}class P{static TREND_SVG='';static HORZ_SVG='';static RAY_SVG='';static BOX_SVG='';div;activeIcon=null;buttons=[];_commandFunctions;_handlerID;_drawingTool;constructor(t,e,i,s){this._handlerID=t,this._commandFunctions=s,this._drawingTool=new C(e,i,(()=>this.removeActiveAndSave())),this.div=this._makeToolBox(),this._makeContextMenu(),s.push((t=>{if((t.metaKey||t.ctrlKey)&&"KeyZ"===t.code){const t=this._drawingTool.drawings.pop();return t&&this._drawingTool.delete(t),!0}return!1}))}toJSON(){const{...t}=this;return t}_makeToolBox(){let t=document.createElement("div");t.classList.add("toolbox"),this.buttons.push(this._makeToolBoxElement(k,"KeyT",P.TREND_SVG)),this.buttons.push(this._makeToolBoxElement(w,"KeyH",P.HORZ_SVG)),this.buttons.push(this._makeToolBoxElement(R,"KeyR",P.RAY_SVG)),this.buttons.push(this._makeToolBoxElement(I,"KeyB",P.BOX_SVG));for(const e of this.buttons)t.appendChild(e);return t}_makeToolBoxElement(t,e,i){const s=document.createElement("div");s.classList.add("toolbox-button");const n=document.createElementNS("http://www.w3.org/2000/svg","svg");n.setAttribute("width","29"),n.setAttribute("height","29");const o=document.createElementNS("http://www.w3.org/2000/svg","g");o.innerHTML=i,o.setAttribute("fill",window.pane.color),n.appendChild(o),s.appendChild(n);const r={div:s,group:o,type:t};return s.addEventListener("click",(()=>this._onIconClick(r))),this._commandFunctions.push((t=>this._handlerID===window.handlerInFocus&&(!(!t.altKey||t.code!==e)&&(t.preventDefault(),this._onIconClick(r),!0)))),s}_onIconClick(t){this.activeIcon&&(this.activeIcon.div.classList.remove("active-toolbox-button"),window.setCursor("crosshair"),this._drawingTool?.stopDrawing(),this.activeIcon===t)?this.activeIcon=null:(this.activeIcon=t,this.activeIcon.div.classList.add("active-toolbox-button"),window.setCursor("crosshair"),this._drawingTool?.beginDrawing(this.activeIcon.type))}removeActiveAndSave(){window.setCursor("default"),this.activeIcon&&this.activeIcon.div.classList.remove("active-toolbox-button"),this.activeIcon=null,this.saveDrawings()}_makeContextMenu(){const t=new M,e=new N(this.saveDrawings),i=new T(this.saveDrawings);t.menuItem("Color Picker",(t=>e.openMenu(t)),(()=>{document.removeEventListener("click",e.closeMenu),e._div.style.display="none"})),t.menuItem("Style",(t=>i.openMenu(t)),(()=>{document.removeEventListener("click",i.closeMenu),i._div.style.display="none"})),t.separator(),t.menuItem("Delete Drawing",(()=>this._drawingTool.delete(c.lastHoveredObject)))}addNewDrawing(t){this._drawingTool.addNewDrawing(t)}clearDrawings(){this._drawingTool.clearDrawings()}saveDrawings(){const t=[];for(const e of this._drawingTool.drawings)e instanceof E&&t.push({type:e._type,p1:e._p1,p2:e._p2,color:e._options.lineColor,style:e._options.lineStyle});const e=JSON.stringify(t);window.callbackFunction(`save_drawings${this._handlerID}_~_${e}`)}loadDrawings(t){t.forEach((t=>{const e={lineColor:t.color,lineStyle:t.style};switch(t.type){case"Box":this._drawingTool.addNewDrawing(new I(t.p1,t.p2,e));break;case"TrendLine":this._drawingTool.addNewDrawing(new k(t.p1,t.p2,e))}}))}}class A{_handler;_div;left;right;constructor(t){this._handler=t,this._div=document.createElement("div"),this._div.classList.add("topbar");const e=t=>{const e=document.createElement("div");return e.classList.add("topbar-container"),e.style.justifyContent=t,this._div.appendChild(e),e};this.left=e("flex-start"),this.right=e("flex-end")}makeSwitcher(t,e,i,s="left"){const n=document.createElement("div");let o;n.style.margin="4px 12px";const r={elem:n,callbackName:i,intervalElements:t.map((t=>{const i=document.createElement("button");i.classList.add("topbar-button"),i.classList.add("switcher-button"),i.style.margin="0px 2px",i.innerText=t,t==e&&(o=i,i.classList.add("active-switcher-button"));const s=A.getClientWidth(i);return i.style.minWidth=s+1+"px",i.addEventListener("click",(()=>r.onItemClicked(i))),n.appendChild(i),i})),onItemClicked:t=>{t!=o&&(o.classList.remove("active-switcher-button"),t.classList.add("active-switcher-button"),o=t,window.callbackFunction(`${r.callbackName}_~_${t.innerText}`))}};return this.appendWidget(n,s,!0),r}makeTextBoxWidget(t,e="left"){const i=document.createElement("div");return i.classList.add("topbar-textbox"),i.innerText=t,this.appendWidget(i,e,!0),i}makeMenu(t,e,i,s,n="right"){let o=document.createElement("div");o.classList.add("topbar-menu");let r=!1;t.forEach((t=>{let e=this.makeButton(t,null,!1,!1);e.elem.addEventListener("click",(()=>{a.elem.innerText=e.elem.innerText+" ↓",window.callbackFunction(`${s}_~_${e.elem.innerText}`),o.style.display="none",r=!1})),e.elem.style.margin="4px 4px",e.elem.style.padding="2px 2px",o.appendChild(e.elem)}));let a=this.makeButton(e+" ↓",null,i,!0,n);a.elem.addEventListener("click",(()=>{if(r=!r,!r)return void(o.style.display="none");let t=a.elem.getBoundingClientRect();o.style.display="flex",o.style.flexDirection="column";let e=t.x+t.width/2;o.style.left=e-o.clientWidth/2+"px",o.style.top=t.y+t.height+"px"})),document.body.appendChild(o)}makeButton(t,e,i,s=!0,n="left"){let o=document.createElement("button");o.classList.add("topbar-button"),o.innerText=t,document.body.appendChild(o),o.style.minWidth=o.clientWidth+1+"px",document.body.removeChild(o);let r={elem:o,callbackName:e};return e&&o.addEventListener("click",(()=>window.callbackFunction(`${r.callbackName}_~_${o.innerText}`))),s&&this.appendWidget(o,n,i),r}makeSeparator(t="left"){const e=document.createElement("div");e.classList.add("topbar-seperator");("left"==t?this.left:this.right).appendChild(e)}appendWidget(t,e,i){const s="left"==e?this.left:this.right;i?("left"==e&&s.appendChild(t),this.makeSeparator(e),"right"==e&&s.appendChild(t)):s.appendChild(t),this._handler.reSize()}static getClientWidth(t){document.body.appendChild(t);const e=t.clientWidth;return document.body.removeChild(t),e}}window.pane={...b},window.containerDiv=document.getElementById("container")||document.createElement("div"),window.setCursor=t=>{t&&(window.cursor=t),document.body.style.cursor=window.cursor},window.cursor="default",window.Table=y,window.HorizontalLine=w;class B{id;commandFunctions=[];wrapper;div;chart;scale;precision=2;series;volumeSeries;legend;_topBar;toolBox;spinner;_seriesList=[];constructor(t,e,i,s,n){this.reSize=this.reSize.bind(this),this.id=t,this.scale={width:e,height:i},this.wrapper=document.createElement("div"),this.wrapper.classList.add("handler"),this.wrapper.style.float=s,this.div=document.createElement("div"),this.div.style.position="relative",this.wrapper.appendChild(this.div),window.containerDiv.append(this.wrapper),this.chart=this._createChart(),this.series=this.createCandlestickSeries(),this.volumeSeries=this.createVolumeSeries(),this.legend=new x(this),document.addEventListener("keydown",(t=>{for(let e=0;ewindow.handlerInFocus=this.id)),this.reSize(),n&&window.addEventListener("resize",(()=>this.reSize()))}reSize(){let t=0!==this.scale.height&&this._topBar?._div.offsetHeight||0;this.chart.resize(window.innerWidth*this.scale.width,window.innerHeight*this.scale.height-t),this.wrapper.style.width=100*this.scale.width+"%",this.wrapper.style.height=100*this.scale.height+"%",0===this.scale.height||0===this.scale.width?(this.legend.div.style.display="none",this.toolBox&&(this.toolBox.div.style.display="none")):(this.legend.div.style.display="flex",this.toolBox&&(this.toolBox.div.style.display="flex"))}_createChart(){return s(this.div,{width:window.innerWidth*this.scale.width,height:window.innerHeight*this.scale.height,layout:{textColor:window.pane.color,background:{color:"#000000",type:n.Solid},fontSize:12},rightPriceScale:{scaleMargins:{top:.3,bottom:.25}},timeScale:{timeVisible:!0,secondsVisible:!1},crosshair:{mode:o.Normal,vertLine:{labelBackgroundColor:"rgb(46, 46, 46)"},horzLine:{labelBackgroundColor:"rgb(55, 55, 55)"}},grid:{vertLines:{color:"rgba(29, 30, 38, 5)"},horzLines:{color:"rgba(29, 30, 58, 5)"}},handleScroll:{vertTouchDrag:!0}})}createCandlestickSeries(){const t="rgba(39, 157, 130, 100)",e="rgba(200, 97, 100, 100)",i=this.chart.addCandlestickSeries({upColor:t,borderUpColor:t,wickUpColor:t,downColor:e,borderDownColor:e,wickDownColor:e});return i.priceScale().applyOptions({scaleMargins:{top:.2,bottom:.2}}),i}createVolumeSeries(){const t=this.chart.addHistogramSeries({color:"#26a69a",priceFormat:{type:"volume"},priceScaleId:"volume_scale"});return t.priceScale().applyOptions({scaleMargins:{top:.8,bottom:0}}),t}createLineSeries(t,e){const i=this.chart.addLineSeries({...e});return this._seriesList.push(i),this.legend.makeSeriesRow(t,i),{name:t,series:i}}createToolBox(){this.toolBox=new P(this.id,this.chart,this.series,this.commandFunctions),this.div.appendChild(this.toolBox.div)}createTopBar(){return this._topBar=new A(this),this.wrapper.prepend(this._topBar._div),this._topBar}toJSON(){const{chart:t,...e}=this;return e}static syncCharts(t,e,i=!1){function s(t,e){e?(t.chart.setCrosshairPosition(e.value||e.close,e.time,t.series),t.legend.legendHandler(e,!0)):t.chart.clearCrosshairPosition()}function n(t,e){return e.time&&e.seriesData.get(t)||null}const o=e=>{e&&t.chart.timeScale().setVisibleLogicalRange(e)},r=t=>{t&&e.chart.timeScale().setVisibleLogicalRange(t)},a=i=>{s(e,n(t.series,i))},l=i=>{s(t,n(e.series,i))};let d=e;function h(t,e,s,n,o,r){t.wrapper.addEventListener("mouseover",(()=>{d!==t&&(d=t,e.chart.unsubscribeCrosshairMove(s),t.chart.subscribeCrosshairMove(n),i||(e.chart.timeScale().unsubscribeVisibleLogicalRangeChange(o),t.chart.timeScale().subscribeVisibleLogicalRangeChange(r)))}))}h(e,t,a,l,r,o),h(t,e,l,a,o,r),e.chart.subscribeCrosshairMove(l),i||e.chart.timeScale().subscribeVisibleLogicalRangeChange(o)}static makeSearchBox(t){const e=document.createElement("div");e.classList.add("searchbox"),e.style.display="none";const i=document.createElement("div");i.innerHTML='';const s=document.createElement("input");return s.type="text",e.appendChild(i),e.appendChild(s),t.div.appendChild(e),t.commandFunctions.push((i=>(console.log("1"),window.handlerInFocus===t.id&&(console.log(e.style),"none"===e.style.display?(console.log("3"),!!/^[a-zA-Z0-9]$/.test(i.key)&&(console.log("4"),e.style.display="flex",s.focus(),!0)):("Enter"===i.key||"Escape"===i.key)&&("Enter"===i.key&&window.callbackFunction(`search${t.id}_~_${s.value}`),e.style.display="none",s.value="",!0))))),s.addEventListener("input",(()=>s.value=s.value.toUpperCase())),{window:e,box:s}}static makeSpinner(t){t.spinner=document.createElement("div"),t.spinner.classList.add("spinner"),t.wrapper.appendChild(t.spinner);let e=0;!function i(){t.spinner&&(e+=10,t.spinner.style.transform=`translate(-50%, -50%) rotate(${e}deg)`,requestAnimationFrame(i))}()}static _styleMap={"--bg-color":"backgroundColor","--hover-bg-color":"hoverBackgroundColor","--click-bg-color":"clickBackgroundColor","--active-bg-color":"activeBackgroundColor","--muted-bg-color":"mutedBackgroundColor","--border-color":"borderColor","--color":"color","--active-color":"activeColor"};static setRootStyles(t){const e=document.documentElement.style;for(const[i,s]of Object.entries(this._styleMap))e.setProperty(i,t[s])}}window.Handler=B;export{B as Handler}; +import{LineStyle as t,createChart as e,ColorType as i,CrosshairMode as s}from"lightweight-charts";function o(t){if(void 0===t)throw new Error("Value is undefined");return t}class n{_chart=void 0;_series=void 0;requestUpdate(){this._requestUpdate&&this._requestUpdate()}_requestUpdate;attached({chart:t,series:e,requestUpdate:i}){this._chart=t,this._series=e,this._series.subscribeDataChanged(this._fireDataUpdated),this._requestUpdate=i,this.requestUpdate()}detached(){this._chart=void 0,this._series=void 0,this._requestUpdate=void 0}get chart(){return o(this._chart)}get series(){return o(this._series)}_fireDataUpdated(t){this.dataUpdated&&this.dataUpdated(t)}}const r={lineColor:"#1E80F0",lineStyle:t.Solid,width:4};var a;!function(t){t[t.NONE=0]="NONE",t[t.HOVERING=1]="HOVERING",t[t.DRAGGING=2]="DRAGGING",t[t.DRAGGINGP1=3]="DRAGGINGP1",t[t.DRAGGINGP2=4]="DRAGGINGP2",t[t.DRAGGINGP3=5]="DRAGGINGP3",t[t.DRAGGINGP4=6]="DRAGGINGP4"}(a||(a={}));class l extends n{_paneViews=[];_options;_points=[];_state=a.NONE;_startDragPoint=null;_latestHoverPoint=null;static _mouseIsDown=!1;static hoveredObject=null;static lastHoveredObject=null;_listeners=[];constructor(t){super(),this._options={...r,...t}}updateAllViews(){this._paneViews.forEach((t=>t.update()))}paneViews(){return this._paneViews}applyOptions(t){this._options={...this._options,...t},this.requestUpdate()}updatePoints(...t){for(let e=0;ei.name===t&&i.listener===e));this._listeners.splice(this._listeners.indexOf(i),1)}_handleHoverInteraction(t){if(this._latestHoverPoint=t.point,l._mouseIsDown)this._handleDragInteraction(t);else if(this._mouseIsOverDrawing(t)){if(this._state!=a.NONE)return;this._moveToState(a.HOVERING),l.hoveredObject=l.lastHoveredObject=this}else{if(this._state==a.NONE)return;this._moveToState(a.NONE),l.hoveredObject===this&&(l.hoveredObject=null)}}static _eventToPoint(t,e){if(!e||!t.point||!t.logical)return null;const i=e.coordinateToPrice(t.point.y);return null==i?null:{time:t.time||null,logical:t.logical,price:i.valueOf()}}static _getDiff(t,e){return{logical:t.logical-e.logical,price:t.price-e.price}}_addDiffToPoint(t,e,i){t&&(t.logical=t.logical+e,t.price=t.price+i,t.time=this.series.dataByIndex(t.logical)?.time||null)}_handleMouseDownInteraction=()=>{l._mouseIsDown=!0,this._onMouseDown()};_handleMouseUpInteraction=()=>{l._mouseIsDown=!1,this._moveToState(a.HOVERING)};_handleDragInteraction(t){if(this._state!=a.DRAGGING&&this._state!=a.DRAGGINGP1&&this._state!=a.DRAGGINGP2&&this._state!=a.DRAGGINGP3&&this._state!=a.DRAGGINGP4)return;const e=l._eventToPoint(t,this.series);if(!e)return;this._startDragPoint=this._startDragPoint||e;const i=l._getDiff(e,this._startDragPoint);this._onDrag(i),this.requestUpdate(),this._startDragPoint=e}}class d{_options;constructor(t){this._options=t}}class h extends d{_p1;_p2;_hovered;constructor(t,e,i,s){super(i),this._p1=t,this._p2=e,this._hovered=s}_getScaledCoordinates(t){return null===this._p1.x||null===this._p1.y||null===this._p2.x||null===this._p2.y?null:{x1:Math.round(this._p1.x*t.horizontalPixelRatio),y1:Math.round(this._p1.y*t.verticalPixelRatio),x2:Math.round(this._p2.x*t.horizontalPixelRatio),y2:Math.round(this._p2.y*t.verticalPixelRatio)}}_drawEndCircle(t,e,i){t.context.fillStyle="#000",t.context.beginPath(),t.context.arc(e,i,9,0,2*Math.PI),t.context.stroke(),t.context.fill()}}function c(e,i){const s={[t.Solid]:[],[t.Dotted]:[e.lineWidth,e.lineWidth],[t.Dashed]:[2*e.lineWidth,2*e.lineWidth],[t.LargeDashed]:[6*e.lineWidth,6*e.lineWidth],[t.SparseDotted]:[e.lineWidth,4*e.lineWidth]}[i];e.setLineDash(s)}class p extends h{constructor(t,e,i,s){super(t,e,i,s)}draw(t){t.useBitmapCoordinateSpace((t=>{const e=t.context,i=this._getScaledCoordinates(t);if(!i)return;e.lineWidth=this._options.width,e.strokeStyle=this._options.lineColor,c(e,this._options.lineStyle),e.fillStyle=this._options.fillColor;const s=Math.min(i.x1,i.x2),o=Math.min(i.y1,i.y2),n=Math.abs(i.x1-i.x2),r=Math.abs(i.y1-i.y2);e.strokeRect(s,o,n,r),e.fillRect(s,o,n,r),this._hovered&&(this._drawEndCircle(t,s,o),this._drawEndCircle(t,s+n,o),this._drawEndCircle(t,s+n,o+r),this._drawEndCircle(t,s,o+r))}))}}class u{_source;constructor(t){this._source=t}}class _ extends u{_p1={x:null,y:null};_p2={x:null,y:null};_source;constructor(t){super(t),this._source=t}update(){if(!this._source.p1||!this._source.p2)return;const t=this._source.series,e=t.priceToCoordinate(this._source.p1.price),i=t.priceToCoordinate(this._source.p2.price),s=this._getX(this._source.p1),o=this._getX(this._source.p2);this._p1={x:s,y:e},this._p2={x:o,y:i}}_getX(t){return this._source.chart.timeScale().logicalToCoordinate(t.logical)}}class m extends _{constructor(t){super(t)}renderer(){return new p(this._p1,this._p2,this._source._options,this._source.hovered)}}class v extends l{_paneViews=[];_hovered=!1;constructor(t,e,i){super(),this.points.push(t),this.points.push(e),this._options={...r,...i}}setFirstPoint(t){this.updatePoints(t)}setSecondPoint(t){this.updatePoints(null,t)}get p1(){return this.points[0]}get p2(){return this.points[1]}get hovered(){return this._hovered}}const g={fillEnabled:!0,fillColor:"rgba(255, 255, 255, 0.2)",...r};class w extends v{_type="Box";constructor(t,e,i){super(t,e,i),this._options={...this._options,...g},this._paneViews=[new m(this)]}_moveToState(t){switch(t){case a.NONE:document.body.style.cursor="default",this._hovered=!1,this._unsubscribe("mousedown",this._handleMouseDownInteraction);break;case a.HOVERING:document.body.style.cursor="pointer",this._hovered=!0,this._unsubscribe("mouseup",this._handleMouseUpInteraction),this._subscribe("mousedown",this._handleMouseDownInteraction),this.chart.applyOptions({handleScroll:!0});break;case a.DRAGGINGP1:case a.DRAGGINGP2:case a.DRAGGINGP3:case a.DRAGGINGP4:case a.DRAGGING:document.body.style.cursor="grabbing",document.body.addEventListener("mouseup",this._handleMouseUpInteraction),this._subscribe("mouseup",this._handleMouseUpInteraction),this.chart.applyOptions({handleScroll:!1})}this._state=t}_onDrag(t){this._state!=a.DRAGGING&&this._state!=a.DRAGGINGP1||this._addDiffToPoint(this.p1,t.logical,t.price),this._state!=a.DRAGGING&&this._state!=a.DRAGGINGP2||this._addDiffToPoint(this.p2,t.logical,t.price),this._state!=a.DRAGGING&&(this._state==a.DRAGGINGP3&&(this._addDiffToPoint(this.p1,t.logical,0),this._addDiffToPoint(this.p2,0,t.price)),this._state==a.DRAGGINGP4&&(this._addDiffToPoint(this.p1,0,t.price),this._addDiffToPoint(this.p2,t.logical,0)))}_onMouseDown(){this._startDragPoint=null;const t=this._latestHoverPoint,e=this._paneViews[0]._p1,i=this._paneViews[0]._p2;if(!(e.x&&i.x&&e.y&&i.y))return this._moveToState(a.DRAGGING);const s=10;Math.abs(t.x-e.x)l-p&&rd-p&&a{if(null==this._point.y)return;const e=t.context,i=Math.round(this._point.y*t.verticalPixelRatio),s=this._point.x?this._point.x*t.horizontalPixelRatio:0;e.lineWidth=this._options.width,e.strokeStyle=this._options.lineColor,c(e,this._options.lineStyle),e.beginPath(),e.moveTo(s,i),e.lineTo(t.bitmapSize.width,i),e.stroke()}))}}class b extends u{_source;_point={x:null,y:null};constructor(t){super(t),this._source=t}update(){const t=this._source._point,e=this._source.chart.timeScale(),i=this._source.series;"RayLine"==this._source._type&&(this._point.x=e.logicalToCoordinate(t.logical)),this._point.y=i.priceToCoordinate(t.price)}renderer(){return new y(this._point,this._source._options)}}class x extends l{_type="HorizontalLine";_paneViews;_point;_callbackName;_startDragPoint=null;constructor(t,e,i=null){super(e),this._point=t,this._point.time=null,this._paneViews=[new b(this)],this._callbackName=i}updatePoints(...t){for(const e of t)e&&(this._point.price=e.price);this.requestUpdate()}_moveToState(t){switch(t){case a.NONE:document.body.style.cursor="default",this._unsubscribe("mousedown",this._handleMouseDownInteraction);break;case a.HOVERING:document.body.style.cursor="pointer",this._unsubscribe("mouseup",this._childHandleMouseUpInteraction),this._subscribe("mousedown",this._handleMouseDownInteraction),this.chart.applyOptions({handleScroll:!0});break;case a.DRAGGING:document.body.style.cursor="grabbing",this._subscribe("mouseup",this._childHandleMouseUpInteraction),this.chart.applyOptions({handleScroll:!1})}this._state=t}_onDrag(t){this._addDiffToPoint(this._point,0,t.price),this.requestUpdate()}_mouseIsOverDrawing(t,e=4){if(!t.point)return!1;const i=this.series.priceToCoordinate(this._point.price);return!!i&&Math.abs(i-t.point.y){this._handleMouseUpInteraction(),this._callbackName&&(console.log(window.callbackFunction),window.callbackFunction(`${this._callbackName}_~_${this._point.price.toFixed(8)}`))}}class C extends x{_type="RayLine";constructor(t,e){super(t,e),this._point.time=t.time}updatePoints(...t){for(const e of t)e&&(this._point=e);this.requestUpdate()}_onDrag(t){this._addDiffToPoint(this._point,t.logical,t.price),this.requestUpdate()}_mouseIsOverDrawing(t,e=4){if(!t.point)return!1;const i=this.series.priceToCoordinate(this._point.price),s=this._point.time?this.chart.timeScale().timeToCoordinate(this._point.time):null;return!(!i||!s)&&(Math.abs(i-t.point.y)s-e)}}class f extends h{constructor(t,e,i,s){super(t,e,i,s)}draw(t){t.useBitmapCoordinateSpace((t=>{if(null===this._p1.x||null===this._p1.y||null===this._p2.x||null===this._p2.y)return;const e=t.context,i=this._getScaledCoordinates(t);i&&(e.lineWidth=this._options.width,e.strokeStyle=this._options.lineColor,c(e,this._options.lineStyle),e.beginPath(),e.moveTo(i.x1,i.y1),e.lineTo(i.x2,i.y2),e.stroke(),this._hovered&&(this._drawEndCircle(t,i.x1,i.y1),this._drawEndCircle(t,i.x2,i.y2)))}))}}class D extends _{constructor(t){super(t)}renderer(){return new f(this._p1,this._p2,this._source._options,this._source.hovered)}}class E extends v{_type="TrendLine";constructor(t,e,i){super(t,e,i),this._paneViews=[new D(this)]}_moveToState(t){switch(t){case a.NONE:document.body.style.cursor="default",this._hovered=!1,this.requestUpdate(),this._unsubscribe("mousedown",this._handleMouseDownInteraction);break;case a.HOVERING:document.body.style.cursor="pointer",this._hovered=!0,this.requestUpdate(),this._subscribe("mousedown",this._handleMouseDownInteraction),this._unsubscribe("mouseup",this._handleMouseDownInteraction),this.chart.applyOptions({handleScroll:!0});break;case a.DRAGGINGP1:case a.DRAGGINGP2:case a.DRAGGING:document.body.style.cursor="grabbing",this._subscribe("mouseup",this._handleMouseUpInteraction),this.chart.applyOptions({handleScroll:!1})}this._state=t}_onDrag(t){this._state!=a.DRAGGING&&this._state!=a.DRAGGINGP1||this._addDiffToPoint(this.p1,t.logical,t.price),this._state!=a.DRAGGING&&this._state!=a.DRAGGINGP2||this._addDiffToPoint(this.p2,t.logical,t.price)}_onMouseDown(){this._startDragPoint=null;const t=this._latestHoverPoint;if(!t)return;const e=this._paneViews[0]._p1,i=this._paneViews[0]._p2;if(!(e.x&&i.x&&e.y&&i.y))return this._moveToState(a.DRAGGING);Math.abs(t.x-e.x)<10&&Math.abs(t.y-e.y)<10?this._moveToState(a.DRAGGINGP1):Math.abs(t.x-i.x)<10&&Math.abs(t.y-i.y)<10?this._moveToState(a.DRAGGINGP2):this._moveToState(a.DRAGGING)}_mouseIsOverDrawing(t,e=4){if(!t.point)return!1;const i=this._paneViews[0]._p1.x,s=this._paneViews[0]._p1.y,o=this._paneViews[0]._p2.x,n=this._paneViews[0]._p2.y;if(!(i&&o&&s&&n))return!1;const r=t.point.x,a=t.point.y;if(r<=Math.min(i,o)-e||r>=Math.max(i,o)+e)return!1;return Math.abs((n-s)*r-(o-i)*a+o*s-n*i)/Math.sqrt((n-s)**2+(o-i)**2)<=e}}class k extends d{_point={x:null,y:null};constructor(t,e){super(e),this._point=t}draw(t){t.useBitmapCoordinateSpace((t=>{if(null==this._point.x)return;const e=t.context,i=this._point.x*t.horizontalPixelRatio;e.lineWidth=this._options.width,e.strokeStyle=this._options.lineColor,c(e,this._options.lineStyle),e.beginPath(),e.moveTo(i,0),e.lineTo(i,t.bitmapSize.height),e.stroke()}))}}class L extends u{_source;_point={x:null,y:null};constructor(t){super(t),this._source=t}update(){const t=this._source._point,e=this._source.chart.timeScale(),i=this._source.series;this._point.x=e.logicalToCoordinate(t.logical),this._point.y=i.priceToCoordinate(t.price)}renderer(){return new k(this._point,this._source._options)}}class G extends l{_type="VerticalLine";_paneViews;_point;_callbackName;_startDragPoint=null;constructor(t,e,i=null){super(e),this._point=t,this._paneViews=[new L(this)],this._callbackName=i}updatePoints(...t){for(const e of t)e&&(this._point=e);this.requestUpdate()}_moveToState(t){switch(t){case a.NONE:document.body.style.cursor="default",this._unsubscribe("mousedown",this._handleMouseDownInteraction);break;case a.HOVERING:document.body.style.cursor="pointer",this._unsubscribe("mouseup",this._childHandleMouseUpInteraction),this._subscribe("mousedown",this._handleMouseDownInteraction),this.chart.applyOptions({handleScroll:!0});break;case a.DRAGGING:document.body.style.cursor="grabbing",this._subscribe("mouseup",this._childHandleMouseUpInteraction),this.chart.applyOptions({handleScroll:!1})}this._state=t}_onDrag(t){this._addDiffToPoint(this._point,t.logical,0),this.requestUpdate()}_mouseIsOverDrawing(t,e=4){if(!t.point)return!1;const i=this.chart.timeScale();let s;return s=this._point.time?i.timeToCoordinate(this._point.time):i.logicalToCoordinate(this._point.logical),!!s&&Math.abs(s-t.point.x){this._handleMouseUpInteraction(),this._callbackName&&(console.log(window.callbackFunction),window.callbackFunction(`${this._callbackName}_~_${this._point.price.toFixed(8)}`))}}class S{_div;callbackName;borderColor;borderWidth;table;rows={};headings;widths;alignments;footer;header;constructor(t,e,i,s,o,n,r=!1,a,l,d,h,c){this._div=document.createElement("div"),this.callbackName=null,this.borderColor=l,this.borderWidth=d,r?(this._div.style.position="absolute",this._div.style.cursor="move"):(this._div.style.position="relative",this._div.style.float=n),this._div.style.zIndex="2000",this.reSize(t,e),this._div.style.display="flex",this._div.style.flexDirection="column",this._div.style.borderRadius="5px",this._div.style.color="white",this._div.style.fontSize="12px",this._div.style.fontVariantNumeric="tabular-nums",this.table=document.createElement("table"),this.table.style.width="100%",this.table.style.borderCollapse="collapse",this._div.style.overflow="hidden",this.headings=i,this.widths=s.map((t=>100*t+"%")),this.alignments=o;let p=this.table.createTHead().insertRow();for(let t=0;t0?c[t]:a,e.style.color=h[t],p.appendChild(e)}let u,_,m=document.createElement("div");if(m.style.overflowY="auto",m.style.overflowX="hidden",m.style.backgroundColor=a,m.appendChild(this.table),this._div.appendChild(m),window.containerDiv.appendChild(this._div),!r)return;let v=t=>{this._div.style.left=t.clientX-u+"px",this._div.style.top=t.clientY-_+"px"},g=()=>{document.removeEventListener("mousemove",v),document.removeEventListener("mouseup",g)};this._div.addEventListener("mousedown",(t=>{u=t.clientX-this._div.offsetLeft,_=t.clientY-this._div.offsetTop,document.addEventListener("mousemove",v),document.addEventListener("mouseup",g)}))}divToButton(t,e){t.addEventListener("mouseover",(()=>t.style.backgroundColor="rgba(60, 60, 60, 0.6)")),t.addEventListener("mouseout",(()=>t.style.backgroundColor="transparent")),t.addEventListener("mousedown",(()=>t.style.backgroundColor="rgba(60, 60, 60)")),t.addEventListener("click",(()=>window.callbackFunction(e))),t.addEventListener("mouseup",(()=>t.style.backgroundColor="rgba(60, 60, 60, 0.6)"))}newRow(t,e=!1){let i=this.table.insertRow();i.style.cursor="default";for(let s=0;s\n \`\n `,o=`\n \n `,n=document.createElement("div");n.style.display="flex",n.style.alignItems="center";let r=document.createElement("div"),a=document.createElement("div");a.classList.add("legend-toggle-switch");let l=document.createElementNS("http://www.w3.org/2000/svg","svg");l.setAttribute("width","22"),l.setAttribute("height","16");let d=document.createElementNS("http://www.w3.org/2000/svg","g");d.innerHTML=s;let h=!0;a.addEventListener("click",(()=>{h?(h=!1,d.innerHTML=o,e.applyOptions({visible:!1})):(h=!0,e.applyOptions({visible:!0}),d.innerHTML=s)})),l.appendChild(d),a.appendChild(l),n.appendChild(r),n.appendChild(a),this.div.appendChild(n);const c=e.options().baseLineColor;this._lines.push({name:t,div:r,row:n,toggle:a,series:e,solid:c.startsWith("rgba")?c.replace(/[^,]+(?=\))/,"1"):c})}legendItemFormat(t,e){return t.toFixed(e).toString().padStart(8," ")}shorthandFormat(t){const e=Math.abs(t);return e>=1e6?(t/1e6).toFixed(1)+"M":e>=1e3?(t/1e3).toFixed(1)+"K":t.toString().padStart(8," ")}legendHandler(t,e=!1){if(!this.ohlcEnabled&&!this.linesEnabled&&!this.percentEnabled)return;const i=this.handler.series.options();if(!t.time)return this.candle.style.color="transparent",void(this.candle.innerHTML=this.candle.innerHTML.replace(i.upColor,"").replace(i.downColor,""));let s,o=null;if(e){const e=this.handler.chart.timeScale();let i=e.timeToCoordinate(t.time);i&&(o=e.coordinateToLogical(i.valueOf())),o&&(s=this.handler.series.dataByIndex(o.valueOf()))}else s=t.seriesData.get(this.handler.series);this.candle.style.color="";let n='';if(s){if(this.ohlcEnabled&&(n+=`O ${this.legendItemFormat(s.open,this.handler.precision)} `,n+=`| H ${this.legendItemFormat(s.high,this.handler.precision)} `,n+=`| L ${this.legendItemFormat(s.low,this.handler.precision)} `,n+=`| C ${this.legendItemFormat(s.close,this.handler.precision)} `),this.percentEnabled){let t=(s.close-s.open)/s.open*100,e=t>0?i.upColor:i.downColor,o=`${t>=0?"+":""}${t.toFixed(2)} %`;this.colorBasedOnCandle?n+=`| ${o}`:n+="| "+o}if(this.handler.volumeSeries){let e;e=o?this.handler.volumeSeries.dataByIndex(o):t.seriesData.get(this.handler.volumeSeries),e&&(n+=this.ohlcEnabled?`
V ${this.shorthandFormat(e.value)}`:"")}}this.candle.innerHTML=n+"
",this._lines.forEach((i=>{if(!this.linesEnabled)return void(i.row.style.display="none");let s,n;if(i.row.style.display="flex",s=e&&o?i.series.dataByIndex(o):t.seriesData.get(i.series),"Histogram"==i.series.seriesType())n=this.shorthandFormat(s.value);else{const t=i.series.options().priceFormat;n=this.legendItemFormat(s.value,t.precision)}i.div.innerHTML=` ${i.name} : ${n}`}))}}class T{_chart;_series;_finishDrawingCallback=null;_drawings=[];_activeDrawing=null;_isDrawing=!1;_drawingType=null;constructor(t,e,i=null){this._chart=t,this._series=e,this._finishDrawingCallback=i,this._chart.subscribeClick(this._clickHandler),this._chart.subscribeCrosshairMove(this._moveHandler)}_clickHandler=t=>this._onClick(t);_moveHandler=t=>this._onMouseMove(t);beginDrawing(t){this._drawingType=t,this._isDrawing=!0}stopDrawing(){this._isDrawing=!1,this._activeDrawing=null}get drawings(){return this._drawings}addNewDrawing(t){this._series.attachPrimitive(t),this._drawings.push(t)}delete(t){if(null==t)return;const e=this._drawings.indexOf(t);-1!=e&&(this._drawings.splice(e,1),t.detach())}clearDrawings(){for(const t of this._drawings)t.detach();this._drawings=[]}_onClick(t){if(!this._isDrawing)return;const e=l._eventToPoint(t,this._series);if(e)if(null==this._activeDrawing){if(null==this._drawingType)return;this._activeDrawing=new this._drawingType(e,e),this._series.attachPrimitive(this._activeDrawing),this._drawingType==x&&this._onClick(t)}else{if(this._drawings.push(this._activeDrawing),this.stopDrawing(),!this._finishDrawingCallback)return;this._finishDrawingCallback()}}_onMouseMove(t){if(!t)return;for(const e of this._drawings)e._handleHoverInteraction(t);if(!this._isDrawing||!this._activeDrawing)return;const e=l._eventToPoint(t,this._series);e&&this._activeDrawing.updatePoints(null,e)}}class N{div;hoverItem;constructor(){this._onRightClick=this._onRightClick.bind(this),this.div=document.createElement("div"),this.div.classList.add("context-menu"),document.body.appendChild(this.div),this.hoverItem=null,document.body.addEventListener("contextmenu",this._onRightClick)}_handleClick=t=>this._onClick(t);_onClick(t){t.target&&(this.div.contains(t.target)||(this.div.style.display="none",document.body.removeEventListener("click",this._handleClick)))}_onRightClick(t){l.hoveredObject&&(t.preventDefault(),this.div.style.left=t.clientX+"px",this.div.style.top=t.clientY+"px",this.div.style.display="block",document.body.addEventListener("click",this._handleClick))}menuItem(t,e,i=null){const s=document.createElement("span");s.classList.add("context-menu-item"),this.div.appendChild(s);const o=document.createElement("span");if(o.innerText=t,o.style.pointerEvents="none",s.appendChild(o),i){let t=document.createElement("span");t.innerText="►",t.style.fontSize="8px",t.style.pointerEvents="none",s.appendChild(t)}if(s.addEventListener("mouseover",(()=>{this.hoverItem&&this.hoverItem.closeAction&&this.hoverItem.closeAction(),this.hoverItem={elem:o,action:e,closeAction:i}})),i){let t;s.addEventListener("mouseover",(()=>t=setTimeout((()=>e(s.getBoundingClientRect())),100))),s.addEventListener("mouseout",(()=>clearTimeout(t)))}else s.addEventListener("click",(t=>{e(t),this.div.style.display="none"}))}separator(){const t=document.createElement("div");t.style.width="90%",t.style.height="1px",t.style.margin="3px 0px",t.style.backgroundColor=window.pane.borderColor,this.div.appendChild(t)}}class R{static _styles=[{name:"Solid",var:t.Solid},{name:"Dotted",var:t.Dotted},{name:"Dashed",var:t.Dashed},{name:"Large Dashed",var:t.LargeDashed},{name:"Sparse Dotted",var:t.SparseDotted}];_div;_saveDrawings;constructor(t){this._saveDrawings=t,this._div=document.createElement("div"),this._div.classList.add("context-menu"),R._styles.forEach((t=>{this._div.appendChild(this._makeTextBox(t.name,t.var))})),window.containerDiv.appendChild(this._div)}_makeTextBox(t,e){const i=document.createElement("span");return i.classList.add("context-menu-item"),i.innerText=t,i.addEventListener("click",(()=>{l.lastHoveredObject?.applyOptions({lineStyle:e}),this._saveDrawings()})),i}openMenu(t){this._div.style.top=t.top-30+"px",this._div.style.left=t.right+"px",this._div.style.display="block",setTimeout((()=>document.addEventListener("mousedown",(t=>{this._div.contains(t.target)||this.closeMenu()}))),10)}closeMenu(){document.removeEventListener("click",this.closeMenu),this._div.style.display="none"}}class P{static colors=["#EBB0B0","#E9CEA1","#E5DF80","#ADEB97","#A3C3EA","#D8BDED","#E15F5D","#E1B45F","#E2D947","#4BE940","#639AE1","#D7A0E8","#E42C2A","#E49D30","#E7D827","#3CFF0A","#3275E4","#B06CE3","#F3000D","#EE9A14","#F1DA13","#2DFC0F","#1562EE","#BB00EF","#B50911","#E3860E","#D2BD11","#48DE0E","#1455B4","#6E009F","#7C1713","#B76B12","#8D7A13","#479C12","#165579","#51007E"];_div;saveDrawings;opacity=0;_opacitySlider;_opacityLabel;rgba;constructor(t){this.saveDrawings=t,this._div=document.createElement("div"),this._div.classList.add("color-picker");let e=document.createElement("div");e.style.margin="10px",e.style.display="flex",e.style.flexWrap="wrap",P.colors.forEach((t=>e.appendChild(this.makeColorBox(t))));let i=document.createElement("div");i.style.backgroundColor=window.pane.borderColor,i.style.height="1px",i.style.width="130px";let s=document.createElement("div");s.style.margin="10px";let o=document.createElement("div");o.style.color="lightgray",o.style.fontSize="12px",o.innerText="Opacity",this._opacityLabel=document.createElement("div"),this._opacityLabel.style.color="lightgray",this._opacityLabel.style.fontSize="12px",this._opacitySlider=document.createElement("input"),this._opacitySlider.type="range",this._opacitySlider.value=(100*this.opacity).toString(),this._opacityLabel.innerText=this._opacitySlider.value+"%",this._opacitySlider.oninput=()=>{this._opacityLabel.innerText=this._opacitySlider.value+"%",this.opacity=parseInt(this._opacitySlider.value)/100,this.updateColor()},s.appendChild(o),s.appendChild(this._opacitySlider),s.appendChild(this._opacityLabel),this._div.appendChild(e),this._div.appendChild(i),this._div.appendChild(s),window.containerDiv.appendChild(this._div)}_updateOpacitySlider(){this._opacitySlider.value=(100*this.opacity).toString(),this._opacityLabel.innerText=this._opacitySlider.value+"%"}makeColorBox(t){const e=document.createElement("div");e.style.width="18px",e.style.height="18px",e.style.borderRadius="3px",e.style.margin="3px",e.style.boxSizing="border-box",e.style.backgroundColor=t,e.addEventListener("mouseover",(()=>e.style.border="2px solid lightgray")),e.addEventListener("mouseout",(()=>e.style.border="none"));const i=P.extractRGBA(t);return e.addEventListener("click",(()=>{this.rgba=i,this.updateColor()})),e}static extractRGBA(t){const e=document.createElement("div");e.style.color=t,document.body.appendChild(e);const i=getComputedStyle(e).color;document.body.removeChild(e);const s=i.match(/\d+/g)?.map(Number);if(!s)return[];let o=i.includes("rgba")?parseFloat(i.split(",")[3]):1;return[s[0],s[1],s[2],o]}updateColor(){if(!l.lastHoveredObject||!this.rgba)return;const t=`rgba(${this.rgba[0]}, ${this.rgba[1]}, ${this.rgba[2]}, ${this.opacity})`;l.lastHoveredObject.applyOptions({lineColor:t}),this.saveDrawings()}openMenu(t){l.lastHoveredObject&&(this.rgba=P.extractRGBA(l.lastHoveredObject._options.lineColor),this.opacity=this.rgba[3],this._updateOpacitySlider(),this._div.style.top=t.top-30+"px",this._div.style.left=t.right+"px",this._div.style.display="flex",setTimeout((()=>document.addEventListener("mousedown",(t=>{this._div.contains(t.target)||this.closeMenu()}))),10))}closeMenu(){document.body.removeEventListener("click",this.closeMenu),this._div.style.display="none"}}class B{static TREND_SVG='';static HORZ_SVG='';static RAY_SVG='';static BOX_SVG='';div;activeIcon=null;buttons=[];_commandFunctions;_handlerID;_drawingTool;constructor(t,e,i,s){this._handlerID=t,this._commandFunctions=s,this._drawingTool=new T(e,i,(()=>this.removeActiveAndSave())),this.div=this._makeToolBox(),this._makeContextMenu(),s.push((t=>{if((t.metaKey||t.ctrlKey)&&"KeyZ"===t.code){const t=this._drawingTool.drawings.pop();return t&&this._drawingTool.delete(t),!0}return!1}))}toJSON(){const{...t}=this;return t}_makeToolBox(){let t=document.createElement("div");t.classList.add("toolbox"),this.buttons.push(this._makeToolBoxElement(E,"KeyT",B.TREND_SVG)),this.buttons.push(this._makeToolBoxElement(x,"KeyH",B.HORZ_SVG)),this.buttons.push(this._makeToolBoxElement(C,"KeyR",B.RAY_SVG)),this.buttons.push(this._makeToolBoxElement(w,"KeyB",B.BOX_SVG)),this.buttons.push(this._makeToolBoxElement(G,"KeyV",B.RAY_SVG));for(const e of this.buttons)t.appendChild(e);return t}_makeToolBoxElement(t,e,i){const s=document.createElement("div");s.classList.add("toolbox-button");const o=document.createElementNS("http://www.w3.org/2000/svg","svg");o.setAttribute("width","29"),o.setAttribute("height","29");const n=document.createElementNS("http://www.w3.org/2000/svg","g");n.innerHTML=i,n.setAttribute("fill",window.pane.color),o.appendChild(n),s.appendChild(o);const r={div:s,group:n,type:t};return s.addEventListener("click",(()=>this._onIconClick(r))),this._commandFunctions.push((t=>this._handlerID===window.handlerInFocus&&(!(!t.altKey||t.code!==e)&&(t.preventDefault(),this._onIconClick(r),!0)))),s}_onIconClick(t){this.activeIcon&&(this.activeIcon.div.classList.remove("active-toolbox-button"),window.setCursor("crosshair"),this._drawingTool?.stopDrawing(),this.activeIcon===t)?this.activeIcon=null:(this.activeIcon=t,this.activeIcon.div.classList.add("active-toolbox-button"),window.setCursor("crosshair"),this._drawingTool?.beginDrawing(this.activeIcon.type))}removeActiveAndSave=()=>{window.setCursor("default"),this.activeIcon&&this.activeIcon.div.classList.remove("active-toolbox-button"),this.activeIcon=null,this.saveDrawings()};_makeContextMenu(){const t=new N,e=new P(this.saveDrawings),i=new R(this.saveDrawings);t.menuItem("Color Picker",(t=>e.openMenu(t)),(()=>{document.removeEventListener("click",e.closeMenu),e._div.style.display="none"})),t.menuItem("Style",(t=>i.openMenu(t)),(()=>{document.removeEventListener("click",i.closeMenu),i._div.style.display="none"})),t.separator(),t.menuItem("Delete Drawing",(()=>this._drawingTool.delete(l.lastHoveredObject)))}addNewDrawing(t){this._drawingTool.addNewDrawing(t)}clearDrawings(){this._drawingTool.clearDrawings()}saveDrawings=()=>{const t=[];for(const e of this._drawingTool.drawings)t.push({points:e.points,options:e._options});const e=JSON.stringify(t);window.callbackFunction(`save_drawings${this._handlerID}_~_${e}`)};loadDrawings(t){t.forEach((t=>{switch(t.type){case"Box":this._drawingTool.addNewDrawing(new w(t.points[0],t.points[1],t.options));break;case"TrendLine":this._drawingTool.addNewDrawing(new E(t.points[0],t.points[1],t.options));break;case"HorizontalLine":this._drawingTool.addNewDrawing(new x(t.points[0],t.options));break;case"RayLine":this._drawingTool.addNewDrawing(new C(t.points[0],t.options));break;case"VerticalLine":this._drawingTool.addNewDrawing(new G(t.points[0],t.options))}}))}}class A{_handler;_div;left;right;constructor(t){this._handler=t,this._div=document.createElement("div"),this._div.classList.add("topbar");const e=t=>{const e=document.createElement("div");return e.classList.add("topbar-container"),e.style.justifyContent=t,this._div.appendChild(e),e};this.left=e("flex-start"),this.right=e("flex-end")}makeSwitcher(t,e,i,s="left"){const o=document.createElement("div");let n;o.style.margin="4px 12px";const r={elem:o,callbackName:i,intervalElements:t.map((t=>{const i=document.createElement("button");i.classList.add("topbar-button"),i.classList.add("switcher-button"),i.style.margin="0px 2px",i.innerText=t,t==e&&(n=i,i.classList.add("active-switcher-button"));const s=A.getClientWidth(i);return i.style.minWidth=s+1+"px",i.addEventListener("click",(()=>r.onItemClicked(i))),o.appendChild(i),i})),onItemClicked:t=>{t!=n&&(n.classList.remove("active-switcher-button"),t.classList.add("active-switcher-button"),n=t,window.callbackFunction(`${r.callbackName}_~_${t.innerText}`))}};return this.appendWidget(o,s,!0),r}makeTextBoxWidget(t,e="left"){const i=document.createElement("div");return i.classList.add("topbar-textbox"),i.innerText=t,this.appendWidget(i,e,!0),i}makeMenu(t,e,i,s,o="right"){let n=document.createElement("div");n.classList.add("topbar-menu");let r=!1;t.forEach((t=>{let e=this.makeButton(t,null,!1,!1);e.elem.addEventListener("click",(()=>{a.elem.innerText=e.elem.innerText+" ↓",window.callbackFunction(`${s}_~_${e.elem.innerText}`),n.style.display="none",r=!1})),e.elem.style.margin="4px 4px",e.elem.style.padding="2px 2px",n.appendChild(e.elem)}));let a=this.makeButton(e+" ↓",null,i,!0,o);a.elem.addEventListener("click",(()=>{if(r=!r,!r)return void(n.style.display="none");let t=a.elem.getBoundingClientRect();n.style.display="flex",n.style.flexDirection="column";let e=t.x+t.width/2;n.style.left=e-n.clientWidth/2+"px",n.style.top=t.y+t.height+"px"})),document.body.appendChild(n)}makeButton(t,e,i,s=!0,o="left"){let n=document.createElement("button");n.classList.add("topbar-button"),n.innerText=t,document.body.appendChild(n),n.style.minWidth=n.clientWidth+1+"px",document.body.removeChild(n);let r={elem:n,callbackName:e};return e&&n.addEventListener("click",(()=>window.callbackFunction(`${r.callbackName}_~_${n.innerText}`))),s&&this.appendWidget(n,o,i),r}makeSeparator(t="left"){const e=document.createElement("div");e.classList.add("topbar-seperator");("left"==t?this.left:this.right).appendChild(e)}appendWidget(t,e,i){const s="left"==e?this.left:this.right;i?("left"==e&&s.appendChild(t),this.makeSeparator(e),"right"==e&&s.appendChild(t)):s.appendChild(t),this._handler.reSize()}static getClientWidth(t){document.body.appendChild(t);const e=t.clientWidth;return document.body.removeChild(t),e}}window.pane={...I},window.containerDiv=document.getElementById("container")||document.createElement("div"),window.setCursor=t=>{t&&(window.cursor=t),document.body.style.cursor=window.cursor},window.cursor="default",window.Table=S,window.HorizontalLine=x,window.TrendLine=E,window.Box=w,window.RayLine=C,window.VerticalLine=G;class O{id;commandFunctions=[];wrapper;div;chart;scale;precision=2;series;volumeSeries;legend;_topBar;toolBox;spinner;_seriesList=[];constructor(t,e,i,s,o){this.reSize=this.reSize.bind(this),this.id=t,this.scale={width:e,height:i},this.wrapper=document.createElement("div"),this.wrapper.classList.add("handler"),this.wrapper.style.float=s,this.div=document.createElement("div"),this.div.style.position="relative",this.wrapper.appendChild(this.div),window.containerDiv.append(this.wrapper),this.chart=this._createChart(),this.series=this.createCandlestickSeries(),this.volumeSeries=this.createVolumeSeries(),this.legend=new M(this),document.addEventListener("keydown",(t=>{for(let e=0;ewindow.handlerInFocus=this.id)),this.reSize(),o&&window.addEventListener("resize",(()=>this.reSize()))}reSize(){let t=0!==this.scale.height&&this._topBar?._div.offsetHeight||0;this.chart.resize(window.innerWidth*this.scale.width,window.innerHeight*this.scale.height-t),this.wrapper.style.width=100*this.scale.width+"%",this.wrapper.style.height=100*this.scale.height+"%",0===this.scale.height||0===this.scale.width?(this.legend.div.style.display="none",this.toolBox&&(this.toolBox.div.style.display="none")):(this.legend.div.style.display="flex",this.toolBox&&(this.toolBox.div.style.display="flex"))}_createChart(){return e(this.div,{width:window.innerWidth*this.scale.width,height:window.innerHeight*this.scale.height,layout:{textColor:window.pane.color,background:{color:"#000000",type:i.Solid},fontSize:12},rightPriceScale:{scaleMargins:{top:.3,bottom:.25}},timeScale:{timeVisible:!0,secondsVisible:!1},crosshair:{mode:s.Normal,vertLine:{labelBackgroundColor:"rgb(46, 46, 46)"},horzLine:{labelBackgroundColor:"rgb(55, 55, 55)"}},grid:{vertLines:{color:"rgba(29, 30, 38, 5)"},horzLines:{color:"rgba(29, 30, 58, 5)"}},handleScroll:{vertTouchDrag:!0}})}createCandlestickSeries(){const t="rgba(39, 157, 130, 100)",e="rgba(200, 97, 100, 100)",i=this.chart.addCandlestickSeries({upColor:t,borderUpColor:t,wickUpColor:t,downColor:e,borderDownColor:e,wickDownColor:e});return i.priceScale().applyOptions({scaleMargins:{top:.2,bottom:.2}}),i}createVolumeSeries(){const t=this.chart.addHistogramSeries({color:"#26a69a",priceFormat:{type:"volume"},priceScaleId:"volume_scale"});return t.priceScale().applyOptions({scaleMargins:{top:.8,bottom:0}}),t}createLineSeries(t,e){const i=this.chart.addLineSeries({...e});return this._seriesList.push(i),this.legend.makeSeriesRow(t,i),{name:t,series:i}}createHistogramSeries(t,e){const i=this.chart.addHistogramSeries({...e});return this._seriesList.push(i),this.legend.makeSeriesRow(t,i),{name:t,series:i}}createToolBox(){this.toolBox=new B(this.id,this.chart,this.series,this.commandFunctions),this.div.appendChild(this.toolBox.div)}createTopBar(){return this._topBar=new A(this),this.wrapper.prepend(this._topBar._div),this._topBar}toJSON(){const{chart:t,...e}=this;return e}static syncCharts(t,e,i=!1){function s(t,e){e?(t.chart.setCrosshairPosition(e.value||e.close,e.time,t.series),t.legend.legendHandler(e,!0)):t.chart.clearCrosshairPosition()}function o(t,e){return e.time&&e.seriesData.get(t)||null}const n=t.chart.timeScale(),r=e.chart.timeScale(),a=t=>{t&&n.setVisibleLogicalRange(t)},l=t=>{t&&r.setVisibleLogicalRange(t)},d=i=>{s(e,o(t.series,i))},h=i=>{s(t,o(e.series,i))};let c=e;function p(t,e,s,o,n,r){t.wrapper.addEventListener("mouseover",(()=>{c!==t&&(c=t,e.chart.unsubscribeCrosshairMove(s),t.chart.subscribeCrosshairMove(o),i||(e.chart.timeScale().unsubscribeVisibleLogicalRangeChange(n),t.chart.timeScale().subscribeVisibleLogicalRangeChange(r)))}))}p(e,t,d,h,l,a),p(t,e,h,d,a,l),e.chart.subscribeCrosshairMove(h);const u=r.getVisibleLogicalRange();u&&n.setVisibleLogicalRange(u),i||e.chart.timeScale().subscribeVisibleLogicalRangeChange(a)}static makeSearchBox(t){const e=document.createElement("div");e.classList.add("searchbox"),e.style.display="none";const i=document.createElement("div");i.innerHTML='';const s=document.createElement("input");return s.type="text",e.appendChild(i),e.appendChild(s),t.div.appendChild(e),t.commandFunctions.push((i=>(console.log("1"),window.handlerInFocus===t.id&&(console.log(e.style),"none"===e.style.display?(console.log("3"),!!/^[a-zA-Z0-9]$/.test(i.key)&&(console.log("4"),e.style.display="flex",s.focus(),!0)):("Enter"===i.key||"Escape"===i.key)&&("Enter"===i.key&&window.callbackFunction(`search${t.id}_~_${s.value}`),e.style.display="none",s.value="",!0))))),s.addEventListener("input",(()=>s.value=s.value.toUpperCase())),{window:e,box:s}}static makeSpinner(t){t.spinner=document.createElement("div"),t.spinner.classList.add("spinner"),t.wrapper.appendChild(t.spinner);let e=0;!function i(){t.spinner&&(e+=10,t.spinner.style.transform=`translate(-50%, -50%) rotate(${e}deg)`,requestAnimationFrame(i))}()}static _styleMap={"--bg-color":"backgroundColor","--hover-bg-color":"hoverBackgroundColor","--click-bg-color":"clickBackgroundColor","--active-bg-color":"activeBackgroundColor","--muted-bg-color":"mutedBackgroundColor","--border-color":"borderColor","--color":"color","--active-color":"activeColor"};static setRootStyles(t){const e=document.documentElement.style;for(const[i,s]of Object.entries(this._styleMap))e.setProperty(i,t[s])}}window.Handler=O;export{O as Handler}; diff --git a/lightweight_charts/table.py b/lightweight_charts/table.py index a93b129..dca3390 100644 --- a/lightweight_charts/table.py +++ b/lightweight_charts/table.py @@ -52,16 +52,7 @@ class Row(dict): def delete(self): self.run_script(f"{self._table.id}.deleteRow('{self.id}')") self._table.pop(self.id) - - # # TODO this might be useful in abschart - # def _call(self, method_name: str, *args): - # new_args = [] - # for arg in args: - # if isinstance(arg, str): - # arg = f"'{arg}'" - # new_args.append(arg) - # self.run_script(f"{self._table.id}.{method_name}({', '.join(new_args)})") - + class Table(Pane, dict): VALUE = 'CELL__~__VALUE__~__PLACEHOLDER' diff --git a/setup.py b/setup.py index 13df00e..9f8a3d1 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ setup( python_requires='>=3.8', install_requires=[ 'pandas', - 'pywebview>=4.3', + 'pywebview>=5.0.5', ], package_data={ 'lightweight_charts': ['js/*.js'], diff --git a/src/box/box.ts b/src/box/box.ts index 56cf5e7..0973bb3 100644 --- a/src/box/box.ts +++ b/src/box/box.ts @@ -3,7 +3,7 @@ import { } from 'lightweight-charts'; import { Point } from '../drawing/data-source'; -import { Drawing, InteractionState } from '../drawing/drawing'; +import { InteractionState } from '../drawing/drawing'; import { DrawingOptions, defaultOptions } from '../drawing/options'; import { BoxPaneView } from './pane-view'; import { TwoPointDrawing } from '../drawing/two-point-drawing'; @@ -54,13 +54,13 @@ export class Box extends TwoPointDrawing { switch(state) { case InteractionState.NONE: document.body.style.cursor = "default"; - this.applyOptions({showCircles: false}); + this._hovered = false; this._unsubscribe("mousedown", this._handleMouseDownInteraction); break; case InteractionState.HOVERING: document.body.style.cursor = "pointer"; - this.applyOptions({showCircles: true}); + this._hovered = true; this._unsubscribe("mouseup", this._handleMouseUpInteraction); this._subscribe("mousedown", this._handleMouseDownInteraction) this.chart.applyOptions({handleScroll: true}); @@ -82,19 +82,19 @@ export class Box extends TwoPointDrawing { _onDrag(diff: any) { if (this._state == InteractionState.DRAGGING || this._state == InteractionState.DRAGGINGP1) { - Drawing._addDiffToPoint(this._p1, diff.time, diff.logical, diff.price); + this._addDiffToPoint(this.p1, diff.logical, diff.price); } if (this._state == InteractionState.DRAGGING || this._state == InteractionState.DRAGGINGP2) { - Drawing._addDiffToPoint(this._p2, diff.time, diff.logical, diff.price); + this._addDiffToPoint(this.p2, diff.logical, diff.price); } if (this._state != InteractionState.DRAGGING) { if (this._state == InteractionState.DRAGGINGP3) { - Drawing._addDiffToPoint(this._p1, diff.time, diff.logical, 0); - Drawing._addDiffToPoint(this._p2, 0, 0, diff.price); + this._addDiffToPoint(this.p1, diff.logical, 0); + this._addDiffToPoint(this.p2, 0, diff.price); } if (this._state == InteractionState.DRAGGINGP4) { - Drawing._addDiffToPoint(this._p1, 0, 0, diff.price); - Drawing._addDiffToPoint(this._p2, diff.time, diff.logical, 0); + this._addDiffToPoint(this.p1, 0, diff.price); + this._addDiffToPoint(this.p2, diff.logical, 0); } } } diff --git a/src/box/pane-renderer.ts b/src/box/pane-renderer.ts index f43e7e65..0addabe 100644 --- a/src/box/pane-renderer.ts +++ b/src/box/pane-renderer.ts @@ -2,12 +2,13 @@ import { ViewPoint } from "../drawing/pane-view"; import { CanvasRenderingTarget2D } from "fancy-canvas"; import { TwoPointDrawingPaneRenderer } from "../drawing/pane-renderer"; import { BoxOptions } from "./box"; +import { setLineStyle } from "../helpers/canvas-rendering"; export class BoxPaneRenderer extends TwoPointDrawingPaneRenderer { declare _options: BoxOptions; - constructor(p1: ViewPoint, p2: ViewPoint, text1: string, text2: string, options: BoxOptions) { - super(p1, p2, text1, text2, options) + constructor(p1: ViewPoint, p2: ViewPoint, options: BoxOptions, showCircles: boolean) { + super(p1, p2, options, showCircles) } draw(target: CanvasRenderingTarget2D) { @@ -21,6 +22,7 @@ export class BoxPaneRenderer extends TwoPointDrawingPaneRenderer { ctx.lineWidth = this._options.width; ctx.strokeStyle = this._options.lineColor; + setLineStyle(ctx, this._options.lineStyle) ctx.fillStyle = this._options.fillColor; const mainX = Math.min(scaled.x1, scaled.x2); @@ -31,7 +33,7 @@ export class BoxPaneRenderer extends TwoPointDrawingPaneRenderer { ctx.strokeRect(mainX, mainY, width, height); ctx.fillRect(mainX, mainY, width, height); - if (!this._options.showCircles) return; + if (!this._hovered) return; this._drawEndCircle(scope, mainX, mainY); this._drawEndCircle(scope, mainX+width, mainY); this._drawEndCircle(scope, mainX+width, mainY+height); diff --git a/src/box/pane-view.ts b/src/box/pane-view.ts index dab2720..295cc4e 100644 --- a/src/box/pane-view.ts +++ b/src/box/pane-view.ts @@ -11,9 +11,8 @@ export class BoxPaneView extends TwoPointDrawingPaneView { return new BoxPaneRenderer( this._p1, this._p2, - '' + this._source._p1.price.toFixed(1), - '' + this._source._p2.price.toFixed(1), this._source._options as BoxOptions, + this._source.hovered, ); } } \ No newline at end of file diff --git a/src/drawing/data-source.ts b/src/drawing/data-source.ts index b6bdac9..d311984 100644 --- a/src/drawing/data-source.ts +++ b/src/drawing/data-source.ts @@ -1,23 +1,15 @@ import { - IChartApi, - ISeriesApi, Logical, - SeriesOptionsMap, Time, } from 'lightweight-charts'; -import { DrawingOptions } from './options'; - export interface Point { time: Time | null; logical: Logical; price: number; } -export interface DrawingDataSource { - chart: IChartApi; - series: ISeriesApi; - options: DrawingOptions; - p1: Point; - p2: Point; +export interface DiffPoint { + logical: number; + price: number; } diff --git a/src/drawing/drawing-tool.ts b/src/drawing/drawing-tool.ts index 2b9b19e..e8a61ef 100644 --- a/src/drawing/drawing-tool.ts +++ b/src/drawing/drawing-tool.ts @@ -5,6 +5,7 @@ import { SeriesType, } from 'lightweight-charts'; import { Drawing } from './drawing'; +import { HorizontalLine } from '../horizontal-line/horizontal-line'; export class DrawingTool { @@ -69,9 +70,10 @@ export class DrawingTool { if (this._activeDrawing == null) { if (this._drawingType == null) return; - + // TODO this line wont work for horizontals ? this._activeDrawing = new this._drawingType(point, point); this._series.attachPrimitive(this._activeDrawing); + if (this._drawingType == HorizontalLine) this._onClick(param); } else { this._drawings.push(this._activeDrawing); diff --git a/src/drawing/drawing.ts b/src/drawing/drawing.ts index 59820e8..60e7151 100644 --- a/src/drawing/drawing.ts +++ b/src/drawing/drawing.ts @@ -1,10 +1,14 @@ -import { ISeriesApi, MouseEventParams, SeriesType, Time, Logical } from 'lightweight-charts'; +import { + ISeriesApi, + Logical, + MouseEventParams, + SeriesType +} from 'lightweight-charts'; import { PluginBase } from '../plugin-base'; -import { Point } from './data-source'; -import { DrawingPaneView } from './pane-view'; +import { DiffPoint, Point } from './data-source'; import { DrawingOptions, defaultOptions } from './options'; -import { convertTime } from '../helpers/time'; +import { DrawingPaneView } from './pane-view'; export enum InteractionState { NONE, @@ -16,17 +20,12 @@ export enum InteractionState { DRAGGINGP4, } -interface DiffPoint { - time: number | null; - logical: number; - price: number; -} - export abstract class Drawing extends PluginBase { _paneViews: DrawingPaneView[] = []; _options: DrawingOptions; abstract _type: string; + protected _points: (Point|null)[] = []; protected _state: InteractionState = InteractionState.NONE; @@ -66,7 +65,13 @@ export abstract class Drawing extends PluginBase { this.requestUpdate(); } - public abstract updatePoints(...points: (Point | null)[]): void; + public updatePoints(...points: (Point | null)[]) { + for (let i=0; i) { @@ -121,35 +129,27 @@ export abstract class Drawing extends PluginBase { protected static _getDiff(p1: Point, p2: Point): DiffPoint { const diff: DiffPoint = { - time: null, logical: p1.logical-p2.logical, price: p1.price-p2.price, } - if (p1.time && p2.time) { - diff.time = convertTime(p1.time)-convertTime(p2.time); - } return diff; } - protected static _addDiffToPoint(point: Point, timeDiff: number | null, logicalDiff: number, priceDiff: number) { - if (timeDiff != null && point.time != null) { - point.time = (convertTime(point.time)+timeDiff)/1000 as Time; - } - else { - point.time = null; - } + protected _addDiffToPoint(point: Point | null, logicalDiff: number, priceDiff: number) { + if (!point) return; point.logical = point.logical + logicalDiff as Logical; point.price = point.price+priceDiff; + point.time = this.series.dataByIndex(point.logical)?.time || null; } protected _handleMouseDownInteraction = () => { - if (Drawing._mouseIsDown) return; + // if (Drawing._mouseIsDown) return; Drawing._mouseIsDown = true; this._onMouseDown(); } protected _handleMouseUpInteraction = () => { - if (!Drawing._mouseIsDown) return; + // if (!Drawing._mouseIsDown) return; Drawing._mouseIsDown = false; this._moveToState(InteractionState.HOVERING); } @@ -174,12 +174,7 @@ export abstract class Drawing extends PluginBase { } protected abstract _onMouseDown(): void; - protected abstract _onDrag(diff: any): void; // TODO any? + protected abstract _onDrag(diff: DiffPoint): void; protected abstract _moveToState(state: InteractionState): void; protected abstract _mouseIsOverDrawing(param: MouseEventParams): boolean; - - // toJSON() { - // const {series, chart, ...serialized} = this; - // return serialized; - // } } diff --git a/src/drawing/options.ts b/src/drawing/options.ts index d2e8bab..9a3f0fd 100644 --- a/src/drawing/options.ts +++ b/src/drawing/options.ts @@ -1,18 +1,14 @@ import { LineStyle } from "lightweight-charts"; + export interface DrawingOptions { lineColor: string; lineStyle: LineStyle width: number; - showLabels: boolean; - showCircles: boolean, } - export const defaultOptions: DrawingOptions = { - lineColor: 'rgb(255, 255, 255)', + lineColor: '#1E80F0', lineStyle: LineStyle.Solid, width: 4, - showLabels: true, - showCircles: false, -}; \ No newline at end of file +}; diff --git a/src/drawing/pane-renderer.ts b/src/drawing/pane-renderer.ts index 5183e49..7d3d658 100644 --- a/src/drawing/pane-renderer.ts +++ b/src/drawing/pane-renderer.ts @@ -17,15 +17,13 @@ export abstract class DrawingPaneRenderer implements ISeriesPrimitivePaneRendere export abstract class TwoPointDrawingPaneRenderer extends DrawingPaneRenderer { _p1: ViewPoint; _p2: ViewPoint; - _text1: string; - _text2: string; + protected _hovered: boolean; - constructor(p1: ViewPoint, p2: ViewPoint, text1: string, text2: string, options: DrawingOptions) { + constructor(p1: ViewPoint, p2: ViewPoint, options: DrawingOptions, hovered: boolean) { super(options); this._p1 = p1; this._p2 = p2; - this._text1 = text1; - this._text2 = text2; + this._hovered = hovered; } abstract draw(target: CanvasRenderingTarget2D): void; diff --git a/src/drawing/pane-view.ts b/src/drawing/pane-view.ts index 8f2a772..d186d94 100644 --- a/src/drawing/pane-view.ts +++ b/src/drawing/pane-view.ts @@ -33,11 +33,12 @@ export abstract class TwoPointDrawingPaneView extends DrawingPaneView { } update() { + if (!this._source.p1 || !this._source.p2) return; const series = this._source.series; - const y1 = series.priceToCoordinate(this._source._p1.price); - const y2 = series.priceToCoordinate(this._source._p2.price); - const x1 = this._getX(this._source._p1); - const x2 = this._getX(this._source._p2); + const y1 = series.priceToCoordinate(this._source.p1.price); + const y2 = series.priceToCoordinate(this._source.p2.price); + const x1 = this._getX(this._source.p1); + const x2 = this._getX(this._source.p2); this._p1 = { x: x1, y: y1 }; this._p2 = { x: x2, y: y2 }; if (!x1 || !x2 || !y1 || !y2) return; @@ -47,9 +48,6 @@ export abstract class TwoPointDrawingPaneView extends DrawingPaneView { _getX(p: Point) { const timeScale = this._source.chart.timeScale(); - if (!p.time) { - return timeScale.logicalToCoordinate(p.logical); - } - return timeScale.timeToCoordinate(p.time); + return timeScale.logicalToCoordinate(p.logical); } } diff --git a/src/drawing/two-point-drawing.ts b/src/drawing/two-point-drawing.ts index 13db602..22bdb3a 100644 --- a/src/drawing/two-point-drawing.ts +++ b/src/drawing/two-point-drawing.ts @@ -5,18 +5,18 @@ import { TwoPointDrawingPaneView } from './pane-view'; export abstract class TwoPointDrawing extends Drawing { - _p1: Point; - _p2: Point; _paneViews: TwoPointDrawingPaneView[] = []; + protected _hovered: boolean = false; + constructor( p1: Point, p2: Point, options?: Partial ) { super() - this._p1 = p1; - this._p2 = p2; + this.points.push(p1); + this.points.push(p2); this._options = { ...defaultOptions, ...options, @@ -31,9 +31,8 @@ export abstract class TwoPointDrawing extends Drawing { this.updatePoints(null, point); } - public updatePoints(...points: (Point|null)[]) { - this._p1 = points[0] || this._p1; - this._p2 = points[1] || this._p2; - this.requestUpdate(); - } + get p1() { return this.points[0]; } + get p2() { return this.points[1]; } + + get hovered() { return this._hovered; } } diff --git a/src/general/global-params.ts b/src/general/global-params.ts index 256162e..9f5a642 100644 --- a/src/general/global-params.ts +++ b/src/general/global-params.ts @@ -1,4 +1,8 @@ +import { Box } from "../box/box"; import { HorizontalLine } from "../horizontal-line/horizontal-line"; +import { RayLine } from "../horizontal-line/ray-line"; +import { TrendLine } from "../trend-line/trend-line"; +import { VerticalLine } from "../vertical-line/vertical-line"; import { Table } from "./table"; export interface GlobalParams extends Window { @@ -10,7 +14,13 @@ export interface GlobalParams extends Window { cursor: string; Handler: any; Table: typeof Table; + HorizontalLine: typeof HorizontalLine; + TrendLine: typeof TrendLine; + Box: typeof Box; + RayLine: typeof RayLine; + VerticalLine: typeof VerticalLine; + } interface paneStyle { @@ -48,7 +58,12 @@ export function globalParamInit() { } window.cursor = 'default'; window.Table = Table; + window.HorizontalLine = HorizontalLine; + window.TrendLine = TrendLine; + window.Box = Box; + window.RayLine = RayLine; + window.VerticalLine = VerticalLine; } diff --git a/src/general/handler.ts b/src/general/handler.ts index 78d4b8e..e6587e8 100644 --- a/src/general/handler.ts +++ b/src/general/handler.ts @@ -52,8 +52,14 @@ export class Handler { public _seriesList: ISeriesApi[] = []; - - constructor(chartId: string, innerWidth: number, innerHeight: number, position: string, autoSize: boolean) { + // TODO make some subcharts in the vite dev window and mess with the CSS to see if you can not need the position param. also see if you can remove resizing each time the window resizes? + constructor( + chartId: string, + innerWidth: number, + innerHeight: number, + position: string, + autoSize: boolean + ) { this.reSize = this.reSize.bind(this) this.id = chartId @@ -224,8 +230,15 @@ export class Handler { return param.seriesData.get(series) || null; } - const setChildRange = (timeRange: LogicalRange | null) => { if(timeRange) childChart.chart.timeScale().setVisibleLogicalRange(timeRange); } - const setParentRange = (timeRange: LogicalRange | null) => { if(timeRange) parentChart.chart.timeScale().setVisibleLogicalRange(timeRange); } + const childTimeScale = childChart.chart.timeScale(); + const parentTimeScale = parentChart.chart.timeScale(); + + const setChildRange = (timeRange: LogicalRange | null) => { + if(timeRange) childTimeScale.setVisibleLogicalRange(timeRange); + } + const setParentRange = (timeRange: LogicalRange | null) => { + if(timeRange) parentTimeScale.setVisibleLogicalRange(timeRange); + } const setParentCrosshair = (param: MouseEventParams) => { crosshairHandler(parentChart, getPoint(childChart.series, param)) @@ -235,7 +248,14 @@ export class Handler { } let selected = parentChart - function addMouseOverListener(thisChart: Handler, otherChart: Handler, thisCrosshair: MouseEventHandler