diff --git a/lightweight_charts/abstract.py b/lightweight_charts/abstract.py index d5156d2..f8455a1 100644 --- a/lightweight_charts/abstract.py +++ b/lightweight_charts/abstract.py @@ -7,7 +7,7 @@ import pandas as pd from .table import Table from .toolbox import ToolBox -from .drawings import HorizontalLine, TwoPointDrawing, VerticalSpan +from .drawings import Box, HorizontalLine, TrendLine, TwoPointDrawing, VerticalSpan from .topbar import TopBar from .util import ( Pane, Events, IDGen, as_enum, jbool, js_json, TIME, NUM, FLOAT, @@ -45,9 +45,9 @@ class Window: return self.loaded = True - # 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 + if hasattr(self, '_return_q'): + while not self.run_script_and_get('document.readyState == "complete"'): + continue # scary, but works initial_script = '' self.scripts.extend(self.final_scripts) @@ -283,13 +283,13 @@ class SeriesCommon(Pane): :return: The id of the marker placed. """ try: - time = self._last_bar['time'] if not time else self._single_datetime_format(time) + formatted_time = self._last_bar['time'] if not time else self._single_datetime_format(time) except TypeError: raise TypeError('Chart marker created before data was set.') marker_id = self.win._id_gen.generate() self.run_script(f""" {self.id}.markers.push({{ - time: {time if isinstance(time, float) else f"'{time}'"}, + time: {time if isinstance(formatted_time, float) else f"'{formatted_time}'"}, position: '{marker_position(position)}', color: '{color}', shape: '{marker_shape(shape)}', @@ -719,11 +719,11 @@ class AbstractChart(Candlestick, Pane): end_time: TIME, end_value: NUM, round: bool = False, - color: str = '#1E80F0', + line_color: str = '#1E80F0', width: int = 2, style: LINE_STYLE = 'solid', ) -> TwoPointDrawing: - return TwoPointDrawing("TrendLine", *locals().values()) + return TrendLine(*locals().values()) def box( self, @@ -733,10 +733,11 @@ class AbstractChart(Candlestick, Pane): end_value: NUM, round: bool = False, color: str = '#1E80F0', + fill_color: str = 'rgba(255, 255, 255, 0.2)', width: int = 2, style: LINE_STYLE = 'solid', ) -> TwoPointDrawing: - return TwoPointDrawing("Box", *locals().values()) + return Box(*locals().values()) def ray_line( self, diff --git a/lightweight_charts/drawings.py b/lightweight_charts/drawings.py index f2ea593..31517bc 100644 --- a/lightweight_charts/drawings.py +++ b/lightweight_charts/drawings.py @@ -6,11 +6,11 @@ from typing import Union, Optional from lightweight_charts.util import js_json -from .util import NUM, Pane, as_enum, LINE_STYLE, TIME +from .util import NUM, Pane, as_enum, LINE_STYLE, TIME, snake_to_camel class Drawing(Pane): - def __init__(self, chart, color, width, style, func=None): + def __init__(self, chart, func=None): super().__init__(chart.win) self.chart = chart @@ -34,13 +34,10 @@ class TwoPointDrawing(Drawing): end_time: TIME, end_value: NUM, round: bool, - color, - width, - style, + options: dict, func=None ): - super().__init__(chart, color, width, style, func) - + super().__init__(chart, func) def make_js_point(time, price): formatted_time = self.chart._single_datetime_format(time) @@ -54,14 +51,14 @@ class TwoPointDrawing(Drawing): "price": {price} }}''' + options_string = '\n'.join(f'{key}: {val},' for key, val in options.items()) + 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)}, - width: {width}, + {options_string} }} ) {chart.id}.series.attachPrimitive({self.id}) @@ -70,7 +67,7 @@ class TwoPointDrawing(Drawing): class HorizontalLine(Drawing): def __init__(self, chart, price, color, width, style, text, axis_label_visible, func): - super().__init__(chart, color, width, style, func) + super().__init__(chart, func) self.price = price self.run_script(f''' @@ -113,7 +110,7 @@ class HorizontalLine(Drawing): class VerticalLine(Drawing): def __init__(self, chart, time, color, width, style, text, axis_label_visible, func): - super().__init__(chart, color, width, style, func) + super().__init__(chart, func) self.time = time self.run_script(f''' @@ -136,7 +133,68 @@ class VerticalLine(Drawing): def label(self, text: str): # TODO self.run_script(f'{self.id}.updateLabel("{text}")') - + +class Box(TwoPointDrawing): + def __init__(self, + chart, + start_time: TIME, + start_value: NUM, + end_time: TIME, + end_value: NUM, + round: bool, + line_color: str, + fill_color: str, + width: int, + style: LINE_STYLE, + func=None): + + super().__init__( + "Box", + chart, + start_time, + start_value, + end_time, + end_value, + round, + { + "lineColor": f'"{line_color}"', + "fillColor": f'"{fill_color}"', + "width": width, + "lineStyle": as_enum(style, LINE_STYLE) + }, + func + ) + + +class TrendLine(TwoPointDrawing): + def __init__(self, + chart, + start_time: TIME, + start_value: NUM, + end_time: TIME, + end_value: NUM, + round: bool, + line_color: str, + width: int, + style: LINE_STYLE, + func=None): + + super().__init__( + "TrendLine", + chart, + start_time, + start_value, + end_time, + end_value, + round, + { + "lineColor": f'"line_color"', + "width": width, + "lineStyle": as_enum(style, LINE_STYLE) + }, + func + ) + 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)'): diff --git a/lightweight_charts/js/bundle.js b/lightweight_charts/js/bundle.js index 2595670..08b8b60 100644 --- a/lightweight_charts/js/bundle.js +++ b/lightweight_charts/js/bundle.js @@ -1 +1 @@ -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={...g,...i},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}; +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 g 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 v={fillEnabled:!0,fillColor:"rgba(255, 255, 255, 0.2)",...r};class w extends g{_type="Box";constructor(t,e,i){super(t,e,i),this._options={...v,...i},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 g{_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 g=t=>{this._div.style.left=t.clientX-u+"px",this._div.style.top=t.clientY-_+"px"},v=()=>{document.removeEventListener("mousemove",g),document.removeEventListener("mouseup",v)};this._div.addEventListener("mousedown",(t=>{u=t.clientX-this._div.offsetLeft,_=t.clientY-this._div.offsetTop,document.addEventListener("mousemove",g),document.addEventListener("mouseup",v)}))}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 M{_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{colorOption;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,e){this.colorOption=e,this.saveDrawings=t,this._div=document.createElement("div"),this._div.classList.add("color-picker");let i=document.createElement("div");i.style.margin="10px",i.style.display="flex",i.style.flexWrap="wrap",N.colors.forEach((t=>i.appendChild(this.makeColorBox(t))));let s=document.createElement("div");s.style.backgroundColor=window.pane.borderColor,s.style.height="1px",s.style.width="130px";let o=document.createElement("div");o.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()},o.appendChild(n),o.appendChild(this._opacitySlider),o.appendChild(this._opacityLabel),this._div.appendChild(i),this._div.appendChild(s),this._div.appendChild(o),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 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({[this.colorOption]:t}),this.saveDrawings()}openMenu(t){l.lastHoveredObject&&(this.rgba=N.extractRGBA(l.lastHoveredObject._options[this.colorOption]),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{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"}}function P(t){const e=[];for(const i of t)0==e.length?e.push(i.toUpperCase()):i==i.toUpperCase()?e.push(" "+i):e.push(i);return e.join("")}class B{saveDrawings;drawingTool;div;hoverItem;items=[];constructor(t,e){this.saveDrawings=t,this.drawingTool=e,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){if(!l.hoveredObject)return;for(const t of this.items)this.div.removeChild(t);this.items=[];for(const t of Object.keys(l.hoveredObject._options)){let e;if(t.toLowerCase().includes("color"))e=new N(this.saveDrawings,t);else{if("lineStyle"!==t)continue;e=new R(this.saveDrawings)}let i=t=>e.openMenu(t);this.menuItem(P(t),i,(()=>{document.removeEventListener("click",e.closeMenu),e._div.style.display="none"}))}this.separator(),this.menuItem("Delete Drawing",(()=>this.drawingTool.delete(l.lastHoveredObject))),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"}));this.items.push(s)}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),this.items.push(t)}}class A{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 M(e,i,(()=>this.removeActiveAndSave())),this.div=this._makeToolBox(),new B(this.saveDrawings,this._drawingTool),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",A.TREND_SVG)),this.buttons.push(this._makeToolBoxElement(x,"KeyH",A.HORZ_SVG)),this.buttons.push(this._makeToolBoxElement(C,"KeyR",A.RAY_SVG)),this.buttons.push(this._makeToolBoxElement(w,"KeyB",A.BOX_SVG)),this.buttons.push(this._makeToolBoxElement(G,"KeyV",A.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()};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 O{_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=O.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 H{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 T(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 A(this.id,this.chart,this.series,this.commandFunctions),this.div.appendChild(this.toolBox.div)}createTopBar(){return this._topBar=new O(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=H;export{H as Handler}; diff --git a/lightweight_charts/polygon.py b/lightweight_charts/polygon.py index 623f51e..bf18cfc 100644 --- a/lightweight_charts/polygon.py +++ b/lightweight_charts/polygon.py @@ -247,7 +247,7 @@ class PolygonAPI: df = await async_get_bar_data(ticker, timeframe, start_date, end_date, limit) - self._chart.set(df, render_drawings=_tickers.get(self._chart) == ticker) + self._chart.set(df, keep_drawings=_tickers.get(self._chart) == ticker) _tickers[self._chart] = ticker if not live: diff --git a/lightweight_charts/widgets.py b/lightweight_charts/widgets.py index ff27678..630573b 100644 --- a/lightweight_charts/widgets.py +++ b/lightweight_charts/widgets.py @@ -63,8 +63,8 @@ class WxChart(abstract.AbstractChart): self.webview.Bind(wx.html2.EVT_WEBVIEW_LOADED, lambda e: wx.CallLater(500, self.win.on_js_load)) self.webview.Bind(wx.html2.EVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, lambda e: emit_callback(self, e.GetString())) self.webview.AddScriptMessageHandler('wx_msg') - self.webview.SetPage(abstract.TEMPLATE, '') - self.webview.AddUserScript(abstract.JS['toolbox']) if toolbox else None + self.webview.LoadURL(f'file:///{abstract.INDEX}') + # self.webview.AddUserScript(abstract.JS['toolbox']) if toolbox else None def get_webview(self): return self.webview diff --git a/src/context-menu/color-picker.ts b/src/context-menu/color-picker.ts index 6d5582d..1ee6b56 100644 --- a/src/context-menu/color-picker.ts +++ b/src/context-menu/color-picker.ts @@ -22,7 +22,9 @@ export class ColorPicker { private _opacityLabel: HTMLDivElement; private rgba: number[] | undefined; - constructor(saveDrawings: Function) { + constructor(saveDrawings: Function, + private colorOption: string, + ) { this.saveDrawings = saveDrawings this._div = document.createElement('div'); @@ -114,12 +116,12 @@ export class ColorPicker { updateColor() { if (!Drawing.lastHoveredObject || !this.rgba) return; const oColor = `rgba(${this.rgba[0]}, ${this.rgba[1]}, ${this.rgba[2]}, ${this.opacity})` - Drawing.lastHoveredObject.applyOptions({lineColor: oColor}) + Drawing.lastHoveredObject.applyOptions({[this.colorOption]: oColor}) this.saveDrawings() } openMenu(rect: DOMRect) { if (!Drawing.lastHoveredObject) return; - this.rgba = ColorPicker.extractRGBA(Drawing.lastHoveredObject._options.lineColor) + this.rgba = ColorPicker.extractRGBA(Drawing.lastHoveredObject._options[this.colorOption]) this.opacity = this.rgba[3]; this._updateOpacitySlider(); this._div.style.top = (rect.top-30)+'px' diff --git a/src/context-menu/context-menu.ts b/src/context-menu/context-menu.ts index e42c6b4..a06a8f1 100644 --- a/src/context-menu/context-menu.ts +++ b/src/context-menu/context-menu.ts @@ -1,5 +1,21 @@ import { Drawing } from "../drawing/drawing"; +import { DrawingTool } from "../drawing/drawing-tool"; import { GlobalParams } from "../general/global-params"; +import { ColorPicker } from "./color-picker"; +import { StylePicker } from "./style-picker"; + + +export function camelToTitle(inputString: string) { + const result = []; + for (const c of inputString) { + if (result.length == 0) { + result.push(c.toUpperCase()); + } else if (c == c.toUpperCase()) { + result.push(' '+c); + } else result.push(c); + } + return result.join(''); +} interface Item { elem: HTMLSpanElement; @@ -13,8 +29,12 @@ declare const window: GlobalParams; export class ContextMenu { private div: HTMLDivElement private hoverItem: Item | null; + private items: HTMLElement[] = [] - constructor() { + constructor( + private saveDrawings: Function, + private drawingTool: DrawingTool, + ) { this._onRightClick = this._onRightClick.bind(this); this.div = document.createElement('div'); this.div.classList.add('context-menu'); @@ -35,6 +55,50 @@ export class ContextMenu { private _onRightClick(ev: MouseEvent) { if (!Drawing.hoveredObject) return; + + for (const item of this.items) { + this.div.removeChild(item); + } + this.items = []; + + for (const optionName of Object.keys(Drawing.hoveredObject._options)) { + let subMenu; + if (optionName.toLowerCase().includes('color')) { + subMenu = new ColorPicker(this.saveDrawings, optionName); + } else if (optionName === 'lineStyle') { + subMenu = new StylePicker(this.saveDrawings) + } else continue; + + let onClick = (rect: DOMRect) => subMenu.openMenu(rect) + this.menuItem(camelToTitle(optionName), onClick, () => { + document.removeEventListener('click', subMenu.closeMenu) + subMenu._div.style.display = 'none' + }) + } + + let onClickDelete = () => this.drawingTool.delete(Drawing.lastHoveredObject); + this.separator() + this.menuItem('Delete Drawing', onClickDelete) + + // const colorPicker = new ColorPicker(this.saveDrawings) + // const stylePicker = new StylePicker(this.saveDrawings) + + // let onClickDelete = () => this._drawingTool.delete(Drawing.lastHoveredObject); + // let onClickColor = (rect: DOMRect) => colorPicker.openMenu(rect) + // let onClickStyle = (rect: DOMRect) => stylePicker.openMenu(rect) + + // contextMenu.menuItem('Color Picker', onClickColor, () => { + // document.removeEventListener('click', colorPicker.closeMenu) + // colorPicker._div.style.display = 'none' + // }) + // contextMenu.menuItem('Style', onClickStyle, () => { + // document.removeEventListener('click', stylePicker.closeMenu) + // stylePicker._div.style.display = 'none' + // }) + // contextMenu.separator() + // contextMenu.menuItem('Delete Drawing', onClickDelete) + + ev.preventDefault(); this.div.style.left = ev.clientX + 'px'; this.div.style.top = ev.clientY + 'px'; @@ -70,6 +134,9 @@ export class ContextMenu { item.addEventListener('mouseover', () => timeout = setTimeout(() => action(item.getBoundingClientRect()), 100)) item.addEventListener('mouseout', () => clearTimeout(timeout)) } + + this.items.push(item); + } public separator() { const separator = document.createElement('div') @@ -78,6 +145,8 @@ export class ContextMenu { separator.style.margin = '3px 0px' separator.style.backgroundColor = window.pane.borderColor this.div.appendChild(separator) + + this.items.push(separator); } } diff --git a/src/general/toolbox.ts b/src/general/toolbox.ts index 7f547d1..088ed5b 100644 --- a/src/general/toolbox.ts +++ b/src/general/toolbox.ts @@ -4,8 +4,6 @@ import { Box } from "../box/box"; import { Drawing } from "../drawing/drawing"; import { ContextMenu } from "../context-menu/context-menu"; import { GlobalParams } from "./global-params"; -import { StylePicker } from "../context-menu/style-picker"; -import { ColorPicker } from "../context-menu/color-picker"; import { IChartApi, ISeriesApi, SeriesType } from "lightweight-charts"; import { HorizontalLine } from "../horizontal-line/horizontal-line"; import { RayLine } from "../horizontal-line/ray-line"; @@ -42,7 +40,7 @@ export class ToolBox { this._commandFunctions = commandFunctions; this._drawingTool = new DrawingTool(chart, series, () => this.removeActiveAndSave()); this.div = this._makeToolBox() - this._makeContextMenu(); + new ContextMenu(this.saveDrawings, this._drawingTool); commandFunctions.push((event: KeyboardEvent) => { if ((event.metaKey || event.ctrlKey) && event.code === 'KeyZ') { @@ -130,27 +128,6 @@ export class ToolBox { this.saveDrawings() } - private _makeContextMenu() { - const contextMenu = new ContextMenu() - const colorPicker = new ColorPicker(this.saveDrawings) - const stylePicker = new StylePicker(this.saveDrawings) - - let onClickDelete = () => this._drawingTool.delete(Drawing.lastHoveredObject); - let onClickColor = (rect: DOMRect) => colorPicker.openMenu(rect) - let onClickStyle = (rect: DOMRect) => stylePicker.openMenu(rect) - - contextMenu.menuItem('Color Picker', onClickColor, () => { - document.removeEventListener('click', colorPicker.closeMenu) - colorPicker._div.style.display = 'none' - }) - contextMenu.menuItem('Style', onClickStyle, () => { - document.removeEventListener('click', stylePicker.closeMenu) - stylePicker._div.style.display = 'none' - }) - contextMenu.separator() - contextMenu.menuItem('Delete Drawing', onClickDelete) - } - // renderDrawings() { // if (this.mouseDown) return // this.drawings.forEach((item) => { diff --git a/src/websocket-test/server.ts b/src/websocket-test/server.ts deleted file mode 100644 index d9271a7..0000000 --- a/src/websocket-test/server.ts +++ /dev/null @@ -1,62 +0,0 @@ -// import { Handler } from "../general/handler" - - - -// interface Command { -// type: string, -// id: string, -// method: string, -// args: string, - -// } - -// class Interpreter { -// private _tokens: string[]; -// private cwd: string; -// private _i: number; - -// private objects = {}; - -// constructor() { - -// } - -// private _next() { -// this._i++; -// this.cwd = this._tokens[this._i]; -// return this.cwd; -// } - -// private _handleCommand(command: string[]) { -// const type = this.cwd; -// switch (this.cwd) { -// case "auth": -// break; -// case "create": -// return this._create(); -// case "obj": -// break; -// case "": -// } -// } - -// private static readonly createMap = { -// "Handler": Handler, -// } - - -// // create, HorizontalLine, id -// private _create() { -// const type = this.cwd; -// this._next(); - -// Interpreter.createMap[type](...this.cwd) -// } - -// private _obj() { -// const id = this._next(); -// const method = this._next(); -// const args = this._next(); -// this.objects[id][method](args); -// } -// } \ No newline at end of file diff --git a/test/run_tests.py b/test/run_tests.py index 10f895b..a8496ca 100644 --- a/test/run_tests.py +++ b/test/run_tests.py @@ -19,4 +19,4 @@ if __name__ == '__main__': loader = unittest.TestLoader() cases = [loader.loadTestsFromTestCase(module) for module in TEST_CASES] suite = unittest.TestSuite(cases) - unittest.TextTestRunner().run(suite) \ No newline at end of file + unittest.TextTestRunner(verbosity=2).run(suite) \ No newline at end of file diff --git a/test/test_chart.py b/test/test_chart.py index 604b072..42db0a7 100644 --- a/test/test_chart.py +++ b/test/test_chart.py @@ -1,13 +1,10 @@ import unittest import pandas as pd -from util import BARS +from util import BARS, Tester from lightweight_charts import Chart -class TestChart(unittest.TestCase): - def setUp(self): - self.chart = Chart() - +class TestChart(Tester): def test_data_is_renamed(self): uppercase_df = pd.DataFrame(BARS.copy()).rename({'date': 'Date', 'open': 'OPEN', 'high': 'HIgh', 'low': 'Low', 'close': 'close', 'volUME': 'volume'}) result = self.chart._df_datetime_format(uppercase_df) @@ -19,9 +16,6 @@ class TestChart(unittest.TestCase): self.assertEqual(result0, self.chart.lines()[0]) self.assertEqual(result1, self.chart.lines()[1]) - def tearDown(self): - self.chart.exit() - if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/test/test_returns.py b/test/test_returns.py index 6aad993..a06da8d 100644 --- a/test/test_returns.py +++ b/test/test_returns.py @@ -15,7 +15,7 @@ class TestReturns(Tester): self.assertIsNotNone(screenshot_data) def test_save_drawings(self): - self.chart.exit() + async def main(): asyncio.create_task(self.chart.show_async()); @@ -28,7 +28,7 @@ class TestReturns(Tester): self.assertTrue(len(self.chart.toolbox.drawings) > 0) self.chart.exit() - self.chart = Chart(toolbox=True, debug=True) + self.chart = Chart(toolbox=True, width=100, height=100) self.chart.set(BARS) self.chart.topbar.textbox('symbol', 'SYM', align='right') self.chart.toolbox.save_drawings_under(self.chart.topbar['symbol']) diff --git a/test/test_topbar.py b/test/test_topbar.py index b364971..e1ba108 100644 --- a/test/test_topbar.py +++ b/test/test_topbar.py @@ -2,21 +2,20 @@ import unittest import pandas as pd from lightweight_charts import Chart +from util import Tester -class TestTopBar(unittest.TestCase): +class TestTopBar(Tester): def test_switcher_fires_event(self): - chart = Chart() - chart.topbar.switcher('a', ('1', '2'), func=lambda c: (self.assertEqual(c.topbar['a'].value, '2'), c.exit())) - chart.run_script(f'{chart.topbar["a"].id}.intervalElements[1].dispatchEvent(new Event("click"))') - chart.show(block=True) + self.chart.topbar.switcher('a', ('1', '2'), func=lambda c: (self.assertEqual(c.topbar['a'].value, '2'), c.exit())) + self.chart.run_script(f'{self.chart.topbar["a"].id}.intervalElements[1].dispatchEvent(new Event("click"))') + self.chart.show(block=True) def test_button_fires_event(self): - chart = Chart() - chart.topbar.button('a', '1', func=lambda c: (self.assertEqual(c.topbar['a'].value, '2'), c.exit())) - chart.topbar['a'].set('2') - chart.run_script(f'{chart.topbar["a"].id}.elem.dispatchEvent(new Event("click"))') - chart.show(block=True) + self.chart.topbar.button('a', '1', func=lambda c: (self.assertEqual(c.topbar['a'].value, '2'), c.exit())) + self.chart.topbar['a'].set('2') + self.chart.run_script(f'{self.chart.topbar["a"].id}.elem.dispatchEvent(new Event("click"))') + self.chart.show(block=True) if __name__ == '__main__':