mdas-jsview-sdk 1.0.12-uat.0 → 1.0.13-uat.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -52,4 +52,4 @@ const Mh=t=>t&&t.enabled&&t.modifierKey,Ch=(t,e)=>t&&e[t+"Key"],_h=(t,e)=>t&&!e[
52
52
  * Copyright 2024 Chart.js Contributors
53
53
  * Released under the MIT license
54
54
  * https://github.com/chartjs/chartjs-chart-financial/blob/master/LICENSE.md
55
- */function Mu(t,e,n,i){const s=null===e,o=null===n,a=!(!t||s&&o)&&function(t,e){const{x:n,y:i,base:s,width:o,height:a}=t.getProps(["x","low","high","width","height"],e);let r,c,l,d,h;return t.horizontal?(h=a/2,r=Math.min(n,s),c=Math.max(n,s),l=i-h,d=i+h):(h=o/2,r=n-h,c=n+h,l=Math.min(i,s),d=Math.max(i,s)),{left:r,top:l,right:c,bottom:d}}(t,i);return a&&(s||e>=a.left&&e<=a.right)&&(o||n>=a.top&&n<=a.bottom)}class Cu extends Mo{static defaults={backgroundColors:{up:"rgba(75, 192, 192, 0.5)",down:"rgba(255, 99, 132, 0.5)",unchanged:"rgba(201, 203, 207, 0.5)"},borderColors:{up:"rgb(75, 192, 192)",down:"rgb(255, 99, 132)",unchanged:"rgb(201, 203, 207)"}};height(){return this.base-this.y}inRange(t,e,n){return Mu(this,t,e,n)}inXRange(t,e){return Mu(this,t,null,e)}inYRange(t,e){return Mu(this,null,t,e)}getRange(t){return"x"===t?this.width/2:this.height/2}getCenterPoint(t){const{x:e,low:n,high:i}=this.getProps(["x","low","high"],t);return{x:e,y:(i+n)/2}}tooltipPosition(t){const{x:e,open:n,close:i}=this.getProps(["x","open","close"],t);return{x:e,y:(n+i)/2}}}const _u=eo.defaults;class Du extends Cu{static id="ohlc";static defaults={...Cu.defaults,lineWidth:2,armLength:null,armLengthRatio:.8};draw(t){const e=this,{x:n,open:i,high:s,low:o,close:a}=e,r=mt(e.armLengthRatio,_u.elements.ohlc.armLengthRatio);let c=mt(e.armLength,_u.elements.ohlc.armLength);null===c&&(c=e.width*r*.5),t.strokeStyle=a<i?mt(e.options.borderColors?e.options.borderColors.up:void 0,_u.elements.ohlc.borderColors.up):a>i?mt(e.options.borderColors?e.options.borderColors.down:void 0,_u.elements.ohlc.borderColors.down):mt(e.options.borderColors?e.options.borderColors.unchanged:void 0,_u.elements.ohlc.borderColors.unchanged),t.lineWidth=mt(e.lineWidth,_u.elements.ohlc.lineWidth),t.beginPath(),t.moveTo(n,s),t.lineTo(n,o),t.moveTo(n-c,i),t.lineTo(n,i),t.moveTo(n+c,a),t.lineTo(n,a),t.stroke()}}class Tu extends Ci{static overrides={label:"",parsing:!1,hover:{mode:"label"},animations:{numbers:{type:"number",properties:["x","y","base","width","open","high","low","close"]}},scales:{x:{type:"timeseries",offset:!0,ticks:{major:{enabled:!0},source:"data",maxRotation:0,autoSkip:!0,autoSkipPadding:75,sampleSize:100}},y:{type:"linear"}},plugins:{tooltip:{intersect:!1,mode:"index",callbacks:{label(t){const e=t.parsed;if(!dt(e.y))return Le.plugins.tooltip.callbacks.label(t);const{o:n,h:i,l:s,c:o}=e;return`O: ${n} H: ${i} L: ${s} C: ${o}`}}}}};getLabelAndValue(t){const e=this,n=e.getParsed(t),i=e._cachedMeta.iScale.axis,{o:s,h:o,l:a,c:r}=n,c=`O: ${s} H: ${o} L: ${a} C: ${r}`;return{label:`${e._cachedMeta.iScale.getLabelForValue(n[i])}`,value:c}}getUserBounds(t){const{min:e,max:n,minDefined:i,maxDefined:s}=t.getUserBounds();return{min:i?e:Number.NEGATIVE_INFINITY,max:s?n:Number.POSITIVE_INFINITY}}getMinMax(t){const e=this._cachedMeta,n=e._parsed,i=e.iScale.axis,s=this._getOtherScale(t),{min:o,max:a}=this.getUserBounds(s);if(n.length<2)return{min:0,max:1};if(t===e.iScale)return{min:n[0][i],max:n[n.length-1][i]};const r=n.filter((({x:t})=>t>=o&&t<a));let c=Number.POSITIVE_INFINITY,l=Number.NEGATIVE_INFINITY;for(let t=0;t<r.length;t++){const e=r[t];c=Math.min(c,e.l),l=Math.max(l,e.h)}return{min:c,max:l}}calculateElementProperties(t,e,n,i){const s=this,o=s._cachedMeta.vScale,a=o.getBasePixel(),r=s._calculateBarIndexPixels(t,e,i),c=s.chart.data.datasets[s.index].data[t],l=o.getPixelForValue(c.o),d=o.getPixelForValue(c.h),h=o.getPixelForValue(c.l),u=o.getPixelForValue(c.c);return{base:n?a:h,x:r.center,y:(h+d)/2,width:r.size,open:l,high:d,low:h,close:u}}draw(){const t=this,e=t.chart,n=t._cachedMeta.data;Fe(e.ctx,e.chartArea);for(let e=0;e<n.length;++e)n[e].draw(t._ctx);qe(e.ctx)}}class Pu extends Cu{static id="candlestick";static defaults={...Cu.defaults,borderWidth:1};draw(t){const e=this,{x:n,open:i,high:s,low:o,close:a}=e;let r,c=e.options.borderColors;"string"==typeof c&&(c={up:c,down:c,unchanged:c}),a<i?(r=mt(c?c.up:void 0,Le.elements.candlestick.borderColors.up),t.fillStyle=mt(e.options.backgroundColors?e.options.backgroundColors.up:void 0,Le.elements.candlestick.backgroundColors.up)):a>i?(r=mt(c?c.down:void 0,Le.elements.candlestick.borderColors.down),t.fillStyle=mt(e.options.backgroundColors?e.options.backgroundColors.down:void 0,Le.elements.candlestick.backgroundColors.down)):(r=mt(c?c.unchanged:void 0,Le.elements.candlestick.borderColors.unchanged),t.fillStyle=mt(e.backgroundColors?e.backgroundColors.unchanged:void 0,Le.elements.candlestick.backgroundColors.unchanged)),t.lineWidth=mt(e.options.borderWidth,Le.elements.candlestick.borderWidth),t.strokeStyle=r,t.beginPath(),t.moveTo(n,s),t.lineTo(n,Math.min(i,a)),t.moveTo(n,o),t.lineTo(n,Math.max(i,a)),t.stroke(),t.fillRect(n-e.width/2,a,e.width,i-a),t.strokeRect(n-e.width/2,a,e.width,i-a),t.closePath()}}class Eu extends Tu{static id="candlestick";static defaults={...Tu.defaults,dataElementType:Pu.id};static defaultRoutes=Ci.defaultRoutes;updateElements(t,e,n,i){const s="reset"===i,o=this._getRuler(),{sharedOptions:a,includeOptions:r}=this._getSharedOptions(e,i);for(let c=e;c<e+n;c++){const e=a||this.resolveDataElementOptions(c,i),n=this.calculateElementProperties(c,o,s,e);r&&(n.options=e),this.updateElement(t[c],c,n,i)}}}eo.register(...er,yh,ku,Eu,Pu);class Au extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for IntradayChartWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for IntradayChartWidget");const i=g(e.symbol);if(!i.valid)throw new Error(`Invalid symbol: ${i.error}`);this.type="intraday-chart",this.wsManager=e.wsManager,this.symbol=i.sanitized,this.debug=e.debug||!1,this.source=e.source||"blueocean",this.rangeBack=e.rangeBack||0,this.chartType=e.chartType||"line",this.autoRefresh=void 0===e.autoRefresh||e.autoRefresh,this.refreshInterval=e.refreshInterval||6e4,this.refreshTimer=null,this.isUserInteracting=!1,this.marketIsOpen=null,this.marketStatusChecked=!1,this.chartData=null,this.chartInstance=null,this.unsubscribe=null,this.livePrice=null,this.companyName="",this.exchangeName="",this.mic="",this.symbolEditor=null,this.cached5DData=null,this.is5DDataLoading=!1,this.createWidgetStructure(),this.setupSymbolEditor(),this.initialize()}createWidgetStructure(){this.container.innerHTML='\n <div class="intraday-chart-widget">\n <div class="chart-header">\n <div class="chart-title-section">\n <div class="company-market-info">\n <span class="intraday-company-name"></span>\n </div>\n <h3 class="intraday-chart-symbol editable-symbol"\n title="Double-click to edit symbol"\n data-original-symbol="">AAPL</h3>\n </div>\n <div class="chart-change positive">+0.00 (+0.00%)</div>\n </div>\n\n <div class="chart-controls">\n <div class="chart-range-selector">\n <button class="range-btn active" data-range="0">1D</button>\n <button class="range-btn" data-range="5">5D</button>\n </div>\n <div class="chart-type-selector">\n <button class="type-btn active" data-type="line" title="Line Chart">Line</button>\n <button class="type-btn" data-type="area" title="Area Chart">Mountain</button>\n <button class="type-btn" data-type="candlestick" title="Candlestick Chart">Candles</button>\n </div>\n <button class="zoom-reset-btn" title="Reset Zoom">\n <svg width="16" height="16" viewBox="0 0 16 16" fill="none">\n <path d="M2 8a6 6 0 1 1 12 0A6 6 0 0 1 2 8zm6-7a7 7 0 1 0 0 14A7 7 0 0 0 8 1z" fill="currentColor"/>\n <path d="M5 8h6M8 5v6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>\n </svg>\n Reset Zoom\n </button>\n </div>\n\n <div class="chart-container">\n <canvas id="intradayChart"></canvas>\n </div>\n\n <div class="chart-stats">\n <div class="stat-item stat-open">\n <span class="stat-label">Open</span>\n <span class="stat-value">$0.00</span>\n </div>\n <div class="stat-item stat-high">\n <span class="stat-label">High</span>\n <span class="stat-value">$0.00</span>\n </div>\n <div class="stat-item stat-low">\n <span class="stat-label">Low</span>\n <span class="stat-value">$0.00</span>\n </div>\n <div class="stat-item stat-close">\n <span class="stat-label">Close</span>\n <span class="stat-value">$0.00</span>\n </div>\n <div class="stat-item stat-volume">\n <span class="stat-label">Volume</span>\n <span class="stat-value">0</span>\n </div>\n </div>\n\n <div class="widget-loading-overlay hidden">\n <div class="loading-spinner"></div>\n <div class="loading-text">Loading chart data...</div>\n </div>\n </div>\n';const t=this.container.querySelector(".intraday-chart-symbol");t&&(t.textContent=this.symbol),this.setupRangeButtons(),this.setupChartTypeButtons(),this.addStyles()}setupSymbolEditor(){this.symbolEditor=new s(this,{maxLength:10,placeholder:"Enter symbol...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,symbolType:"nightsession"});const t=this.container.querySelector(".intraday-chart-symbol");t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol)}async handleSymbolChange(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;this.debug&&(console.log("[IntradayChartWidget] Symbol change requested:",t),console.log("[IntradayChartWidget] Validation data:",n));const i=g(t);if(!i.valid)return this.debug&&console.log("[IntradayChartWidget] Invalid symbol:",i.error),{success:!1,error:i.error};const s=i.sanitized;if(n&&(this.companyName=n.comp_name||"",this.exchangeName=n.market_name||"",this.mic=n.mic||"",this.debug&&console.log("[IntradayChartWidget] Extracted company info:",{companyName:this.companyName,exchangeName:this.exchangeName})),s===this.symbol)return this.debug&&console.log("[IntradayChartWidget] Same symbol, no change needed"),{success:!0};try{return this.showLoading(),this.stopAutoRefresh(),this.unsubscribe&&(this.debug&&console.log(`[IntradayChartWidget] Unsubscribing from ${e}`),this.unsubscribe(),this.unsubscribe=null),this.chartInstance&&(this.debug&&console.log("[IntradayChartWidget] Clearing chart for symbol change"),this.chartInstance.destroy(),this.chartInstance=null),this.chartData=null,this.livePrice=null,this.cached5DData=null,this.is5DDataLoading=!1,this.symbol=s,this.updateCompanyName(),await this.loadChartData(),this.debug&&console.log(`[IntradayChartWidget] Successfully changed symbol from ${e} to ${s}`),{success:!0}}catch(t){return console.error("[IntradayChartWidget] Error changing symbol:",t),this.showError(`Failed to load data for ${s}`),{success:!1,error:`Failed to load ${s}`}}}async initialize(){await this.validateInitialSymbol(),this.updateCompanyName(),await this.loadChartData()}async validateInitialSymbol(){try{const t=this.wsManager.getApiService();if(!t)return void(this.debug&&console.log("[IntradayChartWidget] API service not available for initial validation"));const e=await t.quotel1(this.symbol);if(e&&e.data&&e.data[0]){const t=e.data[0];this.companyName=t.comp_name||"",this.exchangeName=t.market_name||"",this.mic=t.mic||"",this.debug&&console.log("[IntradayChartWidget] Initial symbol validated:",{symbol:this.symbol,companyName:this.companyName,exchangeName:this.exchangeName,mic:this.mic})}}catch(t){this.debug&&console.warn("[IntradayChartWidget] Initial symbol validation failed:",t)}}updateCompanyName(){const t=this.container.querySelector(".intraday-company-name");t&&(t.textContent=this.companyName||"")}setupRangeButtons(){const t=this.container.querySelectorAll(".range-btn");t.forEach((e=>{e.addEventListener("click",(async e=>{const n=parseInt(e.target.getAttribute("data-range"));t.forEach((t=>t.classList.remove("active"))),e.target.classList.add("active"),this.rangeBack=n,await this.loadChartData(),0===n?(await this.startAutoRefresh(),await this.subscribeToLivePrice()):(this.stopAutoRefresh(),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null,this.livePrice=null))}))}))}setupChartTypeButtons(){const t=this.container.querySelectorAll(".type-btn");t.forEach((e=>{e.addEventListener("click",(e=>{const n=e.target.getAttribute("data-type");t.forEach((t=>t.classList.remove("active"))),e.target.classList.add("active"),this.chartType=n,this.renderChart(),this.updateStats()}))}))}addStyles(){if(this.options.skipStyleInjection||document.querySelector('link[href*="mdas-styles.css"]'))this.debug&&console.log("[IntradayChartWidget] Skipping style injection - external styles detected");else if(!document.querySelector("#intraday-chart-styles"))try{const t=document.createElement("style");t.id="intraday-chart-styles",t.textContent="\n .intraday-chart-widget {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;\n background: white;\n border-radius: 12px;\n padding: 20px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n position: relative;\n width: 100%;\n min-width: 600px;\n max-width: 1400px;\n margin: 0 auto;\n }\n\n .chart-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n padding-bottom: 15px;\n border-bottom: 1px solid #e5e7eb;\n }\n\n .chart-title-section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .company-market-info {\n display: flex;\n gap: 8px;\n align-items: center;\n font-size: 0.75em;\n color: #6b7280;\n }\n\n .intraday-company-name {\n font-weight: 500;\n }\n\n .intraday-chart-symbol {\n font-size: 1.5em;\n font-weight: 700;\n color: #1f2937;\n margin: 0;\n }\n\n .intraday-chart-source {\n font-size: 0.75em;\n padding: 4px 8px;\n border-radius: 4px;\n background: #e0e7ff;\n color: #4338ca;\n font-weight: 600;\n }\n\n .chart-change {\n font-size: 1.1em;\n font-weight: 600;\n padding: 6px 12px;\n border-radius: 6px;\n }\n\n .chart-change.positive {\n color: #059669;\n background: #d1fae5;\n }\n\n .chart-change.negative {\n color: #dc2626;\n background: #fee2e2;\n }\n\n .chart-controls {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 15px;\n }\n\n .chart-range-selector {\n display: flex;\n gap: 8px;\n }\n\n .range-btn {\n padding: 8px 16px;\n border: 1px solid #e5e7eb;\n background: white;\n border-radius: 6px;\n font-size: 0.875em;\n font-weight: 600;\n color: #6b7280;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .range-btn:hover {\n background: #f9fafb;\n border-color: #d1d5db;\n }\n\n .range-btn.active {\n background: #667eea;\n color: white;\n border-color: #667eea;\n }\n\n .range-btn:focus {\n outline: none;\n box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);\n }\n\n .chart-type-selector {\n display: flex;\n gap: 8px;\n }\n\n .type-btn {\n padding: 8px 16px;\n border: 1px solid #e5e7eb;\n background: white;\n border-radius: 6px;\n font-size: 0.875em;\n font-weight: 600;\n color: #6b7280;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .type-btn:hover {\n background: #f9fafb;\n border-color: #d1d5db;\n }\n\n .type-btn.active {\n background: #10b981;\n color: white;\n border-color: #10b981;\n }\n\n .type-btn:focus {\n outline: none;\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);\n }\n\n .zoom-reset-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 12px;\n border: 1px solid #e5e7eb;\n background: white;\n border-radius: 6px;\n font-size: 0.875em;\n font-weight: 500;\n color: #6b7280;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .zoom-reset-btn:hover {\n background: #f9fafb;\n border-color: #667eea;\n color: #667eea;\n }\n\n .zoom-reset-btn:focus {\n outline: none;\n box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);\n }\n\n .zoom-reset-btn svg {\n flex-shrink: 0;\n }\n\n .chart-container {\n height: 500px;\n margin-bottom: 20px;\n position: relative;\n }\n\n .chart-stats {\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n gap: 15px;\n padding: 15px;\n background: #f9fafb;\n border-radius: 8px;\n }\n\n .stat-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .stat-label {\n font-size: 0.75em;\n color: #6b7280;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .stat-value {\n font-size: 1.1em;\n font-weight: 700;\n color: #1f2937;\n }\n\n .widget-loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(255, 255, 255, 0.4);\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 10px 16px;\n border-radius: 12px;\n z-index: 10;\n pointer-events: none;\n backdrop-filter: blur(1px);\n }\n\n .widget-loading-overlay.hidden {\n display: none;\n }\n\n .loading-spinner {\n width: 20px;\n height: 20px;\n border: 3px solid #e5e7eb;\n border-top-color: #667eea;\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n }\n\n .loading-text {\n color: #6b7280;\n font-size: 0.875em;\n font-weight: 500;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n @media (max-width: 768px) {\n .intraday-chart-widget {\n min-width: unset;\n padding: 15px;\n }\n\n .chart-stats {\n grid-template-columns: repeat(3, 1fr);\n gap: 10px;\n }\n\n .chart-container {\n height: 350px;\n }\n\n .intraday-chart-symbol {\n font-size: 1.2em;\n }\n }\n\n .widget-error {\n padding: 15px;\n background: #fee2e2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n color: #dc2626;\n font-size: 0.9em;\n margin-top: 10px;\n }\n",document.head.appendChild(t),this.debug&&console.log("[IntradayChartWidget] Styles injected successfully")}catch(t){console.warn("[IntradayChartWidget] Failed to inject styles:",t),console.warn('[IntradayChartWidget] Please add <link rel="stylesheet" href="mdas-styles.css"> to your HTML')}}async loadChartData(){try{this.showLoading(),this.clearError();const t=this.wsManager.getApiService();if(!t)throw new Error("API service not available");if(5===this.rangeBack)return void await this.load5DayChartData(t);const e=7;let n=this.rangeBack,i=!1,s=null,o=null,a=!1;for(let r=0;r<e;r++){if(this.debug&&console.log(`[IntradayChartWidget] Loading data for ${this.symbol} from ${this.source}, range_back: ${n} (attempt ${r+1}/${e})`),s=await t.getIntradayChart(this.source,this.symbol,n),console.log("[IntradayChartWidget] API response type:",typeof s),console.log("[IntradayChartWidget] API response:",s),s&&(console.log("[IntradayChartWidget] Response keys:",Object.keys(s)),Array.isArray(s)&&(console.log("[IntradayChartWidget] Response is array, length:",s.length),s.length>0&&(console.log("[IntradayChartWidget] First item:",s[0]),console.log("[IntradayChartWidget] First item keys:",Object.keys(s[0]))))),s&&s.error)throw new Error(s.error||"Server error");if(o=s,s&&s.data&&Array.isArray(s.data)&&(console.log("[IntradayChartWidget] Found data array in response.data"),o=s.data),o&&Array.isArray(o)&&o.length>0){i=!0,n!==this.rangeBack&&(a=!0,this.debug&&console.log(`[IntradayChartWidget] Using fallback data: requested rangeBack ${this.rangeBack}, got data from rangeBack ${n}`));break}console.log(`[IntradayChartWidget] No data for rangeBack ${n}, trying ${n+1}...`),n++}if(!i||!o||0===o.length)throw new Error(`No intraday data available for ${this.symbol} in the past ${e} days`);if(console.log("[IntradayChartWidget] Processing",o.length,"data points"),this.chartData=new T(o),console.log("[IntradayChartWidget] Model created with",this.chartData.dataPoints.length,"points"),0===this.chartData.dataPoints.length)throw console.error("[IntradayChartWidget] Model has no data points after processing"),new Error(`No valid data points for ${this.symbol}`);this.renderChart(),this.updateStats(),this.hideLoading(),this.debug&&console.log(`[IntradayChartWidget] Loaded ${o.length} data points`),0!==this.rangeBack||a?(this.stopAutoRefresh(),this.debug&&console.log("[IntradayChartWidget] Not starting auto-refresh for historical/fallback data")):(await this.startAutoRefresh(),this.subscribeToLivePrice(),this.preload5DDataInBackground())}catch(t){console.error("[IntradayChartWidget] Error loading chart data:",t),this.hideLoading();let e="Failed to load chart data";t.message.includes("No intraday data")?e=t.message:t.message.includes("API service not available")?e="Unable to connect to data service":t.message.includes("HTTP error")||t.message.includes("status: 4")?e=`Unable to load data for ${this.symbol}`:t.message.includes("status: 5")?e="Server error. Please try again later":t.message.includes("Failed to fetch")&&(e="Network error. Please check your connection"),this.showError(e)}}async load5DayChartData(t){try{if(this.cached5DData)return this.debug&&console.log("[IntradayChartWidget] Using cached 5D data"),this.chartData=this.cached5DData,this.renderChart(),this.updateStats(),this.hideLoading(),void this.stopAutoRefresh();const e=await this.fetch5DayData(t);if(this.chartData=new T(e),0===this.chartData.dataPoints.length)throw new Error(`No valid data points for ${this.symbol}`);this.cached5DData=this.chartData,this.renderChart(),this.updateStats(),this.hideLoading(),this.debug&&console.log(`[IntradayChartWidget] 5D chart loaded with ${this.chartData.dataPoints.length} data points`),this.stopAutoRefresh()}catch(t){throw console.error("[IntradayChartWidget] Error loading 5D chart data:",t),t}}async fetch5DayData(t){this.debug&&console.log("[IntradayChartWidget] Fetching 5D chart data with parallel requests (API limit: rangeback 0-6)");const e=[];for(let n=0;n<7;n++)e.push(t.getIntradayChart(this.source,this.symbol,n).then((t=>{let e=t;return t&&t.data&&Array.isArray(t.data)&&(e=t.data),{rangeBack:n,data:e}})).catch((t=>(this.debug&&console.warn(`[IntradayChartWidget] Request failed for rangeback ${n}:`,t),{rangeBack:n,data:null}))));const n=await Promise.all(e);this.debug&&console.log("[IntradayChartWidget] All parallel requests completed");const i=n.filter((t=>{const e=t.data&&Array.isArray(t.data)&&t.data.length>0;return this.debug&&(e?console.log(`[IntradayChartWidget] Rangeback ${t.rangeBack}: ${t.data.length} data points`):console.log(`[IntradayChartWidget] Rangeback ${t.rangeBack}: No data (skipped)`)),e})).slice(0,5);if(0===i.length)throw new Error(`No intraday data available for ${this.symbol} in the past 7 days`);this.debug&&(console.log(`[IntradayChartWidget] Collected ${i.length}/5 days of data`),i.length<5&&console.log(`[IntradayChartWidget] Note: Only ${i.length} days available within API limit (rangeback 0-6)`));const s=[];for(let t=i.length-1;t>=0;t--)s.push(...i[t].data);return this.debug&&console.log(`[IntradayChartWidget] Combined ${s.length} total data points from ${i.length} days`),s}preload5DDataInBackground(){this.is5DDataLoading||this.cached5DData||(this.is5DDataLoading=!0,this.debug&&console.log("[IntradayChartWidget] Starting background preload of 5D data..."),setTimeout((async()=>{try{const t=this.wsManager.getApiService();if(!t)return void(this.debug&&console.log("[IntradayChartWidget] API service not available for preload"));const e=await this.fetch5DayData(t),n=new T(e);this.cached5DData=n,this.debug&&console.log(`[IntradayChartWidget] ✓ Background preload complete: ${n.dataPoints.length} data points cached`)}catch(t){this.debug&&console.warn("[IntradayChartWidget] Background preload failed:",t)}finally{this.is5DDataLoading=!1}}),1e3))}subscribeToLivePrice(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null);const t="bruce"===this.source?"querybrucel1":"queryblueoceanl1";this.debug&&console.log(`[IntradayChartWidget] Subscribing to live price with ${t} for ${this.symbol}`),this.unsubscribe=this.wsManager.subscribe(this.widgetId,[t],this.handleMessage.bind(this),this.symbol)}handleError(t){this.debug&&console.error("[IntradayChartWidget] WebSocket error:",t.message||t)}handleData(t){console.log("[IntradayChartWidget] handleData called with:",t),console.log("[IntradayChartWidget] Looking for symbol:",this.symbol),console.log("[IntradayChartWidget] Message is array?",Array.isArray(t));let e=null;if(Array.isArray(t)){console.log("[IntradayChartWidget] Processing array format, length:",t.length);const n=t.find((t=>t.Symbol===this.symbol));console.log("[IntradayChartWidget] Found symbol data:",n),n&&!n.NotFound&&(e=n)}else"queryblueoceanl1"===t.type||"querybrucel1"===t.type?(console.log("[IntradayChartWidget] Processing wrapped format"),t[0]?.Symbol!==this.symbol||t[0].NotFound||(e=t[0])):t.Symbol!==this.symbol||t.NotFound||(console.log("[IntradayChartWidget] Processing direct format"),e=t);if(!e)return void console.log("[IntradayChartWidget] No matching price data found for symbol:",this.symbol);console.log("[IntradayChartWidget] Price data found:",e);let n=null;void 0!==e.LastPx&&null!==e.LastPx?(n=parseFloat(e.LastPx),console.log("[IntradayChartWidget] Price from LastPx:",n)):void 0!==e.Last&&null!==e.Last?(n=parseFloat(e.Last),console.log("[IntradayChartWidget] Price from Last:",n)):void 0!==e.last_price&&null!==e.last_price?(n=parseFloat(e.last_price),console.log("[IntradayChartWidget] Price from last_price:",n)):void 0!==e.TradePx&&null!==e.TradePx?(n=parseFloat(e.TradePx),console.log("[IntradayChartWidget] Price from TradePx:",n)):void 0!==e.Close&&null!==e.Close?(n=parseFloat(e.Close),console.log("[IntradayChartWidget] Price from Close:",n)):void 0!==e.close&&null!==e.close&&(n=parseFloat(e.close),console.log("[IntradayChartWidget] Price from close:",n)),console.log("[IntradayChartWidget] Extracted price:",n),n&&!isNaN(n)&&n>0?(this.livePrice=n,console.log("[IntradayChartWidget] Updating live price line to:",n),this.updateLivePriceLine(),console.log(`[IntradayChartWidget] Live price update: $${n.toFixed(2)}`)):console.log("[IntradayChartWidget] Invalid price, not updating:",n)}updateLivePriceLine(){this.chartInstance&&this.livePrice?this.chartInstance.options.plugins.annotation&&(this.chartInstance.options.plugins.annotation.annotations.livePriceLine.value=this.livePrice,this.chartInstance.options.plugins.annotation.annotations.livePriceLine.label.content=`$${this.livePrice.toFixed(2)}`,this.chartInstance.options.plugins.annotation.annotations.livePriceLine.label.display=!0,this.debug&&console.log("[IntradayChartWidget] Live price line updated:",{price:this.livePrice,label:this.chartInstance.options.plugins.annotation.annotations.livePriceLine.label.content}),this.chartInstance.update("none")):this.debug&&console.log("[IntradayChartWidget] Cannot update live price line:",{hasChart:!!this.chartInstance,livePrice:this.livePrice})}async isMarketOpen(){const t=new Date,e=t.toLocaleString("en-US",{timeZone:"America/New_York",weekday:"short"}),n=parseInt(t.toLocaleString("en-US",{timeZone:"America/New_York",hour12:!1,hour:"numeric"}),10),i=parseInt(t.toLocaleString("en-US",{timeZone:"America/New_York",minute:"numeric"}),10);if("Sat"===e||"Sun"===e)return!1;const s=n>=20||n<4;if(this.debug&&console.log(`[IntradayChartWidget] Market check: ET hour=${n}, minute=${i}, day=${e}, open=${s}`),!s)return this.debug&&console.log("[IntradayChartWidget] Market is currently closed (outside 8pm-4am ET on weekdays)."),!1;if(!this.marketStatusChecked)try{const t=this.wsManager.getApiService(),e=await t.getMarketStatus(this.source);if(e&&Array.isArray(e)&&e.length>0){const t=e[0].status;return this.marketIsOpen=t&&"open"===t.toLowerCase(),this.marketStatusChecked=!0,this.debug&&console.log(`[IntradayChartWidget] Market status from API: ${t} (${this.marketIsOpen?"Open":"Closed"})`),this.marketIsOpen}}catch(t){return this.debug&&console.warn("[IntradayChartWidget] Failed to get market status from API, assuming open based on time:",t),this.marketIsOpen=!0,this.marketStatusChecked=!0,!0}return!1!==this.marketIsOpen&&(!0!==this.marketIsOpen||(!(3===n&&i>=45)||(this.debug&&console.log("[IntradayChartWidget] Approaching market close, re-checking status..."),this.marketStatusChecked=!1,await this.isMarketOpen())))}async startAutoRefresh(){if(this.stopAutoRefresh(),!this.autoRefresh)return;await this.isMarketOpen()?(this.debug&&console.log(`[IntradayChartWidget] Starting auto-refresh every ${this.refreshInterval/1e3} seconds`),this.refreshTimer=setInterval((async()=>{if(!await this.isMarketOpen())return this.debug&&console.log("[IntradayChartWidget] Market closed. Stopping auto-refresh."),void this.stopAutoRefresh();if(this.isUserInteracting)this.debug&&console.log("[IntradayChartWidget] Skipping refresh - user is interacting");else{this.debug&&console.log("[IntradayChartWidget] Auto-refreshing chart data");try{const t=this.wsManager.getApiService(),e=await t.getIntradayChart(this.source,this.symbol,this.rangeBack);e&&Array.isArray(e)&&(this.chartData=new T(e),this.renderChart(),this.updateStats(),this.debug&&console.log(`[IntradayChartWidget] Auto-refresh complete - ${e.length} data points`))}catch(t){console.error("[IntradayChartWidget] Auto-refresh failed:",t)}}}),this.refreshInterval)):this.debug&&console.log("[IntradayChartWidget] Market is closed. Auto-refresh will not start.")}stopAutoRefresh(){this.refreshTimer&&(this.debug&&console.log("[IntradayChartWidget] Stopping auto-refresh"),clearInterval(this.refreshTimer),this.refreshTimer=null)}parseTimestamp(t){return new Date(t)}renderChart(){if(console.log("[IntradayChartWidget] renderChart called"),!this.chartData||0===this.chartData.dataPoints.length)return void console.error("[IntradayChartWidget] No data to render");console.log("[IntradayChartWidget] Chart data points:",this.chartData.dataPoints.length);const t=this.container.querySelector("#intradayChart");if(console.log("[IntradayChartWidget] Canvas element:",t),!t)return void console.error("[IntradayChartWidget] Canvas element not found");if(this.chartInstance){console.log("[IntradayChartWidget] Destroying existing chart instance");try{this.chartInstance.destroy(),this.chartInstance=null}catch(t){console.error("[IntradayChartWidget] Error destroying chart:",t),this.chartInstance=null}}const e=t.getContext("2d");console.log("[IntradayChartWidget] Canvas context:",e),console.log("[IntradayChartWidget] Preparing chart data...");const n=this.chartData.getStats();console.log("[IntradayChartWidget] Stats:",n);const i=n.change>=0,s=i?"#10b981":"#ef4444";let o,a;const r=this.chartData.dataPoints;if(0===r.length)return console.error("[IntradayChartWidget] No valid data points from model"),void this.showError("No valid chart data available");if(console.log("[IntradayChartWidget] Rendering",r.length,"data points"),"candlestick"===this.chartType){if(o=this.rangeBack>0?r.map(((t,e)=>({x:e,o:t.open,h:t.high,l:t.low,c:t.close}))):r.map((t=>({x:this.parseTimestamp(t.time).getTime(),o:t.open,h:t.high,l:t.low,c:t.close}))).filter((t=>!isNaN(t.x)&&t.x>0)),0===o.length)return console.error("[IntradayChartWidget] No valid candlestick data points after date parsing"),void this.showError("Unable to parse chart data timestamps");a={label:"Price",data:o,color:{up:"#10b981",down:"#ef4444",unchanged:"#6b7280"},borderColor:{up:"#10b981",down:"#ef4444",unchanged:"#6b7280"}}}else{if(o=this.rangeBack>0?r.map(((t,e)=>({x:e,y:t.close}))):r.map((t=>({x:this.parseTimestamp(t.time),y:t.close}))).filter((t=>{const e=t.x.getTime();return!isNaN(e)&&e>0})),0===o.length)return console.error("[IntradayChartWidget] No valid chart data points after date parsing"),void this.showError("Unable to parse chart data timestamps");const t=e.createLinearGradient(0,0,0,300);i?(t.addColorStop(0,"rgba(16, 185, 129, 0.3)"),t.addColorStop(1,"rgba(16, 185, 129, 0.01)")):(t.addColorStop(0,"rgba(239, 68, 68, 0.3)"),t.addColorStop(1,"rgba(239, 68, 68, 0.01)")),a={label:"Price",data:o,borderColor:s,backgroundColor:"area"===this.chartType?t:"transparent",borderWidth:2,fill:"area"===this.chartType,tension:.4,pointRadius:0,pointHoverRadius:4,pointBackgroundColor:s,pointBorderColor:"#fff",pointBorderWidth:2}}let c,l;if(console.log("[IntradayChartWidget] Chart data points prepared:",o.length),console.log("[IntradayChartWidget] First data point - Source time:",r[0].time),console.log("[IntradayChartWidget] First chart point:",o[0]),console.log("[IntradayChartWidget] Last data point - Source time:",r[r.length-1].time),console.log("[IntradayChartWidget] Last chart point:",o[o.length-1]),0===this.rangeBack){const t=o.map((t=>"candlestick"===this.chartType?t.x:t.x.getTime()));c=Math.min(...t),l=Math.max(...t),console.log("[IntradayChartWidget] X-axis bounds:",{min:c,max:l,minDate:new Date(c),maxDate:new Date(l)})}else console.log("[IntradayChartWidget] Using linear scale with",o.length,"data points");const d={type:"candlestick"===this.chartType?"candlestick":"line",data:{datasets:[a]},options:{responsive:!0,maintainAspectRatio:!1,interaction:{intersect:!1,mode:"index"},plugins:{legend:{display:!1},decimation:{enabled:o.length>1e3,algorithm:"lttb",samples:500,threshold:1e3},tooltip:{enabled:!0,backgroundColor:"rgba(0, 0, 0, 0.8)",titleColor:"#fff",bodyColor:"#fff",borderColor:s,borderWidth:1,padding:10,displayColors:!1,callbacks:{title:t=>{const e=t[0].dataIndex,n=r[e];if(n){return new Date(n.time).toLocaleString("en-US",{month:"2-digit",day:"2-digit",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0})}return""},label:t=>{const e=t.dataIndex,n=r[e];return n?"candlestick"===this.chartType?[`Open: $${n.open.toFixed(2)}`,`High: $${n.high.toFixed(2)}`,`Low: $${n.low.toFixed(2)}`,`Close: $${n.close.toFixed(2)}`,`Volume: ${this.chartData.formatVolume(n.volume)}`]:[`Price: $${n.close.toFixed(2)}`,`Open: $${n.open.toFixed(2)}`,`High: $${n.high.toFixed(2)}`,`Low: $${n.low.toFixed(2)}`,`Volume: ${this.chartData.formatVolume(n.volume)}`]:[]}}},zoom:{pan:{enabled:!0,mode:"x",modifierKey:"ctrl"},zoom:{wheel:{enabled:!0,speed:.1},pinch:{enabled:!0},mode:"x"},limits:{x:{min:"original",max:"original"}}},annotation:{annotations:this.createAnnotations(r,n)}},scales:{x:{type:0===this.rangeBack?"time":"linear",min:0===this.rangeBack?c:void 0,max:0===this.rangeBack?l:void 0,time:0===this.rangeBack?{unit:"hour",stepSize:1,displayFormats:{hour:"h:mm a"},tooltipFormat:"MMM d, h:mm a"}:void 0,grid:{display:!1},ticks:{maxTicksLimit:0===this.rangeBack?10:8,color:"#6b7280",autoSkip:!0,maxRotation:0,minRotation:0,callback:(t,e,n)=>{if(this.rangeBack>0){const e=Math.round(t),n=r[e];if(n){return new Date(n.time).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}return""}if("number"==typeof t){return new Date(t).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}return t}}},y:{position:"right",grid:{color:"rgba(0, 0, 0, 0.05)"},ticks:{color:"#6b7280",callback:function(t){return"$"+t.toFixed(2)}},afterDataLimits:t=>{const e=.1*(t.max-t.min);t.max+=e,t.min-=e}}}}};console.log("[IntradayChartWidget] Creating Chart.js instance with config:",d),console.log("[IntradayChartWidget] Chart available?",void 0!==eo);try{this.chartInstance=new eo(e,d),console.log("[IntradayChartWidget] Chart instance created successfully:",this.chartInstance)}catch(t){throw console.error("[IntradayChartWidget] Error creating chart:",t),t}t.addEventListener("mouseenter",(()=>{this.isUserInteracting=!0})),t.addEventListener("mouseleave",(()=>{this.isUserInteracting=!1})),this.setupZoomReset()}createAnnotations(t,e){const n={livePriceLine:{type:"line",scaleID:"y",value:this.livePrice||e.close,borderColor:"#667eea",borderWidth:2,borderDash:[5,5],label:{display:!0,content:this.livePrice?`$${this.livePrice.toFixed(2)}`:`$${e.close.toFixed(2)}`,enabled:!0,position:"end",backgroundColor:"rgb(102, 126, 234)",color:"#ffffff",font:{size:12,weight:"bold",family:"system-ui, -apple-system, sans-serif"},padding:{top:4,bottom:4,left:8,right:8},borderRadius:4,xAdjust:-10,yAdjust:0}}};if(this.rangeBack>0&&t.length>0){let e=null;t.forEach(((t,i)=>{const s=new Date(t.time).toDateString();e!==s&&null!==e&&(n[`daySeparator${i}`]={type:"line",scaleID:"x",value:i,borderColor:"rgba(0, 0, 0, 0.1)",borderWidth:1,borderDash:[3,3]}),e=s}))}return n}setupZoomReset(){const t=this.container.querySelector(".zoom-reset-btn");t&&t.addEventListener("click",(()=>{this.chartInstance&&(this.chartInstance.resetZoom(),this.debug&&console.log("[IntradayChartWidget] Zoom reset"))}))}updateStats(){if(!this.chartData)return;const t=this.chartData.getStats(),e=this.container.querySelector(".stat-high .stat-value"),n=this.container.querySelector(".stat-low .stat-value"),i=this.container.querySelector(".stat-open .stat-value"),s=this.container.querySelector(".stat-close .stat-value"),o=this.container.querySelector(".stat-volume .stat-value"),a=this.container.querySelector(".chart-change");if(e&&(e.textContent=`$${t.high.toFixed(2)}`),n&&(n.textContent=`$${t.low.toFixed(2)}`),i&&(i.textContent=`$${t.open.toFixed(2)}`),s&&(s.textContent=`$${t.close.toFixed(2)}`),o&&(o.textContent=this.chartData.formatVolume(t.volume)),a){const e=t.change>=0?"positive":"negative";a.className=`chart-change ${e}`;const n=t.change>=0?"+":"";a.textContent=`${n}${t.change.toFixed(2)} (${n}${t.changePercent.toFixed(2)}%)`}}async refreshData(){await this.loadChartData()}destroy(){this.stopAutoRefresh(),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.chartInstance&&(this.chartInstance.destroy(),this.chartInstance=null),this.symbolEditor&&(this.symbolEditor.destroy(),this.symbolEditor=null),super.destroy()}}class Lu{constructor(t){this.stateManager=t,this.widgets=new Map}createWidget(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const i=this.generateWidgetId();let s;switch(t){case"quote-level1":s=new b(e,n,i);break;case"night-session":s=new x(e,n,i);break;case"options":s=new w(e,n,i);break;case"option-chain":s=new M(e,n,i);break;case"data":s=new _(e,n,i);break;case"combined-market":s=new D(e,n,i);break;case"intraday-chart":s=new Au(e,n,i);break;default:throw new Error(`Unknown widget type: ${t}`)}return this.widgets.set(i,s),s}generateWidgetId(){return`widget_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}destroyWidget(t){const e=this.widgets.get(t);e&&(e.destroy(),this.widgets.delete(t))}getWidgetCount(){return this.widgets.size}destroy(){this.widgets.forEach((t=>t.destroy())),this.widgets.clear()}}class Iu{constructor(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"https://mdas-api-dev.viewtrade.dev";this.token=t,this.baseUrl=e}async request(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n=`${this.baseUrl}${t}`,i={method:"GET",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",...e.headers},...e};try{const t=await fetch(n,i);if(!t.ok)throw new Error(`HTTP error! status: ${t.status}`);return await t.json()}catch(t){throw console.error(`[ApiService] Request failed for ${n}:`,t),t}}async getOptionChainDates(t){return this.request(`/api/quote/option-chain-dates?symbol=${t}&response_camel_case=false`)}async quoteOptionl1(t){return this.request(`/api/quote/option-level1?option_names=${t}&response_camel_case=false`)}async quotel1(t){return this.request(`/api/quote/level1?symbols=${t}&response_camel_case=false`)}async quoteBlueOcean(t){return this.request(`/api/quote/night-session/level1/blueocean?symbols=${t}&response_camel_case=false`)}async getIntradayChart(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return this.request(`/api/quote/night-session/intraday/${t}?symbol=${e}&range_back=${n}`)}async getMarketStatus(t){return this.request(`/api/quote/night-session/market-status/${t}`)}}class Ou{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.config={debug:t.debug||!1,token:t.token,baseUrl:t.baseUrl||"ws://localhost:3000/wssub",apiBaseUrl:t.apiBaseUrl,maxConnections:t.maxConnections||1,reconnectOptions:{maxAttempts:t.reconnectOptions?.maxAttempts||10,initialDelay:t.reconnectOptions?.initialDelay||1e3,maxDelay:t.reconnectOptions?.maxDelay||3e4,backoff:t.reconnectOptions?.backoff||1.5,jitter:t.reconnectOptions?.jitter||.3},timeouts:{connection:t.timeouts?.connection||1e4,inactivity:t.timeouts?.inactivity||9e4},enableActivityMonitoring:!1},this.apiService=new Iu(this.config.token,this.config.apiBaseUrl),this.connection=null,this.connectionState="disconnected",this.reconnectAttempts=0,this.reconnectTimer=null,this.connectionStartTime=null,this.subscriptions=new Map,this.activeSubscriptions=new Set,this.messageQueue=[],this.symbolSubscriptions=new Map,this.typeSubscriptions=new Map,this.lastMessageCache=new Map,this.connectionPromise=null,this.reloginCallback=t.reloginCallback||null,this.isReloginInProgress=!1,this.lastMessageReceived=null,this.activityCheckInterval=null,this.isOnline="undefined"==typeof navigator||navigator.onLine,this.onlineHandler=null,this.offlineHandler=null,this.circuitBreaker={state:"closed",failureCount:0,failureThreshold:5,timeout:6e4,lastFailureTime:null,resetTimer:null},this.metrics={successfulConnections:0,failedConnections:0,totalReconnects:0,avgConnectionTime:0,lastSuccessfulConnection:null,messagesReceived:0,messagesSent:0},this.handleOpen=this.handleOpen.bind(this),this.handleMessage=this.handleMessage.bind(this),this.handleClose=this.handleClose.bind(this),this.handleError=this.handleError.bind(this),this.isManualDisconnect=!1,this._initNetworkMonitoring(),this.config.debug&&console.log("[WebSocketManager] Initialized with config:",this.config)}async connect(){return"connected"===this.connectionState||"connecting"===this.connectionState?Promise.resolve():new Promise(((t,e)=>{this.connectionPromise={resolve:t,reject:e},this._connect()}))}subscribe(t,e,n){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{};Array.isArray(e)||(e=[e]);const o=this.subscriptions.get(t);if(o){if(this.config.debug&&console.log(`[WebSocketManager] Cleaning up existing subscription for widget ${t} before re-subscribing`),o.symbol){const e=this.symbolSubscriptions.get(o.symbol);e&&(e.delete(t),0===e.size&&this.symbolSubscriptions.delete(o.symbol))}o.types&&o.types.forEach((e=>{const n=this.typeSubscriptions.get(e);n&&(n.delete(t),0===n.size&&this.typeSubscriptions.delete(e))}))}const a={types:new Set(e),callback:n,symbol:i?.toUpperCase(),additionalParams:s};if(this.subscriptions.set(t,a),i){const e=i.toUpperCase();this.symbolSubscriptions.has(e)||this.symbolSubscriptions.set(e,new Set),this.symbolSubscriptions.get(e).add(t)}return e.forEach((e=>{this.typeSubscriptions.has(e)||this.typeSubscriptions.set(e,new Set),this.typeSubscriptions.get(e).add(t)})),this.config.debug&&console.log(`[WebSocketManager] Widget ${t} subscribed to:`,e,`for symbol: ${i}`,s),"connected"===this.connectionState&&(this._sendSubscriptions(e),e.forEach((e=>{const s=i?`${e}:${i}`:e,o=this.lastMessageCache.get(s);if(o&&this.activeSubscriptions.has(s)){this.config.debug&&console.log(`[WebSocketManager] Sending cached data to new widget ${t} for ${s}`);try{n({event:"data",data:o,widgetId:t})}catch(e){console.error(`[WebSocketManager] Error sending cached data to widget ${t}:`,e)}}}))),()=>this.unsubscribe(t)}unsubscribe(t){const e=this.subscriptions.get(t);if(e){if(e.symbol&&e.types&&e.types.forEach((t=>{this.sendUnsubscribe(t,e.symbol,e.additionalParams||{})})),e.symbol){const n=this.symbolSubscriptions.get(e.symbol);n&&(n.delete(t),0===n.size&&this.symbolSubscriptions.delete(e.symbol))}e.types.forEach((e=>{const n=this.typeSubscriptions.get(e);n&&(n.delete(t),0===n.size&&this.typeSubscriptions.delete(e))})),this.subscriptions.delete(t),this.config.debug&&console.log(`[WebSocketManager] Widget ${t} unsubscribed`),this._cleanupUnusedSubscriptions()}}sendUnsubscribe(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};try{let i,s;"queryoptionchain"===t?(s=`${t}:${e}:${n.date||""}`,i={type:t,underlying:e,date:n.date,unsubscribe:!0}):"queryoptionl1"===t?(s=`${t}:${e}`,i={type:t,optionName:e,unsubscribe:!0}):(s=`${t}:${e}`,i={type:t,symbol:e,unsubscribe:!0}),this.config.debug&&console.log(`[WebSocketManager] Sending unsubscribe for ${s}:`,i),this.send(i),this.activeSubscriptions.delete(s),this.config.debug&&console.log(`[WebSocketManager] Removed ${s} from active subscriptions`)}catch(t){console.error("[WebSocketManager] Error sending unsubscribe:",t)}}sendUnsubscribeAll(){try{const t={unsubscribe_all:!0};this.config.debug&&console.log("[WebSocketManager] Sending unsubscribe all"),this.send(t),this.activeSubscriptions.clear()}catch(t){console.error("[WebSocketManager] Error sending unsubscribe all:",t)}}send(t){if("connected"!==this.connectionState||!this.connection||1!==this.connection.readyState)return this.messageQueue&&Array.isArray(this.messageQueue)||(this.messageQueue=[]),this.messageQueue.push(t),this.config.debug&&console.log("[WebSocketManager] Message queued (not connected):",t),!1;try{return this.connection.send(JSON.stringify(t)),this.config.debug&&console.log("[WebSocketManager] Sent message:",t),this.metrics.messagesSent++,!0}catch(t){return console.error("[WebSocketManager] Error sending message:",t),!1}}getConnectionState(){return this.connectionState}getSubscriptionCount(){return this.subscriptions.size}_connect(){if("connecting"!==this.connectionState){this.isManualDisconnect=!1,this.connectionState="connecting",this.connectionStartTime=Date.now();try{this.connection&&(this.connection.removeEventListener("open",this.handleOpen),this.connection.removeEventListener("message",this.handleMessage),this.connection.removeEventListener("close",this.handleClose),this.connection.removeEventListener("error",this.handleError),this.connection=null);const t=`${this.config.baseUrl}?token=${this.config.token}`;this.config.debug&&console.log("[WebSocketManager] Connecting to:",t),this.connection=new WebSocket(t);const e=setTimeout((()=>{this.connection&&0===this.connection.readyState&&(console.error("[WebSocketManager] Connection timeout"),this.connection.close(),this.connectionState="disconnected",this._updateCircuitBreaker(!1),this.connectionPromise&&(this.connectionPromise.reject(new Error("Connection timeout")),this.connectionPromise=null),this._scheduleReconnect())}),this.config.timeouts.connection);this.connection.addEventListener("open",(t=>{clearTimeout(e),this.handleOpen(t)})),this.connection.addEventListener("message",this.handleMessage),this.connection.addEventListener("close",(t=>{clearTimeout(e),this.handleClose(t)})),this.connection.addEventListener("error",(t=>{clearTimeout(e),this.handleError(t)}))}catch(t){console.error("[WebSocketManager] Connection error:",t),this.connectionState="disconnected",this.connectionPromise&&(this.connectionPromise.reject(t),this.connectionPromise=null),this._scheduleReconnect()}}}handleOpen(t){const e=Date.now()-this.connectionStartTime;this.connectionState="connected",this.reconnectAttempts=0,this.lastMessageReceived=Date.now(),this.metrics.successfulConnections++,this.metrics.lastSuccessfulConnection=Date.now();const n=this.metrics.avgConnectionTime,i=this.metrics.successfulConnections;this.metrics.avgConnectionTime=(n*(i-1)+e)/i,this._updateCircuitBreaker(!0),this.config.debug&&console.log("[WebSocketManager] Connected successfully",{connectionTime:`${e}ms`,avgConnectionTime:`${Math.round(this.metrics.avgConnectionTime)}ms`,attempt:this.metrics.totalReconnects+1}),this.connectionPromise&&(this.connectionPromise.resolve(),this.connectionPromise=null);try{this._startActivityMonitoring(),this._sendAllSubscriptions(),this._processMessageQueue(),this._notifyWidgets("connection",{status:"connected"})}catch(t){console.error("[WebSocketManager] Error in handleOpen:",t)}}handleMessage(t){try{let e;this.lastMessageReceived=Date.now(),this.metrics.messagesReceived++;try{e=JSON.parse(t.data)}catch(n){const i=t.data.toString();if(this.config.debug&&console.log("[WebSocketManager] Received plain text message:",i),i.toLowerCase().includes("session revoked")||i.toLowerCase().includes("please relogin")||i.toLowerCase().includes("session expired")||i.toLowerCase().includes("unauthorized")||i.toLowerCase().includes("authentication failed"))return this.config.debug&&console.log("[WebSocketManager] Session revoked detected, attempting relogin..."),void this._handleSessionRevoked(i);const s=this._getTargetTypeFromErrorMessage(i);e=i.toLowerCase().includes("no night session")||i.toLowerCase().includes("no data")?{type:"error",message:i,error:i,noData:!0,targetType:s}:{type:"error",message:i,error:i,targetType:s}}this.config.debug&&console.log("[WebSocketManager] Processed message:",e),this._routeMessage(e)}catch(t){console.error("[WebSocketManager] Error handling message:",t),this._notifyWidgets("error",{error:"Failed to process message",details:t.message})}}handleClose(t){const e="connected"===this.connectionState;this.connectionState="disconnected",this.activeSubscriptions.clear(),this._stopActivityMonitoring(),this.config.debug&&console.log(`[WebSocketManager] Connection closed: ${t.code} ${t.reason}`),e&&this._notifyWidgets("connection",{status:"disconnected",code:t.code,reason:t.reason}),!this.isManualDisconnect&&this.subscriptions.size>0?(this.metrics.totalReconnects++,this._scheduleReconnect()):this.isManualDisconnect&&(this.config.debug&&console.log("[WebSocketManager] Manual disconnect - skipping reconnection"),this.isManualDisconnect=!1)}handleError(t){const e=this.connection?this.connection.readyState:"no connection";console.error("[WebSocketManager] WebSocket error:",{readyState:e,stateName:{0:"CONNECTING",1:"OPEN",2:"CLOSING",3:"CLOSED"}[e]||"UNKNOWN",url:this.connection?.url,error:t}),this.connection&&(3===this.connection.readyState?(console.log("[WebSocketManager] Connection closed due to error"),this.connectionState="disconnected",!this.isManualDisconnect&&this.subscriptions.size>0?(console.log("[WebSocketManager] Attempting reconnection after error..."),this._scheduleReconnect()):this.isManualDisconnect&&console.log("[WebSocketManager] Manual disconnect - skipping reconnection after error")):2===this.connection.readyState&&console.log("[WebSocketManager] Connection closing...")),this._notifyWidgets("connection",{status:"error",error:"WebSocket connection error",readyState:e,details:"Connection failed or was closed unexpectedly"}),this.connectionPromise&&(this.connectionPromise.reject(new Error("WebSocket connection failed")),this.connectionPromise=null)}_sendAllSubscriptions(){try{const t=new Set;this.subscriptions.forEach((e=>{e&&e.types&&e.types.forEach((e=>t.add(e)))})),this._sendSubscriptions([...t])}catch(t){console.error("[WebSocketManager] Error sending subscriptions:",t)}}_sendSubscriptions(t){t.forEach((t=>{if("queryoptionchain"===t)this._sendOptionChainSubscriptions();else{this._getSymbolsForType(t).forEach((e=>{const n=`${t}:${e}`;if(this.activeSubscriptions.has(n))this.config.debug&&console.log(`[WebSocketManager] Subscription ${n} already active, skipping`);else{let i;i="queryoptionl1"===t?{type:t,optionName:e}:{type:t,symbol:e},this.config.debug&&console.log(`[WebSocketManager] Sending subscription for ${n}`),this.send(i)&&this.activeSubscriptions.add(n)}}))}}))}_sendOptionChainSubscriptions(){this.subscriptions.forEach(((t,e)=>{if(t.types.has("queryoptionchain")&&t.symbol&&t.additionalParams?.date){const e=`queryoptionchain:${t.symbol}:${t.additionalParams.date}`;if(this.activeSubscriptions.has(e))this.config.debug&&console.log(`[WebSocketManager] Option chain subscription ${e} already active, skipping`);else{const n={type:"queryoptionchain",underlying:t.symbol,date:t.additionalParams.date};this.config.debug&&console.log(`[WebSocketManager] Sending option chain subscription for ${e}`),this.send(n)&&this.activeSubscriptions.add(e)}}}))}_getSymbolsForType(t){const e=new Set;return this.subscriptions.forEach(((n,i)=>{n.types.has(t)&&n.symbol&&e.add(n.symbol)})),[...e]}_routeMessage(t){if(Array.isArray(t)&&0===t.length)return void(this.config.debug&&console.log("[WebSocketManager] Received empty array, ignoring"));if(this._cacheMessage(t),t.targetType){const e=this.typeSubscriptions.get(t.targetType);if(e&&e.size>0)return this.config.debug&&console.log(`[WebSocketManager] Routing ${t.targetType} error to specific widgets:`,[...e]),void e.forEach((e=>{const n=this.subscriptions.get(e);if(n)try{n.callback({event:"data",data:t,widgetId:e})}catch(t){console.error(`[WebSocketManager] Error in widget ${e} callback:`,t)}}))}const e=this._getRelevantWidgets(t);if(this.config.debug&&e.size>0&&console.log(`[WebSocketManager] Routing message to ${e.size} relevant widgets`),0===e.size&&this.subscriptions.size>0)return this.config.debug&&console.log("[WebSocketManager] No specific routing found, broadcasting to all widgets"),void this.subscriptions.forEach(((e,n)=>{try{e.callback({event:"data",data:t,widgetId:n})}catch(t){console.error(`[WebSocketManager] Error in widget ${n} callback:`,t)}}));e.forEach((e=>{const n=this.subscriptions.get(e);if(n)try{n.callback({event:"data",data:t,widgetId:e})}catch(t){console.error(`[WebSocketManager] Error in widget ${e} callback:`,t)}}))}_cacheMessage(t){try{const e=this._extractSymbol(t),n=this._extractMessageType(t);if(n&&e){const i=`${n}:${e}`;this.lastMessageCache.set(i,t),this.config.debug&&console.log(`[WebSocketManager] Cached message for ${i}`)}if(Array.isArray(t)&&t.length>0&&t[0].Symbol){const e=t[0].Symbol,n=this._extractMessageType(t);if(n&&e){const i=`${n}:${e}`;this.lastMessageCache.set(i,t),this.config.debug&&console.log(`[WebSocketManager] Cached array message for ${i}`)}}t.Data&&Array.isArray(t.Data)&&t.Data.forEach((e=>{if(e.Symbol){const n=`queryl1:${e.Symbol}`;this.lastMessageCache.set(n,t),this.config.debug&&console.log(`[WebSocketManager] Cached data item for ${n}`)}}))}catch(t){console.error("[WebSocketManager] Error caching message:",t)}}_getRelevantWidgets(t){const e=new Set;return this._addRelevantWidgetsForItem(t,e),e}_addRelevantWidgetsForItem(t,e){t.Data&&Array.isArray(t.Data)?t.Data.forEach((t=>{this._processDataItem(t,e)})):t&&t[0]&&void 0!==t[0].Strike&&t[0].Expire&&!t[0].underlyingSymbol?this._processOptionChainData(t,e):this._processDataItem(t,e)}_processOptionChainData(t,e){if(!t||0===t.length)return;const n=t[0],i=this._extractUnderlyingFromOption(n.Symbol),s=n.Expire;if(this.config.debug&&console.log("[WebSocketManager] Processing option chain data:",{underlyingSymbol:i,expireDate:s,contractCount:t.length}),i){const t=this.symbolSubscriptions.get(i);t&&t.forEach((t=>{const n=this.subscriptions.get(t);if(n&&n.types.has("queryoptionchain")){let o=!0;if(n.additionalParams?.date&&s){o=this._normalizeDate(n.additionalParams.date)===this._normalizeDate(s)}o&&(e.add(t),this.config.debug&&console.log(`[WebSocketManager] Routing option chain data for ${i} (${s}) to widget ${t}`))}}))}}_processDataItem(t,e){const n=this._extractSymbol(t),i=this._extractMessageType(t);if(this.config.debug&&console.log("[WebSocketManager] Processing data item:",{symbol:n,messageType:i,dataItem:t}),n){const t=this.symbolSubscriptions.get(n);t&&t.forEach((t=>{const s=this.subscriptions.get(t);if(s){this._isDataTypeCompatible(i,s.types)&&(e.add(t),this.config.debug&&console.log(`[WebSocketManager] Routing ${i} data for ${n} to widget ${t}`))}}))}if(i){const t=this.typeSubscriptions.get(i);t&&t.forEach((t=>{const s=this.subscriptions.get(t);!s||s.symbol&&s.symbol!==n||(e.add(t),this.config.debug&&console.log(`[WebSocketManager] Routing ${i} data to type-subscribed widget ${t}`))}))}}_isDataTypeCompatible(t,e){if(e.has(t))return!0;const n={queryoptionchain:["queryoptionchain","optionchain","option-chain"],queryl1:["queryl1","stock","equity"],queryoptionl1:["queryoptionl1","option","options"],queryblueoceanl1:["queryblueoceanl1","blueoceanl1","blueocean"],querybrucel1:["querybrucel1","brucel1","bruce"]};for(const i of e){if((n[i]||[i]).includes(t))return!0}return!1}_normalizeDate(t){if(!t)return null;if(t.includes("/")){const[e,n,i]=t.split("/");return`${i}-${e.padStart(2,"0")}-${n.padStart(2,"0")}`}return t}_extractSymbol(t){return Array.isArray(t)&&t[0]&&t[0].Source&&null!==t[0].Symbol&&(t[0].Source.toLowerCase().includes("blueocean")||t[0].Source.toLowerCase().includes("bruce"))&&(t=t[0]),t.Symbol&&!t.Underlying&&t.Strike?this._extractUnderlyingFromOption(t.Symbol):t.Symbol||t.RootSymbol||t.Underlying||t.symbol||t.rootSymbol||t.underlying}_isOptionSymbol(t){return/^[A-Z]+\d{6}[CP]\d+$/.test(t)}_extractUnderlyingFromOption(t){const e=t.match(/^([A-Z]+)\d{6}[CP]\d+$/),n=e?e[1]:t;return{SPXW:"SPX$",SPX:"SPX$",NDXP:"NDX$",RUT:"RUT$",VIX:"VIX$",DJX:"DJX$"}[n]||n}_extractMessageType(t){return t.type?t.type:t[0]&&t[0].Source&&(t[0].Source.toLowerCase().includes("blueocean")||t[0].Source.toLowerCase().includes("blueocean-d")||t[0].Source.toLowerCase().includes("bruce"))?t[0].Source.toLowerCase().includes("bruce")?"querybrucel1":"queryblueoceanl1":void 0!==t.Strike||void 0!==t.Expire||t.Symbol&&this._isOptionSymbol(t.Symbol)?"queryoptionl1":void 0!==t.BidPx||void 0!==t.AskPx?"queryl1":null}_notifyWidgets(t,e){try{this.subscriptions.forEach(((n,i)=>{try{n&&n.callback&&n.callback({event:t,data:e,widgetId:i})}catch(t){console.error(`[WebSocketManager] Error notifying widget ${i}:`,t)}}))}catch(t){console.error("[WebSocketManager] Error in _notifyWidgets:",t)}}_processMessageQueue(){if(!this.messageQueue||!Array.isArray(this.messageQueue))return this.config.debug&&console.warn("[WebSocketManager] messageQueue not properly initialized, creating new array"),void(this.messageQueue=[]);for(this.config.debug&&this.messageQueue.length>0&&console.log(`[WebSocketManager] Processing ${this.messageQueue.length} queued messages`);this.messageQueue.length>0;){const t=this.messageQueue.shift();t&&this.send(t)}}_cleanupUnusedSubscriptions(){const t=new Set;this.subscriptions.forEach((e=>{e.types.forEach((e=>t.add(e)))})),this.activeSubscriptions.forEach((e=>{const[n]=e.split(":");t.has(n)||this.activeSubscriptions.delete(e)}))}_scheduleReconnect(){if(!this.isOnline)return this.config.debug&&console.log("[WebSocketManager] Network offline, waiting for network to return..."),void this._notifyWidgets("connection",{status:"offline",reason:"Network offline - will reconnect when network returns"});if("open"===this.circuitBreaker.state){const t=Date.now()-this.circuitBreaker.lastFailureTime,e=this.circuitBreaker.timeout-t;return console.warn(`[WebSocketManager] Circuit breaker OPEN, will retry in ${Math.round(e/1e3)}s`),void this._notifyWidgets("connection",{status:"circuit_open",reason:"Too many connection failures - circuit breaker active",retryIn:Math.round(e/1e3)})}if(this.reconnectAttempts>=this.config.reconnectOptions.maxAttempts)return console.error("[WebSocketManager] Max reconnection attempts reached"),this.metrics.failedConnections++,void this._notifyWidgets("connection",{status:"failed",error:"Max reconnection attempts reached",maxAttempts:this.config.reconnectOptions.maxAttempts,canRetry:!0});this.connectionState="reconnecting",this.reconnectAttempts++;const t=this.config.reconnectOptions.initialDelay*Math.pow(this.config.reconnectOptions.backoff,this.reconnectAttempts-1),e=Math.min(t,this.config.reconnectOptions.maxDelay),n=e*this.config.reconnectOptions.jitter,i=(2*Math.random()-1)*n,s=Math.max(0,e+i);console.log(`[WebSocketManager] Scheduling reconnect attempt ${this.reconnectAttempts}/${this.config.reconnectOptions.maxAttempts} in ${Math.round(s)}ms`),this._notifyWidgets("connection",{status:"reconnecting",attempt:this.reconnectAttempts,maxAttempts:this.config.reconnectOptions.maxAttempts,delay:Math.round(s),estimatedTime:new Date(Date.now()+s).toISOString()}),this.reconnectTimer=setTimeout((()=>{this._connect()}),s)}_resetState(){this.connectionState="disconnected",this.reconnectAttempts=0,this.connectionPromise=null,this.subscriptions&&this.subscriptions instanceof Map||(this.subscriptions=new Map),this.activeSubscriptions&&this.activeSubscriptions instanceof Set||(this.activeSubscriptions=new Set),this.messageQueue&&Array.isArray(this.messageQueue)||(this.messageQueue=[]),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}_getTargetTypeFromErrorMessage(t){const e=t.toLowerCase();return e.includes("night session")||e.includes("blue ocean")||e.includes("bruce")||e.includes("blueocean")?e[0].toLowerCase().includes("bruce")?"querybrucel1":"queryblueoceanl1":e.includes("option chain")||e.includes("expiration date")?"queryoptionchain":e.includes("option")||e.includes("strike")||e.includes("expiration")?"queryoptionl1":e.includes("stock")||e.includes("equity")||e.includes("bid")||e.includes("ask")?"queryl1":null}async _handleSessionRevoked(t){if(this.isReloginInProgress)this.config.debug&&console.log("[WebSocketManager] Relogin already in progress, skipping...");else{this.isReloginInProgress=!0;try{if(this._notifyWidgets("session_revoked",{message:t,status:"attempting_relogin"}),this.config.debug&&console.log("[WebSocketManager] Session revoked, attempting relogin..."),this._closeConnection(),!this.reloginCallback||"function"!=typeof this.reloginCallback)throw new Error("No relogin callback provided");{const t=await this.reloginCallback();if(!(t&&"string"==typeof t&&t.length>0))throw new Error("Relogin failed: Invalid token received");this.updateToken(t),this.config.debug&&console.log("[WebSocketManager] Relogin successful, reconnecting..."),await this._reconnectAndResubscribe()}}catch(e){console.error("[WebSocketManager] Relogin failed:",e),this._notifyWidgets("session_revoked",{message:t,status:"relogin_failed",error:e.message}),this.connectionState="failed"}finally{this.isReloginInProgress=!1}}}async _reconnectAndResubscribe(){try{this.connectionState="disconnected",this.reconnectAttempts=0,this.activeSubscriptions.clear(),await this.connect(),this.config.debug&&console.log("[WebSocketManager] Reconnected successfully after relogin"),this._notifyWidgets("session_revoked",{status:"relogin_successful"})}catch(t){throw console.error("[WebSocketManager] Failed to reconnect after relogin:",t),t}}_closeConnection(){this.connection&&(this.connection.removeEventListener("open",this.handleOpen),this.connection.removeEventListener("message",this.handleMessage),this.connection.removeEventListener("close",this.handleClose),this.connection.removeEventListener("error",this.handleError),this.connection.readyState===WebSocket.OPEN&&this.connection.close(),this.connection=null),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.connectionState="disconnected"}setReloginCallback(t){this.reloginCallback=t,this.config.debug&&console.log("[WebSocketManager] Relogin callback set")}updateToken(t){this.config.token=t,this.apiService=new Iu(this.config.token,this.config.apiBaseUrl),this.config.debug&&console.log("[WebSocketManager] Token updated")}getApiService(){return this.apiService}async manualReconnect(){this.config.debug&&console.log("[WebSocketManager] Manual reconnect initiated"),this.reconnectAttempts=0,this.connectionState="disconnected",this.isManualDisconnect=!1,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this._notifyWidgets("connection",{status:"reconnecting",attempt:1,maxAttempts:this.config.reconnectOptions.maxAttempts,manual:!0});try{return await this.connect(),!0}catch(t){return console.error("[WebSocketManager] Manual reconnect failed:",t),!1}}_initNetworkMonitoring(){"undefined"!=typeof window&&(this.onlineHandler=()=>{this.isOnline=!0,this.config.debug&&console.log("[WebSocketManager] Network online detected"),"disconnected"===this.connectionState&&this.subscriptions.size>0&&(console.log("[WebSocketManager] Network restored, attempting reconnection..."),this.reconnectAttempts=0,this._scheduleReconnect())},this.offlineHandler=()=>{this.isOnline=!1,this.config.debug&&console.log("[WebSocketManager] Network offline detected"),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this._notifyWidgets("connection",{status:"offline",reason:"Network offline"})},window.addEventListener("online",this.onlineHandler),window.addEventListener("offline",this.offlineHandler))}_cleanupNetworkMonitoring(){"undefined"!=typeof window&&(this.onlineHandler&&(window.removeEventListener("online",this.onlineHandler),this.onlineHandler=null),this.offlineHandler&&(window.removeEventListener("offline",this.offlineHandler),this.offlineHandler=null))}_startActivityMonitoring(){this.config.enableActivityMonitoring?(this._stopActivityMonitoring(),this.activityCheckInterval=setInterval((()=>{if("connected"!==this.connectionState)return;const t=Date.now()-this.lastMessageReceived;t>this.config.timeouts.inactivity?(console.warn(`[WebSocketManager] No messages received in ${this.config.timeouts.inactivity}ms, connection may be dead`),this.connection&&this.connection.close()):this.config.debug&&t>3e4&&console.log(`[WebSocketManager] Last message: ${Math.round(t/1e3)}s ago`)}),15e3)):this.config.debug&&console.log("[WebSocketManager] Activity monitoring disabled")}_stopActivityMonitoring(){this.activityCheckInterval&&(clearInterval(this.activityCheckInterval),this.activityCheckInterval=null)}_updateCircuitBreaker(t){t?(this.circuitBreaker.failureCount=0,this.circuitBreaker.state="closed",this.circuitBreaker.resetTimer&&(clearTimeout(this.circuitBreaker.resetTimer),this.circuitBreaker.resetTimer=null),this.config.debug&&console.log("[WebSocketManager] Circuit breaker CLOSED - connection healthy")):(this.circuitBreaker.failureCount++,this.circuitBreaker.lastFailureTime=Date.now(),this.config.debug&&console.log(`[WebSocketManager] Circuit breaker failure ${this.circuitBreaker.failureCount}/${this.circuitBreaker.failureThreshold}`),this.circuitBreaker.failureCount>=this.circuitBreaker.failureThreshold&&(this.circuitBreaker.state="open",console.warn("[WebSocketManager] Circuit breaker OPEN - too many failures"),this.circuitBreaker.resetTimer=setTimeout((()=>{this.circuitBreaker.state="half-open",this.circuitBreaker.failureCount=0,console.log("[WebSocketManager] Circuit breaker HALF-OPEN - will attempt one reconnection"),this.subscriptions.size>0&&"connected"!==this.connectionState&&(this.reconnectAttempts=0,this._scheduleReconnect())}),this.circuitBreaker.timeout)))}getConnectionMetrics(){const t=this._calculateConnectionQuality();return{state:this.connectionState,quality:t,isOnline:this.isOnline,circuitBreakerState:this.circuitBreaker.state,reconnectAttempts:this.reconnectAttempts,maxReconnectAttempts:this.config.reconnectOptions.maxAttempts,successfulConnections:this.metrics.successfulConnections,failedConnections:this.metrics.failedConnections,totalReconnects:this.metrics.totalReconnects,avgConnectionTime:Math.round(this.metrics.avgConnectionTime),lastSuccessfulConnection:this.metrics.lastSuccessfulConnection?new Date(this.metrics.lastSuccessfulConnection).toISOString():null,messagesReceived:this.metrics.messagesReceived,messagesSent:this.metrics.messagesSent,activeSubscriptions:this.subscriptions.size,lastMessageReceived:this.lastMessageReceived?`${Math.round((Date.now()-this.lastMessageReceived)/1e3)}s ago`:"never"}}_calculateConnectionQuality(){if("connected"!==this.connectionState)return"poor";const t=this.lastMessageReceived?Date.now()-this.lastMessageReceived:1/0;return t<3e4&&this.metrics.avgConnectionTime<2e3?"excellent":t<6e4&&this.metrics.avgConnectionTime<5e3?"good":t<this.config.timeouts.inactivity?"fair":"poor"}enableMetricsLogging(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:3e4;this.metricsInterval&&clearInterval(this.metricsInterval),this.metricsInterval=setInterval((()=>{console.log("[WebSocketManager] Metrics:",this.getConnectionMetrics())}),t),console.log("[WebSocketManager] Metrics logging enabled (every "+t/1e3+"s)")}disableMetricsLogging(){this.metricsInterval&&(clearInterval(this.metricsInterval),this.metricsInterval=null,console.log("[WebSocketManager] Metrics logging disabled"))}disconnect(){this.isManualDisconnect=!0,this._stopActivityMonitoring(),this._cleanupNetworkMonitoring(),this.circuitBreaker.resetTimer&&(clearTimeout(this.circuitBreaker.resetTimer),this.circuitBreaker.resetTimer=null),this.disableMetricsLogging(),this._closeConnection(),this._notifyWidgets("connection",{status:"disconnected",manual:!0}),this.config.debug&&console.log("[WebSocketManager] Disconnected and cleaned up")}}class zu{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.config={apiBaseUrl:t.apiBaseUrl||"https://mdas-api-dev.viewtrade.dev",loginEndpoint:t.loginEndpoint||"/api/user/login",refreshEndpoint:t.refreshEndpoint||"/api/user/refresh",refreshBuffer:t.refreshBuffer||300,maxRetries:t.maxRetries||3,debug:t.debug||!1},this.credentials=null,this.currentToken=null,this.tokenExpiry=null,this.refreshTimer=null,this.isAuthenticating=!1,this.retryCount=0,this.authenticationPromise=null,this.listeners=new Map,this.config.debug&&console.log("[AuthManager] Initialized with config:",{...this.config,loginEndpoint:this.config.loginEndpoint})}setCredentials(t,e){this.credentials={user_name:t,password:e},this.config.debug&&console.log("[AuthManager] Credentials set for user:",t)}async authenticate(){if(this.isAuthenticating&&this.authenticationPromise)return this.config.debug&&console.log("[AuthManager] Authentication already in progress, waiting..."),await this.authenticationPromise;if(!this.credentials)throw new Error("No credentials provided. Call setCredentials() first.");this.isAuthenticating=!0,this.authenticationPromise=this._performAuthentication();try{return await this.authenticationPromise}finally{this.isAuthenticating=!1,this.authenticationPromise=null}}async getValidToken(){return this.currentToken?this._isTokenNearExpiry()?(this.config.debug&&console.log("[AuthManager] Token near expiry, refreshing..."),await this.refreshToken()):this.currentToken:await this.authenticate()}async refreshToken(){if(this.isAuthenticating)return this.currentToken;this.isAuthenticating=!0;try{const t=await this._performRefresh();return this._handleAuthSuccess(t),this.currentToken}catch(t){return this.config.debug&&console.log("[AuthManager] Token refresh failed, attempting re-login"),await this.authenticate()}finally{this.isAuthenticating=!1}}async handleAuthError(t){if(this.config.debug&&console.log("[AuthManager] Handling auth error:",t),this.retryCount>=this.config.maxRetries){const t=new Error(`Authentication failed after ${this.config.maxRetries} attempts`);throw this._notifyListeners("auth_failed",{error:t}),this.retryCount=0,t}this.retryCount++,this._clearToken();try{const t=await this.authenticate();return this.retryCount=0,this._notifyListeners("auth_recovered",{token:t}),t}catch(t){throw this._notifyListeners("auth_retry_failed",{error:t,attempt:this.retryCount}),t}}addEventListener(t,e){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e)}removeEventListener(t,e){this.listeners.has(t)&&this.listeners.get(t).delete(e)}destroy(){this._clearToken(),this.credentials=null,this.listeners.clear(),this.config.debug&&console.log("[AuthManager] Destroyed")}async _performLogin(){const t=`${this.config.apiBaseUrl}${this.config.loginEndpoint}`;this.config.debug&&console.log("[AuthManager] Attempting login to:",t);const e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({user_name:this.credentials.user_name,password:this.credentials.password})});if(!e.ok){const t=await e.json().catch((()=>({})));throw new Error(t.message||`Login failed: ${e.status} ${e.statusText}`)}const n=await e.json(),i=n.access_token,s=n.refresh_token,o=n.access_expiration,a=n.user_id;if(!i)throw console.error("[AuthManager] Login response missing token:",n),new Error("No token received from login response");let r=null;if(o)try{if(r=new Date(o),isNaN(r.getTime()))throw new Error("Invalid expiration date format")}catch(t){console.warn("[AuthManager] Could not parse expiration date:",n.expiration),r=new Date(Date.now()+36e5)}else r=new Date(Date.now()+18e6);return this.config.debug&&console.log("[AuthManager] Login successful:",{tokenLength:i.length,expiresAt:r.toISOString(),timeUntilExpiry:Math.round((r.getTime()-Date.now())/1e3/60)+" minutes"}),{token:i,refreshToken:s,expirationDate:r,user_id:a}}async _performRefresh(){return this.config.debug&&console.log("[AuthManager] No refresh endpoint available, performing full re-authentication"),await this._performLogin()}async _performAuthentication(){try{const t=await this._performLogin();return this._handleAuthSuccess(t),this.currentToken}catch(t){throw this._handleAuthError(t),t}}_handleAuthSuccess(t){this.currentToken=t.token,t.expirationDate&&(this.tokenExpiry=t.expirationDate),this.retryCount=0,this._scheduleTokenRefresh(),this.config.debug&&console.log("[AuthManager] Authentication successful, token expires:",this.tokenExpiry),this._notifyListeners("auth_success",{token:this.currentToken,expiresAt:this.tokenExpiry})}_handleAuthError(t){this.config.debug&&console.error("[AuthManager] Authentication failed:",t.message),this._notifyListeners("auth_error",{error:t})}_isTokenNearExpiry(){if(!this.tokenExpiry)return!0;const t=new Date,e=1e3*this.config.refreshBuffer;return t>=new Date(this.tokenExpiry.getTime()-e)}_scheduleTokenRefresh(){if(this.refreshTimer&&clearTimeout(this.refreshTimer),!this.tokenExpiry)return;const t=new Date,e=1e3*this.config.refreshBuffer,n=new Date(this.tokenExpiry.getTime()-e),i=Math.max(0,n.getTime()-t.getTime());this.config.debug&&console.log(`[AuthManager] Scheduling token refresh in ${i/1e3} seconds`),this.refreshTimer=setTimeout((async()=>{try{await this.refreshToken()}catch(t){console.error("[AuthManager] Scheduled refresh failed:",t)}}),i)}_clearToken(){this.currentToken=null,this.tokenExpiry=null,this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null)}_notifyListeners(t,e){this.listeners.has(t)&&this.listeners.get(t).forEach((n=>{try{n(e)}catch(e){console.error(`[AuthManager] Error in ${t} listener:`,e)}}))}}class Wu{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.config={username:t.username,password:t.password,token:t.token,authCallback:t.authCallback,wsUrl:t.wsUrl||"https://mdas-api-dev.viewtrade.dev/wss/sub",apiBaseUrl:t.apiBaseUrl||"https://mdas-api-dev.viewtrade.dev",debug:t.debug||!1,maxConnections:t.maxConnections||1,autoAuth:!1!==t.autoAuth,...t},this.stateManager=new e,this.widgetManager=new Lu(this.stateManager),this.wsManager=null,this.authManager=null,this.isInitialized=!1,this._validateAuthConfig()}async initialize(){try{return this.isInitialized?(console.warn("SDK already initialized"),this):(this.config.debug&&console.log("Initializing MDAS SDK with config:",{hasCredentials:!(!this.config.username||!this.config.password),hasToken:!!this.config.token,hasAuthCallback:!!this.config.authCallback,wsUrl:this.config.wsUrl,autoAuth:this.config.autoAuth}),this.config.username&&this.config.password&&await this._initializeBuiltInAuth(),this.isInitialized=!0,this)}catch(t){throw console.error("Failed to initialize SDK:",t),t}}async createWidget(t,e){let n,i,s,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(!this.isInitialized)throw new Error("SDK not initialized. Call initialize() first.");if(this.wsManager||await this._initializeWebSocketManager(),"object"!=typeof t||null===t||Array.isArray(t))n=t,i=e,s=o||{};else if(n=t.type,s=t.options||{},t.containerEl&&1===t.containerEl.nodeType)i=t.containerEl;else if(t.containerId){const e=String(t.containerId).replace(/^#/,"");let n="undefined"!=typeof document?document.getElementById(e):null;n||"undefined"==typeof document||(n=document.createElement("div"),n.id=e,document.body.appendChild(n)),i=n}else{const t="mdas-widget";let e="undefined"!=typeof document?document.getElementById(t):null;e||"undefined"==typeof document||(e=document.createElement("div"),e.id=t,document.body.appendChild(e)),i=e}const a={...s,wsManager:this.wsManager,debug:this.config.debug};return a.reconnect=async()=>await this.manualReconnect(),this.widgetManager.createWidget(n,i,a)}async login(t,e){this.authManager||(this.authManager=new zu({apiBaseUrl:this.config.apiBaseUrl,debug:this.config.debug})),this.authManager.setCredentials(t,e);const n=await this.authManager.authenticate();return this.wsManager&&await this._reconnectWithNewToken(n),n}async destroy(){this.authManager&&(this.authManager.destroy(),this.authManager=null),this.wsManager&&(this.wsManager.disconnect(),this.wsManager=null),this.widgetManager&&this.widgetManager.destroy(),this.isInitialized=!1}getConnectionState(){return this.wsManager?this.wsManager.getConnectionState():"disconnected"}getActiveWidgetCount(){return this.widgetManager?this.widgetManager.getWidgetCount():0}getSubscriptionCount(){return this.wsManager?this.wsManager.getSubscriptionCount():0}isAuthenticated(){return this.authManager?!!this.authManager.currentToken:!!this.config.token}disconnect(){this.wsManager&&(this.wsManager.disconnect(),this.config.debug&&console.log("[MdasSDK] WebSocket disconnected"))}async connect(){if(this.wsManager)try{await this.wsManager.connect(),this.config.debug&&console.log("[MdasSDK] WebSocket reconnected")}catch(t){throw console.error("[MdasSDK] Failed to reconnect:",t),t}else this.config.debug&&console.log("[MdasSDK] No WebSocketManager exists, initializing..."),await this._initializeWebSocketManager()}isConnected(){return!!this.wsManager&&"connected"===this.wsManager.getConnectionState()}async manualReconnect(){return!!this.wsManager&&(this.config.debug&&console.log("[MdasSDK] Manual reconnect requested"),await this.wsManager.manualReconnect())}_validateAuthConfig(){const t=this.config.username&&this.config.password,e=this.config.token,n=this.config.authCallback;if(!t&&!e&&!n)throw new Error("Authentication required: Provide username/password, token, or authCallback");[t,e,n].filter(Boolean).length>1&&console.warn("Multiple auth methods provided. Priority: credentials > token > authCallback")}async _initializeBuiltInAuth(){this.authManager=new zu({apiBaseUrl:this.config.apiBaseUrl,debug:this.config.debug}),this.authManager.setCredentials(this.config.username,this.config.password),this.authManager.addEventListener("auth_error",(t=>{console.error("Authentication error:",t.error)})),this.authManager.addEventListener("auth_success",(t=>{this.config.debug&&console.log("Authentication successful")})),this.authManager.addEventListener("auth_failed",(t=>{console.error("Authentication failed after retries:",t.error)})),this.config.autoAuth&&await this.authManager.authenticate()}async _initializeWebSocketManager(){const t=await this._getToken();this.wsManager=new Ou({debug:this.config.debug,token:t,baseUrl:this.config.wsUrl,apiBaseUrl:this.config.apiBaseUrl,maxConnections:this.config.maxConnections,reconnectOptions:{maxAttempts:5,delay:1e3,backoff:2},reloginCallback:async()=>{try{if(this.authManager){this.config.debug&&console.log("[MdasSDK] Attempting relogin after session revoked...");const t=await this.authManager.authenticate();return this.config.debug&&console.log("[MdasSDK] Relogin successful, new token obtained"),t}return this.config.authCallback?await this.config.authCallback():(console.error("[MdasSDK] No authentication method available for relogin"),null)}catch(t){return console.error("[MdasSDK] Relogin failed:",t),null}}}),this.wsManager.addEventListener=this.wsManager.addEventListener||(()=>{});try{await this.wsManager.connect(),this.config.debug&&console.log("WebSocketManager connected successfully")}catch(t){t.message.includes("401")||t.message.includes("403")?await this._handleWebSocketAuthError(t):console.error("Failed to connect WebSocketManager:",t)}}async _getToken(){if(this.authManager)return await this.authManager.getValidToken();if(this.config.authCallback){const t=await this.config.authCallback();return t.token||t}return this.config.token}async _handleWebSocketAuthError(t){if(!this.authManager)throw t;try{const e=await this.authManager.handleAuthError(t);await this._reconnectWithNewToken(e)}catch(t){throw console.error("Failed to recover from auth error:",t),t}}async _reconnectWithNewToken(t){this.wsManager&&(this.wsManager.updateToken(t),this.wsManager.disconnect(),await this.wsManager.connect())}}"undefined"!=typeof window&&(window.MdasSDK=Wu,window.MdasSDK.MdasSDK=Wu,window.MdasSDK.MarketDataModel=i,window.MdasSDK.NightSessionModel=y,window.MdasSDK.OptionsModel=v,window.MdasSDK.IntradayChartModel=T,window.MdasSDK.CombinedMarketWidget=D,window.MdasSDK.IntradayChartWidget=Au),t.CombinedMarketWidget=D,t.IntradayChartModel=T,t.IntradayChartWidget=Au,t.MarketDataModel=i,t.MdasSDK=Wu,t.NightSessionModel=y,t.OptionChainWidget=M,t.OptionsModel=v,t.default=Wu,Object.defineProperty(t,"__esModule",{value:!0})}));//# sourceMappingURL=mdas-sdk.min.js.map
55
+ */function Mu(t,e,n,i){const s=null===e,o=null===n,a=!(!t||s&&o)&&function(t,e){const{x:n,y:i,base:s,width:o,height:a}=t.getProps(["x","low","high","width","height"],e);let r,c,l,d,h;return t.horizontal?(h=a/2,r=Math.min(n,s),c=Math.max(n,s),l=i-h,d=i+h):(h=o/2,r=n-h,c=n+h,l=Math.min(i,s),d=Math.max(i,s)),{left:r,top:l,right:c,bottom:d}}(t,i);return a&&(s||e>=a.left&&e<=a.right)&&(o||n>=a.top&&n<=a.bottom)}class Cu extends Mo{static defaults={backgroundColors:{up:"rgba(75, 192, 192, 0.5)",down:"rgba(255, 99, 132, 0.5)",unchanged:"rgba(201, 203, 207, 0.5)"},borderColors:{up:"rgb(75, 192, 192)",down:"rgb(255, 99, 132)",unchanged:"rgb(201, 203, 207)"}};height(){return this.base-this.y}inRange(t,e,n){return Mu(this,t,e,n)}inXRange(t,e){return Mu(this,t,null,e)}inYRange(t,e){return Mu(this,null,t,e)}getRange(t){return"x"===t?this.width/2:this.height/2}getCenterPoint(t){const{x:e,low:n,high:i}=this.getProps(["x","low","high"],t);return{x:e,y:(i+n)/2}}tooltipPosition(t){const{x:e,open:n,close:i}=this.getProps(["x","open","close"],t);return{x:e,y:(n+i)/2}}}const _u=eo.defaults;class Du extends Cu{static id="ohlc";static defaults={...Cu.defaults,lineWidth:2,armLength:null,armLengthRatio:.8};draw(t){const e=this,{x:n,open:i,high:s,low:o,close:a}=e,r=mt(e.armLengthRatio,_u.elements.ohlc.armLengthRatio);let c=mt(e.armLength,_u.elements.ohlc.armLength);null===c&&(c=e.width*r*.5),t.strokeStyle=a<i?mt(e.options.borderColors?e.options.borderColors.up:void 0,_u.elements.ohlc.borderColors.up):a>i?mt(e.options.borderColors?e.options.borderColors.down:void 0,_u.elements.ohlc.borderColors.down):mt(e.options.borderColors?e.options.borderColors.unchanged:void 0,_u.elements.ohlc.borderColors.unchanged),t.lineWidth=mt(e.lineWidth,_u.elements.ohlc.lineWidth),t.beginPath(),t.moveTo(n,s),t.lineTo(n,o),t.moveTo(n-c,i),t.lineTo(n,i),t.moveTo(n+c,a),t.lineTo(n,a),t.stroke()}}class Tu extends Ci{static overrides={label:"",parsing:!1,hover:{mode:"label"},animations:{numbers:{type:"number",properties:["x","y","base","width","open","high","low","close"]}},scales:{x:{type:"timeseries",offset:!0,ticks:{major:{enabled:!0},source:"data",maxRotation:0,autoSkip:!0,autoSkipPadding:75,sampleSize:100}},y:{type:"linear"}},plugins:{tooltip:{intersect:!1,mode:"index",callbacks:{label(t){const e=t.parsed;if(!dt(e.y))return Le.plugins.tooltip.callbacks.label(t);const{o:n,h:i,l:s,c:o}=e;return`O: ${n} H: ${i} L: ${s} C: ${o}`}}}}};getLabelAndValue(t){const e=this,n=e.getParsed(t),i=e._cachedMeta.iScale.axis,{o:s,h:o,l:a,c:r}=n,c=`O: ${s} H: ${o} L: ${a} C: ${r}`;return{label:`${e._cachedMeta.iScale.getLabelForValue(n[i])}`,value:c}}getUserBounds(t){const{min:e,max:n,minDefined:i,maxDefined:s}=t.getUserBounds();return{min:i?e:Number.NEGATIVE_INFINITY,max:s?n:Number.POSITIVE_INFINITY}}getMinMax(t){const e=this._cachedMeta,n=e._parsed,i=e.iScale.axis,s=this._getOtherScale(t),{min:o,max:a}=this.getUserBounds(s);if(n.length<2)return{min:0,max:1};if(t===e.iScale)return{min:n[0][i],max:n[n.length-1][i]};const r=n.filter((({x:t})=>t>=o&&t<a));let c=Number.POSITIVE_INFINITY,l=Number.NEGATIVE_INFINITY;for(let t=0;t<r.length;t++){const e=r[t];c=Math.min(c,e.l),l=Math.max(l,e.h)}return{min:c,max:l}}calculateElementProperties(t,e,n,i){const s=this,o=s._cachedMeta.vScale,a=o.getBasePixel(),r=s._calculateBarIndexPixels(t,e,i),c=s.chart.data.datasets[s.index].data[t],l=o.getPixelForValue(c.o),d=o.getPixelForValue(c.h),h=o.getPixelForValue(c.l),u=o.getPixelForValue(c.c);return{base:n?a:h,x:r.center,y:(h+d)/2,width:r.size,open:l,high:d,low:h,close:u}}draw(){const t=this,e=t.chart,n=t._cachedMeta.data;Fe(e.ctx,e.chartArea);for(let e=0;e<n.length;++e)n[e].draw(t._ctx);qe(e.ctx)}}class Pu extends Cu{static id="candlestick";static defaults={...Cu.defaults,borderWidth:1};draw(t){const e=this,{x:n,open:i,high:s,low:o,close:a}=e;let r,c=e.options.borderColors;"string"==typeof c&&(c={up:c,down:c,unchanged:c}),a<i?(r=mt(c?c.up:void 0,Le.elements.candlestick.borderColors.up),t.fillStyle=mt(e.options.backgroundColors?e.options.backgroundColors.up:void 0,Le.elements.candlestick.backgroundColors.up)):a>i?(r=mt(c?c.down:void 0,Le.elements.candlestick.borderColors.down),t.fillStyle=mt(e.options.backgroundColors?e.options.backgroundColors.down:void 0,Le.elements.candlestick.backgroundColors.down)):(r=mt(c?c.unchanged:void 0,Le.elements.candlestick.borderColors.unchanged),t.fillStyle=mt(e.backgroundColors?e.backgroundColors.unchanged:void 0,Le.elements.candlestick.backgroundColors.unchanged)),t.lineWidth=mt(e.options.borderWidth,Le.elements.candlestick.borderWidth),t.strokeStyle=r,t.beginPath(),t.moveTo(n,s),t.lineTo(n,Math.min(i,a)),t.moveTo(n,o),t.lineTo(n,Math.max(i,a)),t.stroke(),t.fillRect(n-e.width/2,a,e.width,i-a),t.strokeRect(n-e.width/2,a,e.width,i-a),t.closePath()}}class Eu extends Tu{static id="candlestick";static defaults={...Tu.defaults,dataElementType:Pu.id};static defaultRoutes=Ci.defaultRoutes;updateElements(t,e,n,i){const s="reset"===i,o=this._getRuler(),{sharedOptions:a,includeOptions:r}=this._getSharedOptions(e,i);for(let c=e;c<e+n;c++){const e=a||this.resolveDataElementOptions(c,i),n=this.calculateElementProperties(c,o,s,e);r&&(n.options=e),this.updateElement(t[c],c,n,i)}}}eo.register(...er,yh,ku,Eu,Pu);class Au extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for IntradayChartWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for IntradayChartWidget");const i=g(e.symbol);if(!i.valid)throw new Error(`Invalid symbol: ${i.error}`);this.type="intraday-chart",this.wsManager=e.wsManager,this.symbol=i.sanitized,this.debug=e.debug||!1,this.source=e.source||"blueocean",this.rangeBack=e.rangeBack||0,this.chartType=e.chartType||"line",this.autoRefresh=void 0===e.autoRefresh||e.autoRefresh,this.refreshInterval=e.refreshInterval||6e4,this.refreshTimer=null,this.isUserInteracting=!1,this.marketIsOpen=null,this.marketStatusChecked=!1,this.chartData=null,this.chartInstance=null,this.unsubscribe=null,this.livePrice=null,this.companyName="",this.exchangeName="",this.mic="",this.symbolEditor=null,this.cached1DData=null,this.cached5DData=null,this.is5DDataLoading=!1,this.createWidgetStructure(),this.setupSymbolEditor(),this.initialize()}createWidgetStructure(){this.container.innerHTML='\n <div class="intraday-chart-widget">\n <div class="chart-header">\n <div class="chart-title-section">\n <div class="company-market-info">\n <span class="intraday-company-name"></span>\n </div>\n <h3 class="intraday-chart-symbol editable-symbol"\n title="Double-click to edit symbol"\n data-original-symbol="">AAPL</h3>\n </div>\n <div class="chart-change positive">+0.00 (+0.00%)</div>\n </div>\n\n <div class="chart-controls">\n <div class="chart-range-selector">\n <button class="range-btn active" data-range="0">1D</button>\n <button class="range-btn" data-range="5">5D</button>\n </div>\n <div class="chart-type-selector">\n <button class="type-btn active" data-type="line" title="Line Chart">Line</button>\n <button class="type-btn" data-type="area" title="Area Chart">Mountain</button>\n <button class="type-btn" data-type="candlestick" title="Candlestick Chart">Candles</button>\n </div>\n <button class="zoom-reset-btn" title="Reset Zoom">\n <svg width="16" height="16" viewBox="0 0 16 16" fill="none">\n <path d="M2 8a6 6 0 1 1 12 0A6 6 0 0 1 2 8zm6-7a7 7 0 1 0 0 14A7 7 0 0 0 8 1z" fill="currentColor"/>\n <path d="M5 8h6M8 5v6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>\n </svg>\n Reset Zoom\n </button>\n </div>\n\n <div class="chart-container">\n <canvas id="intradayChart"></canvas>\n </div>\n\n <div class="chart-stats">\n <div class="stats-header">Daily Stats</div>\n <div class="stats-grid">\n <div class="stat-item stat-open">\n <span class="stat-label">Open</span>\n <span class="stat-value">$0.00</span>\n </div>\n <div class="stat-item stat-high">\n <span class="stat-label">High</span>\n <span class="stat-value">$0.00</span>\n </div>\n <div class="stat-item stat-low">\n <span class="stat-label">Low</span>\n <span class="stat-value">$0.00</span>\n </div>\n <div class="stat-item stat-close">\n <span class="stat-label">Close</span>\n <span class="stat-value">$0.00</span>\n </div>\n <div class="stat-item stat-volume">\n <span class="stat-label">Volume</span>\n <span class="stat-value">0</span>\n </div>\n </div>\n </div>\n\n <div class="widget-loading-overlay hidden">\n <div class="loading-spinner"></div>\n <div class="loading-text">Loading chart data...</div>\n </div>\n </div>\n';const t=this.container.querySelector(".intraday-chart-symbol");t&&(t.textContent=this.symbol),this.setupRangeButtons(),this.setupChartTypeButtons(),this.addStyles()}setupSymbolEditor(){this.symbolEditor=new s(this,{maxLength:10,placeholder:"Enter symbol...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,symbolType:"nightsession"});const t=this.container.querySelector(".intraday-chart-symbol");t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol)}async handleSymbolChange(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;this.debug&&(console.log("[IntradayChartWidget] Symbol change requested:",t),console.log("[IntradayChartWidget] Validation data:",n));const i=g(t);if(!i.valid)return this.debug&&console.log("[IntradayChartWidget] Invalid symbol:",i.error),{success:!1,error:i.error};const s=i.sanitized;if(n&&(this.companyName=n.comp_name||"",this.exchangeName=n.market_name||"",this.mic=n.mic||"",this.debug&&console.log("[IntradayChartWidget] Extracted company info:",{companyName:this.companyName,exchangeName:this.exchangeName})),s===this.symbol)return this.debug&&console.log("[IntradayChartWidget] Same symbol, no change needed"),{success:!0};try{return this.showLoading(),this.stopAutoRefresh(),this.unsubscribe&&(this.debug&&console.log(`[IntradayChartWidget] Unsubscribing from ${e}`),this.unsubscribe(),this.unsubscribe=null),this.chartInstance&&(this.debug&&console.log("[IntradayChartWidget] Clearing chart for symbol change"),this.chartInstance.destroy(),this.chartInstance=null),this.chartData=null,this.livePrice=null,this.cached1DData=null,this.cached5DData=null,this.is5DDataLoading=!1,this.symbol=s,this.updateCompanyName(),await this.loadChartData(),this.debug&&console.log(`[IntradayChartWidget] Successfully changed symbol from ${e} to ${s}`),{success:!0}}catch(t){return console.error("[IntradayChartWidget] Error changing symbol:",t),this.showError(`Failed to load data for ${s}`),{success:!1,error:`Failed to load ${s}`}}}async initialize(){await this.validateInitialSymbol(),this.updateCompanyName(),await this.loadChartData()}async validateInitialSymbol(){try{const t=this.wsManager.getApiService();if(!t)return void(this.debug&&console.log("[IntradayChartWidget] API service not available for initial validation"));const e=await t.quotel1(this.symbol);if(e&&e.data&&e.data[0]){const t=e.data[0];this.companyName=t.comp_name||"",this.exchangeName=t.market_name||"",this.mic=t.mic||"",this.debug&&console.log("[IntradayChartWidget] Initial symbol validated:",{symbol:this.symbol,companyName:this.companyName,exchangeName:this.exchangeName,mic:this.mic})}}catch(t){this.debug&&console.warn("[IntradayChartWidget] Initial symbol validation failed:",t)}}updateCompanyName(){const t=this.container.querySelector(".intraday-company-name");t&&(t.textContent=this.companyName||"")}setupRangeButtons(){const t=this.container.querySelectorAll(".range-btn");t.forEach((e=>{e.addEventListener("click",(async e=>{const n=parseInt(e.target.getAttribute("data-range"));t.forEach((t=>t.classList.remove("active"))),e.target.classList.add("active"),this.rangeBack=n,await this.loadChartData(),0===n?(await this.startAutoRefresh(),await this.subscribeToLivePrice()):(this.stopAutoRefresh(),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null,this.livePrice=null))}))}))}setupChartTypeButtons(){const t=this.container.querySelectorAll(".type-btn");t.forEach((e=>{e.addEventListener("click",(e=>{const n=e.target.getAttribute("data-type");t.forEach((t=>t.classList.remove("active"))),e.target.classList.add("active"),this.chartType=n,this.renderChart()}))}))}addStyles(){if(this.options.skipStyleInjection||document.querySelector('link[href*="mdas-styles.css"]'))this.debug&&console.log("[IntradayChartWidget] Skipping style injection - external styles detected");else if(!document.querySelector("#intraday-chart-styles"))try{const t=document.createElement("style");t.id="intraday-chart-styles",t.textContent="\n .intraday-chart-widget {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;\n background: white;\n border-radius: 12px;\n padding: 20px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n position: relative;\n width: 100%;\n min-width: 600px;\n max-width: 1400px;\n margin: 0 auto;\n }\n\n .chart-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n padding-bottom: 15px;\n border-bottom: 1px solid #e5e7eb;\n }\n\n .chart-title-section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .company-market-info {\n display: flex;\n gap: 8px;\n align-items: center;\n font-size: 0.75em;\n color: #6b7280;\n }\n\n .intraday-company-name {\n font-weight: 500;\n }\n\n .intraday-chart-symbol {\n font-size: 1.5em;\n font-weight: 700;\n color: #1f2937;\n margin: 0;\n }\n\n .intraday-chart-source {\n font-size: 0.75em;\n padding: 4px 8px;\n border-radius: 4px;\n background: #e0e7ff;\n color: #4338ca;\n font-weight: 600;\n }\n\n .chart-change {\n font-size: 1.1em;\n font-weight: 600;\n padding: 6px 12px;\n border-radius: 6px;\n }\n\n .chart-change.positive {\n color: #059669;\n background: #d1fae5;\n }\n\n .chart-change.negative {\n color: #dc2626;\n background: #fee2e2;\n }\n\n .chart-controls {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 15px;\n }\n\n .chart-range-selector {\n display: flex;\n gap: 8px;\n }\n\n .range-btn {\n padding: 8px 16px;\n border: 1px solid #e5e7eb;\n background: white;\n border-radius: 6px;\n font-size: 0.875em;\n font-weight: 600;\n color: #6b7280;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .range-btn:hover {\n background: #f9fafb;\n border-color: #d1d5db;\n }\n\n .range-btn.active {\n background: #667eea;\n color: white;\n border-color: #667eea;\n }\n\n .range-btn:focus {\n outline: none;\n box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);\n }\n\n .chart-type-selector {\n display: flex;\n gap: 8px;\n }\n\n .type-btn {\n padding: 8px 16px;\n border: 1px solid #e5e7eb;\n background: white;\n border-radius: 6px;\n font-size: 0.875em;\n font-weight: 600;\n color: #6b7280;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .type-btn:hover {\n background: #f9fafb;\n border-color: #d1d5db;\n }\n\n .type-btn.active {\n background: #10b981;\n color: white;\n border-color: #10b981;\n }\n\n .type-btn:focus {\n outline: none;\n box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);\n }\n\n .zoom-reset-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 12px;\n border: 1px solid #e5e7eb;\n background: white;\n border-radius: 6px;\n font-size: 0.875em;\n font-weight: 500;\n color: #6b7280;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .zoom-reset-btn:hover {\n background: #f9fafb;\n border-color: #667eea;\n color: #667eea;\n }\n\n .zoom-reset-btn:focus {\n outline: none;\n box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);\n }\n\n .zoom-reset-btn svg {\n flex-shrink: 0;\n }\n\n .chart-container {\n height: 500px;\n margin-bottom: 20px;\n position: relative;\n }\n\n .chart-stats {\n padding: 15px;\n background: #f9fafb;\n border-radius: 8px;\n }\n\n .stats-header {\n font-size: 0.875em;\n font-weight: 700;\n color: #374151;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n padding-bottom: 8px;\n border-bottom: 2px solid #e5e7eb;\n }\n\n .stats-grid {\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n gap: 15px;\n }\n\n .stat-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .stat-label {\n font-size: 0.75em;\n color: #6b7280;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .stat-value {\n font-size: 1.1em;\n font-weight: 700;\n color: #1f2937;\n }\n\n .widget-loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(255, 255, 255, 0.4);\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 10px 16px;\n border-radius: 12px;\n z-index: 10;\n pointer-events: none;\n backdrop-filter: blur(1px);\n }\n\n .widget-loading-overlay.hidden {\n display: none;\n }\n\n .loading-spinner {\n width: 20px;\n height: 20px;\n border: 3px solid #e5e7eb;\n border-top-color: #667eea;\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n }\n\n .loading-text {\n color: #6b7280;\n font-size: 0.875em;\n font-weight: 500;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n @media (max-width: 768px) {\n .intraday-chart-widget {\n min-width: unset;\n padding: 15px;\n }\n\n .stats-grid {\n grid-template-columns: repeat(3, 1fr);\n gap: 10px;\n }\n\n .stats-header {\n font-size: 0.8em;\n margin-bottom: 10px;\n }\n\n .chart-container {\n height: 350px;\n }\n\n .intraday-chart-symbol {\n font-size: 1.2em;\n }\n }\n\n .widget-error {\n padding: 15px;\n background: #fee2e2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n color: #dc2626;\n font-size: 0.9em;\n margin-top: 10px;\n }\n",document.head.appendChild(t),this.debug&&console.log("[IntradayChartWidget] Styles injected successfully")}catch(t){console.warn("[IntradayChartWidget] Failed to inject styles:",t),console.warn('[IntradayChartWidget] Please add <link rel="stylesheet" href="mdas-styles.css"> to your HTML')}}async loadChartData(){try{this.showLoading(),this.clearError();const t=this.wsManager.getApiService();if(!t)throw new Error("API service not available");if(5===this.rangeBack)return void await this.load5DayChartData(t);if(0===this.rangeBack&&this.cached1DData)return this.debug&&console.log("[IntradayChartWidget] Using cached 1D data"),this.chartData=this.cached1DData,this.renderChart(),this.hideLoading(),await this.startAutoRefresh(),void this.subscribeToLivePrice();const e=7;let n=this.rangeBack,i=!1,s=null,o=null,a=!1;for(let r=0;r<e;r++){if(this.debug&&console.log(`[IntradayChartWidget] Loading data for ${this.symbol} from ${this.source}, range_back: ${n} (attempt ${r+1}/${e})`),s=await t.getIntradayChart(this.source,this.symbol,n),console.log("[IntradayChartWidget] API response type:",typeof s),console.log("[IntradayChartWidget] API response:",s),s&&(console.log("[IntradayChartWidget] Response keys:",Object.keys(s)),Array.isArray(s)&&(console.log("[IntradayChartWidget] Response is array, length:",s.length),s.length>0&&(console.log("[IntradayChartWidget] First item:",s[0]),console.log("[IntradayChartWidget] First item keys:",Object.keys(s[0]))))),s&&s.error)throw new Error(s.error||"Server error");if(o=s,s&&s.data&&Array.isArray(s.data)&&(console.log("[IntradayChartWidget] Found data array in response.data"),o=s.data),o&&Array.isArray(o)&&o.length>0){i=!0,n!==this.rangeBack&&(a=!0,this.debug&&console.log(`[IntradayChartWidget] Using fallback data: requested rangeBack ${this.rangeBack}, got data from rangeBack ${n}`));break}console.log(`[IntradayChartWidget] No data for rangeBack ${n}, trying ${n+1}...`),n++}if(!i||!o||0===o.length)throw new Error(`No intraday data available for ${this.symbol} in the past ${e} days`);if(console.log("[IntradayChartWidget] Processing",o.length,"data points"),this.chartData=new T(o),console.log("[IntradayChartWidget] Model created with",this.chartData.dataPoints.length,"points"),0===this.chartData.dataPoints.length)throw console.error("[IntradayChartWidget] Model has no data points after processing"),new Error(`No valid data points for ${this.symbol}`);0===this.rangeBack&&(this.cached1DData=this.chartData,this.debug&&console.log("[IntradayChartWidget] Cached 1D data for instant switching")),this.renderChart(),this.hideLoading(),this.debug&&console.log(`[IntradayChartWidget] Loaded ${o.length} data points`),0!==this.rangeBack||a?(this.stopAutoRefresh(),this.debug&&console.log("[IntradayChartWidget] Not starting auto-refresh for historical/fallback data")):(await this.startAutoRefresh(),this.subscribeToLivePrice(),this.preload5DDataInBackground())}catch(t){console.error("[IntradayChartWidget] Error loading chart data:",t),this.hideLoading();let e="Failed to load chart data";t.message.includes("No intraday data")?e=t.message:t.message.includes("API service not available")?e="Unable to connect to data service":t.message.includes("HTTP error")||t.message.includes("status: 4")?e=`Unable to load data for ${this.symbol}`:t.message.includes("status: 5")?e="Server error. Please try again later":t.message.includes("Failed to fetch")&&(e="Network error. Please check your connection"),this.showError(e)}}async load5DayChartData(t){try{if(this.cached5DData)return this.debug&&console.log("[IntradayChartWidget] Using cached 5D data"),this.chartData=this.cached5DData,this.renderChart(),this.hideLoading(),this.subscribeToLivePrice(),void this.stopAutoRefresh();const e=await this.fetch5DayData(t);if(this.chartData=new T(e),0===this.chartData.dataPoints.length)throw new Error(`No valid data points for ${this.symbol}`);this.cached5DData=this.chartData,this.renderChart(),this.hideLoading(),this.debug&&console.log(`[IntradayChartWidget] 5D chart loaded with ${this.chartData.dataPoints.length} data points`),this.subscribeToLivePrice(),this.stopAutoRefresh()}catch(t){throw console.error("[IntradayChartWidget] Error loading 5D chart data:",t),t}}async fetch5DayData(t){this.debug&&console.log("[IntradayChartWidget] Fetching 5D chart data with parallel requests (API limit: rangeback 0-6)");const e=[];for(let n=0;n<7;n++)e.push(t.getIntradayChart(this.source,this.symbol,n).then((t=>{let e=t;return t&&t.data&&Array.isArray(t.data)&&(e=t.data),{rangeBack:n,data:e}})).catch((t=>(this.debug&&console.warn(`[IntradayChartWidget] Request failed for rangeback ${n}:`,t),{rangeBack:n,data:null}))));const n=await Promise.all(e);this.debug&&console.log("[IntradayChartWidget] All parallel requests completed");const i=n.filter((t=>{const e=t.data&&Array.isArray(t.data)&&t.data.length>0;return this.debug&&(e?console.log(`[IntradayChartWidget] Rangeback ${t.rangeBack}: ${t.data.length} data points`):console.log(`[IntradayChartWidget] Rangeback ${t.rangeBack}: No data (skipped)`)),e})).slice(0,5);if(0===i.length)throw new Error(`No intraday data available for ${this.symbol} in the past 7 days`);this.debug&&(console.log(`[IntradayChartWidget] Collected ${i.length}/5 days of data`),i.length<5&&console.log(`[IntradayChartWidget] Note: Only ${i.length} days available within API limit (rangeback 0-6)`));const s=[];for(let t=i.length-1;t>=0;t--)s.push(...i[t].data);return this.debug&&console.log(`[IntradayChartWidget] Combined ${s.length} total data points from ${i.length} days`),s}preload5DDataInBackground(){this.is5DDataLoading||this.cached5DData||(this.is5DDataLoading=!0,this.debug&&console.log("[IntradayChartWidget] Starting background preload of 5D data..."),setTimeout((async()=>{try{const t=this.wsManager.getApiService();if(!t)return void(this.debug&&console.log("[IntradayChartWidget] API service not available for preload"));const e=await this.fetch5DayData(t),n=new T(e);this.cached5DData=n,this.debug&&console.log(`[IntradayChartWidget] ✓ Background preload complete: ${n.dataPoints.length} data points cached`)}catch(t){this.debug&&console.warn("[IntradayChartWidget] Background preload failed:",t)}finally{this.is5DDataLoading=!1}}),1e3))}subscribeToLivePrice(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null);const t="bruce"===this.source?"querybrucel1":"queryblueoceanl1";this.debug&&console.log(`[IntradayChartWidget] Subscribing to live price with ${t} for ${this.symbol}`),this.unsubscribe=this.wsManager.subscribe(this.widgetId,[t],this.handleMessage.bind(this),this.symbol)}handleError(t){this.debug&&console.error("[IntradayChartWidget] WebSocket error:",t.message||t)}handleData(t){console.log("[IntradayChartWidget] handleData called with:",t),console.log("[IntradayChartWidget] Looking for symbol:",this.symbol),console.log("[IntradayChartWidget] Message is array?",Array.isArray(t));let e=null;if(Array.isArray(t)){console.log("[IntradayChartWidget] Processing array format, length:",t.length);const n=t.find((t=>t.Symbol===this.symbol));console.log("[IntradayChartWidget] Found symbol data:",n),n&&!n.NotFound&&(e=n)}else"queryblueoceanl1"===t.type||"querybrucel1"===t.type?(console.log("[IntradayChartWidget] Processing wrapped format"),t[0]?.Symbol!==this.symbol||t[0].NotFound||(e=t[0])):t.Symbol!==this.symbol||t.NotFound||(console.log("[IntradayChartWidget] Processing direct format"),e=t);if(!e)return void console.log("[IntradayChartWidget] No matching price data found for symbol:",this.symbol);console.log("[IntradayChartWidget] Price data found:",e);let n=null;void 0!==e.LastPx&&null!==e.LastPx?(n=parseFloat(e.LastPx),console.log("[IntradayChartWidget] Price from LastPx:",n)):void 0!==e.Last&&null!==e.Last?(n=parseFloat(e.Last),console.log("[IntradayChartWidget] Price from Last:",n)):void 0!==e.last_price&&null!==e.last_price?(n=parseFloat(e.last_price),console.log("[IntradayChartWidget] Price from last_price:",n)):void 0!==e.TradePx&&null!==e.TradePx?(n=parseFloat(e.TradePx),console.log("[IntradayChartWidget] Price from TradePx:",n)):void 0!==e.Close&&null!==e.Close?(n=parseFloat(e.Close),console.log("[IntradayChartWidget] Price from Close:",n)):void 0!==e.close&&null!==e.close&&(n=parseFloat(e.close),console.log("[IntradayChartWidget] Price from close:",n)),console.log("[IntradayChartWidget] Extracted price:",n),n&&!isNaN(n)&&n>0?(this.livePrice=n,console.log("[IntradayChartWidget] Updating live price line to:",n),this.updateLivePriceLine(),console.log(`[IntradayChartWidget] Live price update: $${n.toFixed(2)}`)):console.log("[IntradayChartWidget] Invalid price, not updating:",n),this.updateStatsFromLiveData(e)}updateStatsFromLiveData(t){if(!t)return;const e={high:void 0!==t.HighPx&&null!==t.HighPx?parseFloat(t.HighPx):null,low:void 0!==t.LowPx&&null!==t.LowPx?parseFloat(t.LowPx):null,open:void 0!==t.OpenPx&&null!==t.OpenPx?parseFloat(t.OpenPx):null,close:void 0!==t.LastPx&&null!==t.LastPx?parseFloat(t.LastPx):void 0!==t.TradePx&&null!==t.TradePx?parseFloat(t.TradePx):null,volume:void 0!==t.Volume&&null!==t.Volume?parseInt(t.Volume):null,change:void 0!==t.Change&&null!==t.Change?parseFloat(t.Change):null,changePercent:void 0!==t.ChangePercent&&null!==t.ChangePercent?100*parseFloat(t.ChangePercent):null};this.debug&&console.log("[IntradayChartWidget] Updating stats from live data:",e);const n=this.container.querySelector(".stat-high .stat-value"),i=this.container.querySelector(".stat-low .stat-value"),s=this.container.querySelector(".stat-open .stat-value"),o=this.container.querySelector(".stat-close .stat-value"),a=this.container.querySelector(".stat-volume .stat-value"),r=this.container.querySelector(".chart-change");if(n&&null!==e.high&&(n.textContent=`$${e.high.toFixed(2)}`),i&&null!==e.low&&(i.textContent=`$${e.low.toFixed(2)}`),s&&null!==e.open&&(s.textContent=`$${e.open.toFixed(2)}`),o&&null!==e.close&&(o.textContent=`$${e.close.toFixed(2)}`),a&&null!==e.volume&&(a.textContent=this.formatVolume(e.volume)),r&&null!==e.change&&null!==e.changePercent){const t=e.change>=0?"positive":"negative";r.className=`chart-change ${t}`;const n=e.change>=0?"+":"";r.textContent=`${n}${e.change.toFixed(2)} (${n}${e.changePercent.toFixed(2)}%)`}}formatVolume(t){return t>=1e6?(t/1e6).toFixed(2)+"M":t>=1e3?(t/1e3).toFixed(2)+"K":t.toString()}updateLivePriceLine(){this.chartInstance&&this.livePrice?this.chartInstance.options.plugins.annotation&&(this.chartInstance.options.plugins.annotation.annotations.livePriceLine.value=this.livePrice,this.chartInstance.options.plugins.annotation.annotations.livePriceLine.label.content=`$${this.livePrice.toFixed(2)}`,this.chartInstance.options.plugins.annotation.annotations.livePriceLine.label.display=!0,this.debug&&console.log("[IntradayChartWidget] Live price line updated:",{price:this.livePrice,label:this.chartInstance.options.plugins.annotation.annotations.livePriceLine.label.content}),this.chartInstance.update("none")):this.debug&&console.log("[IntradayChartWidget] Cannot update live price line:",{hasChart:!!this.chartInstance,livePrice:this.livePrice})}async isMarketOpen(){const t=new Date,e=t.toLocaleString("en-US",{timeZone:"America/New_York",weekday:"short"}),n=parseInt(t.toLocaleString("en-US",{timeZone:"America/New_York",hour12:!1,hour:"numeric"}),10),i=parseInt(t.toLocaleString("en-US",{timeZone:"America/New_York",minute:"numeric"}),10);if("Sat"===e||"Sun"===e)return!1;const s=n>=20||n<4;if(this.debug&&console.log(`[IntradayChartWidget] Market check: ET hour=${n}, minute=${i}, day=${e}, open=${s}`),!s)return this.debug&&console.log("[IntradayChartWidget] Market is currently closed (outside 8pm-4am ET on weekdays)."),!1;if(!this.marketStatusChecked)try{const t=this.wsManager.getApiService(),e=await t.getMarketStatus(this.source);if(e&&Array.isArray(e)&&e.length>0){const t=e[0].status;return this.marketIsOpen=t&&"open"===t.toLowerCase(),this.marketStatusChecked=!0,this.debug&&console.log(`[IntradayChartWidget] Market status from API: ${t} (${this.marketIsOpen?"Open":"Closed"})`),this.marketIsOpen}}catch(t){return this.debug&&console.warn("[IntradayChartWidget] Failed to get market status from API, assuming open based on time:",t),this.marketIsOpen=!0,this.marketStatusChecked=!0,!0}return!1!==this.marketIsOpen&&(!0!==this.marketIsOpen||(!(3===n&&i>=45)||(this.debug&&console.log("[IntradayChartWidget] Approaching market close, re-checking status..."),this.marketStatusChecked=!1,await this.isMarketOpen())))}async startAutoRefresh(){if(this.stopAutoRefresh(),!this.autoRefresh)return;await this.isMarketOpen()?(this.debug&&console.log(`[IntradayChartWidget] Starting auto-refresh every ${this.refreshInterval/1e3} seconds`),this.refreshTimer=setInterval((async()=>{if(!await this.isMarketOpen())return this.debug&&console.log("[IntradayChartWidget] Market closed. Stopping auto-refresh."),void this.stopAutoRefresh();if(this.isUserInteracting)this.debug&&console.log("[IntradayChartWidget] Skipping refresh - user is interacting");else{this.debug&&console.log("[IntradayChartWidget] Auto-refreshing chart data");try{const t=this.wsManager.getApiService(),e=await t.getIntradayChart(this.source,this.symbol,this.rangeBack);e&&Array.isArray(e)&&(this.chartData=new T(e),this.renderChart(),this.debug&&console.log(`[IntradayChartWidget] Auto-refresh complete - ${e.length} data points`))}catch(t){console.error("[IntradayChartWidget] Auto-refresh failed:",t)}}}),this.refreshInterval)):this.debug&&console.log("[IntradayChartWidget] Market is closed. Auto-refresh will not start.")}stopAutoRefresh(){this.refreshTimer&&(this.debug&&console.log("[IntradayChartWidget] Stopping auto-refresh"),clearInterval(this.refreshTimer),this.refreshTimer=null)}parseTimestamp(t){return new Date(t)}renderChart(){if(console.log("[IntradayChartWidget] renderChart called"),!this.chartData||0===this.chartData.dataPoints.length)return void console.error("[IntradayChartWidget] No data to render");console.log("[IntradayChartWidget] Chart data points:",this.chartData.dataPoints.length);const t=this.container.querySelector("#intradayChart");if(console.log("[IntradayChartWidget] Canvas element:",t),!t)return void console.error("[IntradayChartWidget] Canvas element not found");if(this.chartInstance){console.log("[IntradayChartWidget] Destroying existing chart instance");try{this.chartInstance.destroy(),this.chartInstance=null}catch(t){console.error("[IntradayChartWidget] Error destroying chart:",t),this.chartInstance=null}}const e=t.getContext("2d");console.log("[IntradayChartWidget] Canvas context:",e),console.log("[IntradayChartWidget] Preparing chart data...");const n=this.chartData.getStats();console.log("[IntradayChartWidget] Stats:",n);const i=n.change>=0,s=i?"#10b981":"#ef4444";let o,a;const r=this.chartData.dataPoints;if(0===r.length)return console.error("[IntradayChartWidget] No valid data points from model"),void this.showError("No valid chart data available");if(console.log("[IntradayChartWidget] Rendering",r.length,"data points"),"candlestick"===this.chartType){if(o=this.rangeBack>0?r.map(((t,e)=>({x:e,o:t.open,h:t.high,l:t.low,c:t.close}))):r.map((t=>({x:this.parseTimestamp(t.time).getTime(),o:t.open,h:t.high,l:t.low,c:t.close}))).filter((t=>!isNaN(t.x)&&t.x>0)),0===o.length)return console.error("[IntradayChartWidget] No valid candlestick data points after date parsing"),void this.showError("Unable to parse chart data timestamps");a={label:"Price",data:o,color:{up:"#10b981",down:"#ef4444",unchanged:"#6b7280"},borderColor:{up:"#10b981",down:"#ef4444",unchanged:"#6b7280"}}}else{if(o=this.rangeBack>0?r.map(((t,e)=>({x:e,y:t.close}))):r.map((t=>({x:this.parseTimestamp(t.time),y:t.close}))).filter((t=>{const e=t.x.getTime();return!isNaN(e)&&e>0})),0===o.length)return console.error("[IntradayChartWidget] No valid chart data points after date parsing"),void this.showError("Unable to parse chart data timestamps");const t=e.createLinearGradient(0,0,0,300);i?(t.addColorStop(0,"rgba(16, 185, 129, 0.3)"),t.addColorStop(1,"rgba(16, 185, 129, 0.01)")):(t.addColorStop(0,"rgba(239, 68, 68, 0.3)"),t.addColorStop(1,"rgba(239, 68, 68, 0.01)")),a={label:"Price",data:o,borderColor:s,backgroundColor:"area"===this.chartType?t:"transparent",borderWidth:2,fill:"area"===this.chartType,tension:.4,pointRadius:0,pointHoverRadius:4,pointBackgroundColor:s,pointBorderColor:"#fff",pointBorderWidth:2}}let c,l;if(console.log("[IntradayChartWidget] Chart data points prepared:",o.length),console.log("[IntradayChartWidget] First data point - Source time:",r[0].time),console.log("[IntradayChartWidget] First chart point:",o[0]),console.log("[IntradayChartWidget] Last data point - Source time:",r[r.length-1].time),console.log("[IntradayChartWidget] Last chart point:",o[o.length-1]),0===this.rangeBack){const t=o.map((t=>"candlestick"===this.chartType?t.x:t.x.getTime()));c=Math.min(...t),l=Math.max(...t),console.log("[IntradayChartWidget] X-axis bounds:",{min:c,max:l,minDate:new Date(c),maxDate:new Date(l)})}else console.log("[IntradayChartWidget] Using linear scale with",o.length,"data points");const d={type:"candlestick"===this.chartType?"candlestick":"line",data:{datasets:[a]},options:{responsive:!0,maintainAspectRatio:!1,interaction:{intersect:!1,mode:"index"},plugins:{legend:{display:!1},decimation:{enabled:o.length>1e3,algorithm:"lttb",samples:500,threshold:1e3},tooltip:{enabled:!0,backgroundColor:"rgba(0, 0, 0, 0.8)",titleColor:"#fff",bodyColor:"#fff",borderColor:s,borderWidth:1,padding:10,displayColors:!1,callbacks:{title:t=>{const e=t[0].dataIndex,n=r[e];if(n){return new Date(n.time).toLocaleString("en-US",{month:"2-digit",day:"2-digit",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit",hour12:!0})}return""},label:t=>{const e=t.dataIndex,n=r[e];return n?"candlestick"===this.chartType?[`Open: $${n.open.toFixed(2)}`,`High: $${n.high.toFixed(2)}`,`Low: $${n.low.toFixed(2)}`,`Close: $${n.close.toFixed(2)}`,`Volume: ${this.chartData.formatVolume(n.volume)}`]:[`Price: $${n.close.toFixed(2)}`,`Open: $${n.open.toFixed(2)}`,`High: $${n.high.toFixed(2)}`,`Low: $${n.low.toFixed(2)}`,`Volume: ${this.chartData.formatVolume(n.volume)}`]:[]}}},zoom:{pan:{enabled:!0,mode:"x",modifierKey:"ctrl"},zoom:{wheel:{enabled:!0,speed:.1},pinch:{enabled:!0},mode:"x"},limits:{x:{min:"original",max:"original"}}},annotation:{annotations:this.createAnnotations(r,n)}},scales:{x:{type:0===this.rangeBack?"time":"linear",min:0===this.rangeBack?c:void 0,max:0===this.rangeBack?l:void 0,time:0===this.rangeBack?{unit:"hour",stepSize:1,displayFormats:{hour:"h:mm a"},tooltipFormat:"MMM d, h:mm a"}:void 0,grid:{display:!1},ticks:{maxTicksLimit:0===this.rangeBack?10:8,color:"#6b7280",autoSkip:!0,maxRotation:0,minRotation:0,callback:(t,e,n)=>{if(this.rangeBack>0){const e=Math.round(t),n=r[e];if(n){return new Date(n.time).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}return""}if("number"==typeof t){return new Date(t).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}return t}}},y:{position:"right",grid:{color:"rgba(0, 0, 0, 0.05)"},ticks:{color:"#6b7280",callback:function(t){return"$"+t.toFixed(2)}},afterDataLimits:t=>{const e=.1*(t.max-t.min);t.max+=e,t.min-=e}}}}};console.log("[IntradayChartWidget] Creating Chart.js instance with config:",d),console.log("[IntradayChartWidget] Chart available?",void 0!==eo);try{this.chartInstance=new eo(e,d),console.log("[IntradayChartWidget] Chart instance created successfully:",this.chartInstance)}catch(t){throw console.error("[IntradayChartWidget] Error creating chart:",t),t}t.addEventListener("mouseenter",(()=>{this.isUserInteracting=!0})),t.addEventListener("mouseleave",(()=>{this.isUserInteracting=!1})),this.setupZoomReset()}createAnnotations(t,e){const n={livePriceLine:{type:"line",scaleID:"y",value:this.livePrice||e.close,borderColor:"#667eea",borderWidth:2,borderDash:[5,5],label:{display:!0,content:this.livePrice?`$${this.livePrice.toFixed(2)}`:`$${e.close.toFixed(2)}`,enabled:!0,position:"end",backgroundColor:"rgb(102, 126, 234)",color:"#ffffff",font:{size:12,weight:"bold",family:"system-ui, -apple-system, sans-serif"},padding:{top:4,bottom:4,left:8,right:8},borderRadius:4,xAdjust:-10,yAdjust:0}}};if(this.rangeBack>0&&t.length>0){let e=null;t.forEach(((t,i)=>{const s=new Date(t.time).toDateString();e!==s&&null!==e&&(n[`daySeparator${i}`]={type:"line",scaleID:"x",value:i,borderColor:"rgba(0, 0, 0, 0.1)",borderWidth:1,borderDash:[3,3]}),e=s}))}return n}setupZoomReset(){const t=this.container.querySelector(".zoom-reset-btn");t&&t.addEventListener("click",(()=>{this.chartInstance&&(this.chartInstance.resetZoom(),this.debug&&console.log("[IntradayChartWidget] Zoom reset"))}))}updateStats(){if(!this.chartData)return;const t=this.chartData.getStats(),e=this.container.querySelector(".stat-high .stat-value"),n=this.container.querySelector(".stat-low .stat-value"),i=this.container.querySelector(".stat-open .stat-value"),s=this.container.querySelector(".stat-close .stat-value"),o=this.container.querySelector(".stat-volume .stat-value"),a=this.container.querySelector(".chart-change");if(e&&(e.textContent=`$${t.high.toFixed(2)}`),n&&(n.textContent=`$${t.low.toFixed(2)}`),i&&(i.textContent=`$${t.open.toFixed(2)}`),s&&(s.textContent=`$${t.close.toFixed(2)}`),o&&(o.textContent=this.chartData.formatVolume(t.volume)),a){const e=t.change>=0?"positive":"negative";a.className=`chart-change ${e}`;const n=t.change>=0?"+":"";a.textContent=`${n}${t.change.toFixed(2)} (${n}${t.changePercent.toFixed(2)}%)`}}async refreshData(){await this.loadChartData()}destroy(){this.stopAutoRefresh(),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.chartInstance&&(this.chartInstance.destroy(),this.chartInstance=null),this.symbolEditor&&(this.symbolEditor.destroy(),this.symbolEditor=null),super.destroy()}}class Lu{constructor(t){this.stateManager=t,this.widgets=new Map}createWidget(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const i=this.generateWidgetId();let s;switch(t){case"quote-level1":s=new b(e,n,i);break;case"night-session":s=new x(e,n,i);break;case"options":s=new w(e,n,i);break;case"option-chain":s=new M(e,n,i);break;case"data":s=new _(e,n,i);break;case"combined-market":s=new D(e,n,i);break;case"intraday-chart":s=new Au(e,n,i);break;default:throw new Error(`Unknown widget type: ${t}`)}return this.widgets.set(i,s),s}generateWidgetId(){return`widget_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}destroyWidget(t){const e=this.widgets.get(t);e&&(e.destroy(),this.widgets.delete(t))}getWidgetCount(){return this.widgets.size}destroy(){this.widgets.forEach((t=>t.destroy())),this.widgets.clear()}}class Iu{constructor(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"https://mdas-api-dev.viewtrade.dev";this.token=t,this.baseUrl=e}async request(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n=`${this.baseUrl}${t}`,i={method:"GET",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",...e.headers},...e};try{const t=await fetch(n,i);if(!t.ok)throw new Error(`HTTP error! status: ${t.status}`);return await t.json()}catch(t){throw console.error(`[ApiService] Request failed for ${n}:`,t),t}}async getOptionChainDates(t){return this.request(`/api/quote/option-chain-dates?symbol=${t}&response_camel_case=false`)}async quoteOptionl1(t){return this.request(`/api/quote/option-level1?option_names=${t}&response_camel_case=false`)}async quotel1(t){return this.request(`/api/quote/level1?symbols=${t}&response_camel_case=false`)}async quoteBlueOcean(t){return this.request(`/api/quote/night-session/level1/blueocean?symbols=${t}&response_camel_case=false`)}async getIntradayChart(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return this.request(`/api/quote/night-session/intraday/${t}?symbol=${e}&range_back=${n}`)}async getMarketStatus(t){return this.request(`/api/quote/night-session/market-status/${t}`)}}class Ou{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.config={debug:t.debug||!1,token:t.token,baseUrl:t.baseUrl||"ws://localhost:3000/wssub",apiBaseUrl:t.apiBaseUrl,maxConnections:t.maxConnections||1,reconnectOptions:{maxAttempts:t.reconnectOptions?.maxAttempts||10,initialDelay:t.reconnectOptions?.initialDelay||1e3,maxDelay:t.reconnectOptions?.maxDelay||3e4,backoff:t.reconnectOptions?.backoff||1.5,jitter:t.reconnectOptions?.jitter||.3},timeouts:{connection:t.timeouts?.connection||1e4,inactivity:t.timeouts?.inactivity||9e4},enableActivityMonitoring:!1},this.apiService=new Iu(this.config.token,this.config.apiBaseUrl),this.connection=null,this.connectionState="disconnected",this.reconnectAttempts=0,this.reconnectTimer=null,this.connectionStartTime=null,this.subscriptions=new Map,this.activeSubscriptions=new Set,this.messageQueue=[],this.symbolSubscriptions=new Map,this.typeSubscriptions=new Map,this.lastMessageCache=new Map,this.connectionPromise=null,this.reloginCallback=t.reloginCallback||null,this.isReloginInProgress=!1,this.lastMessageReceived=null,this.activityCheckInterval=null,this.isOnline="undefined"==typeof navigator||navigator.onLine,this.onlineHandler=null,this.offlineHandler=null,this.circuitBreaker={state:"closed",failureCount:0,failureThreshold:5,timeout:6e4,lastFailureTime:null,resetTimer:null},this.metrics={successfulConnections:0,failedConnections:0,totalReconnects:0,avgConnectionTime:0,lastSuccessfulConnection:null,messagesReceived:0,messagesSent:0},this.handleOpen=this.handleOpen.bind(this),this.handleMessage=this.handleMessage.bind(this),this.handleClose=this.handleClose.bind(this),this.handleError=this.handleError.bind(this),this.isManualDisconnect=!1,this._initNetworkMonitoring(),this.config.debug&&console.log("[WebSocketManager] Initialized with config:",this.config)}async connect(){return"connected"===this.connectionState||"connecting"===this.connectionState?Promise.resolve():new Promise(((t,e)=>{this.connectionPromise={resolve:t,reject:e},this._connect()}))}subscribe(t,e,n){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,s=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{};Array.isArray(e)||(e=[e]);const o=this.subscriptions.get(t);if(o){if(this.config.debug&&console.log(`[WebSocketManager] Cleaning up existing subscription for widget ${t} before re-subscribing`),o.symbol){const e=this.symbolSubscriptions.get(o.symbol);e&&(e.delete(t),0===e.size&&this.symbolSubscriptions.delete(o.symbol))}o.types&&o.types.forEach((e=>{const n=this.typeSubscriptions.get(e);n&&(n.delete(t),0===n.size&&this.typeSubscriptions.delete(e))}))}const a={types:new Set(e),callback:n,symbol:i?.toUpperCase(),additionalParams:s};if(this.subscriptions.set(t,a),i){const e=i.toUpperCase();this.symbolSubscriptions.has(e)||this.symbolSubscriptions.set(e,new Set),this.symbolSubscriptions.get(e).add(t)}return e.forEach((e=>{this.typeSubscriptions.has(e)||this.typeSubscriptions.set(e,new Set),this.typeSubscriptions.get(e).add(t)})),this.config.debug&&console.log(`[WebSocketManager] Widget ${t} subscribed to:`,e,`for symbol: ${i}`,s),"connected"===this.connectionState&&(this._sendSubscriptions(e),e.forEach((e=>{const s=i?`${e}:${i}`:e,o=this.lastMessageCache.get(s);if(o&&this.activeSubscriptions.has(s)){this.config.debug&&console.log(`[WebSocketManager] Sending cached data to new widget ${t} for ${s}`);try{n({event:"data",data:o,widgetId:t})}catch(e){console.error(`[WebSocketManager] Error sending cached data to widget ${t}:`,e)}}}))),()=>this.unsubscribe(t)}unsubscribe(t){const e=this.subscriptions.get(t);if(e){if(e.symbol&&e.types&&e.types.forEach((t=>{this.sendUnsubscribe(t,e.symbol,e.additionalParams||{})})),e.symbol){const n=this.symbolSubscriptions.get(e.symbol);n&&(n.delete(t),0===n.size&&this.symbolSubscriptions.delete(e.symbol))}e.types.forEach((e=>{const n=this.typeSubscriptions.get(e);n&&(n.delete(t),0===n.size&&this.typeSubscriptions.delete(e))})),this.subscriptions.delete(t),this.config.debug&&console.log(`[WebSocketManager] Widget ${t} unsubscribed`),this._cleanupUnusedSubscriptions()}}sendUnsubscribe(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};try{let i,s;"queryoptionchain"===t?(s=`${t}:${e}:${n.date||""}`,i={type:t,underlying:e,date:n.date,unsubscribe:!0}):"queryoptionl1"===t?(s=`${t}:${e}`,i={type:t,optionName:e,unsubscribe:!0}):(s=`${t}:${e}`,i={type:t,symbol:e,unsubscribe:!0}),this.config.debug&&console.log(`[WebSocketManager] Sending unsubscribe for ${s}:`,i),this.send(i),this.activeSubscriptions.delete(s),this.config.debug&&console.log(`[WebSocketManager] Removed ${s} from active subscriptions`)}catch(t){console.error("[WebSocketManager] Error sending unsubscribe:",t)}}sendUnsubscribeAll(){try{const t={unsubscribe_all:!0};this.config.debug&&console.log("[WebSocketManager] Sending unsubscribe all"),this.send(t),this.activeSubscriptions.clear()}catch(t){console.error("[WebSocketManager] Error sending unsubscribe all:",t)}}send(t){if("connected"!==this.connectionState||!this.connection||1!==this.connection.readyState)return this.messageQueue&&Array.isArray(this.messageQueue)||(this.messageQueue=[]),this.messageQueue.push(t),this.config.debug&&console.log("[WebSocketManager] Message queued (not connected):",t),!1;try{return this.connection.send(JSON.stringify(t)),this.config.debug&&console.log("[WebSocketManager] Sent message:",t),this.metrics.messagesSent++,!0}catch(t){return console.error("[WebSocketManager] Error sending message:",t),!1}}getConnectionState(){return this.connectionState}getSubscriptionCount(){return this.subscriptions.size}_connect(){if("connecting"!==this.connectionState){this.isManualDisconnect=!1,this.connectionState="connecting",this.connectionStartTime=Date.now();try{this.connection&&(this.connection.removeEventListener("open",this.handleOpen),this.connection.removeEventListener("message",this.handleMessage),this.connection.removeEventListener("close",this.handleClose),this.connection.removeEventListener("error",this.handleError),this.connection=null);const t=`${this.config.baseUrl}?token=${this.config.token}`;this.config.debug&&console.log("[WebSocketManager] Connecting to:",t),this.connection=new WebSocket(t);const e=setTimeout((()=>{this.connection&&0===this.connection.readyState&&(console.error("[WebSocketManager] Connection timeout"),this.connection.close(),this.connectionState="disconnected",this._updateCircuitBreaker(!1),this.connectionPromise&&(this.connectionPromise.reject(new Error("Connection timeout")),this.connectionPromise=null),this._scheduleReconnect())}),this.config.timeouts.connection);this.connection.addEventListener("open",(t=>{clearTimeout(e),this.handleOpen(t)})),this.connection.addEventListener("message",this.handleMessage),this.connection.addEventListener("close",(t=>{clearTimeout(e),this.handleClose(t)})),this.connection.addEventListener("error",(t=>{clearTimeout(e),this.handleError(t)}))}catch(t){console.error("[WebSocketManager] Connection error:",t),this.connectionState="disconnected",this.connectionPromise&&(this.connectionPromise.reject(t),this.connectionPromise=null),this._scheduleReconnect()}}}handleOpen(t){const e=Date.now()-this.connectionStartTime;this.connectionState="connected",this.reconnectAttempts=0,this.lastMessageReceived=Date.now(),this.metrics.successfulConnections++,this.metrics.lastSuccessfulConnection=Date.now();const n=this.metrics.avgConnectionTime,i=this.metrics.successfulConnections;this.metrics.avgConnectionTime=(n*(i-1)+e)/i,this._updateCircuitBreaker(!0),this.config.debug&&console.log("[WebSocketManager] Connected successfully",{connectionTime:`${e}ms`,avgConnectionTime:`${Math.round(this.metrics.avgConnectionTime)}ms`,attempt:this.metrics.totalReconnects+1}),this.connectionPromise&&(this.connectionPromise.resolve(),this.connectionPromise=null);try{this._startActivityMonitoring(),this._sendAllSubscriptions(),this._processMessageQueue(),this._notifyWidgets("connection",{status:"connected"})}catch(t){console.error("[WebSocketManager] Error in handleOpen:",t)}}handleMessage(t){try{let e;this.lastMessageReceived=Date.now(),this.metrics.messagesReceived++;try{e=JSON.parse(t.data)}catch(n){const i=t.data.toString();if(this.config.debug&&console.log("[WebSocketManager] Received plain text message:",i),i.toLowerCase().includes("session revoked")||i.toLowerCase().includes("please relogin")||i.toLowerCase().includes("session expired")||i.toLowerCase().includes("unauthorized")||i.toLowerCase().includes("authentication failed"))return this.config.debug&&console.log("[WebSocketManager] Session revoked detected, attempting relogin..."),void this._handleSessionRevoked(i);const s=this._getTargetTypeFromErrorMessage(i);e=i.toLowerCase().includes("no night session")||i.toLowerCase().includes("no data")?{type:"error",message:i,error:i,noData:!0,targetType:s}:{type:"error",message:i,error:i,targetType:s}}this.config.debug&&console.log("[WebSocketManager] Processed message:",e),this._routeMessage(e)}catch(t){console.error("[WebSocketManager] Error handling message:",t),this._notifyWidgets("error",{error:"Failed to process message",details:t.message})}}handleClose(t){const e="connected"===this.connectionState;this.connectionState="disconnected",this.activeSubscriptions.clear(),this._stopActivityMonitoring(),this.config.debug&&console.log(`[WebSocketManager] Connection closed: ${t.code} ${t.reason}`),e&&this._notifyWidgets("connection",{status:"disconnected",code:t.code,reason:t.reason}),!this.isManualDisconnect&&this.subscriptions.size>0?(this.metrics.totalReconnects++,this._scheduleReconnect()):this.isManualDisconnect&&(this.config.debug&&console.log("[WebSocketManager] Manual disconnect - skipping reconnection"),this.isManualDisconnect=!1)}handleError(t){const e=this.connection?this.connection.readyState:"no connection";console.error("[WebSocketManager] WebSocket error:",{readyState:e,stateName:{0:"CONNECTING",1:"OPEN",2:"CLOSING",3:"CLOSED"}[e]||"UNKNOWN",url:this.connection?.url,error:t}),this.connection&&(3===this.connection.readyState?(console.log("[WebSocketManager] Connection closed due to error"),this.connectionState="disconnected",!this.isManualDisconnect&&this.subscriptions.size>0?(console.log("[WebSocketManager] Attempting reconnection after error..."),this._scheduleReconnect()):this.isManualDisconnect&&console.log("[WebSocketManager] Manual disconnect - skipping reconnection after error")):2===this.connection.readyState&&console.log("[WebSocketManager] Connection closing...")),this._notifyWidgets("connection",{status:"error",error:"WebSocket connection error",readyState:e,details:"Connection failed or was closed unexpectedly"}),this.connectionPromise&&(this.connectionPromise.reject(new Error("WebSocket connection failed")),this.connectionPromise=null)}_sendAllSubscriptions(){try{const t=new Set;this.subscriptions.forEach((e=>{e&&e.types&&e.types.forEach((e=>t.add(e)))})),this._sendSubscriptions([...t])}catch(t){console.error("[WebSocketManager] Error sending subscriptions:",t)}}_sendSubscriptions(t){t.forEach((t=>{if("queryoptionchain"===t)this._sendOptionChainSubscriptions();else{this._getSymbolsForType(t).forEach((e=>{const n=`${t}:${e}`;if(this.activeSubscriptions.has(n))this.config.debug&&console.log(`[WebSocketManager] Subscription ${n} already active, skipping`);else{let i;i="queryoptionl1"===t?{type:t,optionName:e}:{type:t,symbol:e},this.config.debug&&console.log(`[WebSocketManager] Sending subscription for ${n}`),this.send(i)&&this.activeSubscriptions.add(n)}}))}}))}_sendOptionChainSubscriptions(){this.subscriptions.forEach(((t,e)=>{if(t.types.has("queryoptionchain")&&t.symbol&&t.additionalParams?.date){const e=`queryoptionchain:${t.symbol}:${t.additionalParams.date}`;if(this.activeSubscriptions.has(e))this.config.debug&&console.log(`[WebSocketManager] Option chain subscription ${e} already active, skipping`);else{const n={type:"queryoptionchain",underlying:t.symbol,date:t.additionalParams.date};this.config.debug&&console.log(`[WebSocketManager] Sending option chain subscription for ${e}`),this.send(n)&&this.activeSubscriptions.add(e)}}}))}_getSymbolsForType(t){const e=new Set;return this.subscriptions.forEach(((n,i)=>{n.types.has(t)&&n.symbol&&e.add(n.symbol)})),[...e]}_routeMessage(t){if(Array.isArray(t)&&0===t.length)return void(this.config.debug&&console.log("[WebSocketManager] Received empty array, ignoring"));if(this._cacheMessage(t),t.targetType){const e=this.typeSubscriptions.get(t.targetType);if(e&&e.size>0)return this.config.debug&&console.log(`[WebSocketManager] Routing ${t.targetType} error to specific widgets:`,[...e]),void e.forEach((e=>{const n=this.subscriptions.get(e);if(n)try{n.callback({event:"data",data:t,widgetId:e})}catch(t){console.error(`[WebSocketManager] Error in widget ${e} callback:`,t)}}))}const e=this._getRelevantWidgets(t);if(this.config.debug&&e.size>0&&console.log(`[WebSocketManager] Routing message to ${e.size} relevant widgets`),0===e.size&&this.subscriptions.size>0)return this.config.debug&&console.log("[WebSocketManager] No specific routing found, broadcasting to all widgets"),void this.subscriptions.forEach(((e,n)=>{try{e.callback({event:"data",data:t,widgetId:n})}catch(t){console.error(`[WebSocketManager] Error in widget ${n} callback:`,t)}}));e.forEach((e=>{const n=this.subscriptions.get(e);if(n)try{n.callback({event:"data",data:t,widgetId:e})}catch(t){console.error(`[WebSocketManager] Error in widget ${e} callback:`,t)}}))}_cacheMessage(t){try{const e=this._extractSymbol(t),n=this._extractMessageType(t);if(n&&e){const i=`${n}:${e}`;this.lastMessageCache.set(i,t),this.config.debug&&console.log(`[WebSocketManager] Cached message for ${i}`)}if(Array.isArray(t)&&t.length>0&&t[0].Symbol){const e=t[0].Symbol,n=this._extractMessageType(t);if(n&&e){const i=`${n}:${e}`;this.lastMessageCache.set(i,t),this.config.debug&&console.log(`[WebSocketManager] Cached array message for ${i}`)}}t.Data&&Array.isArray(t.Data)&&t.Data.forEach((e=>{if(e.Symbol){const n=`queryl1:${e.Symbol}`;this.lastMessageCache.set(n,t),this.config.debug&&console.log(`[WebSocketManager] Cached data item for ${n}`)}}))}catch(t){console.error("[WebSocketManager] Error caching message:",t)}}_getRelevantWidgets(t){const e=new Set;return this._addRelevantWidgetsForItem(t,e),e}_addRelevantWidgetsForItem(t,e){t.Data&&Array.isArray(t.Data)?t.Data.forEach((t=>{this._processDataItem(t,e)})):t&&t[0]&&void 0!==t[0].Strike&&t[0].Expire&&!t[0].underlyingSymbol?this._processOptionChainData(t,e):this._processDataItem(t,e)}_processOptionChainData(t,e){if(!t||0===t.length)return;const n=t[0],i=this._extractUnderlyingFromOption(n.Symbol),s=n.Expire;if(this.config.debug&&console.log("[WebSocketManager] Processing option chain data:",{underlyingSymbol:i,expireDate:s,contractCount:t.length}),i){const t=this.symbolSubscriptions.get(i);t&&t.forEach((t=>{const n=this.subscriptions.get(t);if(n&&n.types.has("queryoptionchain")){let o=!0;if(n.additionalParams?.date&&s){o=this._normalizeDate(n.additionalParams.date)===this._normalizeDate(s)}o&&(e.add(t),this.config.debug&&console.log(`[WebSocketManager] Routing option chain data for ${i} (${s}) to widget ${t}`))}}))}}_processDataItem(t,e){const n=this._extractSymbol(t),i=this._extractMessageType(t);if(this.config.debug&&console.log("[WebSocketManager] Processing data item:",{symbol:n,messageType:i,dataItem:t}),n){const t=this.symbolSubscriptions.get(n);t&&t.forEach((t=>{const s=this.subscriptions.get(t);if(s){this._isDataTypeCompatible(i,s.types)&&(e.add(t),this.config.debug&&console.log(`[WebSocketManager] Routing ${i} data for ${n} to widget ${t}`))}}))}if(i){const t=this.typeSubscriptions.get(i);t&&t.forEach((t=>{const s=this.subscriptions.get(t);!s||s.symbol&&s.symbol!==n||(e.add(t),this.config.debug&&console.log(`[WebSocketManager] Routing ${i} data to type-subscribed widget ${t}`))}))}}_isDataTypeCompatible(t,e){if(e.has(t))return!0;const n={queryoptionchain:["queryoptionchain","optionchain","option-chain"],queryl1:["queryl1","stock","equity"],queryoptionl1:["queryoptionl1","option","options"],queryblueoceanl1:["queryblueoceanl1","blueoceanl1","blueocean"],querybrucel1:["querybrucel1","brucel1","bruce"]};for(const i of e){if((n[i]||[i]).includes(t))return!0}return!1}_normalizeDate(t){if(!t)return null;if(t.includes("/")){const[e,n,i]=t.split("/");return`${i}-${e.padStart(2,"0")}-${n.padStart(2,"0")}`}return t}_extractSymbol(t){return Array.isArray(t)&&t[0]&&t[0].Source&&null!==t[0].Symbol&&(t[0].Source.toLowerCase().includes("blueocean")||t[0].Source.toLowerCase().includes("bruce"))&&(t=t[0]),t.Symbol&&!t.Underlying&&t.Strike?this._extractUnderlyingFromOption(t.Symbol):t.Symbol||t.RootSymbol||t.Underlying||t.symbol||t.rootSymbol||t.underlying}_isOptionSymbol(t){return/^[A-Z]+\d{6}[CP]\d+$/.test(t)}_extractUnderlyingFromOption(t){const e=t.match(/^([A-Z]+)\d{6}[CP]\d+$/),n=e?e[1]:t;return{SPXW:"SPX$",SPX:"SPX$",NDXP:"NDX$",RUT:"RUT$",VIX:"VIX$",DJX:"DJX$"}[n]||n}_extractMessageType(t){return t.type?t.type:t[0]&&t[0].Source&&(t[0].Source.toLowerCase().includes("blueocean")||t[0].Source.toLowerCase().includes("blueocean-d")||t[0].Source.toLowerCase().includes("bruce"))?t[0].Source.toLowerCase().includes("bruce")?"querybrucel1":"queryblueoceanl1":void 0!==t.Strike||void 0!==t.Expire||t.Symbol&&this._isOptionSymbol(t.Symbol)?"queryoptionl1":void 0!==t.BidPx||void 0!==t.AskPx?"queryl1":null}_notifyWidgets(t,e){try{this.subscriptions.forEach(((n,i)=>{try{n&&n.callback&&n.callback({event:t,data:e,widgetId:i})}catch(t){console.error(`[WebSocketManager] Error notifying widget ${i}:`,t)}}))}catch(t){console.error("[WebSocketManager] Error in _notifyWidgets:",t)}}_processMessageQueue(){if(!this.messageQueue||!Array.isArray(this.messageQueue))return this.config.debug&&console.warn("[WebSocketManager] messageQueue not properly initialized, creating new array"),void(this.messageQueue=[]);for(this.config.debug&&this.messageQueue.length>0&&console.log(`[WebSocketManager] Processing ${this.messageQueue.length} queued messages`);this.messageQueue.length>0;){const t=this.messageQueue.shift();t&&this.send(t)}}_cleanupUnusedSubscriptions(){const t=new Set;this.subscriptions.forEach((e=>{e.types.forEach((e=>t.add(e)))})),this.activeSubscriptions.forEach((e=>{const[n]=e.split(":");t.has(n)||this.activeSubscriptions.delete(e)}))}_scheduleReconnect(){if(!this.isOnline)return this.config.debug&&console.log("[WebSocketManager] Network offline, waiting for network to return..."),void this._notifyWidgets("connection",{status:"offline",reason:"Network offline - will reconnect when network returns"});if("open"===this.circuitBreaker.state){const t=Date.now()-this.circuitBreaker.lastFailureTime,e=this.circuitBreaker.timeout-t;return console.warn(`[WebSocketManager] Circuit breaker OPEN, will retry in ${Math.round(e/1e3)}s`),void this._notifyWidgets("connection",{status:"circuit_open",reason:"Too many connection failures - circuit breaker active",retryIn:Math.round(e/1e3)})}if(this.reconnectAttempts>=this.config.reconnectOptions.maxAttempts)return console.error("[WebSocketManager] Max reconnection attempts reached"),this.metrics.failedConnections++,void this._notifyWidgets("connection",{status:"failed",error:"Max reconnection attempts reached",maxAttempts:this.config.reconnectOptions.maxAttempts,canRetry:!0});this.connectionState="reconnecting",this.reconnectAttempts++;const t=this.config.reconnectOptions.initialDelay*Math.pow(this.config.reconnectOptions.backoff,this.reconnectAttempts-1),e=Math.min(t,this.config.reconnectOptions.maxDelay),n=e*this.config.reconnectOptions.jitter,i=(2*Math.random()-1)*n,s=Math.max(0,e+i);console.log(`[WebSocketManager] Scheduling reconnect attempt ${this.reconnectAttempts}/${this.config.reconnectOptions.maxAttempts} in ${Math.round(s)}ms`),this._notifyWidgets("connection",{status:"reconnecting",attempt:this.reconnectAttempts,maxAttempts:this.config.reconnectOptions.maxAttempts,delay:Math.round(s),estimatedTime:new Date(Date.now()+s).toISOString()}),this.reconnectTimer=setTimeout((()=>{this._connect()}),s)}_resetState(){this.connectionState="disconnected",this.reconnectAttempts=0,this.connectionPromise=null,this.subscriptions&&this.subscriptions instanceof Map||(this.subscriptions=new Map),this.activeSubscriptions&&this.activeSubscriptions instanceof Set||(this.activeSubscriptions=new Set),this.messageQueue&&Array.isArray(this.messageQueue)||(this.messageQueue=[]),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}_getTargetTypeFromErrorMessage(t){const e=t.toLowerCase();return e.includes("night session")||e.includes("blue ocean")||e.includes("bruce")||e.includes("blueocean")?e[0].toLowerCase().includes("bruce")?"querybrucel1":"queryblueoceanl1":e.includes("option chain")||e.includes("expiration date")?"queryoptionchain":e.includes("option")||e.includes("strike")||e.includes("expiration")?"queryoptionl1":e.includes("stock")||e.includes("equity")||e.includes("bid")||e.includes("ask")?"queryl1":null}async _handleSessionRevoked(t){if(this.isReloginInProgress)this.config.debug&&console.log("[WebSocketManager] Relogin already in progress, skipping...");else{this.isReloginInProgress=!0;try{if(this._notifyWidgets("session_revoked",{message:t,status:"attempting_relogin"}),this.config.debug&&console.log("[WebSocketManager] Session revoked, attempting relogin..."),this._closeConnection(),!this.reloginCallback||"function"!=typeof this.reloginCallback)throw new Error("No relogin callback provided");{const t=await this.reloginCallback();if(!(t&&"string"==typeof t&&t.length>0))throw new Error("Relogin failed: Invalid token received");this.updateToken(t),this.config.debug&&console.log("[WebSocketManager] Relogin successful, reconnecting..."),await this._reconnectAndResubscribe()}}catch(e){console.error("[WebSocketManager] Relogin failed:",e),this._notifyWidgets("session_revoked",{message:t,status:"relogin_failed",error:e.message}),this.connectionState="failed"}finally{this.isReloginInProgress=!1}}}async _reconnectAndResubscribe(){try{this.connectionState="disconnected",this.reconnectAttempts=0,this.activeSubscriptions.clear(),await this.connect(),this.config.debug&&console.log("[WebSocketManager] Reconnected successfully after relogin"),this._notifyWidgets("session_revoked",{status:"relogin_successful"})}catch(t){throw console.error("[WebSocketManager] Failed to reconnect after relogin:",t),t}}_closeConnection(){this.connection&&(this.connection.removeEventListener("open",this.handleOpen),this.connection.removeEventListener("message",this.handleMessage),this.connection.removeEventListener("close",this.handleClose),this.connection.removeEventListener("error",this.handleError),this.connection.readyState===WebSocket.OPEN&&this.connection.close(),this.connection=null),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.connectionState="disconnected"}setReloginCallback(t){this.reloginCallback=t,this.config.debug&&console.log("[WebSocketManager] Relogin callback set")}updateToken(t){this.config.token=t,this.apiService=new Iu(this.config.token,this.config.apiBaseUrl),this.config.debug&&console.log("[WebSocketManager] Token updated")}getApiService(){return this.apiService}async manualReconnect(){this.config.debug&&console.log("[WebSocketManager] Manual reconnect initiated"),this.reconnectAttempts=0,this.connectionState="disconnected",this.isManualDisconnect=!1,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this._notifyWidgets("connection",{status:"reconnecting",attempt:1,maxAttempts:this.config.reconnectOptions.maxAttempts,manual:!0});try{return await this.connect(),!0}catch(t){return console.error("[WebSocketManager] Manual reconnect failed:",t),!1}}_initNetworkMonitoring(){"undefined"!=typeof window&&(this.onlineHandler=()=>{this.isOnline=!0,this.config.debug&&console.log("[WebSocketManager] Network online detected"),"disconnected"===this.connectionState&&this.subscriptions.size>0&&(console.log("[WebSocketManager] Network restored, attempting reconnection..."),this.reconnectAttempts=0,this._scheduleReconnect())},this.offlineHandler=()=>{this.isOnline=!1,this.config.debug&&console.log("[WebSocketManager] Network offline detected"),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this._notifyWidgets("connection",{status:"offline",reason:"Network offline"})},window.addEventListener("online",this.onlineHandler),window.addEventListener("offline",this.offlineHandler))}_cleanupNetworkMonitoring(){"undefined"!=typeof window&&(this.onlineHandler&&(window.removeEventListener("online",this.onlineHandler),this.onlineHandler=null),this.offlineHandler&&(window.removeEventListener("offline",this.offlineHandler),this.offlineHandler=null))}_startActivityMonitoring(){this.config.enableActivityMonitoring?(this._stopActivityMonitoring(),this.activityCheckInterval=setInterval((()=>{if("connected"!==this.connectionState)return;const t=Date.now()-this.lastMessageReceived;t>this.config.timeouts.inactivity?(console.warn(`[WebSocketManager] No messages received in ${this.config.timeouts.inactivity}ms, connection may be dead`),this.connection&&this.connection.close()):this.config.debug&&t>3e4&&console.log(`[WebSocketManager] Last message: ${Math.round(t/1e3)}s ago`)}),15e3)):this.config.debug&&console.log("[WebSocketManager] Activity monitoring disabled")}_stopActivityMonitoring(){this.activityCheckInterval&&(clearInterval(this.activityCheckInterval),this.activityCheckInterval=null)}_updateCircuitBreaker(t){t?(this.circuitBreaker.failureCount=0,this.circuitBreaker.state="closed",this.circuitBreaker.resetTimer&&(clearTimeout(this.circuitBreaker.resetTimer),this.circuitBreaker.resetTimer=null),this.config.debug&&console.log("[WebSocketManager] Circuit breaker CLOSED - connection healthy")):(this.circuitBreaker.failureCount++,this.circuitBreaker.lastFailureTime=Date.now(),this.config.debug&&console.log(`[WebSocketManager] Circuit breaker failure ${this.circuitBreaker.failureCount}/${this.circuitBreaker.failureThreshold}`),this.circuitBreaker.failureCount>=this.circuitBreaker.failureThreshold&&(this.circuitBreaker.state="open",console.warn("[WebSocketManager] Circuit breaker OPEN - too many failures"),this.circuitBreaker.resetTimer=setTimeout((()=>{this.circuitBreaker.state="half-open",this.circuitBreaker.failureCount=0,console.log("[WebSocketManager] Circuit breaker HALF-OPEN - will attempt one reconnection"),this.subscriptions.size>0&&"connected"!==this.connectionState&&(this.reconnectAttempts=0,this._scheduleReconnect())}),this.circuitBreaker.timeout)))}getConnectionMetrics(){const t=this._calculateConnectionQuality();return{state:this.connectionState,quality:t,isOnline:this.isOnline,circuitBreakerState:this.circuitBreaker.state,reconnectAttempts:this.reconnectAttempts,maxReconnectAttempts:this.config.reconnectOptions.maxAttempts,successfulConnections:this.metrics.successfulConnections,failedConnections:this.metrics.failedConnections,totalReconnects:this.metrics.totalReconnects,avgConnectionTime:Math.round(this.metrics.avgConnectionTime),lastSuccessfulConnection:this.metrics.lastSuccessfulConnection?new Date(this.metrics.lastSuccessfulConnection).toISOString():null,messagesReceived:this.metrics.messagesReceived,messagesSent:this.metrics.messagesSent,activeSubscriptions:this.subscriptions.size,lastMessageReceived:this.lastMessageReceived?`${Math.round((Date.now()-this.lastMessageReceived)/1e3)}s ago`:"never"}}_calculateConnectionQuality(){if("connected"!==this.connectionState)return"poor";const t=this.lastMessageReceived?Date.now()-this.lastMessageReceived:1/0;return t<3e4&&this.metrics.avgConnectionTime<2e3?"excellent":t<6e4&&this.metrics.avgConnectionTime<5e3?"good":t<this.config.timeouts.inactivity?"fair":"poor"}enableMetricsLogging(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:3e4;this.metricsInterval&&clearInterval(this.metricsInterval),this.metricsInterval=setInterval((()=>{console.log("[WebSocketManager] Metrics:",this.getConnectionMetrics())}),t),console.log("[WebSocketManager] Metrics logging enabled (every "+t/1e3+"s)")}disableMetricsLogging(){this.metricsInterval&&(clearInterval(this.metricsInterval),this.metricsInterval=null,console.log("[WebSocketManager] Metrics logging disabled"))}disconnect(){this.isManualDisconnect=!0,this._stopActivityMonitoring(),this._cleanupNetworkMonitoring(),this.circuitBreaker.resetTimer&&(clearTimeout(this.circuitBreaker.resetTimer),this.circuitBreaker.resetTimer=null),this.disableMetricsLogging(),this._closeConnection(),this._notifyWidgets("connection",{status:"disconnected",manual:!0}),this.config.debug&&console.log("[WebSocketManager] Disconnected and cleaned up")}}class zu{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.config={apiBaseUrl:t.apiBaseUrl||"https://mdas-api-dev.viewtrade.dev",loginEndpoint:t.loginEndpoint||"/api/user/login",refreshEndpoint:t.refreshEndpoint||"/api/user/refresh",refreshBuffer:t.refreshBuffer||300,maxRetries:t.maxRetries||3,debug:t.debug||!1},this.credentials=null,this.currentToken=null,this.tokenExpiry=null,this.refreshTimer=null,this.isAuthenticating=!1,this.retryCount=0,this.authenticationPromise=null,this.listeners=new Map,this.config.debug&&console.log("[AuthManager] Initialized with config:",{...this.config,loginEndpoint:this.config.loginEndpoint})}setCredentials(t,e){this.credentials={user_name:t,password:e},this.config.debug&&console.log("[AuthManager] Credentials set for user:",t)}async authenticate(){if(this.isAuthenticating&&this.authenticationPromise)return this.config.debug&&console.log("[AuthManager] Authentication already in progress, waiting..."),await this.authenticationPromise;if(!this.credentials)throw new Error("No credentials provided. Call setCredentials() first.");this.isAuthenticating=!0,this.authenticationPromise=this._performAuthentication();try{return await this.authenticationPromise}finally{this.isAuthenticating=!1,this.authenticationPromise=null}}async getValidToken(){return this.currentToken?this._isTokenNearExpiry()?(this.config.debug&&console.log("[AuthManager] Token near expiry, refreshing..."),await this.refreshToken()):this.currentToken:await this.authenticate()}async refreshToken(){if(this.isAuthenticating)return this.currentToken;this.isAuthenticating=!0;try{const t=await this._performRefresh();return this._handleAuthSuccess(t),this.currentToken}catch(t){return this.config.debug&&console.log("[AuthManager] Token refresh failed, attempting re-login"),await this.authenticate()}finally{this.isAuthenticating=!1}}async handleAuthError(t){if(this.config.debug&&console.log("[AuthManager] Handling auth error:",t),this.retryCount>=this.config.maxRetries){const t=new Error(`Authentication failed after ${this.config.maxRetries} attempts`);throw this._notifyListeners("auth_failed",{error:t}),this.retryCount=0,t}this.retryCount++,this._clearToken();try{const t=await this.authenticate();return this.retryCount=0,this._notifyListeners("auth_recovered",{token:t}),t}catch(t){throw this._notifyListeners("auth_retry_failed",{error:t,attempt:this.retryCount}),t}}addEventListener(t,e){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e)}removeEventListener(t,e){this.listeners.has(t)&&this.listeners.get(t).delete(e)}destroy(){this._clearToken(),this.credentials=null,this.listeners.clear(),this.config.debug&&console.log("[AuthManager] Destroyed")}async _performLogin(){const t=`${this.config.apiBaseUrl}${this.config.loginEndpoint}`;this.config.debug&&console.log("[AuthManager] Attempting login to:",t);const e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({user_name:this.credentials.user_name,password:this.credentials.password})});if(!e.ok){const t=await e.json().catch((()=>({})));throw new Error(t.message||`Login failed: ${e.status} ${e.statusText}`)}const n=await e.json(),i=n.access_token,s=n.refresh_token,o=n.access_expiration,a=n.user_id;if(!i)throw console.error("[AuthManager] Login response missing token:",n),new Error("No token received from login response");let r=null;if(o)try{if(r=new Date(o),isNaN(r.getTime()))throw new Error("Invalid expiration date format")}catch(t){console.warn("[AuthManager] Could not parse expiration date:",n.expiration),r=new Date(Date.now()+36e5)}else r=new Date(Date.now()+18e6);return this.config.debug&&console.log("[AuthManager] Login successful:",{tokenLength:i.length,expiresAt:r.toISOString(),timeUntilExpiry:Math.round((r.getTime()-Date.now())/1e3/60)+" minutes"}),{token:i,refreshToken:s,expirationDate:r,user_id:a}}async _performRefresh(){return this.config.debug&&console.log("[AuthManager] No refresh endpoint available, performing full re-authentication"),await this._performLogin()}async _performAuthentication(){try{const t=await this._performLogin();return this._handleAuthSuccess(t),this.currentToken}catch(t){throw this._handleAuthError(t),t}}_handleAuthSuccess(t){this.currentToken=t.token,t.expirationDate&&(this.tokenExpiry=t.expirationDate),this.retryCount=0,this._scheduleTokenRefresh(),this.config.debug&&console.log("[AuthManager] Authentication successful, token expires:",this.tokenExpiry),this._notifyListeners("auth_success",{token:this.currentToken,expiresAt:this.tokenExpiry})}_handleAuthError(t){this.config.debug&&console.error("[AuthManager] Authentication failed:",t.message),this._notifyListeners("auth_error",{error:t})}_isTokenNearExpiry(){if(!this.tokenExpiry)return!0;const t=new Date,e=1e3*this.config.refreshBuffer;return t>=new Date(this.tokenExpiry.getTime()-e)}_scheduleTokenRefresh(){if(this.refreshTimer&&clearTimeout(this.refreshTimer),!this.tokenExpiry)return;const t=new Date,e=1e3*this.config.refreshBuffer,n=new Date(this.tokenExpiry.getTime()-e),i=Math.max(0,n.getTime()-t.getTime());this.config.debug&&console.log(`[AuthManager] Scheduling token refresh in ${i/1e3} seconds`),this.refreshTimer=setTimeout((async()=>{try{await this.refreshToken()}catch(t){console.error("[AuthManager] Scheduled refresh failed:",t)}}),i)}_clearToken(){this.currentToken=null,this.tokenExpiry=null,this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null)}_notifyListeners(t,e){this.listeners.has(t)&&this.listeners.get(t).forEach((n=>{try{n(e)}catch(e){console.error(`[AuthManager] Error in ${t} listener:`,e)}}))}}class Wu{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.config={username:t.username,password:t.password,token:t.token,authCallback:t.authCallback,wsUrl:t.wsUrl||"https://mdas-api-dev.viewtrade.dev/wss/sub",apiBaseUrl:t.apiBaseUrl||"https://mdas-api-dev.viewtrade.dev",debug:t.debug||!1,maxConnections:t.maxConnections||1,autoAuth:!1!==t.autoAuth,...t},this.stateManager=new e,this.widgetManager=new Lu(this.stateManager),this.wsManager=null,this.authManager=null,this.isInitialized=!1,this._validateAuthConfig()}async initialize(){try{return this.isInitialized?(console.warn("SDK already initialized"),this):(this.config.debug&&console.log("Initializing MDAS SDK with config:",{hasCredentials:!(!this.config.username||!this.config.password),hasToken:!!this.config.token,hasAuthCallback:!!this.config.authCallback,wsUrl:this.config.wsUrl,autoAuth:this.config.autoAuth}),this.config.username&&this.config.password&&await this._initializeBuiltInAuth(),this.isInitialized=!0,this)}catch(t){throw console.error("Failed to initialize SDK:",t),t}}async createWidget(t,e){let n,i,s,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(!this.isInitialized)throw new Error("SDK not initialized. Call initialize() first.");if(this.wsManager||await this._initializeWebSocketManager(),"object"!=typeof t||null===t||Array.isArray(t))n=t,i=e,s=o||{};else if(n=t.type,s=t.options||{},t.containerEl&&1===t.containerEl.nodeType)i=t.containerEl;else if(t.containerId){const e=String(t.containerId).replace(/^#/,"");let n="undefined"!=typeof document?document.getElementById(e):null;n||"undefined"==typeof document||(n=document.createElement("div"),n.id=e,document.body.appendChild(n)),i=n}else{const t="mdas-widget";let e="undefined"!=typeof document?document.getElementById(t):null;e||"undefined"==typeof document||(e=document.createElement("div"),e.id=t,document.body.appendChild(e)),i=e}const a={...s,wsManager:this.wsManager,debug:this.config.debug};return a.reconnect=async()=>await this.manualReconnect(),this.widgetManager.createWidget(n,i,a)}async login(t,e){this.authManager||(this.authManager=new zu({apiBaseUrl:this.config.apiBaseUrl,debug:this.config.debug})),this.authManager.setCredentials(t,e);const n=await this.authManager.authenticate();return this.wsManager&&await this._reconnectWithNewToken(n),n}async destroy(){this.authManager&&(this.authManager.destroy(),this.authManager=null),this.wsManager&&(this.wsManager.disconnect(),this.wsManager=null),this.widgetManager&&this.widgetManager.destroy(),this.isInitialized=!1}getConnectionState(){return this.wsManager?this.wsManager.getConnectionState():"disconnected"}getActiveWidgetCount(){return this.widgetManager?this.widgetManager.getWidgetCount():0}getSubscriptionCount(){return this.wsManager?this.wsManager.getSubscriptionCount():0}isAuthenticated(){return this.authManager?!!this.authManager.currentToken:!!this.config.token}disconnect(){this.wsManager&&(this.wsManager.disconnect(),this.config.debug&&console.log("[MdasSDK] WebSocket disconnected"))}async connect(){if(this.wsManager)try{await this.wsManager.connect(),this.config.debug&&console.log("[MdasSDK] WebSocket reconnected")}catch(t){throw console.error("[MdasSDK] Failed to reconnect:",t),t}else this.config.debug&&console.log("[MdasSDK] No WebSocketManager exists, initializing..."),await this._initializeWebSocketManager()}isConnected(){return!!this.wsManager&&"connected"===this.wsManager.getConnectionState()}async manualReconnect(){return!!this.wsManager&&(this.config.debug&&console.log("[MdasSDK] Manual reconnect requested"),await this.wsManager.manualReconnect())}_validateAuthConfig(){const t=this.config.username&&this.config.password,e=this.config.token,n=this.config.authCallback;if(!t&&!e&&!n)throw new Error("Authentication required: Provide username/password, token, or authCallback");[t,e,n].filter(Boolean).length>1&&console.warn("Multiple auth methods provided. Priority: credentials > token > authCallback")}async _initializeBuiltInAuth(){this.authManager=new zu({apiBaseUrl:this.config.apiBaseUrl,debug:this.config.debug}),this.authManager.setCredentials(this.config.username,this.config.password),this.authManager.addEventListener("auth_error",(t=>{console.error("Authentication error:",t.error)})),this.authManager.addEventListener("auth_success",(t=>{this.config.debug&&console.log("Authentication successful")})),this.authManager.addEventListener("auth_failed",(t=>{console.error("Authentication failed after retries:",t.error)})),this.config.autoAuth&&await this.authManager.authenticate()}async _initializeWebSocketManager(){const t=await this._getToken();this.wsManager=new Ou({debug:this.config.debug,token:t,baseUrl:this.config.wsUrl,apiBaseUrl:this.config.apiBaseUrl,maxConnections:this.config.maxConnections,reconnectOptions:{maxAttempts:5,delay:1e3,backoff:2},reloginCallback:async()=>{try{if(this.authManager){this.config.debug&&console.log("[MdasSDK] Attempting relogin after session revoked...");const t=await this.authManager.authenticate();return this.config.debug&&console.log("[MdasSDK] Relogin successful, new token obtained"),t}return this.config.authCallback?await this.config.authCallback():(console.error("[MdasSDK] No authentication method available for relogin"),null)}catch(t){return console.error("[MdasSDK] Relogin failed:",t),null}}}),this.wsManager.addEventListener=this.wsManager.addEventListener||(()=>{});try{await this.wsManager.connect(),this.config.debug&&console.log("WebSocketManager connected successfully")}catch(t){t.message.includes("401")||t.message.includes("403")?await this._handleWebSocketAuthError(t):console.error("Failed to connect WebSocketManager:",t)}}async _getToken(){if(this.authManager)return await this.authManager.getValidToken();if(this.config.authCallback){const t=await this.config.authCallback();return t.token||t}return this.config.token}async _handleWebSocketAuthError(t){if(!this.authManager)throw t;try{const e=await this.authManager.handleAuthError(t);await this._reconnectWithNewToken(e)}catch(t){throw console.error("Failed to recover from auth error:",t),t}}async _reconnectWithNewToken(t){this.wsManager&&(this.wsManager.updateToken(t),this.wsManager.disconnect(),await this.wsManager.connect())}}"undefined"!=typeof window&&(window.MdasSDK=Wu,window.MdasSDK.MdasSDK=Wu,window.MdasSDK.MarketDataModel=i,window.MdasSDK.NightSessionModel=y,window.MdasSDK.OptionsModel=v,window.MdasSDK.IntradayChartModel=T,window.MdasSDK.CombinedMarketWidget=D,window.MdasSDK.IntradayChartWidget=Au),t.CombinedMarketWidget=D,t.IntradayChartModel=T,t.IntradayChartWidget=Au,t.MarketDataModel=i,t.MdasSDK=Wu,t.NightSessionModel=y,t.OptionChainWidget=M,t.OptionsModel=v,t.default=Wu,Object.defineProperty(t,"__esModule",{value:!0})}));//# sourceMappingURL=mdas-sdk.min.js.map