mdas-jsview-sdk 1.0.12-uat.0 → 1.0.14-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.
@@ -1,4 +1,4 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).MdasSDK={})}(this,(function(t){"use strict";class e{constructor(){this.state=new Map,this.listeners=new Map}setState(t,e){this.state.set(t,e),this.notifyListeners(t,e)}getState(t){return this.state.get(t)}subscribe(t,e){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e),()=>{const n=this.listeners.get(t);n&&n.delete(e)}}notifyListeners(t,e){const n=this.listeners.get(t);n&&n.forEach((t=>t(e)))}clear(){this.state.clear(),this.listeners.clear()}}class n{constructor(t,e,n){this.setContainer(t),this.options=e,this.widgetId=n,this.isDestroyed=!1,this.lastData=null,this.lastDataTimestamp=null,this.connectionQuality="disconnected",this.eventListeners=[],this.timeouts=[],this.intervals=[]}setContainer(t){if(!t)throw new Error("DOM element is required for widget");if("string"==typeof t){const e=document.querySelector(t);if(!e)throw new Error(`Element not found: ${t}`);this.container=e}else{if(t.nodeType!==Node.ELEMENT_NODE)throw new Error("Invalid element provided to widget");this.container=t}}showLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.remove("hidden")}hideLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.add("hidden")}showError(t){this.hideLoading(),this.clearError();const e=document.createElement("div");e.className="widget-error",e.textContent=`Error: ${t}`,this.container.appendChild(e)}clearError(){const t=this.container.querySelector(".widget-error");t&&t.remove()}showConnectionQuality(){const t=this.container.querySelector(".connection-quality");if(t&&t.remove(),"live"===this.connectionQuality)return;const e=this.container.querySelector('.last-update, [class*="last-update"], [class*="timestamp"]');if(e){const t=document.createElement("span");switch(t.className="connection-quality",this.connectionQuality){case"offline":t.textContent=" (disconnected)",t.style.color="#dc2626",t.style.fontWeight="500";break;case"reconnecting":t.textContent=" (reconnecting...)",t.style.color="#d97706"}t.style.fontSize="11px",e.appendChild(t)}}handleMessage(t){if(!this.isDestroyed)try{const{event:e,data:n}=t;if(console.log("[BaseWidget] handleMessage called with event:",e,"data:",n),this.debug&&console.log(`[${this.type}] Received:`,e,n),"connection"===e)return void this.handleConnectionStatus(n);if("data"===e)return console.log("[BaseWidget] Caching live data"),this.lastData=n,this.lastDataTimestamp=Date.now(),this.connectionQuality="live",void this.handleData(n);if("session_revoked"===e)return void this.handleSessionRevoked(n);this.handleCustomEvent&&this.handleCustomEvent(e,n)}catch(t){console.error(`[${this.constructor.name}] Error handling message:`,t),this.showError("Error processing data")}}handleSessionRevoked(t){"attempting_relogin"===t.status?(this.connectionQuality="reconnecting",this.lastData?this.showCachedData():this.showLoading()):"relogin_failed"===t.status?(this.connectionQuality="offline",this.showError(t.error||"Session expired. Please refresh the page.")):"relogin_successful"===t.status&&(this.connectionQuality="live",this.hideLoading(),this.clearError())}handleData(t){throw new Error("handleData must be implemented by child widget")}handleConnectionStatus(t){"connected"===t.status?(this.connectionQuality="live",this.clearError(),this.hideLoading()):"reconnecting"===t.status?(this.connectionQuality="reconnecting",this.lastData?this.showCachedData():this.showLoading()):"disconnected"===t.status?(this.connectionQuality="offline",this.lastData?this.showCachedData():this.hideLoading()):"failed"===t.status?(this.connectionQuality="offline",this.handleConnectionFailed(t),this.showConnectionQuality()):"error"===t.status&&(this.connectionQuality="offline",this.lastData?this.showCachedData():this.hideLoading())}showCachedData(){if(this.lastData&&this.lastDataTimestamp){const t=Date.now()-this.lastDataTimestamp,e=t>3e5,n={...this.lastData,_cached:!0,_cacheAge:t,_isStale:e,_quality:this.connectionQuality};this.handleData(n)}}addEventListener(t,e,n){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};t&&e&&n?(t.addEventListener(e,n,i),this.eventListeners.push({element:t,event:e,handler:n,options:i})):console.warn("[BaseWidget] Invalid addEventListener parameters")}setTimeout(t,e){const n=setTimeout(t,e);return this.timeouts.push(n),n}setInterval(t,e){const n=setInterval(t,e);return this.intervals.push(n),n}clearTimeout(t){clearTimeout(t);const e=this.timeouts.indexOf(t);e>-1&&this.timeouts.splice(e,1)}clearInterval(t){clearInterval(t);const e=this.intervals.indexOf(t);e>-1&&this.intervals.splice(e,1)}showInputError(t,e){if(!t)return;this.clearInputError(t);const n=document.createElement("div");n.className="input-error-message",n.textContent=e,n.style.cssText="\n color: #dc2626;\n font-size: 12px;\n margin-top: 4px;\n animation: slideDown 0.2s ease-out;\n ",t.classList.add("input-error"),t.style.borderColor="#dc2626",t.parentNode&&t.parentNode.insertBefore(n,t.nextSibling),t.errorElement=n}clearInputError(t){t&&(t.errorElement&&(t.errorElement.remove(),t.errorElement=null),t.classList.remove("input-error"),t.style.borderColor="")}destroy(){this.isDestroyed||(this.isDestroyed=!0,this.eventListeners.forEach((t=>{let{element:e,event:n,handler:i,options:s}=t;try{e.removeEventListener(n,i,s)}catch(t){console.error("[BaseWidget] Error removing event listener:",t)}})),this.eventListeners=[],this.timeouts.forEach((t=>{clearTimeout(t)})),this.timeouts=[],this.intervals.forEach((t=>{clearInterval(t)})),this.intervals=[],this.lastData=null,this.container&&(this.container.innerHTML=""),this.debug&&console.log(`[${this.constructor.name}] Widget destroyed and cleaned up`))}handleConnectionFailed(t){this.connectionQuality="offline",this.lastData?(this.showCachedData(),this.hideLoading()):this.hideLoading(),this.debug&&console.log(`[${this.constructor.name}] Connection failed after ${t.maxAttempts} attempts`)}}class i{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.symbol=t.Symbol||"",this.companyName=t.CompName||"",this.companyDescription=t.CompDesc||"",this.instrumentType=t.InstrType||"",this.lastPrice=t.LastPx||0,this.previousClose=t.YestClosePx||0,this.open=t.OpenPx||0,this.high=t.HighPx||0,this.low=t.LowPx||0,this.close=t.ClosingPx||0,this.change=t.Change||0,this.changePercent=t.ChangePercent||0,this.vwap=t.VWAP||0,this.volume=t.Volume||0,this.averageVolume=t.AvgVol30d||0,this.yesterdayVolume=t.YesterdayTradeVolume||0,this.bidPrice=t.BidPx||0,this.bidSize=t.BidSz||0,this.bidExchange=t.BidExch||"",this.askPrice=t.AskPx||0,this.askSize=t.AskSz||0,this.askExchange=t.AskExch||"",this.issueMarket=t.IssueMarket||"",this.marketName=t.MarketName||"",this.marketDescription=t.MarketDesc||"",this.mic=t.MIC||"",this.countryCode=t.CountryCode||"",this.timeZone=t.TimeZone||"",this.tradePrice=t.TradePx||0,this.tradeSize=t.TradeSz||0,this.tradeCondition=t.Condition||0,this.tradeTime=t.TradeTime||"",this.tradeRegion=t.TradeRegion||"",this.tradeRegionName=t.TradeRegionName||"",this.preMarketPrice=t.PreLastPx||0,this.preMarketTradeTime=t.PreTradeTime||"",this.postMarketPrice=t.PostLastPx||0,this.postMarketTradeTime=t.PostTradeTime||"",this.high52Week=t.High52wPx||0,this.low52Week=t.Low52wPx||0,this.high52WeekDate=t.High52wDate||"",this.low52WeekDate=t.Low52wDate||"",this.calendarYearHigh=t.CalendarYearHigh||0,this.calendarYearLow=t.CalendarYearLow||0,this.calendarYearHighDate=t.CalendarYearHighDate||"",this.calendarYearLowDate=t.CalendarYearLowDate||"",this.marketCap=t.MktCap||0,this.peRatio=t.PeRatio||0,this.beta=t.FundBeta||0,this.sharesOutstanding=t.ComShrsOut||0,this.dividendYield=t.DivYield||0,this.dividendAmount=t.DivAmt||0,this.dividendRate=t.DividendRate||0,this.dividendPaymentDate=t.DividendPaymentDate||t.PayDate||"",this.dividendExDate=t.DividendExDate||t.DivDateEx||"",this.isin=t.ISIN||"",this.cusip=t.CUSIP||"",this.sedol=t.SEDOL||"",this.gics=t.GICS||"",this.status=t.Status||"",this.dataSource=t.DataSource||"",this.quoteTime=t.QuoteTime||"",this.infoTime=t.InfoTime||""}get openPrice(){return this.open}get description(){return this.companyDescription}get lastUpdate(){return this.quoteTime||this.infoTime}getSecurityType(){return{257:"Stock",262:"ETF",261:"Mutual Fund",258:"Bond",260:"Option",263:"Future",259:"Index"}[this.instrumentType]||"Stock"}getPriceChange(){return this.change}getPriceChangePercent(){return 100*this.changePercent}getDataSource(){return"delay"===this.dataSource.toLowerCase()?"20 mins delayed":"real-time"===this.dataSource.toLowerCase()?"Real-time":null!==this.dataSource&&""!==this.dataSource?this.dataSource:"MDAS Server"}getCurrentPrice(){return this.lastPrice}get52WeekRange(){return`${this.low52Week.toFixed(2)} - ${this.high52Week.toFixed(2)}`}getMarketCapFormatted(){return this.formatMarketCap()}formatPrice(){return`$${(arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.lastPrice).toFixed(2)}`}formatPriceChange(){const t=this.getPriceChange();return`${t>=0?"+":""} ${t.toFixed(2)}`}formatPriceChangePercent(){const t=this.getPriceChangePercent();return`${t>=0?"+":""}${t.toFixed(2)}%`}formatBidAsk(){return`$${this.bidPrice.toFixed(2)} x ${this.bidSize} / $${this.askPrice.toFixed(2)} x ${this.askSize}`}formatVolume(){return 0===this.volume?"N/A":this.volume.toLocaleString()}formatAvgVolume(){return 0===this.averageVolume?"N/A":this.averageVolume.toLocaleString()}formatAverageVolume(){return 0===this.averageVolume?"N/A":this.averageVolume.toLocaleString()}formatDayRange(){return 0===this.low&&0===this.high?"N/A":`$${this.low.toFixed(2)} - $${this.high.toFixed(2)}`}format52WeekRange(){return 0===this.low52Week&&0===this.high52Week?"N/A":`${this.low52Week.toFixed(2)} - ${this.high52Week.toFixed(2)}`}formatMarketCap(){if(0===this.marketCap)return"N/A";const t=1e6*this.marketCap;return t>=1e12?`$${(t/1e12).toFixed(2)}T`:t>=1e9?`$${(t/1e9).toFixed(2)}B`:t>=1e6?`$${(t/1e6).toFixed(2)}M`:t>=1e3?`$${(t/1e3).toFixed(2)}K`:`$${t.toFixed(2)}`}formatAvg30DayVolume(){return this.formatAverageVolume()}static fromApiResponse(t){return new i(t)}}class s{constructor(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.widget=t,this.options={maxLength:e.maxLength||10,placeholder:e.placeholder||"Enter symbol...",onSymbolChange:e.onSymbolChange||(()=>{}),debug:e.debug||!1,autoUppercase:!1!==e.autoUppercase,...e},this.symbolType=e.symbolType||null,this.apiService=this.widget.wsManager.getApiService(),this.isEditing=!1,this.isValidating=!1,this.originalSymbol="",this.elements={},this.addStyles(),this.initialize()}initialize(){this.setupElements(),this.attachEventListeners()}setupElements(){if(this.elements.symbolDisplay=this.widget.container.querySelector(".editable-symbol"),!this.elements.symbolDisplay)throw new Error('No element with class "editable-symbol" found in widget');this.originalSymbol=this.elements.symbolDisplay.textContent.trim(),this.elements.symbolDisplay.dataset.originalSymbol=this.originalSymbol}attachEventListeners(){this.startEditing=this.startEditing.bind(this),this.handleKeydown=this.handleKeydown.bind(this),this.handleInput=this.handleInput.bind(this),this.handleBlur=this.handleBlur.bind(this),this.handleClickOutside=this.handleClickOutside.bind(this),this.elements.symbolDisplay.addEventListener("dblclick",this.startEditing)}startEditing(){this.isEditing||this.isValidating||(this.isEditing=!0,this.originalSymbol=this.elements.symbolDisplay.textContent.trim(),this.options.debug&&(console.log(this.widget.connectionQuality),console.log("[SymbolEditor] Starting edit mode for:",this.originalSymbol)),this.transformToInput())}transformToInput(){const t=this.elements.symbolDisplay,e=window.getComputedStyle(t),n=document.createElement("div");n.className="symbol-edit-container",n.style.display="inline-flex",n.style.alignItems="center",n.style.gap="8px",n.style.verticalAlign="baseline";const i=document.createElement("input");i.type="text",i.value=this.originalSymbol,i.className=t.className+" symbol-input-mode",i.style.fontSize=e.fontSize,i.style.fontWeight=e.fontWeight,i.style.fontFamily=e.fontFamily,i.style.color=e.color,i.style.backgroundColor="transparent",i.style.border="2px solid #3b82f6",i.style.borderRadius="4px",i.style.padding="2px 6px",i.style.margin="0",i.style.outline="none",i.style.width="auto",i.style.minWidth="120px",i.style.maxWidth="200px",i.style.flexShrink="0";const s=document.createElement("button");s.className="symbol-save-btn",s.innerHTML="✓",s.title="Save symbol",s.style.width="28px",s.style.height="28px",s.style.border="none",s.style.borderRadius="4px",s.style.backgroundColor="#059669",s.style.color="white",s.style.cursor="pointer",s.style.fontSize="12px",s.style.display="inline-flex",s.style.alignItems="center",s.style.justifyContent="center",s.style.flexShrink="0",n.appendChild(i),n.appendChild(s),t.style.display="none",t.parentNode.insertBefore(n,t.nextSibling),this.elements.symbolInput=i,this.elements.saveBtn=s,this.elements.editContainer=n,i.focus(),i.select(),i.addEventListener("keydown",this.handleKeydown),i.addEventListener("input",this.handleInput),i.addEventListener("blur",this.handleBlur),document.addEventListener("click",this.handleClickOutside),s.addEventListener("click",(t=>{t.stopPropagation(),this.saveSymbol()}))}async saveSymbol(){if(this.isValidating)return;const t=this.elements.symbolInput.value.trim(),e=await this.validateSymbol(t);if(!e.isValid)return this.showError(e.message),void setTimeout((()=>{this.elements.symbolInput&&(this.elements.symbolInput.value=this.originalSymbol,this.clearError(),this.cancelEditing())}),2e3);this.setLoadingState(!0);try{const n=await this.options.onSymbolChange(t,this.originalSymbol,e.data);if(n&&n.success)this.finishEditing(t),this.showSuccess("Symbol updated successfully");else{const t=n?.message||"Failed to update symbol";this.showError(t)}}catch(t){console.error("[SymbolEditor] Error changing symbol:",t),this.showError("Error updating symbol")}finally{this.setLoadingState(!1)}}cancelEditing(){this.options.debug&&console.log("[SymbolEditor] Cancelling edit mode"),this.finishEditing(this.originalSymbol)}finishEditing(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;this.isEditing=!1;const e=t||this.originalSymbol;this.elements.symbolDisplay.textContent=e,this.elements.symbolDisplay.dataset.originalSymbol=e,this.elements.symbolDisplay.style.display="",this.elements.editContainer&&(this.elements.editContainer.remove(),this.elements.editContainer=null),this.elements.symbolInput=null,this.elements.saveBtn=null,document.removeEventListener("click",this.handleClickOutside)}handleKeydown(t){"Enter"===t.key?(t.preventDefault(),this.saveSymbol()):"Escape"===t.key&&(t.preventDefault(),this.cancelEditing())}handleInput(t){if(this.options.autoUppercase){const e=t.target,n=e.selectionStart,i=e.selectionEnd;e.value=e.value.toUpperCase(),e.setSelectionRange(n,i)}this.clearError()}handleBlur(t){t.relatedTarget&&t.relatedTarget===this.elements.saveBtn||setTimeout((()=>{this.isEditing&&this.cancelEditing()}),150)}handleClickOutside(t){if(!this.isEditing)return;this.elements.symbolInput?.contains(t.target)||this.elements.saveBtn?.contains(t.target)||this.cancelEditing()}async validateSymbol(t){if("live"!==this.widget.connectionQuality)return{isValid:!1,message:"Not connected to data service"};if(!t||0===t.trim().length)return{isValid:!1,message:"Symbol cannot be empty"};const e=t.trim().toUpperCase();try{if("optionsl1"==this.symbolType){const t=await this.apiService.quoteOptionl1(e);return t[0]&&(t[0].error||1==t[0].not_found)?1==t[0].not_found?{isValid:!1,message:"Symbol not found"}:{isValid:!1,message:t[0].error}:{isValid:!0,data:t[0]}}if("quotel1"==this.symbolType||"nightsession"==this.symbolType){const t=await this.apiService.quotel1(e);return t&&t.message?t.message.includes("no data")?{isValid:!1,message:"Symbol not found"}:{isValid:!1,message:t.message}:{isValid:!0,data:t.data[0]}}if("nightsession"==this.symbolType){const t=await this.apiService.quoteBlueOcean(e);return t[0]&&1==t[0].not_found?{isValid:!1,message:"Symbol not found"}:t[0]&&0==t[0].not_found?{isValid:!0,data:t[0]}:{isValid:!1,message:t[0].error}}}catch(t){return console.warn("[SymbolEditor] API validation failed, falling back to basic validation:",t),this.defaultValidator(e)}return this.defaultValidator(e)}defaultValidator(t){return t&&0!==t.length?t.length>this.options.maxLength?{isValid:!1,message:`Symbol too long (max ${this.options.maxLength} chars)`}:{isValid:!0,data:null}:{isValid:!1,message:"Symbol cannot be empty"}}setLoadingState(t){this.isValidating=t,this.elements.saveBtn&&(t?(this.elements.saveBtn.innerHTML='<div class="loading-spinner"></div>',this.elements.saveBtn.disabled=!0):(this.elements.saveBtn.innerHTML="✓",this.elements.saveBtn.disabled=!1)),this.elements.symbolInput&&(this.elements.symbolInput.disabled=t)}showError(t){if(this.elements.symbolInput){this.elements.symbolInput.style.borderColor="#dc2626",this.elements.symbolInput.style.boxShadow="0 0 0 3px rgba(220, 38, 38, 0.1)",this.removeErrorMessage();const e=document.createElement("div");e.className="symbol-error-message",e.textContent=t,e.style.cssText="\n color: #dc2626;\n font-size: 12px;\n margin-bottom: 2px;\n position: absolute;\n background: white;\n z-index: 1000;\n transform: translateY(-100%);\n top: -4px;\n left: 0; // Align with input box\n ";const n=this.elements.editContainer;n&&(n.style.position="relative",n.appendChild(e)),this.elements.errorMessage=e,this.elements.symbolInput.focus()}}removeErrorMessage(){this.elements.errorMessage&&(this.elements.errorMessage.remove(),this.elements.errorMessage=null)}clearError(){this.elements.symbolInput&&(this.elements.symbolInput.style.borderColor="#3b82f6",this.elements.symbolInput.style.boxShadow="0 0 0 3px rgba(59, 130, 246, 0.1)",this.elements.symbolInput.title=""),this.removeErrorMessage()}showSuccess(t){this.options.debug&&console.log("[SymbolEditor] Success:",t)}updateSymbol(t){const e=this.options.autoUppercase?t.toUpperCase():t;this.elements.symbolDisplay.textContent=e,this.elements.symbolDisplay.dataset.originalSymbol=e,this.options.debug&&console.log("[SymbolEditor] Symbol updated externally:",e)}destroy(){this.finishEditing(),this.elements.symbolDisplay?.removeEventListener("dblclick",this.startEditing)}addStyles(){if(document.querySelector("#symbol-editor-styles"))return;const t=document.createElement("style");t.id="symbol-editor-styles",t.textContent='\n .editable-symbol {\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .editable-symbol:hover {\n opacity: 0.8;\n transform: scale(1.02);\n }\n\n .editable-symbol:hover::after {\n content: "✎";\n position: absolute;\n top: -8px;\n right: -8px;\n background: #3b82f6;\n color: white;\n font-size: 10px;\n padding: 2px 4px;\n border-radius: 4px;\n opacity: 0.8;\n pointer-events: none;\n }\n\n .symbol-input-mode {\n text-transform: uppercase;\n }\n\n .symbol-save-btn:hover {\n background-color: #047857 !important;\n transform: scale(1.05);\n }\n\n .symbol-save-btn:disabled {\n background-color: #9ca3af !important;\n cursor: not-allowed !important;\n transform: none !important;\n }\n\n .loading-spinner {\n width: 12px;\n height: 12px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: white;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n ',document.head.appendChild(t)}}const o='\n /* ========================================\n FONT SIZE SYSTEM - CSS Variables\n Adjust --mdas-base-font-size to scale all fonts\n ======================================== */\n :root {\n /* Base font size - change this to scale everything */\n --mdas-base-font-size: 14px;\n\n /* Relative font sizes (in em, scales with base) */\n --mdas-company-name-size: 1.43em; /* 20px at base 14px */\n --mdas-symbol-size: 1.79em; /* 25px at base 14px */\n --mdas-price-size: 2.29em; /* 32px at base 14px */\n --mdas-price-change-size: 1.14em; /* 16px at base 14px */\n --mdas-section-title-size: 1em; /* 14px at base 14px */\n --mdas-data-label-size: 0.93em; /* 13px at base 14px */\n --mdas-data-value-size: 1em; /* 14px at base 14px */\n --mdas-bid-ask-size: 1.07em; /* 15px at base 14px */\n --mdas-small-text-size: 0.86em; /* 12px at base 14px */\n --mdas-badge-size: 0.86em; /* 12px at base 14px */\n --mdas-loading-text-size: 1.14em; /* 16px at base 14px */\n --mdas-no-data-title-size: 1.14em; /* 16px at base 14px */\n --mdas-edit-icon-size: 0.71em; /* 10px at base 14px */\n }\n\n /* Apply base font size to widgets */\n .widget {\n font-size: var(--mdas-base-font-size);\n }\n\n /* ========================================\n OPTIONAL CARD STYLING\n Add \'widget-styled\' class for card design\n ======================================== */\n .widget-styled {\n background: white;\n border-radius: 12px;\n padding: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n border: 1px solid #e5e7eb;\n }\n\n /* Base widget styles */\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.95);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n border-radius: 12px;\n }\n\n .widget-loading-overlay.hidden {\n display: none;\n }\n\n .widget {\n font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;\n width: 100%;\n max-width: 1400px;\n }\n\n /* HEADER STYLES */\n\n .widget-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 20px;\n gap: 16px;\n }\n\n .symbol-section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .night-company-name {\n font-size: var(--mdas-company-name-size);\n font-weight: 500;\n color: #111827;\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .symbol {\n font-size: var(--mdas-symbol-size);\n font-weight: 700;\n color: #111827;\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .symbol:hover {\n opacity: 0.8;\n transform: scale(1.02);\n }\n\n .symbol:hover::after {\n content: "✎";\n position: absolute;\n top: -8px;\n right: -8px;\n background: #3b82f6;\n color: white;\n font-size: var(--mdas-edit-icon-size);\n padding: 2px 4px;\n border-radius: 4px;\n opacity: 0.8;\n }\n\n .data-type {\n background:rgb(214, 228, 250);\n padding: 2px 8px;\n border-radius: 6px;\n font-size: var(--mdas-badge-size);\n font-weight: 500;\n color: #3b82f6;\n }\n\n .price-section {\n text-align: right;\n }\n\n .current-price {\n font-size: var(--mdas-price-size);\n font-weight: 700;\n color: #111827;\n line-height: 1;\n }\n\n .price-change {\n font-size: var(--mdas-price-change-size);\n font-weight: 600;\n margin-top: 4px;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 4px;\n }\n\n .price-change.positive { \n color: #10b981; \n }\n\n .price-change.negative { \n color: #ef4444; \n }\n\n .price-change.neutral { \n color: #6b7280; \n }\n\n .market-data-widget .price-change.positive::before,\n .night-session-widget .price-change.positive::before,\n .options-widget .price-change.positive::before{\n content: "↗";\n }\n\n .market-data-widget .price-change.negative::before,\n .night-session-widget .price-change.negative::before,\n .options-widget .price-change.negative::before {\n content: "↘";\n }\n\n /* BODY SECTION STYLES */\n\n .data-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 32px;\n margin-bottom: 24px;\n }\n\n .data-section {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .section-title {\n font-size: var(--mdas-section-title-size);\n font-weight: 600;\n color: #6b7280;\n margin: 0;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n border-bottom: 1px solid #e5e7eb;\n padding-bottom: 8px;\n }\n\n .data-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n }\n\n .data-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .price-metadata,\n .data-label {\n font-size: var(--mdas-data-label-size);\n color: #6b7280;\n font-weight: 500;\n }\n\n .data-value {\n font-size: var(--mdas-data-value-size);\n font-weight: 600;\n color: #111827;\n text-align: right;\n flex-shrink: 0;\n }\n\n\n /* Footer */\n .widget-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 16px;\n border-top: 1px solid #e5e7eb;\n font-size: var(--mdas-small-text-size);\n color: #9ca3af;\n }\n \n\n /* LOADING STYLES */\n\n\n .loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n }\n\n .loading-spinner {\n width: 40px;\n height: 40px;\n border: 4px solid #e5e7eb;\n border-top-color: #3b82f6;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .loading-text {\n color: #6b7280;\n font-size: var(--mdas-loading-text-size);\n font-weight: 500;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n .widget-error {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n padding: 16px;\n color: #dc2626;\n font-size: var(--mdas-data-value-size);\n font-weight: 600;\n text-align: center;\n z-index: 10;\n max-width: 80%;\n }\n\n /* Price change colors */\n .positive {\n color: #059669;\n }\n\n .negative {\n color: #dc2626;\n }\n\n \n\n /* No data state */\n .no-data-state {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 160px;\n margin: 16px 0;\n background: #f9fafb;\n border: 1px solid #e5e7eb;\n border-radius: 12px;\n padding: 24px 16px;\n width: 100%;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .no-data-content {\n text-align: center;\n max-width: 280px;\n width: 100%;\n word-wrap: break-word;\n overflow-wrap: break-word;\n }\n\n .no-data-icon {\n margin-bottom: 16px;\n display: flex;\n justify-content: center;\n }\n\n .no-data-icon svg {\n opacity: 0.6;\n }\n\n .no-data-title {\n font-size: var(--mdas-no-data-title-size);\n font-weight: 600;\n color: #6b7280;\n margin: 0 0 8px 0;\n }\n\n .no-data-description {\n font-size: var(--mdas-data-value-size);\n color: #9ca3af;\n margin: 0 0 12px 0;\n line-height: 1.4;\n }\n\n .no-data-description strong {\n color: #6b7280;\n font-weight: 600;\n }\n\n .no-data-guidance {\n font-size: var(--mdas-small-text-size);\n color: #9ca3af;\n margin: 0;\n font-style: italic;\n }\n\n /* Error Access State */\n .no-data-state.error-access {\n border: 1px solid #fecaca;\n background:rgb(255, 255, 255);\n \n }\n\n .no-data-title.error {\n color: #ef4444;\n }\n\n .no-data-icon.error svg {\n stroke: #ef4444;\n }\n\n .no-data-icon.error svg circle[fill] {\n fill: #ef4444;\n }\n',a=`\n ${o}\n\n /* Reset and base styles for widget */\n\n .night-session-widget {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n line-height: 1.5;\n max-width: 800px;\n position: relative;\n }\n\n /* STANDARDIZED HEADER LAYOUT */\n\n\n .symbol-section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .night-session-widget .symbol {\n font-size: var(--mdas-symbol-size);\n font-weight: 700;\n color: #111827;\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .night-session-widget .symbol:hover {\n opacity: 0.8;\n transform: scale(1.02);\n }\n\n .night-session-widget .symbol:hover::after {\n content: "✎";\n position: absolute;\n top: -8px;\n right: -8px;\n background: #3b82f6;\n color: white;\n font-size: var(--mdas-edit-icon-size);\n padding: 2px 4px;\n border-radius: 4px;\n opacity: 0.8;\n }\n\n .company-info {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: var(--mdas-section-title-size);\n color: #6b7280;\n }\n\n .market-name {\n font-weight: 600;\n text-transform: uppercase;\n }\n\n /* Company and market info styling */\n .company-market-info {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: var(--mdas-small-text-size);\n color: #6b7280;\n margin-bottom: 4px;\n }\n\n .company-market-info .market-name {\n font-weight: 600;\n }\n\n .company-market-info .market-name::before {\n content: '|';\n margin-right: 8px;\n color: #9ca3af;\n }\n\n .company-market-info .market-mic {\n font-weight: 600;\n }\n\n .company-market-info .market-mic::before {\n content: '•';\n margin-right: 8px;\n }\n\n /* Price metadata (source and last update) */\n .price-metadata {\n margin-top: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n \n\n /* STANDARDIZED GRID LAYOUT */\n .data-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 24px;\n margin-top: 24px;\n }\n\n .data-section {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .night-session-widget .section-title {\n font-size: var(--mdas-small-text-size);\n font-weight: 600;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n border-bottom: 1px solid #e5e7eb;\n padding-bottom: 8px;\n }\n\n .night-session-widget .data-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n min-height: 20px;\n }\n\n\n\n .night-session-widget .data-value {\n font-size: var(--mdas-data-value-size);\n font-weight: 600;\n color: #111827;\n text-align: right;\n }\n\n /* SPECIAL FORMATTING FOR DIFFERENT DATA TYPES */\n .night-session-widget .bid-ask-value {\n font-size: var(--mdas-bid-ask-size);\n font-weight: 700;\n }\n\n .night-session-widget .size-info {\n font-size: var(--mdas-small-text-size);\n color: #6b7280;\n margin-left: 4px;\n }\n\n /* Footer */\n .night-session-widget .widget-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: 24px;\n padding-top: 16px;\n border-top: 1px solid #e5e7eb;\n font-size: var(--mdas-small-text-size);\n color: #9ca3af;\n grid-column: 1 / -1;\n }\n\n /* Loading Overlay */\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.95);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n border-radius: 12px;\n }\n\n .widget-loading-overlay.hidden {\n display: none;\n }\n\n .loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n }\n\n .loading-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid #f3f4f6;\n border-top-color: #3b82f6;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .night-session-widget .loading-text {\n color: #6b7280;\n font-size: var(--mdas-data-value-size);\n font-weight: 500;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n /* Widget Error Styles */\n .night-session-widget .widget-error {\n background: #fef2f2;\n color: #9ca3af;\n padding: 12px;\n border-radius: 8px;\n border: 1px solid #fecaca;\n margin-top: 16px;\n font-size: var(--mdas-data-value-size);\n font-weight: 500;\n }\n\n /* No Data State */\n .night-session-widget .no-data-state {\n padding: 24px;\n text-align: center;\n color: #6b7280;\n grid-column: 1 / -1;\n }\n\n .night-session-widget .no-data-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n }\n\n .night-session-widget .no-data-icon {\n font-size: 3.43em;\n opacity: 0.5;\n }\n\n .night-session-widget .no-data-title {\n font-size: 1.29em;\n font-weight: 600;\n color: #374151;\n }\n\n .night-session-widget .no-data-message {\n font-size: var(--mdas-data-value-size);\n max-width: 300px;\n line-height: 1.5;\n }\n\n .night-session-widget .no-data-suggestion {\n font-size: var(--mdas-small-text-size);\n color: #9ca3af;\n max-width: 350px;\n line-height: 1.4;\n }\n\n /* Dimmed state for no data */\n .widget-header.dimmed,\n .price-section.dimmed {\n opacity: 0.6;\n }\n\n /* Responsive Design */\n @media (max-width: 768px) {\n :root {\n --mdas-base-font-size: 13px; /* Slightly smaller on tablets */\n }\n\n .widget-styled {\n padding: 16px;\n }\n\n .widget-header {\n grid-template-columns: 1fr;\n gap: 12px;\n text-align: center;\n }\n\n .symbol-section {\n align-items: center;\n }\n\n .price-section {\n text-align: center;\n }\n\n .data-grid {\n grid-template-columns: 1fr;\n gap: 16px;\n }\n\n .widget-footer {\n flex-direction: column;\n gap: 8px;\n text-align: center;\n }\n }\n\n @media (max-width: 480px) {\n :root {\n --mdas-base-font-size: 12px; /* Smaller on mobile */\n }\n }\n\n \n`,r=`\n ${o}\n\n /* Reset and base styles for widget */\n\n .market-data-widget {\n color: #333;\n margin: 0 auto;\n position: relative;\n overflow: hidden;\n }\n\n /* MARKET DATA HEADER STYLES */\n\n\n .market-data-widget .company-info {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: var(--mdas-section-title-size);\n color: #6b7280;\n }\n\n .company-name {\n position: relative;\n cursor: help;\n transition: all 0.2s ease;\n }\n\n .company-name:hover {\n border-bottom-color: #3b82f6;\n color: #3b82f6;\n }\n\n\n /* STANDARDIZED GRID LAYOUT */\n \n .data-section {\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n overflow: hidden;\n }\n\n .market-data-widget .section-title {\n font-size: var(--mdas-small-text-size);\n font-weight: 600;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n padding-bottom: 8px;\n }\n\n\n\n /* SPECIAL FORMATTING FOR DIFFERENT DATA TYPES */\n .market-data-widget .bid-ask-value {\n font-size: var(--mdas-bid-ask-size);\n font-weight: 700;\n }\n\n .market-data-widget .size-info {\n font-size: var(--mdas-small-text-size);\n color: #6b7280;\n margin-left: 4px;\n }\n\n .market-data-widget .range-value {\n font-size: var(--mdas-data-label-size);\n }\n\n \n\n /* Tooltip Styles */\n .company-name::after {\n content: attr(data-tooltip);\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n background: #1f2937;\n color: white;\n padding: 8px 12px;\n border-radius: 6px;\n font-size: var(--mdas-small-text-size);\n font-weight: normal;\n white-space: nowrap;\n max-width: 300px;\n white-space: normal;\n line-height: 1.4;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n opacity: 0;\n visibility: hidden;\n transition: all 0.2s ease;\n pointer-events: none;\n margin-bottom: 5px;\n }\n\n .company-name:hover::after {\n opacity: 1;\n visibility: visible;\n }\n\n .company-name::before {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: #1f2937;\n opacity: 0;\n visibility: hidden;\n transition: all 0.2s ease;\n pointer-events: none;\n }\n\n .company-name:hover::before {\n opacity: 1;\n visibility: visible;\n }\n\n /* Loading Overlay */\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.95);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n border-radius: 12px;\n }\n\n .widget-loading-overlay.hidden {\n display: none;\n }\n\n .loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n }\n\n .loading-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid #f3f4f6;\n border-top-color: #3b82f6;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .market-data-widget .loading-text {\n color: #6b7280;\n font-size: var(--mdas-data-value-size);\n font-weight: 500;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n /* Widget Error Styles */\n .market-data-widget .widget-error {\n background: #fef2f2;\n color: #dc2626;\n padding: 12px;\n border-radius: 8px;\n border: 1px solid #fecaca;\n margin-top: 16px;\n font-size: var(--mdas-data-value-size);\n font-weight: 500;\n }\n\n /* Responsive Design */\n\n\n @media (max-width: 600px) {\n .widget-header {\n grid-template-columns: 1fr;\n gap: 12px;\n text-align: center;\n }\n\n .symbol-section {\n align-items: center;\n }\n\n .price-section {\n text-align: center;\n }\n\n .data-grid {\n grid-template-columns: 1fr;\n gap: 16px;\n }\n\n .widget-footer {\n flex-direction: column;\n gap: 8px;\n text-align: center;\n }\n }\n\n @media (max-width: 480px) {\n .company-name::after {\n left: 0;\n transform: none;\n max-width: 250px;\n }\n\n .company-name::before {\n left: 20px;\n transform: none;\n }\n }\n`,c=`\n ${o}\n\n .options-widget {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n color: #333;\n max-width: 1400px;\n width: 100%;\n margin: 0 auto;\n position: relative;\n overflow: hidden;\n }\n\n .widget-content {\n position: relative;\n z-index: 1;\n }\n\n \n .options-widget .symbol-info .symbol {\n font-size: var(--mdas-symbol-size);\n font-weight: 700;\n margin: 0 0 8px 0;\n color: #1f2937;\n }\n\n .options-widget .option-info {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #6b7280;\n font-size: var(--mdas-price-change-size);\n }\n\n .options-widget .underlying-symbol {\n font-weight: 600;\n color: #6b7280;\n }\n\n .options-widget .option-details-section {\n display: flex;\n gap: 24px;\n margin-bottom: 24px;\n padding-bottom: 20px;\n border-bottom: 1px solid #e5e7eb;\n }\n\n .options-widget .option-details-section .detail-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .options-widget .option-details-section .detail-item label {\n font-size: var(--mdas-small-text-size);\n color: #6b7280;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n }\n\n .options-widget .option-details-section .detail-item span {\n font-size: var(--mdas-price-change-size);\n font-weight: 600;\n color: #1f2937;\n }\n\n\n .options-widget .widget-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 16px;\n border-top: 1px solid #e5e7eb;\n font-size: var(--mdas-small-text-size);\n color: #9ca3af;\n }\n\n /* Dimmed state for no-data */\n .widget-header.dimmed {\n opacity: 0.6;\n }\n\n .widget-header.dimmed .symbol {\n color: #9ca3af;\n }\n\n /* Responsive design */\n @media (max-width: 768px) {\n .widget-header {\n flex-direction: column;\n gap: 16px;\n text-align: left;\n }\n\n .price-info {\n text-align: left;\n }\n\n .data-grid {\n grid-template-columns: 1fr;\n gap: 20px;\n }\n\n .option-details-section {\n flex-wrap: wrap;\n gap: 16px;\n }\n\n .current-price {\n font-size: 32px;\n }\n\n .symbol {\n font-size: 20px !important;\n }\n }\n\n @media (max-width: 480px) {\n .widget-styled {\n padding: 12px;\n }\n\n .widget-header {\n margin-bottom: 16px;\n padding-bottom: 16px;\n }\n\n .current-price {\n font-size: 28px;\n }\n\n .data-row {\n grid-template-columns: 1fr;\n gap: 12px;\n }\n\n .option-details-section {\n flex-direction: column;\n gap: 12px;\n }\n\n .no-data-state {\n min-height: 120px;\n padding: 20px 12px;\n }\n\n .no-data-title {\n font-size: 14px;\n }\n\n .no-data-description {\n font-size: 12px;\n }\n\n .no-data-guidance {\n font-size: 11px;\n }\n }\n`;function l(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";const i=document.createElement(t);return n&&(i.className=n),e&&(i.textContent=e),i}function d(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:2,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"--";if(null==t||""===t)return n;const i=parseFloat(t);return isNaN(i)?n:i.toFixed(e)}function h(t){if(!t)return"";return String(t).replace(/[^A-Za-z0-9.\-_+ ]/g,"").substring(0,50)}function u(t){if(t)for(;t.firstChild;)t.removeChild(t.firstChild)}function g(t){if(!t||"string"!=typeof t)return{valid:!1,error:"Symbol is required",sanitized:""};const e=t.trim().toUpperCase();return 0===e.length?{valid:!1,error:"Symbol cannot be empty",sanitized:""}:e.length>10?{valid:!1,error:"Symbol too long (max 10 characters)",sanitized:""}:{valid:!0,sanitized:e}}function p(t){if(!t||"string"!=typeof t)return{valid:!1,error:"Option symbol is required",sanitized:""};const e=t.trim().toUpperCase();if(e.length<15||e.length>21)return{valid:!1,error:"Invalid option symbol length",sanitized:e};const n=e.match(/^([A-Z]{1,6})(\d{2})(0[1-9]|1[0-2])([0-2][0-9]|3[01])([CP])(\d{8})$/);if(!n)return{valid:!1,error:"Invalid option symbol format. Expected: SYMBOL+YYMMDD+C/P+STRIKE",sanitized:e};const[,i,s,o,a,r,c]=n,l=2e3+parseInt(s,10),d=parseInt(o,10),h=parseInt(a,10),u=new Date(l,d-1,h);if(u.getMonth()+1!==d||u.getDate()!==h)return{valid:!1,error:"Invalid expiration date in option symbol",sanitized:e};const g=new Date;g.setHours(0,0,0,0),u<g&&console.warn("Option symbol has expired");return{valid:!0,sanitized:e,parsed:{underlying:i,expiry:u,type:"C"===r?"call":"put",strike:parseInt(c,10)/1e3}}}function m(t){if(!t)return"ET";const[e,n,i]=t.split("-").map(Number),s=new Date(e,n-1,i),o=function(t,e){const n=new Date(t,e,1),i=1+(7-n.getDay())%7;return new Date(t,e,i+7)}(e,2),a=function(t,e){const n=new Date(t,e,1),i=1+(7-n.getDay())%7;return new Date(t,e,i)}(e,10);return s>=o&&s<a?"EDT":"EST"}function f(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{includeSeconds:n=!0,includeTimezone:i=!0,format:s="short"}=e;let o;if(t||(t=Date.now()),o=t instanceof Date?t:new Date(t),isNaN(o.getTime()))return"Invalid Date";const a=o.getUTCFullYear(),r=o.getUTCMonth(),c=o.getUTCDate(),l=o.getUTCHours(),d=o.getUTCMinutes(),h=o.getUTCSeconds(),u=function(t){const e=t.getUTCFullYear(),n=t.getUTCMonth(),i=t.getUTCDate(),s=new Date(Date.UTC(e,n,i)),o=function(t,e){const n=new Date(Date.UTC(t,e,1)),i=1+(7-n.getUTCDay())%7;return new Date(Date.UTC(t,e,i+7))}(e,2),a=function(t,e){const n=new Date(Date.UTC(t,e,1)),i=1+(7-n.getUTCDay())%7;return new Date(Date.UTC(t,e,i))}(e,10);return s>=o&&s<a}(o),g=u?-4:-5,p=new Date(Date.UTC(a,r,c,l+g,d,h)),m=p.getUTCFullYear(),f=p.getUTCMonth()+1,b=p.getUTCDate(),y=p.getUTCHours(),x=p.getUTCMinutes(),v=p.getUTCSeconds(),w=y%12||12,S=y>=12?"PM":"AM",k=x.toString().padStart(2,"0"),M=v.toString().padStart(2,"0"),C=n?`${w}:${k}:${M} ${S}`:`${w}:${k} ${S}`,_=i?u?" EDT":" EST":"";if("long"===s){return`${["January","February","March","April","May","June","July","August","September","Oct","November","December"][f-1]} ${b}, ${m}, ${C}${_}`}return`${f}/${b}/${m}, ${C}${_}`}class b extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for MarketDataWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for MarketDataWidget");this.type="marketdata";const i=g(e.symbol);if(!i.valid)throw new Error(`Invalid symbol: ${i.error}`);this.symbol=i.sanitized,this.wsManager=e.wsManager,this.debug=e.debug||!1,this.styled=void 0===e.styled||e.styled,this.data=null,this.isDestroyed=!1,this.unsubscribe=null,this.loadingTimeout=null,this.createWidgetStructure(),this.initializeSymbolEditor(),this.initialize()}createWidgetStructure(){if(this.container.innerHTML='\n <div class="market-data-widget widget">\n <div class="widget-content">\n \x3c!-- Header Section --\x3e\n <div class="widget-header">\n <div class="symbol-section">\n <h1 class="symbol editable-symbol" \n title="Double-click to edit symbol" \n data-original-symbol="">\n </h1>\n <div class="company-info">\n <span class="company-name" \n title="" \n data-tooltip="">\n </span>\n <span class="data-type">STOCK</span>\n </div>\n </div>\n <div class="price-section">\n <div class="current-price"></div>\n <div class="price-change">\n <span class="change-value"></span>\n <span class="change-percent"></span>\n </div>\n </div>\n </div>\n\n \x3c!-- Data Grid Section --\x3e\n <div class="data-grid">\n <div class="data-section">\n <div class="section-title">Quote</div>\n <div class="data-row">\n <span class="data-label">Bid</span>\n <span class="data-value bid-ask-value bid-data"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Ask</span>\n <span class="data-value bid-ask-value ask-data"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Trading Activity</div>\n <div class="data-row">\n <span class="data-label">Volume</span>\n <span class="data-value volume"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Average Volume</span>\n <span class="data-value avg-volume"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Performance</div>\n <div class="data-row">\n <span class="data-label">Previous Close</span>\n <span class="data-value prev-close"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Open</span>\n <span class="data-value open-price"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Day Range</span>\n <span class="data-value range-value day-range"></span>\n </div>\n <div class="data-row">\n <span class="data-label">52-Week Range</span>\n <span class="data-value range-value week-range"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Key Metrics</div>\n <div class="data-row">\n <span class="data-label">Market Cap</span>\n <span class="data-value market-cap"></span>\n </div>\n <div class="data-row">\n <span class="data-label">P/E Ratio</span>\n <span class="data-value pe-ratio"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Dividend Yield</span>\n <span class="data-value dividend-yield"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Beta</span>\n <span class="data-value beta"></span>\n </div>\n </div>\n </div>\n\n \x3c!-- Footer --\x3e\n <div class="widget-footer">\n <span class="last-update"></span>\n <span class="data-source"></span>\n </div>\n </div>\n \n \x3c!-- Loading Overlay --\x3e\n <div class="widget-loading-overlay">\n <div class="loading-content">\n <div class="loading-spinner"></div>\n <span class="loading-text">Loading market data...</span>\n </div>\n </div>\n </div>\n',this.styled){const t=this.container.querySelector(".market-data-widget");t&&t.classList.add("widget-styled")}this.addStyles()}addStyles(){if(!document.querySelector("#market-data-styles")){const t=document.createElement("style");t.id="market-data-styles",t.textContent=r,document.head.appendChild(t)}}initializeSymbolEditor(){this.symbolEditor=new s(this,{maxLength:10,placeholder:"Enter symbol...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,symbolType:"quotel1"});const t=this.container.querySelector(".symbol");t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol)}async handleSymbolChange(t){this.debug&&console.log("[MarketDataWidget] Symbol change requested:",t);const e=g(t);if(!e.valid)return this.debug&&console.log("[MarketDataWidget] Invalid symbol:",e.error),{success:!1,error:e.error};const n=e.sanitized;if(n===this.symbol)return this.debug&&console.log("[MarketDataWidget] Same symbol, no change needed"),{success:!0};try{const t=this.symbol;return this.showLoading(),this.loadingTimeout&&this.clearTimeout(this.loadingTimeout),this.loadingTimeout=this.setTimeout((()=>{this.debug&&console.log("[MarketDataWidget] Loading timeout for symbol:",n),this.hideLoading(),this.showError(`No data received for ${n}. Please try again.`)}),1e4),this.unsubscribe&&(this.debug&&console.log(`[MarketDataWidget] Unsubscribing from ${t}`),this.wsManager.sendUnsubscribe("queryl1",t),this.unsubscribe(),this.unsubscribe=null),this.symbol=n,this.debug&&console.log(`[MarketDataWidget] Subscribing to ${n}`),this.subscribeToData(),this.debug&&console.log(`[MarketDataWidget] Successfully changed symbol from ${t} to ${n}`),{success:!0}}catch(t){return console.error("[MarketDataWidget] Error changing symbol:",t),this.hideLoading(),this.showError(`Failed to load data for ${n}`),{success:!1,error:`Failed to load ${n}`}}}async initialize(){this.showLoading(),await this.validateInitialSymbol(),this.subscribeToData()}async validateInitialSymbol(){try{const t=this.wsManager.getApiService();if(!t)return void(this.debug&&console.log("[MarketDataWidget] 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.initialValidationData=t,this.debug&&console.log("[MarketDataWidget] Initial symbol validated:",{symbol:this.symbol,companyName:t.comp_name,exchangeName:t.market_name})}}catch(t){this.debug&&console.warn("[MarketDataWidget] Initial symbol validation failed:",t)}}subscribeToData(){this.unsubscribe=this.wsManager.subscribe(this.widgetId,["queryl1"],this.handleMessage.bind(this),this.symbol)}handleData(t){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),"error"===t.type&&t.noData)return this.debug&&console.log("[MarketDataWidget] Received no data message:",t.message),void(this.data?(this.hideLoading(),this.debug&&console.log("[MarketDataWidget] No new data, keeping cached data visible")):this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:t.message}));if("error"!==t.type){if("object"==typeof t&&t.Message){const e=t.Message.toLowerCase();if(e.includes("no data")||e.includes("not found"))return this.debug&&console.log("[MarketDataWidget] Received no data message:",t.Message),void(this.data?(this.hideLoading(),this.debug&&console.log("[MarketDataWidget] No new data, keeping cached data visible")):this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:t.Message}))}if(t.Data&&t.Data.length>0){const e=t.Data.find((t=>t.Symbol===this.symbol));if(e){const t=new i(e);this.data=t,this.updateWidget(t)}else this.debug&&console.log("[MarketDataWidget] No data for symbol in response, keeping cached data"),this.hideLoading()}t._cached&&this.showConnectionQuality()}else{const e=t.message||"Server error";this.data?(this.hideLoading(),this.debug&&console.log("[MarketDataWidget] Error received but keeping cached data:",e)):this.showError(e)}}showNoDataState(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!this.isDestroyed)try{this.hideLoading(),this.clearError();const e=t.Symbol||this.symbol,n=this.container.querySelector(".symbol");n&&(n.textContent=e);const i=this.container.querySelector(".company-name");i&&(i.textContent=`${e} Inc`);const s=this.container.querySelector(".current-price");s&&(s.textContent="$0.00");const o=this.container.querySelector(".price-change");if(o){const t=o.querySelector(".change-value"),e=o.querySelector(".change-percent");t&&(t.textContent="+0.00"),e&&(e.textContent=" (0.00%)"),o.classList.remove("positive","negative"),o.classList.add("neutral")}const a=this.container.querySelector(".widget-header");a&&a.classList.add("dimmed"),this.showNoDataMessage(e,t);const r=this.container.querySelector(".last-update");if(r){const t=f();r.textContent=`Checked: ${t}`}const c=this.container.querySelector(".data-source");c&&(c.textContent="Source: No data available")}catch(t){console.error("Error showing no data state:",t),this.showError("Error displaying no data state")}}showNoDataMessage(t){const e=this.container.querySelector(".data-grid");e&&(e.style.display="none");const n=this.container.querySelector(".no-data-state");n&&n.remove();const i=l("div","","no-data-state"),s=l("div","","no-data-content"),o=document.createElement("div");o.className="no-data-icon",o.innerHTML='<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#9ca3af" stroke-width="2"/>\n <path d="M12 8v4" stroke="#9ca3af" stroke-width="2" stroke-linecap="round"/>\n <circle cx="12" cy="16" r="1" fill="#9ca3af"/>\n </svg>',s.appendChild(o);const a=l("h3","No Market Data","no-data-title");s.appendChild(a);const r=l("p","","no-data-description");r.appendChild(document.createTextNode("Market data for "));const c=l("strong",h(t));r.appendChild(c),r.appendChild(document.createTextNode(" was not found")),s.appendChild(r);const d=l("p","Please check the symbol spelling or try a different symbol","no-data-guidance");s.appendChild(d),i.appendChild(s);const u=this.container.querySelector(".widget-footer");u&&u.parentNode?u.parentNode.insertBefore(i,u):this.container.appendChild(i)}updateWidget(t){if(!this.isDestroyed)try{this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.clearError(),this.hideLoading();const e=this.container.querySelector(".widget-header");e&&e.classList.remove("dimmed");const n=this.container.querySelector(".data-grid");n&&(n.style.display="");const i=this.container.querySelector(".no-data-state");i&&i.remove();const s=this.container.querySelector(".symbol");s&&(s.textContent=t.symbol,s.dataset.originalSymbol=t.symbol);const o=this.container.querySelector(".company-name");if(o){const e=t.companyName||`${t.symbol} Inc`,n=t.companyDescription||`${t.symbol} designs, manufactures, and markets consumer electronics and software.`;o.textContent=e,o.setAttribute("title",n),o.setAttribute("data-tooltip",n)}const a=this.container.querySelector(".current-price");a&&(a.textContent=t.formatPrice());const r=t.getPriceChange(),c=this.container.querySelector(".price-change");if(c){const e=c.querySelector(".change-value"),n=c.querySelector(".change-percent");e&&(e.textContent=t.formatPriceChange()),n&&(n.textContent=` (${t.formatPriceChangePercent()})`),c.classList.remove("positive","negative","neutral"),r>0?c.classList.add("positive"):r<0?c.classList.add("negative"):c.classList.add("neutral")}const h=this.container.querySelector(".bid-data");if(h){u(h);const e=d(t.bidPrice,2,"0.00"),n=t.bidSize||0;h.appendChild(document.createTextNode(`$${e}`));const i=l("span",`× ${n}`,"size-info");h.appendChild(i)}const g=this.container.querySelector(".ask-data");if(g){u(g);const e=d(t.askPrice,2,"0.00"),n=t.askSize||0;g.appendChild(document.createTextNode(`$${e}`));const i=l("span",`× ${n}`,"size-info");g.appendChild(i)}const p=this.container.querySelector(".volume");p&&(p.textContent=t.formatVolume());const m=this.container.querySelector(".avg-volume");m&&(m.textContent=t.formatAverageVolume());const b=this.container.querySelector(".prev-close");b&&(b.textContent=`$${t.previousClose?.toFixed(2)||"0.00"}`);const y=this.container.querySelector(".open-price");y&&(y.textContent=`$${t.openPrice?.toFixed(2)||"0.00"}`);const x=this.container.querySelector(".day-range");x&&(x.textContent=t.formatDayRange());const v=this.container.querySelector(".week-range");v&&(v.textContent=t.format52WeekRange());const w=this.container.querySelector(".market-cap");w&&(w.textContent=t.formatMarketCap());const S=this.container.querySelector(".pe-ratio");S&&(S.textContent=t.peRatio?.toFixed(2)||"N/A");const k=this.container.querySelector(".dividend-yield");k&&(k.textContent=t.dividendYield?`${t.dividendYield.toFixed(2)}%`:"N/A");const M=this.container.querySelector(".beta");M&&(M.textContent=t.beta?.toFixed(2)||"N/A");const C=f(t.quoteTime),_=this.container.querySelector(".last-update");_&&(_.textContent=`Last update: ${C}`);const D=this.container.querySelector(".data-source");D&&(D.textContent=`Source: ${t.getDataSource()}`)}catch(t){console.error("Error updating widget:",t),this.showError("Error updating data")}}showLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.remove("hidden")}hideLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.add("hidden")}showError(t){this.hideLoading(),this.clearError();const e=document.createElement("div");e.className="widget-error",e.textContent=`Error: ${t}`,e.style.cssText="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 15px 20px;\n background: #fee;\n border: 1px solid #f5c6cb;\n border-radius: 6px;\n color: #721c24;\n text-align: center;\n font-size: 14px;\n z-index: 100;\n ",this.container.appendChild(e)}clearError(){const t=this.container.querySelector(".widget-error"),e=this.container.querySelector(".widget-error-container");t&&t.remove(),e&&e.remove()}destroy(){this.isDestroyed=!0,this.loadingTimeout&&(this.clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.symbolEditor&&(this.symbolEditor.destroy(),this.symbolEditor=null),super.destroy()}}class y{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.symbol=t.Symbol||"",this.marketName=t.MarketName||("bruce"===t.Source?.toLowerCase()?"Bruce":"Blue Ocean"),this.marketDesc=null!==t.MarketDesc?t.MarketDesc||"":null,this.countryCode=null!==t.CountryCode?t.CountryCode||"":null,this.volume=null!==t.Volume?t.Volume||0:null,this.askPrice=null!==t.AskPx?t.AskPx||0:null,this.askSize=null!==t.AskSz?t.AskSz||0:null,this.bidPrice=null!==t.BidPx?t.BidPx||0:null,this.bidSize=null!==t.BidSz?t.BidSz||0:null,this.lastPrice=null!==t.LastPx?t.LastPx||0:null,this.quoteTime=null!==t.QuoteTime?t.QuoteTime:null,this.activityTimestamp=null!==t.ActivityTimestamp?t.ActivityTimestamp||"":null,this.tradeSize=null!==t.TradeSz?t.TradeSz||0:null,this.bidExchange=null!==t.BidExch?t.BidExch||"":null,this.askExchange=null!==t.AskExch?t.AskExch||"":null,this.notFound=t.NotFound||!1,this.source=t.Source||"",this.accuAmount=null!==t.AccuAmmount?t.AccuAmmount||0:null,this.change=null!==t.Change?t.Change||0:null,this.changePercent=null!==t.ChangePercent?t.ChangePercent||0:null,this.closingPrice=null!==t.ClosingPx?t.ClosingPx:null!==t.PreviousClose?t.PreviousClose:null,this.highPrice=null!==t.HighPx?t.HighPx||0:null,this.lowPrice=null!==t.LowPx?t.LowPx||0:null,this.openPrice=null!==t.OpenPx?t.OpenPx||0:null,this.tradePrice=null!==t.TradePx?t.TradePx||0:null,this.tradeRegionName=null!==t.TradeRegionName?t.TradeRegionName||"":null,this.companyName=t.comp_name||t.CompanyName||"",this.exchangeName=t.market_name||t.Exchange||"",this.mic=t.mic||""}formatPrice(){return`$${this.lastPrice.toFixed(2)}`}formatBidAsk(){return`$${this.bidPrice.toFixed(2)} × ${this.bidSize} / $${this.askPrice.toFixed(2)} × ${this.askSize}`}formatVolume(){return this.volume.toLocaleString()}formatPriceChange(){if(null===this.change||void 0===this.change)return"--";return`${this.change>0?"+":""}${this.change.toFixed(2)}`}formatPriceChangePercent(){if(null===this.changePercent||void 0===this.changePercent)return"--";return`${this.changePercent>0?"+":""}${this.getPriceChangePercent().toFixed(2)}%`}getDataSource(){return"blueocean-d"===this.source.toLowerCase()||"bruce-d"===this.source.toLowerCase()?"bruce-d"===this.source.toLowerCase()?"Bruce 20 mins delayed":"BlueOcean 20 mins delayed":this.source.toLowerCase().includes("bruce")||this.source.toLowerCase().includes("blueocean")?"bruce"===this.source.toLowerCase()?"Bruce Real-time":"BlueOcean Real-time":null!==this.source&&""!==this.source?this.source:"MDAS"}getPriceChangePercent(){return null===this.changePercent||void 0===this.changePercent?0:Math.abs(this.changePercent)>1?this.changePercent:100*this.changePercent}}class x extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for NightSessionWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for NightSessionWidget");if(!e.source||"blueocean"!==e.source.toLowerCase()&&"bruce"!==e.source.toLowerCase())throw new Error('Source should be either "blueocean" or "bruce"');this.type="nightsession "+e.source;const i=g(e.symbol);if(!i.valid)throw new Error(`Invalid symbol: ${i.error}`);this.symbol=i.sanitized,this.source=e.source||"blueocean",this.wsManager=e.wsManager,this.debug=e.debug||!1,this.styled=void 0===e.styled||e.styled,this.data=null,this.isDestroyed=!1,this.unsubscribe=null,this.symbolEditor=null,this.loadingTimeout=null,this.companyName="",this.exchangeName="",this.mic="",this.createWidgetStructure(),this.initialize()}createWidgetStructure(){if(this.container.innerHTML='\n <div class="night-session-widget widget">\n <div class="widget-content">\n \x3c!-- Header Section --\x3e\n <div class="widget-header">\n <div class="symbol-section">\n <div class="company-market-info">\n <span class="night-company-name"></span>\n <span class="market-name"></span>\n </div>\n <h1 class="symbol editable-symbol"\n title="Double-click to edit symbol"\n data-original-symbol="">\n </h1>\n\n </div>\n <div class="price-section">\n <div class="current-price"></div>\n <div class="price-change">\n <span class="change-value"></span>\n <span class="change-percent"></span>\n </div>\n <div class="price-metadata">\n <div class="last-update"></div>\n <div class="data-source"></div>\n </div>\n </div>\n </div>\n\n \x3c!-- Data Grid Section --\x3e\n <div class="data-grid">\n <div class="data-section">\n <div class="section-title">Quote</div>\n <div class="data-row">\n <span class="data-label">Bid</span>\n <span class="data-value bid-ask-value">\n <span class="bid-price"></span>\n <span class="size-info bid-size"></span>\n </span>\n </div>\n <div class="data-row">\n <span class="data-label">Ask</span>\n <span class="data-value bid-ask-value">\n <span class="ask-price"></span>\n <span class="size-info ask-size"></span>\n </span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Trading Activity</div>\n <div class="data-row">\n <span class="data-label">Volume</span>\n <span class="data-value volume"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Accumulated Amount</span>\n <span class="data-value accu-amount"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Performance</div>\n <div class="data-row">\n <span class="data-label">Previous Close</span>\n <span class="data-value previous-close"></span>\n </div>\n <div class="data-row at-close-row">\n <span class="data-label">At close</span>\n <span class="data-value at-close-time"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Open</span>\n <span class="data-value open-price"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Day Range</span>\n <span class="data-value range-value day-range"></span>\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- Loading Overlay --\x3e\n <div class="widget-loading-overlay">\n <div class="loading-content">\n <div class="loading-spinner"></div>\n <span class="loading-text">Loading night session data...</span>\n </div>\n </div>\n </div>\n',this.styled){const t=this.container.querySelector(".night-session-widget");t&&t.classList.add("widget-styled")}this.addStyles(),this.setupSymbolEditor()}addStyles(){if(!document.querySelector("#night-session-styles")){const t=document.createElement("style");t.id="night-session-styles",t.textContent=a,document.head.appendChild(t)}}setupSymbolEditor(){this.symbolEditor=new s(this,{maxLength:10,placeholder:"Enter symbol...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,cancelOnBlur:!1,symbolType:"nightsession"})}async handleSymbolChange(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;this.debug&&(console.log("[NightSessionWidget] Symbol change requested:",t),console.log("[NightSessionWidget] Validation data:",n));const i=g(t);if(!i.valid)return this.debug&&console.log("[NightSessionWidget] 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("[NightSessionWidget] Extracted company info:",{companyName:this.companyName,exchangeName:this.exchangeName},n)),s===this.symbol)return this.debug&&console.log("[NightSessionWidget] Same symbol, no change needed"),{success:!0};try{const t=this.symbol;return this.showLoading(),this.loadingTimeout&&this.clearTimeout(this.loadingTimeout),this.loadingTimeout=this.setTimeout((()=>{this.debug&&console.log("[NightSessionWidget] Loading timeout for symbol:",s),this.hideLoading(),this.showError(`No data received for ${s}. Please try again.`)}),1e4),this.unsubscribe&&(this.debug&&console.log(`[NightSessionWidget] Unsubscribing from ${t}`),this.unsubscribe(),this.unsubscribe=null),this.symbol=s,this.debug&&console.log(`[NightSessionWidget] Subscribing to ${s}`),this.subscribeToData(),this.debug&&console.log(`[NightSessionWidget] Successfully changed symbol from ${t} to ${s}`),{success:!0}}catch(t){return console.error("[NightSessionWidget] Error changing symbol:",t),this.showError(`Failed to load data for ${s}`),{success:!1,error:`Failed to load ${s}`}}}async initialize(){this.showLoading(),await this.validateInitialSymbol(),this.loadingTimeout=setTimeout((()=>{this.debug&&console.log("[NightSessionWidget] Loading timeout - no data received for:",this.symbol),this.hideLoading(),this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:"No data available for this symbol"})}),1e4),this.subscribeToData()}async validateInitialSymbol(){try{const t=this.wsManager.getApiService();if(!t)return void(this.debug&&console.log("[NightSessionWidget] 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("[NightSessionWidget] Initial symbol validated:",{symbol:this.symbol,companyName:this.companyName,exchangeName:this.exchangeName,mic:this.mic})}}catch(t){this.debug&&console.warn("[NightSessionWidget] Initial symbol validation failed:",t)}}subscribeToData(){const t="bruce"===this.source?"querybrucel1":"queryblueoceanl1";this.unsubscribe=this.wsManager.subscribe(this.widgetId,[t],this.handleMessage.bind(this),this.symbol)}handleData(t){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),"error"===t.type&&t.noData)return this.debug&&console.log("[NightSessionWidget] Received no data message:",t.message),void(this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] No new data, keeping cached data visible")):this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:t.message}));if("error"!==t.type){if(Array.isArray(t)){const e=t.find((t=>t.Symbol===this.symbol));if(e){if(this.debug&&console.log("[NightSessionWidget] Found data for symbol:",e),!0===e.NotFound)this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] No new data, keeping cached data visible")):this.showNoDataState(e);else{const t=new y(e);this.data=t,this.updateWidget(t)}return}this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] No matching data in response, keeping cached data")):this.showNoDataState({Symbol:this.symbol,NotFound:!0})}else if("queryblueoceanl1"===t.type||"querybrucel1"===t.type)if(t[0]?.Symbol===this.symbol)if(!0!==t[0].NotFound&&t[0].MarketName&&"BLUE"===t[0].MarketName){const e=new y(t[0]);this.data=e,this.updateWidget(e)}else this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] No new data, keeping cached data visible")):this.showNoDataState(t[0]);else this.debug&&console.log("[NightSessionWidget] No matching symbol in response, keeping cached data"),this.hideLoading();t._cached}else{const e=t.message||"Server error";e.toLowerCase().includes("access")||e.toLowerCase().includes("permission")||e.toLowerCase().includes("denied")?this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] Access error but keeping cached data")):this.showNoDataState({Symbol:this.symbol,NotFound:!0,isAccessError:!0,message:e}):this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] Error received but keeping cached data:",e)):(console.log("errorMsg",e),this.showError(e))}}updateWidget(t){if(!this.isDestroyed)try{this.hideLoading(),this.clearError();const e=this.container.querySelector(".widget-header");e&&e.classList.remove("dimmed");const n=this.container.querySelector(".price-section");n&&n.classList.remove("dimmed");const i=this.container.querySelector(".data-grid");i&&(i.style.display="grid");const s=this.container.querySelector(".no-data-state");s&&s.remove();const o=this.container.querySelector(".symbol");o&&(o.textContent=t.symbol);const a=this.container.querySelector(".night-company-name");a&&(a.textContent=this.companyName||t.companyName||"");const r=this.container.querySelector(".market-name");r&&(r.textContent=this.exchangeName||t.exchangeName||"");const c=this.container.querySelector(".current-price");c&&(c.textContent=null!==t.lastPrice?t.formatPrice():"--");const l=t.change,d=this.container.querySelector(".price-change");if(d){const e=d.querySelector(".change-value"),n=d.querySelector(".change-percent");e&&(e.textContent=null!==l&&0!==l?l:"--"),n&&(0!==t.changePercent?n.textContent=` (${t.getPriceChangePercent().toFixed(2)}%)`:n.textContent=" (0.00%)"),d.classList.remove("positive","negative","neutral"),l>0?d.classList.add("positive"):l<0?d.classList.add("negative"):d.classList.add("neutral")}const h=this.container.querySelector(".bid-price");h&&(h.textContent=null!==t.bidPrice?`$${t.bidPrice.toFixed(2)} `:"-- ×");const u=this.container.querySelector(".bid-size");u&&(u.textContent=null!==t.bidSize?`× ${t.bidSize}`:"--");const g=this.container.querySelector(".ask-price");g&&(g.textContent=null!==t.askPrice?`$${t.askPrice.toFixed(2)} `:"-- ×");const p=this.container.querySelector(".ask-size");p&&(p.textContent=null!==t.askSize?`× ${t.askSize}`:"--");const m=this.container.querySelector(".volume");m&&(m.textContent=null!==t.volume?t.formatVolume():"--");const b=this.container.querySelector(".accu-amount");b&&(b.textContent=null!==t.accuAmount?`$${t.accuAmount.toLocaleString()}`:"--");const y=this.container.querySelector(".open-price");y&&(y.textContent=null!==t.openPrice?`$${t.openPrice.toFixed(2)}`:"--");const x=this.container.querySelector(".previous-close");x&&(x.textContent=null!==t.closingPrice?`$${t.closingPrice.toFixed(2)}`:"--");const v=this.container.querySelector(".at-close-time");if(v){const e=t.quoteTime?f(new Date(t.quoteTime)):"--";v.textContent=e}const w=this.container.querySelector(".day-range");w&&(null!==t.highPrice&&null!==t.lowPrice?w.textContent=`$${t.lowPrice.toFixed(2)} - $${t.highPrice.toFixed(2)}`:w.textContent="--");const S=this.container.querySelector(".data-source");S&&(S.textContent="Source: "+t.getDataSource());const k=(t=>{if(!t)return f();const e=new Date(t);return isNaN(e.getTime())?f():f(e)})(t.quoteTime),M=this.container.querySelector(".last-update");M&&(M.textContent=`Overnight: ${k}`)}catch(t){console.error("Error updating widget:",t),this.showError("Error updating data")}}showLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.remove("hidden")}hideLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.add("hidden")}showError(t){this.hideLoading(),this.clearError();const e=document.createElement("div");e.className="widget-error",e.textContent=`Error: ${t}`,e.style.cssText="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 15px 20px;\n background: #fee;\n border: 1px solid #f5c6cb;\n border-radius: 6px;\n color: #721c24;\n text-align: center;\n font-size: 14px;\n z-index: 100;\n ",this.container.appendChild(e)}clearError(){const t=this.container.querySelector(".widget-error"),e=this.container.querySelector(".widget-error-container");t&&t.remove(),e&&e.remove()}showNoDataState(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!this.isDestroyed)try{this.hideLoading(),this.clearError();const e=t.Symbol||this.symbol,n=this.container.querySelector(".symbol");n&&(n.textContent=e);const i=this.container.querySelector(".current-price");i&&(i.textContent="$0.00");const s=this.container.querySelector(".price-change");if(s){const t=s.querySelector(".change-value");t&&(t.textContent="+0.00");const e=s.querySelector(".change-percent");e&&(e.textContent=" (0.00%)"),s.classList.remove("positive","negative")}const o=this.container.querySelector(".widget-header");o&&o.classList.add("dimmed");const a=this.container.querySelector(".price-section");a&&a.classList.add("dimmed"),this.showNoDataMessage(e,t);const r=f(),c=this.container.querySelector(".last-update");c&&(c.textContent=`Overnight: ${r}`)}catch(t){console.error("Error showing no data state:",t),this.showError("Error displaying no data state")}}showNoDataMessage(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n=this.container.querySelector(".data-grid");n&&(n.style.display="none");const i=this.container.querySelector(".no-data-state");i&&i.remove();const s=e.isAccessError||e.message&&e.message.toLowerCase().includes("access"),o=l("div","","no-data-state"),a=l("div","","no-data-content");if(s){const n=document.createElement("div");n.className="no-data-icon error",n.innerHTML='<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#ef4444" stroke-width="2"/>\n <path d="M12 8v4" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/>\n <circle cx="12" cy="16" r="1" fill="#ef4444"/>\n </svg>',a.appendChild(n);const i=l("h3",String(e.message||"Access Denied"),"no-data-title error");a.appendChild(i);const s=l("p","","no-data-description");s.appendChild(document.createTextNode("You do not have permission to view night session data for "));const o=l("strong",h(t));s.appendChild(o),a.appendChild(s);const r=l("p","Contact your administrator or try a different symbol","no-data-guidance");a.appendChild(r)}else{const n=document.createElement("div");n.className="no-data-icon",n.innerHTML='<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#9ca3af" stroke-width="2"/>\n <path d="M12 6v6l4 2" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>',a.appendChild(n);const i=l("h3",String(e.message||"No Data Available"),"no-data-title");a.appendChild(i);const s=l("p","","no-data-description");s.appendChild(document.createTextNode("Night session data for "));const o=l("strong",h(t));s.appendChild(o),s.appendChild(document.createTextNode(" was not found")),a.appendChild(s);const r=l("p","Please check the symbol or try again later","no-data-guidance");a.appendChild(r)}o.appendChild(a);const r=this.container.querySelector(".widget-footer");r&&r.parentNode?r.parentNode.insertBefore(o,r):this.container.appendChild(o)}destroy(){this.isDestroyed=!0,this.loadingTimeout&&(this.clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.symbolEditor&&(this.symbolEditor.destroy(),this.symbolEditor=null),super.destroy()}updateSymbol(t){this.handleSymbolChange(t)}}class v{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.rawData=t,this.symbol=t.Symbol||"",this.rootSymbol=t.RootSymbol||"",this.underlying=t.Underlying||"",this.expire=t.Expire||"",this.strike=t.Strike||0,this.type=this.determineOptionType(),this.lastPrice=t.LastPx||0,this.closePx=t.ClosePx||0,this.yestClosePx=t.YestClosePx||0,this.openPx=t.OpenPx||0,this.highPx=t.HighPx||0,this.lowPx=t.LowPx||0,this.bidPx=t.BidPx||0,this.bidSz=t.BidSz||0,this.bidExch=t.BidExch||"",this.askPx=t.AskPx||0,this.askSz=t.AskSz||0,this.askExch=t.AskExch||"",this.volume=t.Volume||0,this.openInterest=t.OpenInst||0,this.primeShare=t.PrimeShare||0,this.exch=t.Exch||"",this.dataSource=t.DataSource||"",this.quoteTime=t.QuoteTime||"",this.notFound=t.NotFound||!1}determineOptionType(){return this.symbol?this.symbol.includes("C")?"call":this.symbol.includes("P")?"put":"unknown":"unknown"}getChange(){return this.lastPrice-this.yestClosePx}getChangePercent(){return 0===this.yestClosePx?0:(this.lastPrice-this.yestClosePx)/this.yestClosePx*100}getSpread(){return this.askPx-this.bidPx}getSpreadPercent(){const t=(this.askPx+this.bidPx)/2;return 0===t?0:(this.askPx-this.bidPx)/t*100}getMidPrice(){return(this.askPx+this.bidPx)/2}formatPrice(t){return t?t.toFixed(2):"0.00"}formatStrike(){return`$${this.formatPrice(this.strike)}`}formatLastPrice(){return`$${this.formatPrice(this.lastPrice)}`}formatClosePx(){return`$${this.formatPrice(this.closePx)}`}formatYestClosePx(){return`$${this.formatPrice(this.yestClosePx)}`}formatOpenPx(){return`$${this.formatPrice(this.openPx)}`}formatHighPx(){return`$${this.formatPrice(this.highPx)}`}formatLowPx(){return`$${this.formatPrice(this.lowPx)}`}formatBid(){return`$${this.formatPrice(this.bidPx)}`}formatAsk(){return`$${this.formatPrice(this.askPx)}`}formatBidSize(){return this.bidSz.toString()}formatAskSize(){return this.askSz.toString()}formatSpread(){return`$${this.formatPrice(this.getSpread())}`}formatMidPrice(){return`$${this.formatPrice(this.getMidPrice())}`}formatChange(){const t=this.getChange();return`${t>=0?"+":""}${this.formatPrice(t)}`}formatChangePercent(){const t=this.getChangePercent();return`${t>=0?"+":""}${t.toFixed(2)}%`}formatVolume(){return this.volume.toLocaleString()}formatOpenInterest(){return this.openInterest.toLocaleString()}formatPrimeShare(){return this.primeShare.toString()}formatExpirationDate(){if(!this.expire)return"N/A";try{return new Date(this.expire).toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"})}catch(t){return this.expire}}formatQuoteTime(){if(!this.quoteTime)return"N/A";try{return new Date(this.quoteTime).toLocaleString("en-US",{year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"})}catch(t){return this.quoteTime}}formatExchange(){return this.exch||"N/A"}formatDataSource(){return this.dataSource||"N/A"}formatDayRange(){return`$${this.formatPrice(this.lowPx)} - $${this.formatPrice(this.highPx)}`}formatOptionType(){return this.type.toUpperCase()}getDaysToExpiration(){if(!this.expire)return 0;try{const t=new Date(this.expire),e=new Date,n=t.getTime()-e.getTime(),i=Math.ceil(n/864e5);return Math.max(0,i)}catch(t){return 0}}formatDaysToExpiration(){const t=this.getDaysToExpiration();return`${t} day${1!==t?"s":""}`}getMoneyness(t){return t&&0!==this.strike?"call"===this.type?t-this.strike:"put"===this.type?this.strike-t:0:0}isInTheMoney(t){return this.getMoneyness(t)>0}parseSymbol(){return{underlying:this.rootSymbol||"",expirationDate:this.expire||"",type:this.type||"",strikePrice:this.strike||0,fullSymbol:this.symbol||""}}getSummary(){return{symbol:this.symbol,underlying:this.underlying,type:this.formatOptionType(),strike:this.formatStrike(),expiration:this.formatExpirationDate(),lastPrice:this.formatLastPrice(),change:this.formatChange(),changePercent:this.formatChangePercent(),volume:this.formatVolume(),openInterest:this.formatOpenInterest(),bid:this.formatBid(),ask:this.formatAsk(),exchange:this.formatExchange()}}getDataSource(){return"opra-d"===this.dataSource.toLowerCase()?"20 mins delayed":"real-time"===this.dataSource.toLowerCase()?"Real-time":"MDAS Server"}static fromApiResponse(t){return new v(t)}isValid(){return!this.notFound&&this.symbol&&this.strike>0}}class w extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for OptionsWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for OptionsWidget");this.type="options";const i=p(e.symbol);if(!i.valid)throw new Error(`Invalid option symbol: ${i.error}`);this.symbol=i.sanitized,this.wsManager=e.wsManager,this.debug=e.debug||!1,this.styled=void 0===e.styled||e.styled,this.data=null,this.isDestroyed=!1,this.unsubscribe=null,this.loadingTimeout=null,this.createWidgetStructure(),this.initializeSymbolEditor(),this.initialize()}createWidgetStructure(){if(this.container.innerHTML='\n <div class="options-widget widget">\n <div class="widget-content">\n \x3c!-- Header Section with Purple Background --\x3e\n <div class="widget-header">\n <div class="symbol-section">\n <h1 class="symbol editable-symbol" \n title="Double-click to edit symbol" \n data-original-symbol="">\n </h1>\n <div class="option-info">\n <span class="underlying-symbol"></span>\n <span class="data-type">OPTIONS</span>\n </div>\n </div>\n <div class="price-section">\n <div class="current-price"></div>\n <div class="price-change">\n <span class="change-value"></span>\n <span class="change-percent"></span>\n </div>\n </div>\n </div>\n\n \x3c!-- Data Grid Section --\x3e\n <div class="data-grid">\n <div class="data-section">\n <div class="section-title">Quote</div>\n <div class="data-row">\n <span class="data-label">Bid</span>\n <span class="data-value bid-ask-value bid-data"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Ask</span>\n <span class="data-value bid-ask-value ask-data"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Trading Activity</div>\n <div class="data-row">\n <span class="data-label">Volume</span>\n <span class="data-value volume"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Open Interest</span>\n <span class="data-value open-interest"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Performance</div>\n <div class="data-row">\n <span class="data-label">Previous Close</span>\n <span class="data-value yesterday-close"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Open</span>\n <span class="data-value open-price"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Day Range</span>\n <span class="data-value range-value day-range"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Contract Info</div>\n <div class="data-row">\n <span class="data-label">Strike Price</span>\n <span class="data-value strike-price"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Expiration</span>\n <span class="data-value expiry-date"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Prime Share</span>\n <span class="data-value prime-share"></span>\n </div>\n </div>\n </div>\n\n \x3c!-- Footer --\x3e\n <div class="widget-footer">\n <span class="last-update"></span>\n <span class="data-source"></span>\n </div>\n </div>\n \n \x3c!-- Loading Overlay --\x3e\n <div class="widget-loading-overlay">\n <div class="loading-content">\n <div class="loading-spinner"></div>\n <span class="loading-text">Loading options data...</span>\n </div>\n </div>\n </div>\n',this.styled){const t=this.container.querySelector(".options-widget");t&&t.classList.add("widget-styled")}this.addStyles()}addStyles(){if(!document.querySelector("#options-widget-styles")){const t=document.createElement("style");t.id="options-widget-styles",t.textContent=c,document.head.appendChild(t)}}initializeSymbolEditor(){this.symbolEditor=new s(this,{maxLength:20,placeholder:"Enter option contract...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,symbolType:"optionsl1"});const t=this.container.querySelector(".symbol");t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol)}async handleSymbolChange(t){this.debug&&console.log("[OptionsWidget] Symbol change requested:",t);const e=p(t);if(!e.valid)return this.debug&&console.log("[OptionsWidget] Invalid option symbol:",e.error),{success:!1,error:e.error};const n=e.sanitized;if(n===this.symbol)return this.debug&&console.log("[OptionsWidget] Same symbol, no change needed"),{success:!0};try{const t=this.symbol;return this.showLoading(),this.loadingTimeout&&this.clearTimeout(this.loadingTimeout),this.loadingTimeout=this.setTimeout((()=>{this.debug&&console.log("[OptionsWidget] Loading timeout for symbol:",n),this.hideLoading(),this.showError(`No data received for ${n}. Please try again.`)}),1e4),this.unsubscribe&&(this.debug&&console.log(`[OptionsWidget] Unsubscribing from ${t}`),this.wsManager.sendUnsubscribe("queryoptionl1",t),this.unsubscribe(),this.unsubscribe=null),this.symbol=n,this.debug&&console.log(`[OptionsWidget] Subscribing to ${n}`),this.subscribeToData(),this.debug&&console.log(`[OptionsWidget] Successfully changed symbol from ${t} to ${n}`),{success:!0}}catch(t){return console.error("[OptionsWidget] Error changing symbol:",t),this.showError(`Failed to load data for ${n}`),{success:!1,error:`Failed to load ${n}`}}}async initialize(){this.showLoading(),await this.validateInitialSymbol(),this.subscribeToData()}async validateInitialSymbol(){try{const t=this.wsManager.getApiService();if(!t)return void(this.debug&&console.log("[OptionsWidget] API service not available for initial validation"));const e=await t.quoteOptionl1(this.symbol);if(e&&e[0]&&!e[0].error&&!e[0].not_found){const t=e[0];this.initialValidationData=t,this.debug&&console.log("[OptionsWidget] Initial symbol validated:",{symbol:this.symbol,underlying:t.Underlying||t.RootSymbol})}}catch(t){this.debug&&console.warn("[OptionsWidget] Initial symbol validation failed:",t)}}subscribeToData(){this.unsubscribe=this.wsManager.subscribe(this.widgetId,["queryoptionl1"],this.handleMessage.bind(this),this.symbol)}handleData(t){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),"error"!==t.type){if(t.RootSymbol===this.symbol||t.Underlying===this.symbol||t.Symbol?.includes(this.symbol))if(this.debug&&console.log("[OptionsWidget] Found matching options data for",this.symbol,":",t),!0===t.NotFound)this.data?(this.hideLoading(),this.debug&&console.log("[OptionsWidget] No new data, keeping cached data visible")):this.showNoDataState(t);else{const e=new v(t);this.data=e,this.updateWidget(e)}else if(Array.isArray(t)){const e=t.find((t=>t.RootSymbol===this.symbol||t.Underlying===this.symbol||t.Symbol?.includes(this.symbol)));if(e)if(!0===e.NotFound)this.data?(this.hideLoading(),this.debug&&console.log("[OptionsWidget] No new data, keeping cached data visible")):this.showNoDataState(e);else{const t=new v(e);this.data=t,this.updateWidget(t)}else this.debug&&console.log("[OptionsWidget] No matching data in response, keeping cached data"),this.hideLoading()}t._cached&&this.showConnectionQuality()}else{const e=t.message||"Server error";this.data?(this.hideLoading(),this.debug&&console.log("[OptionsWidget] Error received but keeping cached data:",e)):this.showError(e)}}showNoDataState(t){if(!this.isDestroyed)try{this.hideLoading();const e=t.Symbol||this.symbol,n=this.container.querySelector(".symbol");n&&(n.textContent=e);const i=this.container.querySelector(".underlying-symbol");i&&(i.textContent=t.RootSymbol||this.symbol);const s=this.container.querySelector(".current-price");s&&(s.textContent="$0.00");const o=this.container.querySelector(".price-change");if(o){const t=o.querySelector(".change-value");t&&(t.textContent="+0.00");const e=o.querySelector(".change-percent");e&&(e.textContent=" (0.00%)"),o.classList.remove("positive","negative")}const a=this.container.querySelector(".widget-header");a&&a.classList.add("dimmed"),this.showNoDataMessage(e);const r=f(),c=this.container.querySelector(".last-update");c&&(c.textContent=`Checked: ${r}`)}catch(t){console.error("Error showing no data state:",t),this.showError("Error displaying no data state")}}showNoDataMessage(t){const e=this.container.querySelector(".data-grid"),n=this.container.querySelector(".option-details-section");e&&(e.style.display="none"),n&&(n.style.display="none");const i=this.container.querySelector(".no-data-state");i&&i.remove();const s=l("div","","no-data-state"),o=l("div","","no-data-content"),a=document.createElement("div");a.className="no-data-icon",a.innerHTML='<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#9ca3af" stroke-width="2"/>\n <path d="M12 6v6l4 2" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>',o.appendChild(a);const r=l("h3","No Data Available","no-data-title");o.appendChild(r);const c=l("p","","no-data-description");c.appendChild(document.createTextNode("Data for "));const d=l("strong",String(t));c.appendChild(d),c.appendChild(document.createTextNode(" was not found")),o.appendChild(c);const h=l("p","Please check the symbol or try again later","no-data-guidance");o.appendChild(h),s.appendChild(o);const u=this.container.querySelector(".widget-footer");u&&u.parentNode?u.parentNode.insertBefore(s,u):this.container.appendChild(s)}updateWidget(t){if(!this.isDestroyed)try{this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.hideLoading(),this.clearError();const e=this.container.querySelector(".widget-header");e&&e.classList.remove("dimmed");const n=this.container.querySelector(".data-grid");n&&(n.style.display="grid");const i=this.container.querySelector(".no-data-state");i&&i.remove();const s=this.container.querySelector(".symbol");s&&(s.textContent=t.symbol,s.dataset.originalSymbol=t.symbol);const o=this.container.querySelector(".underlying-symbol");o&&(o.textContent=t.rootSymbol||t.underlying||"QQQ");const a=this.container.querySelector(".current-price");a&&(a.textContent=t.formatLastPrice());const r=t.getChange(),c=this.container.querySelector(".price-change");if(c){const e=c.querySelector(".change-value"),n=c.querySelector(".change-percent");e&&(e.textContent=t.formatChange()),n&&(n.textContent=` (${t.formatChangePercent()})`),c.classList.remove("positive","negative","neutral"),r>0?c.classList.add("positive"):r<0?c.classList.add("negative"):c.classList.add("neutral")}const d=this.container.querySelector(".bid-data");if(d){u(d),d.appendChild(document.createTextNode(t.formatBid()));const e=l("span",`× ${t.bidSz}`,"size-info");d.appendChild(e)}const h=this.container.querySelector(".ask-data");if(h){u(h),h.appendChild(document.createTextNode(t.formatAsk()));const e=l("span",`× ${t.askSz}`,"size-info");h.appendChild(e)}const g=this.container.querySelector(".volume");g&&(g.textContent=t.formatVolume());const p=this.container.querySelector(".open-interest");p&&(p.textContent=t.formatOpenInterest());const m=this.container.querySelector(".day-range");m&&(m.textContent=t.formatDayRange());const b=this.container.querySelector(".open-price");b&&(b.textContent=t.formatOpenPx());const y=this.container.querySelector(".yesterday-close");y&&(y.textContent=t.formatYestClosePx());const x=this.container.querySelector(".strike-price");x&&(x.textContent=t.formatStrike());const v=this.container.querySelector(".expiry-date");v&&(v.textContent=t.formatExpirationDate());const w=this.container.querySelector(".prime-share");w&&(w.textContent=t.formatPrimeShare());const S=f(t.quoteTime||Date.now()),k=this.container.querySelector(".last-update");k&&(k.textContent=`Last update: ${S}`);const M=this.container.querySelector(".data-source");M&&(M.textContent=`Source: ${t.getDataSource()}`)}catch(t){console.error("Error updating options widget:",t),this.showError("Error updating data")}}formatPrice(t){return t?t.toFixed(2):"0.00"}formatPriceChange(t){return`${t>=0?"+":""}${t.toFixed(2)}`}showLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.remove("hidden")}hideLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.add("hidden")}showError(t){this.hideLoading(),this.clearError();const e=document.createElement("div");e.className="widget-error",e.textContent=`Error: ${t}`,e.style.cssText="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 15px 20px;\n background: #fee;\n border: 1px solid #f5c6cb;\n border-radius: 6px;\n color: #721c24;\n text-align: center;\n font-size: 14px;\n z-index: 100;\n ",this.container.appendChild(e)}clearError(){const t=this.container.querySelector(".widget-error"),e=this.container.querySelector(".widget-error-container");t&&t.remove(),e&&e.remove()}destroy(){this.isDestroyed=!0,this.loadingTimeout&&(this.clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.symbolEditor&&(this.symbolEditor.destroy(),this.symbolEditor=null),super.destroy()}}class S{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.openInterest=t.OpenInst||0,this.volume=t.Vol||0,this.askSize=t.AskSz||0,this.bidSize=t.BidSz||0,this.strike=t.Strike||0,this.askPrice=t.AskPx||0,this.bidPrice=t.BidPx||0,this.lastChange=t.LastChg||0,this.lastPrice=t.LastPx||0,this.dataSource=t.DataSource||"",this.expire=t.Expire||"",this.symbol=t.Symbol||""}}const k=`\n${o}\n\n/* Base styles remain the same until responsive section */\n.option-chain-widget {\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n background: white;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 13px;\n max-width: 100%;\n overflow: hidden;\n position: relative;\n}\n\n.option-chain-widget .widget-header {\n background: #f9fafb;\n padding: 16px;\n border-bottom: 1px solid #e5e7eb;\n}\n\n.option-chain-widget .input-section {\n display: flex;\n gap: 12px;\n align-items: center;\n flex-wrap: wrap;\n}\n\n.option-chain-widget .symbol-input,\n.option-chain-widget .date-select {\n padding: 8px 12px;\n border: 1px solid #d1d5db;\n border-radius: 4px;\n font-size: 14px;\n font-family: inherit;\n min-width: 0;\n flex: 1;\n}\n\n.option-chain-widget .symbol-input {\n text-transform: uppercase;\n min-width: 100px;\n max-width: 120px;\n}\n\n.option-chain-widget .date-select {\n min-width: 140px;\n background: white;\n}\n\n.option-chain-widget .fetch-button {\n padding: 8px 16px;\n background: #3b82f6;\n color: white;\n border: none;\n border-radius: 4px;\n font-size: 14px;\n cursor: pointer;\n font-family: inherit;\n white-space: nowrap;\n}\n\n.option-chain-widget .fetch-button:hover {\n background: #2563eb;\n}\n\n.option-chain-widget .fetch-button:disabled {\n background: #9ca3af;\n cursor: not-allowed;\n}\n\n.option-chain-widget .date-select:disabled {\n background: #f3f4f6;\n color: #9ca3af;\n cursor: not-allowed;\n}\n\n.option-chain-widget .option-chain-table {\n max-height: 800px;\n overflow-x: auto; /* Enable horizontal scrolling */\n overflow-y: auto; /* Enable vertical scrolling if needed */\n -webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */\n width: 100%; /* Ensure full width */\n position: relative; /* Establish positioning context */\n box-sizing: border-box; /* Include padding and border in the element's total width and height */\n}\n\n.option-chain-widget .table-header {\n background: #f3f4f6;\n font-weight: 600;\n border-bottom: 2px solid #d1d5db;\n position: sticky;\n top: 0;\n z-index: 10;\n width: fit-content; /* Ensure it spans the full width */\n box-sizing: border-box; /* Include padding and border in the element's total width and height */\n}\n\n.option-chain-widget .price-change.positive::before,\n.option-chain-widget .price-change.negative::before {\n content: none;\n}\n \n\n/* Better approach - make section headers align with actual column structure */\n.option-chain-widget .section-headers,\n.option-chain-widget .column-headers {\n\n display: grid;\n grid-template-columns: minmax(160px, 1fr) repeat(6, minmax(80px, 1fr)) 80px minmax(160px, 1fr) repeat(6, minmax(80px, 1fr));\n border-bottom: 1px solid #d1d5db;\n background: #f3f4f6;\n position: sticky;\n top: 0;\n z-index: 12;\n box-sizing: border-box; /* Include padding and border in the element's total width and height */\n margin: 0;\n padding: 0;\n min-width: fit-content;\n}\n\n.option-chain-widget .calls-header {\n grid-column: 1 / 8; /* Span across first 7 columns */\n padding: 8px;\n text-align: center;\n font-weight: 600;\n font-size: 14px;\n color: #374151;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f3f4f6;\n}\n\n.option-chain-widget .strike-header {\n grid-column: 8 / 9; /* Strike column */\n background: #e5e7eb;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.option-chain-widget .puts-header {\n grid-column: 9 / -1; /* Span from column 9 to the end */\n padding: 8px;\n text-align: center;\n font-weight: 600;\n font-size: 14px;\n color: #374151;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f3f4f6;\n}\n\n.option-chain-widget .column-headers {\n display: grid;\n grid-template-columns: minmax(160px, 1fr) repeat(6, minmax(80px, 1fr)) 80px minmax(160px, 1fr) repeat(6, minmax(80px, 1fr));\n position: sticky;\n top: 40px;\n z-index: 11;\n background: #f3f4f6;\n}\n\n.option-chain-widget .calls-columns,\n.option-chain-widget .puts-columns {\n display: contents; /* Remove their own grid, use parent grid */\n}\n\n.option-chain-widget .strike-column {\n display: flex;\n align-items: center;\n justify-content: center;\n background: #e5e7eb !important;\n font-weight: bold;\n color: #374151;\n font-size: 11px;\n text-transform: uppercase;\n}\n\n.option-chain-widget .strike-column span {\n background: transparent !important; /* Ensure span doesn't override parent background */\n}\n\n.option-chain-widget .column-headers span {\n padding: 6px 2px; /* Reduce padding to prevent text overflow */\n text-align: center;\n font-size: 11px; /* Reduce font size */\n text-transform: uppercase;\n color: #6b7280;\n font-weight: 600;\n letter-spacing: 0.3px; /* Reduce letter spacing */\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n background: #f3f4f6;\n}\n\n.option-chain-widget .option-row {\n display: grid;\n grid-template-columns: minmax(160px, 1fr) repeat(6, minmax(80px, 1fr)) 80px minmax(160px, 1fr) repeat(6, minmax(80px, 1fr));\n border-bottom: 1px solid #f3f4f6;\n min-height: 32px;\n}\n\n.option-chain-widget .option-row:hover {\n background: #f9fafb;\n}\n .option-chain-widget .option-row:hover .calls-data span,\n.option-chain-widget .option-row:hover .puts-data span,\n.option-chain-widget .option-row:hover .strike-data {\n background: #f9fafb;\n}\n\n.option-chain-widget .calls-data,\n.option-chain-widget .puts-data {\n display: contents; /* Make children participate in parent grid */\n}\n\n.option-chain-widget .strike-data {\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f9fafb;\n font-weight: 600; /* Changed from bold to 600 for consistency */\n color: #6b7280; /* Changed from #1f2937 to match other widgets */\n}\n\n.option-chain-widget .calls-data span,\n.option-chain-widget .puts-data span {\n padding: 6px 4px;\n text-align: center;\n font-size: 12px; /* Changed from 12px to 14px */\n font-weight: 400; /* Added to match data values in other widgets */\n color: #111827; /* Changed from #374151 to match other widgets */\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.option-chain-widget .contract-cell {\n font-size: 10px;\n color: #6b7280;\n text-align: left;\n padding: 4px 6px;\n font-family: monospace;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.option-chain-widget .positive {\n color: #059669;\n}\n\n.option-chain-widget .negative {\n color: #dc2626;\n}\n\n.option-chain-widget .neutral {\n color: #6b7280;\n}\n\n.option-chain-widget .widget-footer {\n background: #f9fafb;\n padding: 8px 16px;\n border-top: 1px solid #e5e7eb;\n text-align: center;\n font-size: 11px;\n color: #6b7280;\n}\n\n.option-chain-widget .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.9);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 20;\n}\n\n.option-chain-widget .widget-loading-overlay.hidden {\n display: none;\n}\n\n.option-chain-widget .loading-content {\n text-align: center;\n}\n\n.option-chain-widget .loading-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid #e5e7eb;\n border-top: 3px solid #3b82f6;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin: 0 auto 12px;\n}\n\n@keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n.option-chain-widget .no-data-state {\n padding: 40px;\n text-align: center;\n color: #6b7280;\n}\n\n.option-chain-widget .widget-error {\n padding: 20px;\n background: #fef2f2;\n color: #dc2626;\n border: 1px solid #fecaca;\n margin: 16px;\n border-radius: 4px;\n text-align: center;\n}\n\n/* RESPONSIVE STYLES */\n\n/* Tablet styles (768px and down) */\n@media (max-width: 768px) {\n .option-chain-widget .input-section {\n flex-direction: column;\n align-items: stretch;\n gap: 8px;\n }\n \n .option-chain-widget .symbol-input,\n .option-chain-widget .date-select {\n width: 100%;\n max-width: none;\n }\n \n .option-chain-widget .fetch-button {\n width: 100%;\n }\n \n .option-chain-widget .calls-columns,\n .option-chain-widget .puts-columns {\n grid-template-columns: 120px repeat(6, minmax(50px, 1fr));\n }\n \n .option-chain-widget .calls-data,\n .option-chain-widget .puts-data {\n grid-template-columns: 120px repeat(6, minmax(50px, 1fr));\n }\n \n .option-chain-widget .column-headers span {\n font-size: 10px;\n padding: 6px 2px;\n }\n \n .option-chain-widget .calls-data span,\n .option-chain-widget .puts-data span {\n font-size: 11px;\n padding: 4px 2px;\n }\n \n .option-chain-widget .contract-cell {\n font-size: 9px;\n padding: 4px 4px;\n }\n \n .option-chain-widget .strike-data {\n font-size: 12px;\n }\n\n /* Update responsive breakpoints to match */\n .option-chain-widget .section-headers {\n grid-template-columns: 1fr 80px 1fr;\n background: #f3f4f6;\n width: 100%;\n\n }\n}\n\n/* Mobile styles (480px and down) */\n@media (max-width: 480px) {\n .option-chain-widget {\n font-size: 12px;\n }\n \n .option-chain-widget .widget-header {\n padding: 12px;\n }\n \n .option-chain-widget .section-headers {\n grid-template-columns: 1fr 60px 1fr;\n background: #f3f4f6;\n }\n \n .option-chain-widget .column-headers {\n grid-template-columns: 1fr 60px 1fr;\n }\n \n .option-chain-widget .option-row {\n grid-template-columns: 1fr 60px 1fr;\n min-height: 28px;\n }\n \n .option-chain-widget .calls-columns,\n .option-chain-widget .puts-columns {\n grid-template-columns: 100px repeat(6, minmax(40px, 1fr));\n }\n \n .option-chain-widget .calls-data,\n .option-chain-widget .puts-data {\n grid-template-columns: 100px repeat(6, minmax(40px, 1fr));\n }\n \n .option-chain-widget .column-headers span {\n font-size: 9px;\n padding: 4px 1px;\n }\n \n .option-chain-widget .calls-data span,\n .option-chain-widget .puts-data span {\n font-size: 10px;\n padding: 3px 1px;\n }\n \n .option-chain-widget .contract-cell {\n font-size: 8px;\n padding: 3px 2px;\n }\n \n .option-chain-widget .strike-data {\n font-size: 11px;\n padding: 3px;\n }\n \n \n .option-chain-widget .option-chain-table {\n max-height: 400px;\n }\n \n .option-chain-widget .no-data-state {\n padding: 20px;\n font-size: 12px;\n }\n \n .option-chain-widget .widget-error {\n margin: 8px;\n padding: 12px;\n font-size: 12px;\n }\n}\n\n/* Very small mobile (320px and down) */\n@media (max-width: 320px) {\n .option-chain-widget .section-headers {\n grid-template-columns: 1fr 50px 1fr;\n }\n \n .option-chain-widget .column-headers {\n grid-template-columns: 1fr 50px 1fr;\n }\n \n .option-chain-widget .option-row {\n grid-template-columns: 1fr 50px 1fr;\n }\n \n .option-chain-widget .calls-columns,\n .option-chain-widget .puts-columns {\n grid-template-columns: 80px repeat(6, minmax(35px, 1fr));\n }\n \n .option-chain-widget .calls-data,\n .option-chain-widget .puts-data {\n grid-template-columns: 80px repeat(6, minmax(35px, 1fr));\n }\n \n .option-chain-widget .column-headers span {\n font-size: 8px;\n padding: 3px 1px;\n }\n \n .option-chain-widget .calls-data span,\n .option-chain-widget .puts-data span {\n font-size: 9px;\n padding: 2px 1px;\n }\n \n .option-chain-widget .contract-cell {\n font-size: 7px;\n padding: 2px 1px;\n }\n}\n`;class M extends n{constructor(t,e,n){if(super(t,e,n),!e.wsManager)throw new Error("WebSocketManager is required for OptionChainWidget");if(this.type="optionchain",this.wsManager=e.wsManager,this.debug=e.debug||!1,this.data=null,this.isDestroyed=!1,this.unsubscribe=null,this.loadingTimeout=null,e.symbol){const t=g(e.symbol);t.valid?this.symbol=t.sanitized:(console.warn("[OptionChainWidget] Invalid initial symbol:",t.error),this.symbol="")}else this.symbol="";this.date="",this.availableDates={},this.fetchRateLimiter=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:500,e=0;return()=>{const n=Date.now(),i=n-e;return i<t?{valid:!1,error:`Please wait ${Math.ceil((t-i)/1e3)} seconds`,remainingMs:t-i}:(e=n,{valid:!0})}}(1e3),this.createWidgetStructure(),this.initialize()}createWidgetStructure(){this.container.innerHTML='\n <div class="option-chain-widget widget">\n <div class="widget-content">\n \x3c!-- Header Section --\x3e\n <div class="widget-header">\n <div class="input-section">\n <input type="text" class="symbol-input" placeholder="Enter Symbol" value="" />\n <select class="date-select" disabled>\n <option value="">Select a date...</option>\n </select>\n <button class="fetch-button" disabled>Search</button>\n </div>\n </div>\n\n \x3c!-- Data Grid Section --\x3e\n <div class="option-chain-table">\n <div class="table-header">\n <div class="section-headers">\n <div class="calls-header">Calls</div>\n <div class="strike-header"></div>\n <div class="puts-header">Puts</div>\n </div>\n <div class="column-headers">\n <div class="calls-columns">\n <span>Contract</span>\n <span>Last</span>\n <span>Change</span>\n <span>Bid</span>\n <span>Ask</span>\n <span>Volume</span>\n <span>Open Int.</span>\n </div>\n <div class="strike-column">\n <span>Strike</span>\n </div>\n <div class="puts-columns">\n <span>Contract</span>\n <span>Last</span>\n <span>Change</span>\n <span>Bid</span>\n <span>Ask</span>\n <span>Volume</span>\n <span>Open Int.</span>\n </div>\n </div>\n </div>\n <div class="option-chain-data-grid">\n \x3c!-- Option chain data will be populated here --\x3e\n </div>\n </div>\n\n \x3c!-- Footer --\x3e\n <div class="widget-footer">\n <span class="last-update"></span>\n <span class="data-source"></span>\n </div>\n </div>\n \n \x3c!-- Loading Overlay --\x3e\n <div class="widget-loading-overlay hidden">\n <div class="loading-content">\n <div class="loading-spinner"></div>\n <span class="loading-text">Loading option chain data...</span>\n </div>\n </div>\n </div>\n',this.addStyles(),this.setupEventListeners()}addStyles(){if(!document.querySelector("#option-chain-styles")){const t=document.createElement("style");t.id="option-chain-styles",t.textContent=k,document.head.appendChild(t)}}setupEventListeners(){this.symbolInput=this.container.querySelector(".symbol-input"),this.dateSelect=this.container.querySelector(".date-select"),this.fetchButton=this.container.querySelector(".fetch-button"),this.dataGrid=this.container.querySelector(".option-chain-data-grid"),this.symbol&&(this.symbolInput.value=this.symbol,this.fetchButton.disabled=!1,this.dateSelect.innerHTML='<option value="">Click search to load dates...</option>'),this.addEventListener(this.symbolInput,"input",(t=>{const e=t.target.value,n=g(e);this.clearInputError(this.symbolInput),n.valid?(this.symbol=n.sanitized,this.symbolInput.value=n.sanitized,this.dateSelect.innerHTML='<option value="">Click search to load dates...</option>',this.dateSelect.disabled=!0,this.fetchButton.disabled=!1,this.availableDates={},this.date=""):""!==e.trim()?(this.showInputError(this.symbolInput,n.error),this.fetchButton.disabled=!0):this.clearDateOptions()})),this.addEventListener(this.dateSelect,"change",(t=>{const e=t.target.value;if(!e)return void(this.date="");const n=function(t){if(!t||"string"!=typeof t)return{valid:!1,error:"Date is required",normalized:""};const e=t.trim();let n;if(/^\d{4}-\d{2}-\d{2}$/.test(e))n=new Date(e);else if(/^\d{2}\/\d{2}\/\d{4}$/.test(e)){const[t,i,s]=e.split("/");n=new Date(s,parseInt(t,10)-1,parseInt(i,10))}else{if(!/^\d+$/.test(e))return{valid:!1,error:"Invalid date format. Use YYYY-MM-DD or MM/DD/YYYY",normalized:""};n=new Date(parseInt(e,10))}return isNaN(n.getTime())?{valid:!1,error:"Invalid date",normalized:""}:{valid:!0,normalized:n.toISOString().split("T")[0]}}(e);n.valid?(this.date=n.normalized,this.symbol&&this.fetchOptionChain()):(this.showError(n.error),this.date="")})),this.addEventListener(this.fetchButton,"click",(()=>{const t=g(this.symbol);if(!t.valid)return void this.showError(t.error||"Please enter a valid symbol first");const e=this.fetchRateLimiter();e.valid?this.loadAvailableDates(t.sanitized):this.showError(e.error)}))}async loadAvailableDates(t){if(t)try{this.symbol=t,this.symbolInput.value=t,this.dateSelect.disabled=!0,this.dateSelect.innerHTML='<option value="">Loading dates...</option>',this.fetchButton.disabled=!0;const e=this.wsManager.getApiService(),n=await e.getOptionChainDates(t);console.log("Available dates:",n.dates_dictionary),this.availableDates=n.dates_dictionary||{},this.populateDateOptions()}catch(e){console.error("[OptionChainWidget] Error loading dates:",e),this.dateSelect.innerHTML='<option value="">Error loading dates</option>',this.fetchButton.disabled=!1,this.showError(`Failed to load dates for ${t}: ${e.message}`)}}populateDateOptions(){this.dateSelect.innerHTML='<option value="">Select expiration date...</option>';const t=Object.keys(this.availableDates).sort();if(0===t.length)return this.dateSelect.innerHTML='<option value="">No dates available</option>',void(this.fetchButton.disabled=!1);t.forEach((t=>{const e=document.createElement("option");e.value=t;const n=this.availableDates[t],i=function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{timezone:n="ET",format:i="short"}=e;if(!t||"string"!=typeof t)return"";const s=t.split("-");if(3!==s.length)return t;const[o,a,r]=s,c=parseInt(a,10)-1,l=parseInt(r,10),d=("long"===i?["January","February","March","April","May","June","July","August","September","October","November","December"]:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"])[c];return`${d} ${l}, ${o}`}(t,{timezone:m(t),format:"short"});e.textContent=i,e.className=`date-option ${n}`,this.dateSelect.appendChild(e)})),this.dateSelect.disabled=!1,this.fetchButton.disabled=!1,t.length>0&&(this.dateSelect.value=t[0],this.date=t[0],this.fetchOptionChain())}clearDateOptions(){this.dateSelect.innerHTML='<option value="">Click fetch to load dates...</option>',this.dateSelect.disabled=!0,this.fetchButton.disabled=!this.symbol,this.symbol=this.symbolInput.value.trim().toUpperCase()||"",this.date="",this.availableDates={}}initialize(){this.hideLoading(),this.symbol&&this.loadAvailableDates(this.symbol)}fetchOptionChain(){if(this.symbol&&this.date)try{this.showLoading(),this.loadingTimeout&&this.clearTimeout(this.loadingTimeout),this.loadingTimeout=this.setTimeout((()=>{this.debug&&console.log("[OptionChainWidget] Loading timeout for:",this.symbol,this.date),this.hideLoading(),this.showError(`No data received for ${this.symbol} on ${this.date}. Please try again.`)}),1e4),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.subscribeToData()}catch(t){console.error("[OptionChainWidget] Error fetching option chain:",t),this.showError(`Failed to load data for ${this.symbol}`)}else console.warn("[OptionChainWidget] Missing symbol or date")}subscribeToData(){this.unsubscribe=this.wsManager.subscribe(this.widgetId,["queryoptionchain"],this.handleMessage.bind(this),this.symbol,{date:this.date})}handleData(t){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),"error"!==t.type)Array.isArray(t)?0===t.length?this.data?(this.hideLoading(),this.debug&&console.log("[OptionChainWidget] No new data, keeping cached data visible")):this.showNoDataState():(this.data=t,this.displayOptionChain(t)):"queryoptionchain"===t.type&&Array.isArray(t.data)&&(0===t.data.length?this.data?(this.hideLoading(),this.debug&&console.log("[OptionChainWidget] No new data, keeping cached data visible")):this.showNoDataState():(this.data=t.data,this.displayOptionChain(t.data))),t._cached&&this.showConnectionQuality();else{const e=t.message||"Server error";this.data?(this.hideLoading(),this.debug&&console.log("[OptionChainWidget] Error received but keeping cached data:",e)):this.showError(e)}}displayOptionChain(t){if(!this.isDestroyed)try{this.hideLoading(),this.clearError(),this.dataGrid.innerHTML="";const e={};t.forEach((t=>{const n=new S(t),i=n.strike;e[i]||(e[i]={call:null,put:null});"call"===C(n.symbol)?e[i].call=n:e[i].put=n}));if(Object.keys(e).sort(((t,e)=>parseFloat(t)-parseFloat(e))).forEach((t=>{const{call:n,put:i}=e[t],s=document.createElement("div");s.classList.add("option-row");const o=t=>{const e=document.createElement("span");if(!t||0===t)return e.className="neutral",e.textContent="0.00",e;const n=parseFloat(t).toFixed(2),i=t>0?"positive":t<0?"negative":"neutral",s=t>0?"+":"";return e.className=`option-chain-${i}`,e.textContent=`${s}${n}`,e},a=t=>d(t,2,"0.00"),r=document.createElement("div");r.className="calls-data",r.appendChild(l("span",n?h(n.symbol):"--","contract-cell")),r.appendChild(l("span",a(n?.lastPrice)));const c=document.createElement("span");n?c.appendChild(o(n.lastChange)):(c.textContent="0.00",c.className="neutral"),r.appendChild(c),r.appendChild(l("span",a(n?.bidPrice))),r.appendChild(l("span",a(n?.askPrice))),r.appendChild(l("span",n?String(n.volume):"0")),r.appendChild(l("span",n?String(n.openInterest):"0"));const u=document.createElement("div");u.className="strike-data",u.textContent=t;const g=document.createElement("div");g.className="puts-data",g.appendChild(l("span",i?h(i.symbol):"","contract-cell")),g.appendChild(l("span",a(i?.lastPrice)));const p=document.createElement("span");i?p.appendChild(o(i.lastChange)):(p.textContent="0.00",p.className="neutral"),g.appendChild(p),g.appendChild(l("span",a(i?.bidPrice))),g.appendChild(l("span",a(i?.askPrice))),g.appendChild(l("span",i?String(i.volume):"0")),g.appendChild(l("span",i?String(i.openInterest):"0")),s.appendChild(r),s.appendChild(u),s.appendChild(g),this.dataGrid.appendChild(s)})),t&&t.length>0){const e=f(t[0].quoteTime||Date.now()),n=this.container.querySelector(".last-update");n&&(n.textContent=`Last update: ${e}`);const i="OPRA-D"===t[0].DataSource?"20 mins delayed":"Real-time",s=this.container.querySelector(".data-source");s&&(s.textContent=`Source: ${i}`)}}catch(t){console.error("Error displaying option chain:",t),this.showError("Error displaying data")}}showLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.remove("hidden")}hideLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.add("hidden")}showError(t){this.hideLoading(),this.clearError();const e=document.createElement("div");e.className="widget-error",e.textContent=`Error: ${t}`,e.style.cssText="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 15px 20px;\n background: #fee;\n border: 1px solid #f5c6cb;\n border-radius: 6px;\n color: #721c24;\n text-align: center;\n font-size: 14px;\n z-index: 100;\n ",this.container.appendChild(e)}clearError(){const t=this.container.querySelector(".widget-error"),e=this.container.querySelector(".widget-error-container");t&&t.remove(),e&&e.remove()}showNoDataState(){this.hideLoading(),this.clearError();const t=l("div","","no-data-state"),e=l("div","","no-data-content"),n=document.createElement("div");n.className="no-data-icon",n.innerHTML='<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#9ca3af" stroke-width="2"/>\n <path d="M12 6v6l4 2" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>',e.appendChild(n);const i=l("h3","No Option Chain Data","no-data-title");e.appendChild(i);const s=l("p","","no-data-description");s.appendChild(document.createTextNode("Option chain data for "));const o=l("strong",h(this.symbol));s.appendChild(o),s.appendChild(document.createTextNode(" on "));const a=l("strong",String(this.date));s.appendChild(a),s.appendChild(document.createTextNode(" was not found")),e.appendChild(s);const r=l("p","Please check the symbol and date or try again later","no-data-guidance");e.appendChild(r),t.appendChild(e);const c=this.container.querySelector(".widget-footer");c&&c.parentNode?c.parentNode.insertBefore(t,c):this.container.appendChild(t)}destroy(){this.isDestroyed=!0,this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.loadingTimeout&&(this.clearTimeout(this.loadingTimeout),this.loadingTimeout=null),super.destroy()}}const C=t=>{const e=t.match(/^(.+?)(\d{6})([CP])(\d{8})$/);if(e){const[,t,n,i,s]=e;return"C"===i?"call":"put"}return null};class _ extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for DataWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for DataWidget");this.type="data",this.symbol=(e.symbol||"").toString().toUpperCase(),this.wsManager=e.wsManager,this.dataSource=e.dataSource||"queryl1",this.debug=e.debug||!1,this.data=null,this.isDestroyed=!1,this.unsubscribe=null,this.loadingTimeout=null,this.createWidgetStructure(),this.initializeSymbolEditor(),this.initialize()}createWidgetStructure(){this.container.innerHTML='\n <div class="data-widget widget">\n <div class="widget-content">\n \x3c!-- Header Section --\x3e\n <div class="widget-header">\n <div class="symbol-section">\n <h1 class="symbol editable-symbol" \n title="Double-click to edit symbol" \n data-original-symbol="">\n </h1>\n <div class="company-info">\n <span class="data-type">Stock</span>\n <span class="company-name"></span>\n </div>\n </div>\n </div>\n\n \x3c!-- Price Section --\x3e\n <div class="price-section">\n <div class="current-price"></div>\n <div class="price-change">\n <span class="change-value"></span>\n <span class="change-percent"></span>\n </div>\n </div>\n\n \x3c!-- Bid/Ask Section --\x3e\n <div class="bid-ask-section">\n <div class="ask">\n <span class="label">Ask</span>\n <span class="value ask-price"></span>\n <span class="size ask-size"></span>\n </div>\n <div class="bid">\n <span class="label">Bid</span>\n <span class="value bid-price"></span>\n <span class="size bid-size"></span>\n </div>\n </div>\n\n \x3c!-- Footer --\x3e\n <div class="widget-footer">\n <span class="last-size">Last Size: <span class="trade-size"></span></span>\n <span class="source">Market Data Powered by MDAS</span>\n </div>\n </div>\n\n \n </div>\n',this.addStyles()}addStyles(){if(!document.querySelector("#data-styles")){const t=document.createElement("style");t.id="data-styles",t.textContent="\n .data-widget {\n position: relative;\n border: 1px solid #ddd;\n border-radius: 8px;\n overflow: hidden;\n background-color: #fff;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n padding: 10px;\n font-family: Arial, sans-serif;\n }\n\n .widget-header {\n display: flex;\n flex-direction: column;\n margin-bottom: 10px;\n }\n\n .symbol {\n font-size: 24px;\n font-weight: bold;\n margin-right: 10px;\n }\n\n .company-info {\n font-size: 12px;\n color: #666;\n }\n\n .trading-info {\n font-size: 12px;\n color: #999;\n }\n\n .price-section {\n display: flex;\n align-items: baseline;\n margin-bottom: 10px;\n }\n\n .current-price {\n font-size: 36px;\n font-weight: bold;\n margin-right: 10px;\n }\n\n .price-change {\n font-size: 18px;\n }\n\n .price-change.positive {\n color: green;\n }\n\n .price-change.negative {\n color: red;\n }\n\n .bid-ask-section {\n display: flex;\n justify-content: space-between;\n margin-bottom: 10px;\n }\n\n .ask, .bid {\n display: flex;\n align-items: center;\n }\n\n .label {\n font-size: 14px;\n margin-right: 5px;\n }\n\n .value {\n font-size: 16px;\n font-weight: bold;\n margin-right: 5px;\n }\n\n .size {\n font-size: 14px;\n color: red;\n }\n\n .widget-footer {\n font-size: 12px;\n color: #333;\n }\n",document.head.appendChild(t)}}initializeSymbolEditor(){this.symbolEditor=new s(this,{maxLength:10,placeholder:"Enter symbol...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,symbolType:"quotel1"});const t=this.container.querySelector(".symbol");t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol)}async handleSymbolChange(t){this.debug&&console.log("[DataWidget] Symbol change requested:",t);const e=t.toUpperCase();if(e===this.symbol)return this.debug&&console.log("[DataWidget] Same symbol, no change needed"),{success:!0};try{const t=this.symbol;return this.showLoading(),this.loadingTimeout&&clearTimeout(this.loadingTimeout),this.loadingTimeout=setTimeout((()=>{this.debug&&console.log("[DataWidget] Loading timeout for symbol:",e),this.hideLoading(),this.showError(`No data received for ${e}. Please try again.`)}),1e4),this.unsubscribe&&(this.debug&&console.log(`[DataWidget] Unsubscribing from ${t}`),this.wsManager.sendUnsubscribe(this.dataSource,t),this.unsubscribe(),this.unsubscribe=null),this.symbol=e,this.debug&&console.log(`[DataWidget] Subscribing to ${e}`),this.subscribeToData(),this.debug&&console.log(`[DataWidget] Successfully changed symbol from ${t} to ${e}`),{success:!0}}catch(t){return console.error("[DataWidget] Error changing symbol:",t),this.hideLoading(),this.showError(`Failed to load data for ${e}`),{success:!1,error:`Failed to load ${e}`}}}initialize(){this.showLoading(),this.subscribeToData()}subscribeToData(){this.unsubscribe=this.wsManager.subscribe(this.widgetId,[this.dataSource],this.handleMessage.bind(this),this.symbol)}showNoDataState(t){let{Symbol:e,NotFound:n,message:i}=t;this.clearError(),this.hideLoading();const s=document.createElement("div");s.className="no-data-state",s.innerHTML=`\n <div class="no-data-content">\n <div class="no-data-icon">\n <svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#9ca3af" stroke-width="2"/>\n <path d="M12 6v6l4 2" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>\n </div>\n <h3 class="no-data-title">No Data Available</h3>\n <p class="no-data-description">Data for <strong>${e}</strong> was not found.</p>\n <p class="no-data-guidance">${i||"Please check the symbol or try again later."}</p>\n </div>\n `,this.container.appendChild(s)}handleData(t){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),"error"===t.type&&t.noData)return this.debug&&console.log("[DataWidget] Received no data message:",t.message),void this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:t.message});if("error"!==t.type){if(t.Data&&t.Data.length>0){const e=t.Data.find((t=>t.Symbol===this.symbol));if(e){const t=new i(e);this.updateWidget(t)}else console.log("hereee"),this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:`No data found for ${this.symbol}`})}t._cached&&this.showConnectionQuality()}else{const e=t.message||"Server error";this.showError(e)}}updateWidget(t){if(!this.isDestroyed)try{this.clearError(),this.hideLoading();const e=this.container.querySelector(".symbol");e&&(e.textContent=t.symbol,e.dataset.originalSymbol=t.symbol);const n=this.container.querySelector(".current-price");n&&(n.textContent=t.formatPrice());const i=this.container.querySelector(".price-change"),s=i.querySelector(".change-value"),o=i.querySelector(".change-percent");s.textContent=t.formatPriceChange(),o.textContent=` (${t.formatPriceChangePercent()})`,i.classList.remove("positive","negative","neutral"),t.getPriceChange()>0?i.classList.add("positive"):t.getPriceChange()<0?i.classList.add("negative"):i.classList.add("neutral");const a=this.container.querySelector(".bid-price");a&&(a.textContent=t.bidPrice.toFixed(2));const r=this.container.querySelector(".ask-price");r&&(r.textContent=t.askPrice.toFixed(2));const c=this.container.querySelector(".bid-size");c&&(c.textContent=`× ${t.bidSize}`);const l=this.container.querySelector(".ask-size");l&&(l.textContent=`× ${t.askSize}`);const d=this.container.querySelector(".trade-size");d&&(d.textContent=t.tradeSize);const h=this.container.querySelector(".last-update");if(h){const e=f(t.quoteTime);h.textContent=`Last update: ${e}`}const u=this.container.querySelector(".data-source");u&&(u.textContent=`Source: ${t.getDataSource()}`)}catch(t){console.error("Error updating widget:",t),this.showError("Error updating data")}}}class D extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for CombinedMarketWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for CombinedMarketWidget");const i=g(e.symbol);if(!i.valid)throw new Error(`Invalid symbol: ${i.error}`);this.type="combined-market",this.wsManager=e.wsManager,this.symbol=i.sanitized,this.debug=e.debug||!1,this.removeNightSession=!0,this.marketData=null,this.nightSessionData=null,this.unsubscribeMarket=null,this.unsubscribeNight=null,this.createWidgetStructure(),this.initializeSymbolEditor(),this.subscribeToData()}createWidgetStructure(){this.container.innerHTML='\n <div class="combined-market-widget">\n <div class="combined-market-header">\n <div class="combined-symbol-section">\n <h1 class="combined-symbol editable-symbol"\n title="Double-click to edit symbol"\n data-original-symbol="">\n </h1>\n <div class="combined-company-info">\n <span class="company-name">\n </span>\n </div>\n </div>\n </div>\n\n <div class="combined-data-container">\n \x3c!-- Regular Market Data (Left) --\x3e\n <div class="combined-market-section regular-market">\n <div class="combined-price-info">\n <div class="combined-current-price">--</div>\n <div class="combined-price-change neutral">\n <span class="combined-change-value">--</span>\n <span class="combined-change-percent">(--)</span>\n </div>\n </div>\n <div class="combined-market-status"><span class="market-status-label"></span> <span class="combined-timestamp">--</span></div>\n </div>\n\n \x3c!-- Night Session Data (Right) --\x3e\n <div class="combined-market-section night-session">\n\n <div class="combined-price-info">\n <div class="combined-current-price">--</div>\n <div class="combined-price-change neutral">\n <span class="combined-change-value">--</span>\n <span class="combined-change-percent">(--)</span>\n </div>\n </div>\n <div class="combined-market-status">Overnight: <span class="combined-timestamp">--</span></div>\n </div>\n </div>\n\n <div class="combined-loading-overlay hidden">\n <div class="combined-loading-content">\n <div class="combined-loading-spinner"></div>\n <span class="combined-loading-text">Loading market data...</span>\n </div>\n </div>\n </div>\n';const t=this.container.querySelector(".combined-symbol");if(t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol),this.removeNightSession&&!this.isNightSessionActive()){const t=this.container.querySelector(".night-session");t&&(t.style.display="none")}this.addStyles()}addStyles(){if(!document.querySelector("#combined-market-styles")){const t=document.createElement("style");t.id="combined-market-styles",t.textContent='\n .combined-market-widget {\n background: #ffffff;\n color: #111827;\n padding: 24px;\n border-radius: 12px;\n border: 1px solid #e5e7eb;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\n position: relative;\n }\n\n .combined-market-widget .combined-market-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n padding-bottom: 16px;\n border-bottom: 1px solid #e5e7eb;\n }\n\n .combined-market-widget .combined-symbol-section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .combined-market-widget .combined-symbol {\n font-size: 24px;\n font-weight: 700;\n margin: 0;\n color: #111827;\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .combined-market-widget .combined-symbol:hover {\n opacity: 0.8;\n transform: scale(1.02);\n }\n\n .combined-market-widget .combined-symbol:hover::after {\n content: "✎";\n position: absolute;\n top: -8px;\n right: -8px;\n background: #3b82f6;\n color: white;\n font-size: 10px;\n padding: 2px 4px;\n border-radius: 4px;\n opacity: 0.8;\n pointer-events: none;\n }\n\n .combined-market-widget .follow-btn {\n background: #ffffff;\n border: 1px solid #d1d5db;\n color: #374151;\n padding: 8px 16px;\n border-radius: 20px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n transition: all 0.2s;\n }\n\n .combined-market-widget .follow-btn:hover {\n border-color: #9ca3af;\n background: #f9fafb;\n }\n\n .combined-market-widget .combined-data-container {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 40px;\n }\n\n .combined-market-widget .combined-market-section {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .combined-market-widget .combined-price-info {\n display: flex;\n flex-direction: column;\n min-height: 90px;\n }\n\n .combined-market-widget .session-label {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n color: #6b7280;\n margin-bottom: 4px;\n font-weight: 500;\n }\n\n .combined-market-widget .session-label .icon {\n font-size: 16px;\n }\n\n .combined-market-widget .combined-current-price {\n font-size: 48px;\n font-weight: 700;\n line-height: 1;\n color: #111827;\n }\n\n .combined-market-widget .combined-price-change {\n display: flex;\n gap: 8px;\n font-size: 20px;\n font-weight: 600;\n margin-top: 4px;\n }\n\n .combined-market-widget .combined-price-change.positive {\n color: #059669;\n }\n\n .combined-market-widget .combined-price-change.negative {\n color: #dc2626;\n }\n\n .combined-market-widget .combined-price-change.neutral {\n color: #6b7280;\n }\n\n .combined-market-widget .combined-market-status {\n font-size: 14px;\n color: #6b7280;\n margin-top: 8px;\n }\n\n .combined-market-widget .combined-timestamp {\n color: #9ca3af;\n font-weight: 400;\n }\n\n /* Loading overlay */\n .combined-market-widget .combined-loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(255, 255, 255, 0.95);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 12px;\n z-index: 10;\n }\n\n .combined-market-widget .combined-loading-overlay.hidden {\n display: none;\n }\n\n .combined-market-widget .combined-loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n }\n\n .combined-market-widget .combined-loading-spinner {\n width: 40px;\n height: 40px;\n border: 3px solid #e5e7eb;\n border-top-color: #3b82f6;\n border-radius: 50%;\n animation: combined-market-spin 1s linear infinite;\n }\n\n .combined-market-widget .combined-loading-text {\n color: #6b7280;\n font-size: 14px;\n font-weight: 500;\n }\n\n @keyframes combined-market-spin {\n to { transform: rotate(360deg); }\n }\n\n /* Responsive */\n @media (max-width: 768px) {\n .combined-market-widget .combined-data-container {\n grid-template-columns: 1fr;\n gap: 30px;\n }\n\n .combined-market-widget .combined-current-price {\n font-size: 36px;\n }\n\n .combined-market-widget .combined-price-change {\n font-size: 16px;\n }\n }\n',document.head.appendChild(t)}}initializeSymbolEditor(){this.symbolEditor=new s(this,{maxLength:10,placeholder:"Enter symbol...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,symbolType:"quotel1"});const t=this.container.querySelector(".combined-symbol");t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol)}async handleSymbolChange(t){this.debug&&console.log("[CombinedMarketWidget] Symbol change requested:",t);const e=g(t);if(!e.valid)return this.debug&&console.log("[CombinedMarketWidget] Invalid symbol:",e.error),{success:!1,error:e.error};const n=e.sanitized;if(n===this.symbol)return this.debug&&console.log("[CombinedMarketWidget] Same symbol, no change needed"),{success:!0};try{const t=this.symbol;return this.showLoading(),this.unsubscribeMarket&&(this.debug&&console.log(`[CombinedMarketWidget] Unsubscribing from market data: ${t}`),this.wsManager.sendUnsubscribe("queryl1",t),this.unsubscribeMarket(),this.unsubscribeMarket=null),this.unsubscribeNight&&(this.debug&&console.log(`[CombinedMarketWidget] Unsubscribing from night session: ${t}`),this.wsManager.sendUnsubscribe("queryblueoceanl1",t),this.unsubscribeNight(),this.unsubscribeNight=null),this.symbol=n,this.marketData=null,this.nightSessionData=null,this.debug&&console.log(`[CombinedMarketWidget] Subscribing to new symbol: ${n}`),this.subscribeToData(),this.debug&&console.log(`[CombinedMarketWidget] Successfully changed symbol from ${t} to ${n}`),{success:!0}}catch(t){return console.error("[CombinedMarketWidget] Error changing symbol:",t),this.hideLoading(),{success:!1,error:`Failed to load ${n}`}}}subscribeToData(){this.showLoading(),this.unsubscribeMarket=this.wsManager.subscribe(`${this.widgetId}-market`,["queryl1"],(t=>{const{event:e,data:n}=t;"connection"!==e?"data"===e&&(n._dataType="market",this.handleMessage({event:e,data:n})):this.handleConnectionStatus(n)}),this.symbol),this.unsubscribeNight=this.wsManager.subscribe(`${this.widgetId}-night`,["queryblueoceanl1"],(t=>{const{event:e,data:n}=t;"connection"!==e&&"data"===e&&(n._dataType="night",this.handleMessage({event:e,data:n}))}),this.symbol)}handleData(t){const e=t._dataType;if(this.debug&&console.log(`[CombinedMarketWidget] handleData called with type: ${e}`,t),e){if("error"===t.type&&t.noData)return this.debug&&console.log(`[CombinedMarketWidget] Received no data message for ${e}:`,t.message),"market"!==e||this.marketData?"night"!==e||this.nightSessionData||this.debug&&console.log("[CombinedMarketWidget] No night session data available"):this.debug&&console.log("[CombinedMarketWidget] No market data available"),void this.hideLoading();if("error"===t.type){const n=t.message||"Server error";return this.debug&&console.log(`[CombinedMarketWidget] Error received for ${e}:`,n),void this.hideLoading()}if("object"==typeof t&&t.Message){const n=t.Message.toLowerCase();if(n.includes("no data")||n.includes("not found"))return this.debug&&console.log(`[CombinedMarketWidget] No data message for ${e}:`,t.Message),void this.hideLoading()}if("market"===e){if(t.Data&&Array.isArray(t.Data)){const e=t.Data.find((t=>t.Symbol===this.symbol));e?(this.debug&&console.log("[CombinedMarketWidget] Market data received:",e),this.marketData=new i(e),this.updateMarketSection(),this.hideLoading()):(this.debug&&console.log("[CombinedMarketWidget] No market data for symbol in response, keeping cached data"),this.hideLoading())}delete t._dataType}if("night"===e){if(Array.isArray(t)){const e=t.find((t=>t.Symbol===this.symbol));e?!0!==e.NotFound&&e.MarketName&&"BLUE"===e.MarketName?(this.debug&&console.log("[CombinedMarketWidget] Night session data received:",e),this.nightSessionData=new y(e),this.updateNightSessionSection(),this.hideLoading()):(this.debug&&console.log("[CombinedMarketWidget] Night session data not found or not available"),this.hideLoading()):(this.debug&&console.log("[CombinedMarketWidget] No night session data for symbol in response, keeping cached data"),this.hideLoading())}else"queryblueoceanl1"!==t.type&&"querybrucel1"!==t.type||(t[0]?.Symbol===this.symbol?!0!==t[0].NotFound&&t[0].MarketName&&"BLUE"===t[0].MarketName?(this.debug&&console.log("[CombinedMarketWidget] Night session data received (wrapped format):",t[0]),this.nightSessionData=new y(t[0]),this.updateNightSessionSection(),this.hideLoading()):(this.debug&&console.log("[CombinedMarketWidget] Night session data not found or not available (wrapped format)"),this.hideLoading()):(this.debug&&console.log("[CombinedMarketWidget] No matching symbol in wrapped response, keeping cached data"),this.hideLoading()));delete t._dataType}t._cached&&this.showConnectionQuality()}else this.debug&&console.warn("[CombinedMarketWidget] No data type specified, attempting to infer from structure")}updateMarketSection(){if(!this.marketData)return;const t=this.container.querySelector(".regular-market"),e=this.container.querySelector(".company-name");if(e){const t=this.marketData.companyName||`${this.marketData.symbol} Inc.`,n=this.marketData.companyDescription||`${this.marketData.symbol}`;e.textContent=t,e.setAttribute("title",n),e.setAttribute("data-tooltip",n)}t.querySelector(".combined-current-price").textContent=this.marketData.formatPrice();const n=t.querySelector(".combined-price-change"),i=t.querySelector(".combined-change-value"),s=t.querySelector(".combined-change-percent"),o=this.marketData.change,a=o>0?"positive":o<0?"negative":"neutral";n.className=`combined-price-change ${a}`,i.textContent=this.marketData.formatPriceChange(),s.textContent=`(${this.marketData.formatPriceChangePercent()})`;const r=t.querySelector(".market-status-label");r&&(r.textContent=`${this.getSessionType()}:`);t.querySelector(".combined-timestamp").textContent=f(this.marketData.quoteTime,{format:"long"}),this.debug&&console.log("[CombinedMarketWidget] Updated market section:",this.marketData)}updateNightSessionSection(){if(!this.nightSessionData)return;const t=this.container.querySelector(".night-session");if(this.removeNightSession&&!this.isNightSessionActive())return t.style.display="none",void(this.debug&&console.log("[CombinedMarketWidget] Night session hidden (market closed)"));t.style.display="";const e=t.querySelector(".combined-current-price");e&&(e.textContent=this.nightSessionData.lastPrice?this.nightSessionData.formatPrice():"--");const n=t.querySelector(".combined-price-change"),i=t.querySelector(".combined-change-value"),s=t.querySelector(".combined-change-percent"),o=this.nightSessionData.change,a=o>0?"positive":o<0?"negative":"neutral";n.className=`combined-price-change ${a}`,i.textContent=null!==this.nightSessionData.change?this.nightSessionData.formatPriceChange():"--",s.textContent=`(${this.nightSessionData.formatPriceChangePercent()})`;t.querySelector(".combined-timestamp").textContent=f(this.nightSessionData.quoteTime,{format:"long"}),this.debug&&(console.log("connection quality:",this.connectionQuality),console.log("[CombinedMarketWidget] Updated night session section:",this.nightSessionData))}getSessionType(){const t=(new Date).toLocaleString("en-US",{timeZone:"America/New_York"}),e=new Date(t),n=e.getHours(),i=e.getMinutes(),s=60*n+i;return console.log("Current EST time:",n+":"+(i<10?"0":"")+i),s>=240&&s<570?"Pre-Market":s>=570&&s<960?"Market Hours":s>=960&&s<1200?"Post-Market":"Market Closed"}isNightSessionActive(){return"Market Closed"===this.getSessionType()}destroy(){this.unsubscribeMarket&&(this.unsubscribeMarket(),this.unsubscribeMarket=null),this.unsubscribeNight&&(this.unsubscribeNight(),this.unsubscribeNight=null),this.symbolEditor&&(this.symbolEditor.destroy(),this.symbolEditor=null),super.destroy()}}class T{constructor(t){Array.isArray(t)?(console.log("[IntradayChartModel] Processing",t.length,"raw data points"),t.length>0&&console.log("[IntradayChartModel] Sample raw point:",t[0]),this.dataPoints=t.map(((t,e)=>{const n={symbol:t.symbol||t.Symbol,time:t.time||t.Time||t.timestamp||t.Timestamp,open:parseFloat(t.open||t.Open)||0,high:parseFloat(t.high||t.High)||0,low:parseFloat(t.low||t.Low)||0,close:parseFloat(t.close||t.Close)||0,volume:parseInt(t.volume||t.Volume)||0,source:t.source||t.Source};return e<2&&console.log(`[IntradayChartModel] Processed point ${e}:`,n),n.time||console.warn(`[IntradayChartModel] Point ${e} missing time:`,t),0===n.close&&console.warn(`[IntradayChartModel] Point ${e} has zero close price:`,t),n})).filter((t=>{if(!t.time||t.close<=0)return!1;const e=new Date(t.time).getTime();return!(isNaN(e)||e<9466848e5)||(console.warn("[IntradayChartModel] Invalid timestamp:",t.time),!1)})),this.dataPoints.sort(((t,e)=>new Date(t.time).getTime()-new Date(e.time).getTime())),console.log("[IntradayChartModel] Valid data points after filtering:",this.dataPoints.length),this.dataPoints.length>0&&console.log("[IntradayChartModel] Time range:",this.dataPoints[0].time,"to",this.dataPoints[this.dataPoints.length-1].time)):(console.warn("[IntradayChartModel] Expected array of data points, got:",typeof t),this.dataPoints=[]),this.symbol=this.dataPoints.length>0?this.dataPoints[0].symbol:null,this.source=this.dataPoints.length>0?this.dataPoints[0].source:null}getTimeLabels(){return this.dataPoints.map((t=>{const e=new Date(t.time);let n=e.getHours();const i=n>=12?"PM":"AM";n%=12,n=n||12;return[`${n}:${e.getMinutes().toString().padStart(2,"0")} ${i}`,`${(e.getMonth()+1).toString().padStart(2,"0")}/${e.getDate().toString().padStart(2,"0")}`]}))}getOHLCData(){return this.dataPoints.map((t=>({x:new Date(t.time).getTime(),o:t.open,h:t.high,l:t.low,c:t.close})))}getClosePrices(){return this.dataPoints.map((t=>t.close))}getVolumeData(){return this.dataPoints.map((t=>t.volume))}getHighPrices(){return this.dataPoints.map((t=>t.high))}getLowPrices(){return this.dataPoints.map((t=>t.low))}getOpenPrices(){return this.dataPoints.map((t=>t.open))}getStats(){if(0===this.dataPoints.length)return{high:0,low:0,open:0,close:0,change:0,changePercent:0,volume:0};this.getClosePrices();const t=this.getHighPrices(),e=this.getLowPrices(),n=this.getVolumeData(),i=this.dataPoints[0],s=this.dataPoints[this.dataPoints.length-1],o=s.close-i.open,a=0!==i.open?o/i.open*100:0;return{high:Math.max(...t),low:Math.min(...e),open:i.open,close:s.close,change:o,changePercent:a,volume:n.reduce(((t,e)=>t+e),0),dataPoints:this.dataPoints.length}}formatPrice(t){return t.toFixed(2)}formatVolume(t){return t>=1e6?(t/1e6).toFixed(2)+"M":t>=1e3?(t/1e3).toFixed(2)+"K":t.toString()}}
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).MdasSDK={})}(this,(function(t){"use strict";class e{constructor(){this.state=new Map,this.listeners=new Map}setState(t,e){this.state.set(t,e),this.notifyListeners(t,e)}getState(t){return this.state.get(t)}subscribe(t,e){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e),()=>{const n=this.listeners.get(t);n&&n.delete(e)}}notifyListeners(t,e){const n=this.listeners.get(t);n&&n.forEach((t=>t(e)))}clear(){this.state.clear(),this.listeners.clear()}}class n{constructor(t,e,n){this.setContainer(t),this.options=e,this.widgetId=n,this.isDestroyed=!1,this.lastData=null,this.lastDataTimestamp=null,this.connectionQuality="disconnected",this.eventListeners=[],this.timeouts=[],this.intervals=[]}setContainer(t){if(!t)throw new Error("DOM element is required for widget");if("string"==typeof t){const e=document.querySelector(t);if(!e)throw new Error(`Element not found: ${t}`);this.container=e}else{if(t.nodeType!==Node.ELEMENT_NODE)throw new Error("Invalid element provided to widget");this.container=t}}showLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.remove("hidden")}hideLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.add("hidden")}showError(t){this.hideLoading(),this.clearError();const e=document.createElement("div");e.className="widget-error",e.textContent=`Error: ${t}`,this.container.appendChild(e)}clearError(){const t=this.container.querySelector(".widget-error");t&&t.remove()}showConnectionQuality(){const t=this.container.querySelector(".connection-quality");if(t&&t.remove(),"live"===this.connectionQuality)return;const e=this.container.querySelector('.last-update, [class*="last-update"], [class*="timestamp"]');if(e){const t=document.createElement("span");switch(t.className="connection-quality",this.connectionQuality){case"offline":t.textContent=" (disconnected)",t.style.color="#dc2626",t.style.fontWeight="500";break;case"reconnecting":t.textContent=" (reconnecting...)",t.style.color="#d97706"}t.style.fontSize="11px",e.appendChild(t)}}handleMessage(t){if(!this.isDestroyed)try{const{event:e,data:n}=t;if(console.log("[BaseWidget] handleMessage called with event:",e,"data:",n),this.debug&&console.log(`[${this.type}] Received:`,e,n),"connection"===e)return void this.handleConnectionStatus(n);if("data"===e)return console.log("[BaseWidget] Caching live data"),this.lastData=n,this.lastDataTimestamp=Date.now(),this.connectionQuality="live",void this.handleData(n);if("session_revoked"===e)return void this.handleSessionRevoked(n);this.handleCustomEvent&&this.handleCustomEvent(e,n)}catch(t){console.error(`[${this.constructor.name}] Error handling message:`,t),this.showError("Error processing data")}}handleSessionRevoked(t){"attempting_relogin"===t.status?(this.connectionQuality="reconnecting",this.lastData?this.showCachedData():this.showLoading()):"relogin_failed"===t.status?(this.connectionQuality="offline",this.showError(t.error||"Session expired. Please refresh the page.")):"relogin_successful"===t.status&&(this.connectionQuality="live",this.hideLoading(),this.clearError())}handleData(t){throw new Error("handleData must be implemented by child widget")}handleConnectionStatus(t){"connected"===t.status?(this.connectionQuality="live",this.clearError(),this.hideLoading()):"reconnecting"===t.status?(this.connectionQuality="reconnecting",this.lastData?this.showCachedData():this.showLoading()):"disconnected"===t.status?(this.connectionQuality="offline",this.lastData?this.showCachedData():this.hideLoading()):"failed"===t.status?(this.connectionQuality="offline",this.handleConnectionFailed(t),this.showConnectionQuality()):"error"===t.status&&(this.connectionQuality="offline",this.lastData?this.showCachedData():this.hideLoading())}showCachedData(){if(this.lastData&&this.lastDataTimestamp){const t=Date.now()-this.lastDataTimestamp,e=t>3e5,n={...this.lastData,_cached:!0,_cacheAge:t,_isStale:e,_quality:this.connectionQuality};this.handleData(n)}}addEventListener(t,e,n){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};t&&e&&n?(t.addEventListener(e,n,i),this.eventListeners.push({element:t,event:e,handler:n,options:i})):console.warn("[BaseWidget] Invalid addEventListener parameters")}setTimeout(t,e){const n=setTimeout(t,e);return this.timeouts.push(n),n}setInterval(t,e){const n=setInterval(t,e);return this.intervals.push(n),n}clearTimeout(t){clearTimeout(t);const e=this.timeouts.indexOf(t);e>-1&&this.timeouts.splice(e,1)}clearInterval(t){clearInterval(t);const e=this.intervals.indexOf(t);e>-1&&this.intervals.splice(e,1)}showInputError(t,e){if(!t)return;this.clearInputError(t);const n=document.createElement("div");n.className="input-error-message",n.textContent=e,n.style.cssText="\n color: #dc2626;\n font-size: 12px;\n margin-top: 4px;\n animation: slideDown 0.2s ease-out;\n ",t.classList.add("input-error"),t.style.borderColor="#dc2626",t.parentNode&&t.parentNode.insertBefore(n,t.nextSibling),t.errorElement=n}clearInputError(t){t&&(t.errorElement&&(t.errorElement.remove(),t.errorElement=null),t.classList.remove("input-error"),t.style.borderColor="")}destroy(){this.isDestroyed||(this.isDestroyed=!0,this.eventListeners.forEach((t=>{let{element:e,event:n,handler:i,options:s}=t;try{e.removeEventListener(n,i,s)}catch(t){console.error("[BaseWidget] Error removing event listener:",t)}})),this.eventListeners=[],this.timeouts.forEach((t=>{clearTimeout(t)})),this.timeouts=[],this.intervals.forEach((t=>{clearInterval(t)})),this.intervals=[],this.lastData=null,this.container&&(this.container.innerHTML=""),this.debug&&console.log(`[${this.constructor.name}] Widget destroyed and cleaned up`))}handleConnectionFailed(t){this.connectionQuality="offline",this.lastData?(this.showCachedData(),this.hideLoading()):this.hideLoading(),this.debug&&console.log(`[${this.constructor.name}] Connection failed after ${t.maxAttempts} attempts`)}}class i{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.symbol=t.Symbol||"",this.companyName=t.CompName||"",this.companyDescription=t.CompDesc||"",this.instrumentType=t.InstrType||"",this.lastPrice=t.LastPx||0,this.previousClose=t.YestClosePx||0,this.open=t.OpenPx||0,this.high=t.HighPx||0,this.low=t.LowPx||0,this.close=t.ClosingPx||0,this.change=t.Change||0,this.changePercent=t.ChangePercent||0,this.vwap=t.VWAP||0,this.volume=t.Volume||0,this.averageVolume=t.AvgVol30d||0,this.yesterdayVolume=t.YesterdayTradeVolume||0,this.bidPrice=t.BidPx||0,this.bidSize=t.BidSz||0,this.bidExchange=t.BidExch||"",this.askPrice=t.AskPx||0,this.askSize=t.AskSz||0,this.askExchange=t.AskExch||"",this.issueMarket=t.IssueMarket||"",this.marketName=t.MarketName||"",this.marketDescription=t.MarketDesc||"",this.mic=t.MIC||"",this.countryCode=t.CountryCode||"",this.timeZone=t.TimeZone||"",this.tradePrice=t.TradePx||0,this.tradeSize=t.TradeSz||0,this.tradeCondition=t.Condition||0,this.tradeTime=t.TradeTime||"",this.tradeRegion=t.TradeRegion||"",this.tradeRegionName=t.TradeRegionName||"",this.preMarketPrice=t.PreLastPx||0,this.preMarketTradeTime=t.PreTradeTime||"",this.postMarketPrice=t.PostLastPx||0,this.postMarketTradeTime=t.PostTradeTime||"",this.high52Week=t.High52wPx||0,this.low52Week=t.Low52wPx||0,this.high52WeekDate=t.High52wDate||"",this.low52WeekDate=t.Low52wDate||"",this.calendarYearHigh=t.CalendarYearHigh||0,this.calendarYearLow=t.CalendarYearLow||0,this.calendarYearHighDate=t.CalendarYearHighDate||"",this.calendarYearLowDate=t.CalendarYearLowDate||"",this.marketCap=t.MktCap||0,this.peRatio=t.PeRatio||0,this.beta=t.FundBeta||0,this.sharesOutstanding=t.ComShrsOut||0,this.dividendYield=t.DivYield||0,this.dividendAmount=t.DivAmt||0,this.dividendRate=t.DividendRate||0,this.dividendPaymentDate=t.DividendPaymentDate||t.PayDate||"",this.dividendExDate=t.DividendExDate||t.DivDateEx||"",this.isin=t.ISIN||"",this.cusip=t.CUSIP||"",this.sedol=t.SEDOL||"",this.gics=t.GICS||"",this.status=t.Status||"",this.dataSource=t.DataSource||"",this.quoteTime=t.QuoteTime||"",this.infoTime=t.InfoTime||""}get openPrice(){return this.open}get description(){return this.companyDescription}get lastUpdate(){return this.quoteTime||this.infoTime}getSecurityType(){return{257:"Stock",262:"ETF",261:"Mutual Fund",258:"Bond",260:"Option",263:"Future",259:"Index"}[this.instrumentType]||"Stock"}getPriceChange(){return this.change}getPriceChangePercent(){return 100*this.changePercent}getDataSource(){return"delay"===this.dataSource.toLowerCase()?"20 mins delayed":"real-time"===this.dataSource.toLowerCase()?"Real-time":null!==this.dataSource&&""!==this.dataSource?this.dataSource:"MDAS Server"}getCurrentPrice(){return this.lastPrice}get52WeekRange(){return`${this.low52Week.toFixed(2)} - ${this.high52Week.toFixed(2)}`}getMarketCapFormatted(){return this.formatMarketCap()}formatPrice(){return`$${(arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.lastPrice).toFixed(2)}`}formatPriceChange(){const t=this.getPriceChange();return`${t>=0?"+":""} ${t.toFixed(2)}`}formatPriceChangePercent(){const t=this.getPriceChangePercent();return`${t>=0?"+":""}${t.toFixed(2)}%`}formatBidAsk(){return`$${this.bidPrice.toFixed(2)} x ${this.bidSize} / $${this.askPrice.toFixed(2)} x ${this.askSize}`}formatVolume(){return 0===this.volume?"N/A":this.volume.toLocaleString()}formatAvgVolume(){return 0===this.averageVolume?"N/A":this.averageVolume.toLocaleString()}formatAverageVolume(){return 0===this.averageVolume?"N/A":this.averageVolume.toLocaleString()}formatDayRange(){return 0===this.low&&0===this.high?"N/A":`$${this.low.toFixed(2)} - $${this.high.toFixed(2)}`}format52WeekRange(){return 0===this.low52Week&&0===this.high52Week?"N/A":`${this.low52Week.toFixed(2)} - ${this.high52Week.toFixed(2)}`}formatMarketCap(){if(0===this.marketCap)return"N/A";const t=1e6*this.marketCap;return t>=1e12?`$${(t/1e12).toFixed(2)}T`:t>=1e9?`$${(t/1e9).toFixed(2)}B`:t>=1e6?`$${(t/1e6).toFixed(2)}M`:t>=1e3?`$${(t/1e3).toFixed(2)}K`:`$${t.toFixed(2)}`}formatAvg30DayVolume(){return this.formatAverageVolume()}static fromApiResponse(t){return new i(t)}}class s{constructor(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.widget=t,this.options={maxLength:e.maxLength||10,placeholder:e.placeholder||"Enter symbol...",onSymbolChange:e.onSymbolChange||(()=>{}),debug:e.debug||!1,autoUppercase:!1!==e.autoUppercase,...e},this.symbolType=e.symbolType||null,this.apiService=this.widget.wsManager.getApiService(),this.isEditing=!1,this.isValidating=!1,this.originalSymbol="",this.elements={},this.addStyles(),this.initialize()}initialize(){this.setupElements(),this.attachEventListeners()}setupElements(){if(this.elements.symbolDisplay=this.widget.container.querySelector(".editable-symbol"),!this.elements.symbolDisplay)throw new Error('No element with class "editable-symbol" found in widget');this.originalSymbol=this.elements.symbolDisplay.textContent.trim(),this.elements.symbolDisplay.dataset.originalSymbol=this.originalSymbol}attachEventListeners(){this.startEditing=this.startEditing.bind(this),this.handleKeydown=this.handleKeydown.bind(this),this.handleInput=this.handleInput.bind(this),this.handleBlur=this.handleBlur.bind(this),this.handleClickOutside=this.handleClickOutside.bind(this),this.elements.symbolDisplay.addEventListener("dblclick",this.startEditing)}startEditing(){this.isEditing||this.isValidating||(this.isEditing=!0,this.originalSymbol=this.elements.symbolDisplay.textContent.trim(),this.options.debug&&(console.log(this.widget.connectionQuality),console.log("[SymbolEditor] Starting edit mode for:",this.originalSymbol)),this.transformToInput())}transformToInput(){const t=this.elements.symbolDisplay,e=window.getComputedStyle(t),n=document.createElement("div");n.className="symbol-edit-container",n.style.display="inline-flex",n.style.alignItems="center",n.style.gap="8px",n.style.verticalAlign="baseline";const i=document.createElement("input");i.type="text",i.value=this.originalSymbol,i.className=t.className+" symbol-input-mode",i.style.fontSize=e.fontSize,i.style.fontWeight=e.fontWeight,i.style.fontFamily=e.fontFamily,i.style.color=e.color,i.style.backgroundColor="transparent",i.style.border="2px solid #3b82f6",i.style.borderRadius="4px",i.style.padding="2px 6px",i.style.margin="0",i.style.outline="none",i.style.width="auto",i.style.minWidth="120px",i.style.maxWidth="200px",i.style.flexShrink="0";const s=document.createElement("button");s.className="symbol-save-btn",s.innerHTML="✓",s.title="Save symbol",s.style.width="28px",s.style.height="28px",s.style.border="none",s.style.borderRadius="4px",s.style.backgroundColor="#059669",s.style.color="white",s.style.cursor="pointer",s.style.fontSize="12px",s.style.display="inline-flex",s.style.alignItems="center",s.style.justifyContent="center",s.style.flexShrink="0",n.appendChild(i),n.appendChild(s),t.style.display="none",t.parentNode.insertBefore(n,t.nextSibling),this.elements.symbolInput=i,this.elements.saveBtn=s,this.elements.editContainer=n,i.focus(),i.select(),i.addEventListener("keydown",this.handleKeydown),i.addEventListener("input",this.handleInput),i.addEventListener("blur",this.handleBlur),document.addEventListener("click",this.handleClickOutside),s.addEventListener("click",(t=>{t.stopPropagation(),this.saveSymbol()}))}async saveSymbol(){if(this.isValidating)return;const t=this.elements.symbolInput.value.trim(),e=await this.validateSymbol(t);if(!e.isValid)return this.showError(e.message),void setTimeout((()=>{this.elements.symbolInput&&(this.elements.symbolInput.value=this.originalSymbol,this.clearError(),this.cancelEditing())}),2e3);this.setLoadingState(!0);try{const n=await this.options.onSymbolChange(t,this.originalSymbol,e.data);if(n&&n.success)this.finishEditing(t),this.showSuccess("Symbol updated successfully");else{const t=n?.message||"Failed to update symbol";this.showError(t)}}catch(t){console.error("[SymbolEditor] Error changing symbol:",t),this.showError("Error updating symbol")}finally{this.setLoadingState(!1)}}cancelEditing(){this.options.debug&&console.log("[SymbolEditor] Cancelling edit mode"),this.finishEditing(this.originalSymbol)}finishEditing(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;this.isEditing=!1;const e=t||this.originalSymbol;this.elements.symbolDisplay.textContent=e,this.elements.symbolDisplay.dataset.originalSymbol=e,this.elements.symbolDisplay.style.display="",this.elements.editContainer&&(this.elements.editContainer.remove(),this.elements.editContainer=null),this.elements.symbolInput=null,this.elements.saveBtn=null,document.removeEventListener("click",this.handleClickOutside)}handleKeydown(t){"Enter"===t.key?(t.preventDefault(),this.saveSymbol()):"Escape"===t.key&&(t.preventDefault(),this.cancelEditing())}handleInput(t){if(this.options.autoUppercase){const e=t.target,n=e.selectionStart,i=e.selectionEnd;e.value=e.value.toUpperCase(),e.setSelectionRange(n,i)}this.clearError()}handleBlur(t){t.relatedTarget&&t.relatedTarget===this.elements.saveBtn||setTimeout((()=>{this.isEditing&&this.cancelEditing()}),150)}handleClickOutside(t){if(!this.isEditing)return;this.elements.symbolInput?.contains(t.target)||this.elements.saveBtn?.contains(t.target)||this.cancelEditing()}async validateSymbol(t){if("live"!==this.widget.connectionQuality)return{isValid:!1,message:"Not connected to data service"};if(!t||0===t.trim().length)return{isValid:!1,message:"Symbol cannot be empty"};const e=t.trim().toUpperCase();try{if("optionsl1"==this.symbolType){const t=await this.apiService.quoteOptionl1(e);return t[0]&&(t[0].error||1==t[0].not_found)?1==t[0].not_found?{isValid:!1,message:"Symbol not found"}:{isValid:!1,message:t[0].error}:{isValid:!0,data:t[0]}}if("quotel1"==this.symbolType||"nightsession"==this.symbolType){const t=await this.apiService.quotel1(e);return t&&t.message?t.message.includes("no data")?{isValid:!1,message:"Symbol not found"}:{isValid:!1,message:t.message}:{isValid:!0,data:t.data[0]}}if("nightsession"==this.symbolType){const t=await this.apiService.quoteBlueOcean(e);return t[0]&&1==t[0].not_found?{isValid:!1,message:"Symbol not found"}:t[0]&&0==t[0].not_found?{isValid:!0,data:t[0]}:{isValid:!1,message:t[0].error}}}catch(t){return console.warn("[SymbolEditor] API validation failed, falling back to basic validation:",t),this.defaultValidator(e)}return this.defaultValidator(e)}defaultValidator(t){return t&&0!==t.length?t.length>this.options.maxLength?{isValid:!1,message:`Symbol too long (max ${this.options.maxLength} chars)`}:{isValid:!0,data:null}:{isValid:!1,message:"Symbol cannot be empty"}}setLoadingState(t){this.isValidating=t,this.elements.saveBtn&&(t?(this.elements.saveBtn.innerHTML='<div class="loading-spinner"></div>',this.elements.saveBtn.disabled=!0):(this.elements.saveBtn.innerHTML="✓",this.elements.saveBtn.disabled=!1)),this.elements.symbolInput&&(this.elements.symbolInput.disabled=t)}showError(t){if(this.elements.symbolInput){this.elements.symbolInput.style.borderColor="#dc2626",this.elements.symbolInput.style.boxShadow="0 0 0 3px rgba(220, 38, 38, 0.1)",this.removeErrorMessage();const e=document.createElement("div");e.className="symbol-error-message",e.textContent=t,e.style.cssText="\n color: #dc2626;\n font-size: 12px;\n margin-bottom: 2px;\n position: absolute;\n background: white;\n z-index: 1000;\n transform: translateY(-100%);\n top: -4px;\n left: 0; // Align with input box\n ";const n=this.elements.editContainer;n&&(n.style.position="relative",n.appendChild(e)),this.elements.errorMessage=e,this.elements.symbolInput.focus()}}removeErrorMessage(){this.elements.errorMessage&&(this.elements.errorMessage.remove(),this.elements.errorMessage=null)}clearError(){this.elements.symbolInput&&(this.elements.symbolInput.style.borderColor="#3b82f6",this.elements.symbolInput.style.boxShadow="0 0 0 3px rgba(59, 130, 246, 0.1)",this.elements.symbolInput.title=""),this.removeErrorMessage()}showSuccess(t){this.options.debug&&console.log("[SymbolEditor] Success:",t)}updateSymbol(t){const e=this.options.autoUppercase?t.toUpperCase():t;this.elements.symbolDisplay.textContent=e,this.elements.symbolDisplay.dataset.originalSymbol=e,this.options.debug&&console.log("[SymbolEditor] Symbol updated externally:",e)}destroy(){this.finishEditing(),this.elements.symbolDisplay?.removeEventListener("dblclick",this.startEditing)}addStyles(){if(document.querySelector("#symbol-editor-styles"))return;const t=document.createElement("style");t.id="symbol-editor-styles",t.textContent='\n .editable-symbol {\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .editable-symbol:hover {\n opacity: 0.8;\n transform: scale(1.02);\n }\n\n .editable-symbol:hover::after {\n content: "✎";\n position: absolute;\n top: -8px;\n right: -8px;\n background: #3b82f6;\n color: white;\n font-size: 10px;\n padding: 2px 4px;\n border-radius: 4px;\n opacity: 0.8;\n pointer-events: none;\n }\n\n .symbol-input-mode {\n text-transform: uppercase;\n }\n\n .symbol-save-btn:hover {\n background-color: #047857 !important;\n transform: scale(1.05);\n }\n\n .symbol-save-btn:disabled {\n background-color: #9ca3af !important;\n cursor: not-allowed !important;\n transform: none !important;\n }\n\n .loading-spinner {\n width: 12px;\n height: 12px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: white;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n ',document.head.appendChild(t)}}const o='\n /* ========================================\n FONT SIZE SYSTEM - CSS Variables\n Adjust --mdas-base-font-size to scale all fonts\n ======================================== */\n :root {\n /* Base font size - change this to scale everything */\n --mdas-base-font-size: 14px;\n\n /* Relative font sizes (in em, scales with base) */\n --mdas-company-name-size: 1.43em; /* 20px at base 14px */\n --mdas-symbol-size: 1.79em; /* 25px at base 14px */\n --mdas-price-size: 2.29em; /* 32px at base 14px */\n --mdas-price-change-size: 1.14em; /* 16px at base 14px */\n --mdas-section-title-size: 1em; /* 14px at base 14px */\n --mdas-data-label-size: 0.93em; /* 13px at base 14px */\n --mdas-data-value-size: 1em; /* 14px at base 14px */\n --mdas-bid-ask-size: 1.07em; /* 15px at base 14px */\n --mdas-small-text-size: 0.86em; /* 12px at base 14px */\n --mdas-badge-size: 0.86em; /* 12px at base 14px */\n --mdas-loading-text-size: 1.14em; /* 16px at base 14px */\n --mdas-no-data-title-size: 1.14em; /* 16px at base 14px */\n --mdas-edit-icon-size: 0.71em; /* 10px at base 14px */\n }\n\n /* Apply base font size to widgets */\n .widget {\n font-size: var(--mdas-base-font-size);\n }\n\n /* ========================================\n OPTIONAL CARD STYLING\n Add \'widget-styled\' class for card design\n ======================================== */\n .widget-styled {\n background: white;\n border-radius: 12px;\n padding: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n border: 1px solid #e5e7eb;\n }\n\n /* Base widget styles */\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.95);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n border-radius: 12px;\n }\n\n .widget-loading-overlay.hidden {\n display: none;\n }\n\n .widget {\n font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;\n width: 100%;\n max-width: 1400px;\n }\n\n /* HEADER STYLES */\n\n .widget-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 20px;\n gap: 16px;\n }\n\n .symbol-section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .night-company-name {\n font-size: var(--mdas-company-name-size);\n font-weight: 500;\n color: #111827;\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .symbol {\n font-size: var(--mdas-symbol-size);\n font-weight: 700;\n color: #111827;\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .symbol:hover {\n opacity: 0.8;\n transform: scale(1.02);\n }\n\n .symbol:hover::after {\n content: "✎";\n position: absolute;\n top: -8px;\n right: -8px;\n background: #3b82f6;\n color: white;\n font-size: var(--mdas-edit-icon-size);\n padding: 2px 4px;\n border-radius: 4px;\n opacity: 0.8;\n }\n\n .data-type {\n background:rgb(214, 228, 250);\n padding: 2px 8px;\n border-radius: 6px;\n font-size: var(--mdas-badge-size);\n font-weight: 500;\n color: #3b82f6;\n }\n\n .price-section {\n text-align: right;\n }\n\n .current-price {\n font-size: var(--mdas-price-size);\n font-weight: 700;\n color: #111827;\n line-height: 1;\n }\n\n .price-change {\n font-size: var(--mdas-price-change-size);\n font-weight: 600;\n margin-top: 4px;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 4px;\n }\n\n .price-change.positive { \n color: #10b981; \n }\n\n .price-change.negative { \n color: #ef4444; \n }\n\n .price-change.neutral { \n color: #6b7280; \n }\n\n .market-data-widget .price-change.positive::before,\n .night-session-widget .price-change.positive::before,\n .options-widget .price-change.positive::before{\n content: "↗";\n }\n\n .market-data-widget .price-change.negative::before,\n .night-session-widget .price-change.negative::before,\n .options-widget .price-change.negative::before {\n content: "↘";\n }\n\n /* BODY SECTION STYLES */\n\n .data-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 32px;\n margin-bottom: 24px;\n }\n\n .data-section {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .section-title {\n font-size: var(--mdas-section-title-size);\n font-weight: 600;\n color: #6b7280;\n margin: 0;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n border-bottom: 1px solid #e5e7eb;\n padding-bottom: 8px;\n }\n\n .data-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n }\n\n .data-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .price-metadata,\n .data-label {\n font-size: var(--mdas-data-label-size);\n color: #6b7280;\n font-weight: 500;\n }\n\n .data-value {\n font-size: var(--mdas-data-value-size);\n font-weight: 600;\n color: #111827;\n text-align: right;\n flex-shrink: 0;\n }\n\n\n /* Footer */\n .widget-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 16px;\n border-top: 1px solid #e5e7eb;\n font-size: var(--mdas-small-text-size);\n color: #9ca3af;\n }\n \n\n /* LOADING STYLES */\n\n\n .loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n }\n\n .loading-spinner {\n width: 40px;\n height: 40px;\n border: 4px solid #e5e7eb;\n border-top-color: #3b82f6;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .loading-text {\n color: #6b7280;\n font-size: var(--mdas-loading-text-size);\n font-weight: 500;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n .widget-error {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background: #fef2f2;\n border: 1px solid #fecaca;\n border-radius: 8px;\n padding: 16px;\n color: #dc2626;\n font-size: var(--mdas-data-value-size);\n font-weight: 600;\n text-align: center;\n z-index: 10;\n max-width: 80%;\n }\n\n /* Price change colors */\n .positive {\n color: #059669;\n }\n\n .negative {\n color: #dc2626;\n }\n\n \n\n /* No data state */\n .no-data-state {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 160px;\n margin: 16px 0;\n background: #f9fafb;\n border: 1px solid #e5e7eb;\n border-radius: 12px;\n padding: 24px 16px;\n width: 100%;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .no-data-content {\n text-align: center;\n max-width: 280px;\n width: 100%;\n word-wrap: break-word;\n overflow-wrap: break-word;\n }\n\n .no-data-icon {\n margin-bottom: 16px;\n display: flex;\n justify-content: center;\n }\n\n .no-data-icon svg {\n opacity: 0.6;\n }\n\n .no-data-title {\n font-size: var(--mdas-no-data-title-size);\n font-weight: 600;\n color: #6b7280;\n margin: 0 0 8px 0;\n }\n\n .no-data-description {\n font-size: var(--mdas-data-value-size);\n color: #9ca3af;\n margin: 0 0 12px 0;\n line-height: 1.4;\n }\n\n .no-data-description strong {\n color: #6b7280;\n font-weight: 600;\n }\n\n .no-data-guidance {\n font-size: var(--mdas-small-text-size);\n color: #9ca3af;\n margin: 0;\n font-style: italic;\n }\n\n /* Error Access State */\n .no-data-state.error-access {\n border: 1px solid #fecaca;\n background:rgb(255, 255, 255);\n \n }\n\n .no-data-title.error {\n color: #ef4444;\n }\n\n .no-data-icon.error svg {\n stroke: #ef4444;\n }\n\n .no-data-icon.error svg circle[fill] {\n fill: #ef4444;\n }\n',a=`\n ${o}\n\n /* Reset and base styles for widget */\n\n .night-session-widget {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n line-height: 1.5;\n max-width: 800px;\n position: relative;\n }\n\n /* STANDARDIZED HEADER LAYOUT */\n\n\n .symbol-section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .night-session-widget .symbol {\n font-size: var(--mdas-symbol-size);\n font-weight: 700;\n color: #111827;\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .night-session-widget .symbol:hover {\n opacity: 0.8;\n transform: scale(1.02);\n }\n\n .night-session-widget .symbol:hover::after {\n content: "✎";\n position: absolute;\n top: -8px;\n right: -8px;\n background: #3b82f6;\n color: white;\n font-size: var(--mdas-edit-icon-size);\n padding: 2px 4px;\n border-radius: 4px;\n opacity: 0.8;\n }\n\n .company-info {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: var(--mdas-section-title-size);\n color: #6b7280;\n }\n\n .market-name {\n font-weight: 600;\n text-transform: uppercase;\n }\n\n /* Company and market info styling */\n .company-market-info {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: var(--mdas-small-text-size);\n color: #6b7280;\n margin-bottom: 4px;\n }\n\n .company-market-info .market-name {\n font-weight: 600;\n }\n\n .company-market-info .market-name::before {\n content: '|';\n margin-right: 8px;\n color: #9ca3af;\n }\n\n .company-market-info .market-mic {\n font-weight: 600;\n }\n\n .company-market-info .market-mic::before {\n content: '•';\n margin-right: 8px;\n }\n\n /* Price metadata (source and last update) */\n .price-metadata {\n margin-top: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n \n\n /* STANDARDIZED GRID LAYOUT */\n .data-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 24px;\n margin-top: 24px;\n }\n\n .data-section {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .night-session-widget .section-title {\n font-size: var(--mdas-small-text-size);\n font-weight: 600;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n border-bottom: 1px solid #e5e7eb;\n padding-bottom: 8px;\n }\n\n .night-session-widget .data-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n min-height: 20px;\n }\n\n\n\n .night-session-widget .data-value {\n font-size: var(--mdas-data-value-size);\n font-weight: 600;\n color: #111827;\n text-align: right;\n }\n\n /* SPECIAL FORMATTING FOR DIFFERENT DATA TYPES */\n .night-session-widget .bid-ask-value {\n font-size: var(--mdas-bid-ask-size);\n font-weight: 700;\n }\n\n .night-session-widget .size-info {\n font-size: var(--mdas-small-text-size);\n color: #6b7280;\n margin-left: 4px;\n }\n\n /* Footer */\n .night-session-widget .widget-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: 24px;\n padding-top: 16px;\n border-top: 1px solid #e5e7eb;\n font-size: var(--mdas-small-text-size);\n color: #9ca3af;\n grid-column: 1 / -1;\n }\n\n /* Loading Overlay */\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.95);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n border-radius: 12px;\n }\n\n .widget-loading-overlay.hidden {\n display: none;\n }\n\n .loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n }\n\n .loading-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid #f3f4f6;\n border-top-color: #3b82f6;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .night-session-widget .loading-text {\n color: #6b7280;\n font-size: var(--mdas-data-value-size);\n font-weight: 500;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n /* Widget Error Styles */\n .night-session-widget .widget-error {\n background: #fef2f2;\n color: #9ca3af;\n padding: 12px;\n border-radius: 8px;\n border: 1px solid #fecaca;\n margin-top: 16px;\n font-size: var(--mdas-data-value-size);\n font-weight: 500;\n }\n\n /* No Data State */\n .night-session-widget .no-data-state {\n padding: 24px;\n text-align: center;\n color: #6b7280;\n grid-column: 1 / -1;\n }\n\n .night-session-widget .no-data-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n }\n\n .night-session-widget .no-data-icon {\n font-size: 3.43em;\n opacity: 0.5;\n }\n\n .night-session-widget .no-data-title {\n font-size: 1.29em;\n font-weight: 600;\n color: #374151;\n }\n\n .night-session-widget .no-data-message {\n font-size: var(--mdas-data-value-size);\n max-width: 300px;\n line-height: 1.5;\n }\n\n .night-session-widget .no-data-suggestion {\n font-size: var(--mdas-small-text-size);\n color: #9ca3af;\n max-width: 350px;\n line-height: 1.4;\n }\n\n /* Dimmed state for no data */\n .widget-header.dimmed,\n .price-section.dimmed {\n opacity: 0.6;\n }\n\n /* Responsive Design */\n @media (max-width: 768px) {\n :root {\n --mdas-base-font-size: 13px; /* Slightly smaller on tablets */\n }\n\n .widget-styled {\n padding: 16px;\n }\n\n .widget-header {\n grid-template-columns: 1fr;\n gap: 12px;\n text-align: center;\n }\n\n .symbol-section {\n align-items: center;\n }\n\n .price-section {\n text-align: center;\n }\n\n .data-grid {\n grid-template-columns: 1fr;\n gap: 16px;\n }\n\n .widget-footer {\n flex-direction: column;\n gap: 8px;\n text-align: center;\n }\n }\n\n @media (max-width: 480px) {\n :root {\n --mdas-base-font-size: 12px; /* Smaller on mobile */\n }\n }\n\n \n`,r=`\n ${o}\n\n /* Reset and base styles for widget */\n\n .market-data-widget {\n color: #333;\n margin: 0 auto;\n position: relative;\n overflow: hidden;\n }\n\n /* MARKET DATA HEADER STYLES */\n\n\n .market-data-widget .company-info {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: var(--mdas-section-title-size);\n color: #6b7280;\n }\n\n .company-name {\n position: relative;\n cursor: help;\n transition: all 0.2s ease;\n }\n\n .company-name:hover {\n border-bottom-color: #3b82f6;\n color: #3b82f6;\n }\n\n\n /* STANDARDIZED GRID LAYOUT */\n \n .data-section {\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n overflow: hidden;\n }\n\n .market-data-widget .section-title {\n font-size: var(--mdas-small-text-size);\n font-weight: 600;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n padding-bottom: 8px;\n }\n\n\n\n /* SPECIAL FORMATTING FOR DIFFERENT DATA TYPES */\n .market-data-widget .bid-ask-value {\n font-size: var(--mdas-bid-ask-size);\n font-weight: 700;\n }\n\n .market-data-widget .size-info {\n font-size: var(--mdas-small-text-size);\n color: #6b7280;\n margin-left: 4px;\n }\n\n .market-data-widget .range-value {\n font-size: var(--mdas-data-label-size);\n }\n\n \n\n /* Tooltip Styles */\n .company-name::after {\n content: attr(data-tooltip);\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n background: #1f2937;\n color: white;\n padding: 8px 12px;\n border-radius: 6px;\n font-size: var(--mdas-small-text-size);\n font-weight: normal;\n white-space: nowrap;\n max-width: 300px;\n white-space: normal;\n line-height: 1.4;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n opacity: 0;\n visibility: hidden;\n transition: all 0.2s ease;\n pointer-events: none;\n margin-bottom: 5px;\n }\n\n .company-name:hover::after {\n opacity: 1;\n visibility: visible;\n }\n\n .company-name::before {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: #1f2937;\n opacity: 0;\n visibility: hidden;\n transition: all 0.2s ease;\n pointer-events: none;\n }\n\n .company-name:hover::before {\n opacity: 1;\n visibility: visible;\n }\n\n /* Loading Overlay */\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.95);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n border-radius: 12px;\n }\n\n .widget-loading-overlay.hidden {\n display: none;\n }\n\n .loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n }\n\n .loading-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid #f3f4f6;\n border-top-color: #3b82f6;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .market-data-widget .loading-text {\n color: #6b7280;\n font-size: var(--mdas-data-value-size);\n font-weight: 500;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n\n /* Widget Error Styles */\n .market-data-widget .widget-error {\n background: #fef2f2;\n color: #dc2626;\n padding: 12px;\n border-radius: 8px;\n border: 1px solid #fecaca;\n margin-top: 16px;\n font-size: var(--mdas-data-value-size);\n font-weight: 500;\n }\n\n /* Responsive Design */\n\n\n @media (max-width: 600px) {\n .widget-header {\n grid-template-columns: 1fr;\n gap: 12px;\n text-align: center;\n }\n\n .symbol-section {\n align-items: center;\n }\n\n .price-section {\n text-align: center;\n }\n\n .data-grid {\n grid-template-columns: 1fr;\n gap: 16px;\n }\n\n .widget-footer {\n flex-direction: column;\n gap: 8px;\n text-align: center;\n }\n }\n\n @media (max-width: 480px) {\n .company-name::after {\n left: 0;\n transform: none;\n max-width: 250px;\n }\n\n .company-name::before {\n left: 20px;\n transform: none;\n }\n }\n`,c=`\n ${o}\n\n .options-widget {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n color: #333;\n max-width: 1400px;\n width: 100%;\n margin: 0 auto;\n position: relative;\n overflow: hidden;\n }\n\n .widget-content {\n position: relative;\n z-index: 1;\n }\n\n \n .options-widget .symbol-info .symbol {\n font-size: var(--mdas-symbol-size);\n font-weight: 700;\n margin: 0 0 8px 0;\n color: #1f2937;\n }\n\n .options-widget .option-info {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #6b7280;\n font-size: var(--mdas-price-change-size);\n }\n\n .options-widget .underlying-symbol {\n font-weight: 600;\n color: #6b7280;\n }\n\n .options-widget .option-details-section {\n display: flex;\n gap: 24px;\n margin-bottom: 24px;\n padding-bottom: 20px;\n border-bottom: 1px solid #e5e7eb;\n }\n\n .options-widget .option-details-section .detail-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .options-widget .option-details-section .detail-item label {\n font-size: var(--mdas-small-text-size);\n color: #6b7280;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n }\n\n .options-widget .option-details-section .detail-item span {\n font-size: var(--mdas-price-change-size);\n font-weight: 600;\n color: #1f2937;\n }\n\n\n .options-widget .widget-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 16px;\n border-top: 1px solid #e5e7eb;\n font-size: var(--mdas-small-text-size);\n color: #9ca3af;\n }\n\n /* Dimmed state for no-data */\n .widget-header.dimmed {\n opacity: 0.6;\n }\n\n .widget-header.dimmed .symbol {\n color: #9ca3af;\n }\n\n /* Responsive design */\n @media (max-width: 768px) {\n .widget-header {\n flex-direction: column;\n gap: 16px;\n text-align: left;\n }\n\n .price-info {\n text-align: left;\n }\n\n .data-grid {\n grid-template-columns: 1fr;\n gap: 20px;\n }\n\n .option-details-section {\n flex-wrap: wrap;\n gap: 16px;\n }\n\n .current-price {\n font-size: 32px;\n }\n\n .symbol {\n font-size: 20px !important;\n }\n }\n\n @media (max-width: 480px) {\n .widget-styled {\n padding: 12px;\n }\n\n .widget-header {\n margin-bottom: 16px;\n padding-bottom: 16px;\n }\n\n .current-price {\n font-size: 28px;\n }\n\n .data-row {\n grid-template-columns: 1fr;\n gap: 12px;\n }\n\n .option-details-section {\n flex-direction: column;\n gap: 12px;\n }\n\n .no-data-state {\n min-height: 120px;\n padding: 20px 12px;\n }\n\n .no-data-title {\n font-size: 14px;\n }\n\n .no-data-description {\n font-size: 12px;\n }\n\n .no-data-guidance {\n font-size: 11px;\n }\n }\n`;function l(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";const i=document.createElement(t);return n&&(i.className=n),e&&(i.textContent=e),i}function d(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:2,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"--";if(null==t||""===t)return n;const i=parseFloat(t);return isNaN(i)?n:i.toFixed(e)}function h(t){if(!t)return"";return String(t).replace(/[^A-Za-z0-9.\-_+ ]/g,"").substring(0,50)}function u(t){if(t)for(;t.firstChild;)t.removeChild(t.firstChild)}function g(t){if(!t||"string"!=typeof t)return{valid:!1,error:"Symbol is required",sanitized:""};const e=t.trim().toUpperCase();return 0===e.length?{valid:!1,error:"Symbol cannot be empty",sanitized:""}:e.length>10?{valid:!1,error:"Symbol too long (max 10 characters)",sanitized:""}:{valid:!0,sanitized:e}}function p(t){if(!t||"string"!=typeof t)return{valid:!1,error:"Option symbol is required",sanitized:""};const e=t.trim().toUpperCase();if(e.length<15||e.length>21)return{valid:!1,error:"Invalid option symbol length",sanitized:e};const n=e.match(/^([A-Z]{1,6})(\d{2})(0[1-9]|1[0-2])([0-2][0-9]|3[01])([CP])(\d{8})$/);if(!n)return{valid:!1,error:"Invalid option symbol format. Expected: SYMBOL+YYMMDD+C/P+STRIKE",sanitized:e};const[,i,s,o,a,r,c]=n,l=2e3+parseInt(s,10),d=parseInt(o,10),h=parseInt(a,10),u=new Date(l,d-1,h);if(u.getMonth()+1!==d||u.getDate()!==h)return{valid:!1,error:"Invalid expiration date in option symbol",sanitized:e};const g=new Date;g.setHours(0,0,0,0),u<g&&console.warn("Option symbol has expired");return{valid:!0,sanitized:e,parsed:{underlying:i,expiry:u,type:"C"===r?"call":"put",strike:parseInt(c,10)/1e3}}}function m(t){if(!t)return"ET";const[e,n,i]=t.split("-").map(Number),s=new Date(e,n-1,i),o=function(t,e){const n=new Date(t,e,1),i=1+(7-n.getDay())%7;return new Date(t,e,i+7)}(e,2),a=function(t,e){const n=new Date(t,e,1),i=1+(7-n.getDay())%7;return new Date(t,e,i)}(e,10);return s>=o&&s<a?"EDT":"EST"}function f(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{includeSeconds:n=!0,includeTimezone:i=!0,format:s="short"}=e;let o;if(t||(t=Date.now()),o=t instanceof Date?t:new Date(t),isNaN(o.getTime()))return"Invalid Date";const a=o.getUTCFullYear(),r=o.getUTCMonth(),c=o.getUTCDate(),l=o.getUTCHours(),d=o.getUTCMinutes(),h=o.getUTCSeconds(),u=function(t){const e=t.getUTCFullYear(),n=t.getUTCMonth(),i=t.getUTCDate(),s=new Date(Date.UTC(e,n,i)),o=function(t,e){const n=new Date(Date.UTC(t,e,1)),i=1+(7-n.getUTCDay())%7;return new Date(Date.UTC(t,e,i+7))}(e,2),a=function(t,e){const n=new Date(Date.UTC(t,e,1)),i=1+(7-n.getUTCDay())%7;return new Date(Date.UTC(t,e,i))}(e,10);return s>=o&&s<a}(o),g=u?-4:-5,p=new Date(Date.UTC(a,r,c,l+g,d,h)),m=p.getUTCFullYear(),f=p.getUTCMonth()+1,b=p.getUTCDate(),y=p.getUTCHours(),x=p.getUTCMinutes(),v=p.getUTCSeconds(),w=y%12||12,S=y>=12?"PM":"AM",k=x.toString().padStart(2,"0"),M=v.toString().padStart(2,"0"),C=n?`${w}:${k}:${M} ${S}`:`${w}:${k} ${S}`,_=i?u?" EDT":" EST":"";if("long"===s){return`${["January","February","March","April","May","June","July","August","September","Oct","November","December"][f-1]} ${b}, ${m}, ${C}${_}`}return`${f}/${b}/${m}, ${C}${_}`}class b extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for MarketDataWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for MarketDataWidget");this.type="marketdata";const i=g(e.symbol);if(!i.valid)throw new Error(`Invalid symbol: ${i.error}`);this.symbol=i.sanitized,this.wsManager=e.wsManager,this.debug=e.debug||!1,this.styled=void 0===e.styled||e.styled,this.data=null,this.isDestroyed=!1,this.unsubscribe=null,this.loadingTimeout=null,this.createWidgetStructure(),this.initializeSymbolEditor(),this.initialize()}createWidgetStructure(){if(this.container.innerHTML='\n <div class="market-data-widget widget">\n <div class="widget-content">\n \x3c!-- Header Section --\x3e\n <div class="widget-header">\n <div class="symbol-section">\n <h1 class="symbol editable-symbol" \n title="Double-click to edit symbol" \n data-original-symbol="">\n </h1>\n <div class="company-info">\n <span class="company-name" \n title="" \n data-tooltip="">\n </span>\n <span class="data-type">STOCK</span>\n </div>\n </div>\n <div class="price-section">\n <div class="current-price"></div>\n <div class="price-change">\n <span class="change-value"></span>\n <span class="change-percent"></span>\n </div>\n </div>\n </div>\n\n \x3c!-- Data Grid Section --\x3e\n <div class="data-grid">\n <div class="data-section">\n <div class="section-title">Quote</div>\n <div class="data-row">\n <span class="data-label">Bid</span>\n <span class="data-value bid-ask-value bid-data"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Ask</span>\n <span class="data-value bid-ask-value ask-data"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Trading Activity</div>\n <div class="data-row">\n <span class="data-label">Volume</span>\n <span class="data-value volume"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Average Volume</span>\n <span class="data-value avg-volume"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Performance</div>\n <div class="data-row">\n <span class="data-label">Previous Close</span>\n <span class="data-value prev-close"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Open</span>\n <span class="data-value open-price"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Day Range</span>\n <span class="data-value range-value day-range"></span>\n </div>\n <div class="data-row">\n <span class="data-label">52-Week Range</span>\n <span class="data-value range-value week-range"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Key Metrics</div>\n <div class="data-row">\n <span class="data-label">Market Cap</span>\n <span class="data-value market-cap"></span>\n </div>\n <div class="data-row">\n <span class="data-label">P/E Ratio</span>\n <span class="data-value pe-ratio"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Dividend Yield</span>\n <span class="data-value dividend-yield"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Beta</span>\n <span class="data-value beta"></span>\n </div>\n </div>\n </div>\n\n \x3c!-- Footer --\x3e\n <div class="widget-footer">\n <span class="last-update"></span>\n <span class="data-source"></span>\n </div>\n </div>\n \n \x3c!-- Loading Overlay --\x3e\n <div class="widget-loading-overlay">\n <div class="loading-content">\n <div class="loading-spinner"></div>\n <span class="loading-text">Loading market data...</span>\n </div>\n </div>\n </div>\n',this.styled){const t=this.container.querySelector(".market-data-widget");t&&t.classList.add("widget-styled")}this.addStyles()}addStyles(){if(!document.querySelector("#market-data-styles")){const t=document.createElement("style");t.id="market-data-styles",t.textContent=r,document.head.appendChild(t)}}initializeSymbolEditor(){this.symbolEditor=new s(this,{maxLength:10,placeholder:"Enter symbol...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,symbolType:"quotel1"});const t=this.container.querySelector(".symbol");t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol)}async handleSymbolChange(t){this.debug&&console.log("[MarketDataWidget] Symbol change requested:",t);const e=g(t);if(!e.valid)return this.debug&&console.log("[MarketDataWidget] Invalid symbol:",e.error),{success:!1,error:e.error};const n=e.sanitized;if(n===this.symbol)return this.debug&&console.log("[MarketDataWidget] Same symbol, no change needed"),{success:!0};try{const t=this.symbol;return this.showLoading(),this.loadingTimeout&&this.clearTimeout(this.loadingTimeout),this.loadingTimeout=this.setTimeout((()=>{this.debug&&console.log("[MarketDataWidget] Loading timeout for symbol:",n),this.hideLoading(),this.showError(`No data received for ${n}. Please try again.`)}),1e4),this.unsubscribe&&(this.debug&&console.log(`[MarketDataWidget] Unsubscribing from ${t}`),this.wsManager.sendUnsubscribe("queryl1",t),this.unsubscribe(),this.unsubscribe=null),this.symbol=n,this.debug&&console.log(`[MarketDataWidget] Subscribing to ${n}`),this.subscribeToData(),this.debug&&console.log(`[MarketDataWidget] Successfully changed symbol from ${t} to ${n}`),{success:!0}}catch(t){return console.error("[MarketDataWidget] Error changing symbol:",t),this.hideLoading(),this.showError(`Failed to load data for ${n}`),{success:!1,error:`Failed to load ${n}`}}}async initialize(){this.showLoading(),await this.validateInitialSymbol(),this.subscribeToData()}async validateInitialSymbol(){try{const t=this.wsManager.getApiService();if(!t)return void(this.debug&&console.log("[MarketDataWidget] 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.initialValidationData=t,this.debug&&console.log("[MarketDataWidget] Initial symbol validated:",{symbol:this.symbol,companyName:t.comp_name,exchangeName:t.market_name})}}catch(t){this.debug&&console.warn("[MarketDataWidget] Initial symbol validation failed:",t)}}subscribeToData(){this.unsubscribe=this.wsManager.subscribe(this.widgetId,["queryl1"],this.handleMessage.bind(this),this.symbol)}handleData(t){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),"error"===t.type&&t.noData)return this.debug&&console.log("[MarketDataWidget] Received no data message:",t.message),void(this.data?(this.hideLoading(),this.debug&&console.log("[MarketDataWidget] No new data, keeping cached data visible")):this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:t.message}));if("error"!==t.type){if("object"==typeof t&&t.Message){const e=t.Message.toLowerCase();if(e.includes("no data")||e.includes("not found"))return this.debug&&console.log("[MarketDataWidget] Received no data message:",t.Message),void(this.data?(this.hideLoading(),this.debug&&console.log("[MarketDataWidget] No new data, keeping cached data visible")):this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:t.Message}))}if(t.Data&&t.Data.length>0){const e=t.Data.find((t=>t.Symbol===this.symbol));if(e){const t=new i(e);this.data=t,this.updateWidget(t)}else this.debug&&console.log("[MarketDataWidget] No data for symbol in response, keeping cached data"),this.hideLoading()}t._cached&&this.showConnectionQuality()}else{const e=t.message||"Server error";this.data?(this.hideLoading(),this.debug&&console.log("[MarketDataWidget] Error received but keeping cached data:",e)):this.showError(e)}}showNoDataState(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!this.isDestroyed)try{this.hideLoading(),this.clearError();const e=t.Symbol||this.symbol,n=this.container.querySelector(".symbol");n&&(n.textContent=e);const i=this.container.querySelector(".company-name");i&&(i.textContent=`${e} Inc`);const s=this.container.querySelector(".current-price");s&&(s.textContent="$0.00");const o=this.container.querySelector(".price-change");if(o){const t=o.querySelector(".change-value"),e=o.querySelector(".change-percent");t&&(t.textContent="+0.00"),e&&(e.textContent=" (0.00%)"),o.classList.remove("positive","negative"),o.classList.add("neutral")}const a=this.container.querySelector(".widget-header");a&&a.classList.add("dimmed"),this.showNoDataMessage(e,t);const r=this.container.querySelector(".last-update");if(r){const t=f();r.textContent=`Checked: ${t}`}const c=this.container.querySelector(".data-source");c&&(c.textContent="Source: No data available")}catch(t){console.error("Error showing no data state:",t),this.showError("Error displaying no data state")}}showNoDataMessage(t){const e=this.container.querySelector(".data-grid");e&&(e.style.display="none");const n=this.container.querySelector(".no-data-state");n&&n.remove();const i=l("div","","no-data-state"),s=l("div","","no-data-content"),o=document.createElement("div");o.className="no-data-icon",o.innerHTML='<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#9ca3af" stroke-width="2"/>\n <path d="M12 8v4" stroke="#9ca3af" stroke-width="2" stroke-linecap="round"/>\n <circle cx="12" cy="16" r="1" fill="#9ca3af"/>\n </svg>',s.appendChild(o);const a=l("h3","No Market Data","no-data-title");s.appendChild(a);const r=l("p","","no-data-description");r.appendChild(document.createTextNode("Market data for "));const c=l("strong",h(t));r.appendChild(c),r.appendChild(document.createTextNode(" was not found")),s.appendChild(r);const d=l("p","Please check the symbol spelling or try a different symbol","no-data-guidance");s.appendChild(d),i.appendChild(s);const u=this.container.querySelector(".widget-footer");u&&u.parentNode?u.parentNode.insertBefore(i,u):this.container.appendChild(i)}updateWidget(t){if(!this.isDestroyed)try{this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.clearError(),this.hideLoading();const e=this.container.querySelector(".widget-header");e&&e.classList.remove("dimmed");const n=this.container.querySelector(".data-grid");n&&(n.style.display="");const i=this.container.querySelector(".no-data-state");i&&i.remove();const s=this.container.querySelector(".symbol");s&&(s.textContent=t.symbol,s.dataset.originalSymbol=t.symbol);const o=this.container.querySelector(".company-name");if(o){const e=t.companyName||`${t.symbol} Inc`,n=t.companyDescription||`${t.symbol} designs, manufactures, and markets consumer electronics and software.`;o.textContent=e,o.setAttribute("title",n),o.setAttribute("data-tooltip",n)}const a=this.container.querySelector(".current-price");a&&(a.textContent=t.formatPrice());const r=t.getPriceChange(),c=this.container.querySelector(".price-change");if(c){const e=c.querySelector(".change-value"),n=c.querySelector(".change-percent");e&&(e.textContent=t.formatPriceChange()),n&&(n.textContent=` (${t.formatPriceChangePercent()})`),c.classList.remove("positive","negative","neutral"),r>0?c.classList.add("positive"):r<0?c.classList.add("negative"):c.classList.add("neutral")}const h=this.container.querySelector(".bid-data");if(h){u(h);const e=d(t.bidPrice,2,"0.00"),n=t.bidSize||0;h.appendChild(document.createTextNode(`$${e}`));const i=l("span",`× ${n}`,"size-info");h.appendChild(i)}const g=this.container.querySelector(".ask-data");if(g){u(g);const e=d(t.askPrice,2,"0.00"),n=t.askSize||0;g.appendChild(document.createTextNode(`$${e}`));const i=l("span",`× ${n}`,"size-info");g.appendChild(i)}const p=this.container.querySelector(".volume");p&&(p.textContent=t.formatVolume());const m=this.container.querySelector(".avg-volume");m&&(m.textContent=t.formatAverageVolume());const b=this.container.querySelector(".prev-close");b&&(b.textContent=`$${t.previousClose?.toFixed(2)||"0.00"}`);const y=this.container.querySelector(".open-price");y&&(y.textContent=`$${t.openPrice?.toFixed(2)||"0.00"}`);const x=this.container.querySelector(".day-range");x&&(x.textContent=t.formatDayRange());const v=this.container.querySelector(".week-range");v&&(v.textContent=t.format52WeekRange());const w=this.container.querySelector(".market-cap");w&&(w.textContent=t.formatMarketCap());const S=this.container.querySelector(".pe-ratio");S&&(S.textContent=t.peRatio?.toFixed(2)||"N/A");const k=this.container.querySelector(".dividend-yield");k&&(k.textContent=t.dividendYield?`${t.dividendYield.toFixed(2)}%`:"N/A");const M=this.container.querySelector(".beta");M&&(M.textContent=t.beta?.toFixed(2)||"N/A");const C=f(t.quoteTime),_=this.container.querySelector(".last-update");_&&(_.textContent=`Last update: ${C}`);const D=this.container.querySelector(".data-source");D&&(D.textContent=`Source: ${t.getDataSource()}`)}catch(t){console.error("Error updating widget:",t),this.showError("Error updating data")}}showLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.remove("hidden")}hideLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.add("hidden")}showError(t){this.hideLoading(),this.clearError();const e=document.createElement("div");e.className="widget-error",e.textContent=`Error: ${t}`,e.style.cssText="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 15px 20px;\n background: #fee;\n border: 1px solid #f5c6cb;\n border-radius: 6px;\n color: #721c24;\n text-align: center;\n font-size: 14px;\n z-index: 100;\n ",this.container.appendChild(e)}clearError(){const t=this.container.querySelector(".widget-error"),e=this.container.querySelector(".widget-error-container");t&&t.remove(),e&&e.remove()}destroy(){this.isDestroyed=!0,this.loadingTimeout&&(this.clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.symbolEditor&&(this.symbolEditor.destroy(),this.symbolEditor=null),super.destroy()}}class y{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.symbol=t.Symbol||"",this.marketName=t.MarketName||("bruce"===t.Source?.toLowerCase()?"Bruce":"Blue Ocean"),this.marketDesc=null!==t.MarketDesc?t.MarketDesc||"":null,this.countryCode=null!==t.CountryCode?t.CountryCode||"":null,this.volume=null!==t.Volume?t.Volume||0:null,this.askPrice=null!==t.AskPx?t.AskPx||0:null,this.askSize=null!==t.AskSz?t.AskSz||0:null,this.bidPrice=null!==t.BidPx?t.BidPx||0:null,this.bidSize=null!==t.BidSz?t.BidSz||0:null,this.lastPrice=null!==t.LastPx?t.LastPx||0:null,this.quoteTime=null!==t.QuoteTime?t.QuoteTime:null,this.activityTimestamp=null!==t.ActivityTimestamp?t.ActivityTimestamp||"":null,this.tradeSize=null!==t.TradeSz?t.TradeSz||0:null,this.bidExchange=null!==t.BidExch?t.BidExch||"":null,this.askExchange=null!==t.AskExch?t.AskExch||"":null,this.notFound=t.NotFound||!1,this.source=t.Source||"",this.accuAmount=null!==t.AccuAmmount?t.AccuAmmount||0:null,this.change=null!==t.Change?t.Change||0:null,this.changePercent=null!==t.ChangePercent?t.ChangePercent||0:null,this.closingPrice=null!==t.ClosingPx?t.ClosingPx:null!==t.PreviousClose?t.PreviousClose:null,this.highPrice=null!==t.HighPx?t.HighPx||0:null,this.lowPrice=null!==t.LowPx?t.LowPx||0:null,this.openPrice=null!==t.OpenPx?t.OpenPx||0:null,this.tradePrice=null!==t.TradePx?t.TradePx||0:null,this.tradeRegionName=null!==t.TradeRegionName?t.TradeRegionName||"":null,this.companyName=t.comp_name||t.CompanyName||"",this.exchangeName=t.market_name||t.Exchange||"",this.mic=t.mic||""}formatPrice(){return`$${this.lastPrice.toFixed(2)}`}formatBidAsk(){return`$${this.bidPrice.toFixed(2)} × ${this.bidSize} / $${this.askPrice.toFixed(2)} × ${this.askSize}`}formatVolume(){return this.volume.toLocaleString()}formatPriceChange(){if(null===this.change||void 0===this.change)return"--";return`${this.change>0?"+":""}${this.change.toFixed(2)}`}formatPriceChangePercent(){if(null===this.changePercent||void 0===this.changePercent)return"--";return`${this.changePercent>0?"+":""}${this.getPriceChangePercent().toFixed(2)}%`}getDataSource(){return"blueocean-d"===this.source.toLowerCase()||"bruce-d"===this.source.toLowerCase()?"bruce-d"===this.source.toLowerCase()?"Bruce 20 mins delayed":"BlueOcean 20 mins delayed":this.source.toLowerCase().includes("bruce")||this.source.toLowerCase().includes("blueocean")||this.source.toLowerCase().includes("onbbo")?"bruce"===this.source.toLowerCase()?"Bruce Real-time":"onbbo"===this.source.toLowerCase()?"ONBBO Real-time":"BlueOcean Real-time":null!==this.source&&""!==this.source?this.source:"MDAS"}getPriceChangePercent(){return null===this.changePercent||void 0===this.changePercent?0:Math.abs(this.changePercent)>1?this.changePercent:100*this.changePercent}}class x extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for NightSessionWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for NightSessionWidget");if(!e.source||"blueocean"!==e.source.toLowerCase()&&"bruce"!==e.source.toLowerCase()&&"onbbo"!==e.source.toLowerCase())throw new Error('Source should be either "blueocean", "bruce", or "onbbo"');this.type="nightsession "+e.source;const i=g(e.symbol);if(!i.valid)throw new Error(`Invalid symbol: ${i.error}`);this.symbol=i.sanitized,this.source=e.source||"blueocean",this.wsManager=e.wsManager,this.debug=e.debug||!1,this.styled=void 0===e.styled||e.styled,this.data=null,this.isDestroyed=!1,this.unsubscribe=null,this.symbolEditor=null,this.loadingTimeout=null,this.companyName="",this.exchangeName="",this.mic="",this.createWidgetStructure(),this.initialize()}createWidgetStructure(){if(this.container.innerHTML='\n <div class="night-session-widget widget">\n <div class="widget-content">\n \x3c!-- Header Section --\x3e\n <div class="widget-header">\n <div class="symbol-section">\n <div class="company-market-info">\n <span class="night-company-name"></span>\n <span class="market-name"></span>\n </div>\n <h1 class="symbol editable-symbol"\n title="Double-click to edit symbol"\n data-original-symbol="">\n </h1>\n\n </div>\n <div class="price-section">\n <div class="current-price"></div>\n <div class="price-change">\n <span class="change-value"></span>\n <span class="change-percent"></span>\n </div>\n <div class="price-metadata">\n <div class="last-update"></div>\n <div class="data-source"></div>\n </div>\n </div>\n </div>\n\n \x3c!-- Data Grid Section --\x3e\n <div class="data-grid">\n <div class="data-section">\n <div class="section-title">Quote</div>\n <div class="data-row">\n <span class="data-label">Bid</span>\n <span class="data-value bid-ask-value">\n <span class="bid-price"></span>\n <span class="size-info bid-size"></span>\n </span>\n </div>\n <div class="data-row">\n <span class="data-label">Ask</span>\n <span class="data-value bid-ask-value">\n <span class="ask-price"></span>\n <span class="size-info ask-size"></span>\n </span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Trading Activity</div>\n <div class="data-row">\n <span class="data-label">Volume</span>\n <span class="data-value volume"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Accumulated Amount</span>\n <span class="data-value accu-amount"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Performance</div>\n <div class="data-row">\n <span class="data-label">Previous Close</span>\n <span class="data-value previous-close"></span>\n </div>\n <div class="data-row at-close-row">\n <span class="data-label">At close</span>\n <span class="data-value at-close-time"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Open</span>\n <span class="data-value open-price"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Day Range</span>\n <span class="data-value range-value day-range"></span>\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- Loading Overlay --\x3e\n <div class="widget-loading-overlay">\n <div class="loading-content">\n <div class="loading-spinner"></div>\n <span class="loading-text">Loading night session data...</span>\n </div>\n </div>\n </div>\n',this.styled){const t=this.container.querySelector(".night-session-widget");t&&t.classList.add("widget-styled")}this.addStyles(),this.setupSymbolEditor()}addStyles(){if(!document.querySelector("#night-session-styles")){const t=document.createElement("style");t.id="night-session-styles",t.textContent=a,document.head.appendChild(t)}}setupSymbolEditor(){this.symbolEditor=new s(this,{maxLength:10,placeholder:"Enter symbol...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,cancelOnBlur:!1,symbolType:"nightsession"})}async handleSymbolChange(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;this.debug&&(console.log("[NightSessionWidget] Symbol change requested:",t),console.log("[NightSessionWidget] Validation data:",n));const i=g(t);if(!i.valid)return this.debug&&console.log("[NightSessionWidget] 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("[NightSessionWidget] Extracted company info:",{companyName:this.companyName,exchangeName:this.exchangeName},n)),s===this.symbol)return this.debug&&console.log("[NightSessionWidget] Same symbol, no change needed"),{success:!0};try{const t=this.symbol;return this.showLoading(),this.loadingTimeout&&this.clearTimeout(this.loadingTimeout),this.loadingTimeout=this.setTimeout((()=>{this.debug&&console.log("[NightSessionWidget] Loading timeout for symbol:",s),this.hideLoading(),this.showError(`No data received for ${s}. Please try again.`)}),1e4),this.unsubscribe&&(this.debug&&console.log(`[NightSessionWidget] Unsubscribing from ${t}`),this.unsubscribe(),this.unsubscribe=null),this.symbol=s,this.debug&&console.log(`[NightSessionWidget] Subscribing to ${s}`),this.subscribeToData(),this.debug&&console.log(`[NightSessionWidget] Successfully changed symbol from ${t} to ${s}`),{success:!0}}catch(t){return console.error("[NightSessionWidget] Error changing symbol:",t),this.showError(`Failed to load data for ${s}`),{success:!1,error:`Failed to load ${s}`}}}async initialize(){this.showLoading(),await this.validateInitialSymbol(),this.loadingTimeout=setTimeout((()=>{this.debug&&console.log("[NightSessionWidget] Loading timeout - no data received for:",this.symbol),this.hideLoading(),this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:"No data available for this symbol"})}),1e4),this.subscribeToData()}async validateInitialSymbol(){try{const t=this.wsManager.getApiService();if(!t)return void(this.debug&&console.log("[NightSessionWidget] 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("[NightSessionWidget] Initial symbol validated:",{symbol:this.symbol,companyName:this.companyName,exchangeName:this.exchangeName,mic:this.mic})}}catch(t){this.debug&&console.warn("[NightSessionWidget] Initial symbol validation failed:",t)}}subscribeToData(){let t;t="bruce"===this.source?"querybrucel1":"onbbo"===this.source?"queryonbbol1":"queryblueoceanl1",this.unsubscribe=this.wsManager.subscribe(this.widgetId,[t],this.handleMessage.bind(this),this.symbol)}handleData(t){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),"error"===t.type&&t.noData)return this.debug&&console.log("[NightSessionWidget] Received no data message:",t.message),void(this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] No new data, keeping cached data visible")):this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:t.message}));if("error"!==t.type){if(Array.isArray(t)){const e=t.find((t=>t.Symbol===this.symbol));if(e){if(this.debug&&console.log("[NightSessionWidget] Found data for symbol:",e),!0===e.NotFound)this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] No new data, keeping cached data visible")):this.showNoDataState(e);else{const t=new y(e);this.data=t,this.updateWidget(t)}return}this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] No matching data in response, keeping cached data")):this.showNoDataState({Symbol:this.symbol,NotFound:!0})}else if("queryblueoceanl1"===t.type||"querybrucel1"===t.type||"queryonbbol1"===t.type)if(t[0]?.Symbol===this.symbol){const e="queryonbbol1"===t.type;if(!0===t[0].NotFound||!e&&(!t[0].MarketName||"BLUE"!==t[0].MarketName))this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] No new data, keeping cached data visible")):this.showNoDataState(t[0]);else{const e=new y(t[0]);this.data=e,this.updateWidget(e)}}else this.debug&&console.log("[NightSessionWidget] No matching symbol in response, keeping cached data"),this.hideLoading();t._cached}else{const e=t.message||"Server error";e.toLowerCase().includes("access")||e.toLowerCase().includes("permission")||e.toLowerCase().includes("denied")?this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] Access error but keeping cached data")):this.showNoDataState({Symbol:this.symbol,NotFound:!0,isAccessError:!0,message:e}):this.data?(this.hideLoading(),this.debug&&console.log("[NightSessionWidget] Error received but keeping cached data:",e)):(console.log("errorMsg",e),this.showError(e))}}updateWidget(t){if(!this.isDestroyed)try{this.hideLoading(),this.clearError();const e=this.container.querySelector(".widget-header");e&&e.classList.remove("dimmed");const n=this.container.querySelector(".price-section");n&&n.classList.remove("dimmed");const i=this.container.querySelector(".data-grid");i&&(i.style.display="grid");const s=this.container.querySelector(".no-data-state");s&&s.remove();const o=this.container.querySelector(".symbol");o&&(o.textContent=t.symbol);const a=this.container.querySelector(".night-company-name");a&&(a.textContent=this.companyName||t.companyName||"");const r=this.container.querySelector(".market-name");r&&(r.textContent=this.exchangeName||t.exchangeName||"");const c=this.container.querySelector(".current-price");c&&(c.textContent=null!==t.lastPrice?t.formatPrice():"--");const l=t.change,d=this.container.querySelector(".price-change");if(d){const e=d.querySelector(".change-value"),n=d.querySelector(".change-percent");e&&(e.textContent=null!==l&&0!==l?l:"--"),n&&(0!==t.changePercent?n.textContent=` (${t.getPriceChangePercent().toFixed(2)}%)`:n.textContent=" (0.00%)"),d.classList.remove("positive","negative","neutral"),l>0?d.classList.add("positive"):l<0?d.classList.add("negative"):d.classList.add("neutral")}const h=this.container.querySelector(".bid-price");h&&(h.textContent=null!==t.bidPrice?`$${t.bidPrice.toFixed(2)} `:"-- ×");const u=this.container.querySelector(".bid-size");u&&(u.textContent=null!==t.bidSize?`× ${t.bidSize}`:"--");const g=this.container.querySelector(".ask-price");g&&(g.textContent=null!==t.askPrice?`$${t.askPrice.toFixed(2)} `:"-- ×");const p=this.container.querySelector(".ask-size");p&&(p.textContent=null!==t.askSize?`× ${t.askSize}`:"--");const m=this.container.querySelector(".volume");m&&(m.textContent=null!==t.volume?t.formatVolume():"--");const b=this.container.querySelector(".accu-amount");b&&(b.textContent=null!==t.accuAmount?`$${t.accuAmount.toLocaleString()}`:"--");const y=this.container.querySelector(".open-price");y&&(y.textContent=null!==t.openPrice?`$${t.openPrice.toFixed(2)}`:"--");const x=this.container.querySelector(".previous-close");x&&(x.textContent=null!==t.closingPrice?`$${t.closingPrice.toFixed(2)}`:"--");const v=this.container.querySelector(".at-close-time");if(v){const e=t.quoteTime?f(new Date(t.quoteTime)):"--";v.textContent=e}const w=this.container.querySelector(".day-range");w&&(null!==t.highPrice&&null!==t.lowPrice?w.textContent=`$${t.lowPrice.toFixed(2)} - $${t.highPrice.toFixed(2)}`:w.textContent="--");const S=this.container.querySelector(".data-source");S&&(S.textContent="Source: "+t.getDataSource());const k=(t=>{if(!t)return f();const e=new Date(t);return isNaN(e.getTime())?f():f(e)})(t.quoteTime),M=this.container.querySelector(".last-update");M&&(M.textContent=`Overnight: ${k}`)}catch(t){console.error("Error updating widget:",t),this.showError("Error updating data")}}showLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.remove("hidden")}hideLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.add("hidden")}showError(t){this.hideLoading(),this.clearError();const e=document.createElement("div");e.className="widget-error",e.textContent=`Error: ${t}`,e.style.cssText="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 15px 20px;\n background: #fee;\n border: 1px solid #f5c6cb;\n border-radius: 6px;\n color: #721c24;\n text-align: center;\n font-size: 14px;\n z-index: 100;\n ",this.container.appendChild(e)}clearError(){const t=this.container.querySelector(".widget-error"),e=this.container.querySelector(".widget-error-container");t&&t.remove(),e&&e.remove()}showNoDataState(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!this.isDestroyed)try{this.hideLoading(),this.clearError();const e=t.Symbol||this.symbol,n=this.container.querySelector(".symbol");n&&(n.textContent=e);const i=this.container.querySelector(".current-price");i&&(i.textContent="$0.00");const s=this.container.querySelector(".price-change");if(s){const t=s.querySelector(".change-value");t&&(t.textContent="+0.00");const e=s.querySelector(".change-percent");e&&(e.textContent=" (0.00%)"),s.classList.remove("positive","negative")}const o=this.container.querySelector(".widget-header");o&&o.classList.add("dimmed");const a=this.container.querySelector(".price-section");a&&a.classList.add("dimmed"),this.showNoDataMessage(e,t);const r=f(),c=this.container.querySelector(".last-update");c&&(c.textContent=`Overnight: ${r}`)}catch(t){console.error("Error showing no data state:",t),this.showError("Error displaying no data state")}}showNoDataMessage(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n=this.container.querySelector(".data-grid");n&&(n.style.display="none");const i=this.container.querySelector(".no-data-state");i&&i.remove();const s=e.isAccessError||e.message&&e.message.toLowerCase().includes("access"),o=l("div","","no-data-state"),a=l("div","","no-data-content");if(s){const n=document.createElement("div");n.className="no-data-icon error",n.innerHTML='<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#ef4444" stroke-width="2"/>\n <path d="M12 8v4" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/>\n <circle cx="12" cy="16" r="1" fill="#ef4444"/>\n </svg>',a.appendChild(n);const i=l("h3",String(e.message||"Access Denied"),"no-data-title error");a.appendChild(i);const s=l("p","","no-data-description");s.appendChild(document.createTextNode("You do not have permission to view night session data for "));const o=l("strong",h(t));s.appendChild(o),a.appendChild(s);const r=l("p","Contact your administrator or try a different symbol","no-data-guidance");a.appendChild(r)}else{const n=document.createElement("div");n.className="no-data-icon",n.innerHTML='<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#9ca3af" stroke-width="2"/>\n <path d="M12 6v6l4 2" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>',a.appendChild(n);const i=l("h3",String(e.message||"No Data Available"),"no-data-title");a.appendChild(i);const s=l("p","","no-data-description");s.appendChild(document.createTextNode("Night session data for "));const o=l("strong",h(t));s.appendChild(o),s.appendChild(document.createTextNode(" was not found")),a.appendChild(s);const r=l("p","Please check the symbol or try again later","no-data-guidance");a.appendChild(r)}o.appendChild(a);const r=this.container.querySelector(".widget-footer");r&&r.parentNode?r.parentNode.insertBefore(o,r):this.container.appendChild(o)}destroy(){this.isDestroyed=!0,this.loadingTimeout&&(this.clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.symbolEditor&&(this.symbolEditor.destroy(),this.symbolEditor=null),super.destroy()}updateSymbol(t){this.handleSymbolChange(t)}}class v{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.rawData=t,this.symbol=t.Symbol||"",this.rootSymbol=t.RootSymbol||"",this.underlying=t.Underlying||"",this.expire=t.Expire||"",this.strike=t.Strike||0,this.type=this.determineOptionType(),this.lastPrice=t.LastPx||0,this.closePx=t.ClosePx||0,this.yestClosePx=t.YestClosePx||0,this.openPx=t.OpenPx||0,this.highPx=t.HighPx||0,this.lowPx=t.LowPx||0,this.bidPx=t.BidPx||0,this.bidSz=t.BidSz||0,this.bidExch=t.BidExch||"",this.askPx=t.AskPx||0,this.askSz=t.AskSz||0,this.askExch=t.AskExch||"",this.volume=t.Volume||0,this.openInterest=t.OpenInst||0,this.primeShare=t.PrimeShare||0,this.exch=t.Exch||"",this.dataSource=t.DataSource||"",this.quoteTime=t.QuoteTime||"",this.notFound=t.NotFound||!1}determineOptionType(){return this.symbol?this.symbol.includes("C")?"call":this.symbol.includes("P")?"put":"unknown":"unknown"}getChange(){return this.lastPrice-this.yestClosePx}getChangePercent(){return 0===this.yestClosePx?0:(this.lastPrice-this.yestClosePx)/this.yestClosePx*100}getSpread(){return this.askPx-this.bidPx}getSpreadPercent(){const t=(this.askPx+this.bidPx)/2;return 0===t?0:(this.askPx-this.bidPx)/t*100}getMidPrice(){return(this.askPx+this.bidPx)/2}formatPrice(t){return t?t.toFixed(2):"0.00"}formatStrike(){return`$${this.formatPrice(this.strike)}`}formatLastPrice(){return`$${this.formatPrice(this.lastPrice)}`}formatClosePx(){return`$${this.formatPrice(this.closePx)}`}formatYestClosePx(){return`$${this.formatPrice(this.yestClosePx)}`}formatOpenPx(){return`$${this.formatPrice(this.openPx)}`}formatHighPx(){return`$${this.formatPrice(this.highPx)}`}formatLowPx(){return`$${this.formatPrice(this.lowPx)}`}formatBid(){return`$${this.formatPrice(this.bidPx)}`}formatAsk(){return`$${this.formatPrice(this.askPx)}`}formatBidSize(){return this.bidSz.toString()}formatAskSize(){return this.askSz.toString()}formatSpread(){return`$${this.formatPrice(this.getSpread())}`}formatMidPrice(){return`$${this.formatPrice(this.getMidPrice())}`}formatChange(){const t=this.getChange();return`${t>=0?"+":""}${this.formatPrice(t)}`}formatChangePercent(){const t=this.getChangePercent();return`${t>=0?"+":""}${t.toFixed(2)}%`}formatVolume(){return this.volume.toLocaleString()}formatOpenInterest(){return this.openInterest.toLocaleString()}formatPrimeShare(){return this.primeShare.toString()}formatExpirationDate(){if(!this.expire)return"N/A";try{return new Date(this.expire).toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"})}catch(t){return this.expire}}formatQuoteTime(){if(!this.quoteTime)return"N/A";try{return new Date(this.quoteTime).toLocaleString("en-US",{year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"})}catch(t){return this.quoteTime}}formatExchange(){return this.exch||"N/A"}formatDataSource(){return this.dataSource||"N/A"}formatDayRange(){return`$${this.formatPrice(this.lowPx)} - $${this.formatPrice(this.highPx)}`}formatOptionType(){return this.type.toUpperCase()}getDaysToExpiration(){if(!this.expire)return 0;try{const t=new Date(this.expire),e=new Date,n=t.getTime()-e.getTime(),i=Math.ceil(n/864e5);return Math.max(0,i)}catch(t){return 0}}formatDaysToExpiration(){const t=this.getDaysToExpiration();return`${t} day${1!==t?"s":""}`}getMoneyness(t){return t&&0!==this.strike?"call"===this.type?t-this.strike:"put"===this.type?this.strike-t:0:0}isInTheMoney(t){return this.getMoneyness(t)>0}parseSymbol(){return{underlying:this.rootSymbol||"",expirationDate:this.expire||"",type:this.type||"",strikePrice:this.strike||0,fullSymbol:this.symbol||""}}getSummary(){return{symbol:this.symbol,underlying:this.underlying,type:this.formatOptionType(),strike:this.formatStrike(),expiration:this.formatExpirationDate(),lastPrice:this.formatLastPrice(),change:this.formatChange(),changePercent:this.formatChangePercent(),volume:this.formatVolume(),openInterest:this.formatOpenInterest(),bid:this.formatBid(),ask:this.formatAsk(),exchange:this.formatExchange()}}getDataSource(){return"opra-d"===this.dataSource.toLowerCase()?"20 mins delayed":"real-time"===this.dataSource.toLowerCase()?"Real-time":"MDAS Server"}static fromApiResponse(t){return new v(t)}isValid(){return!this.notFound&&this.symbol&&this.strike>0}}class w extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for OptionsWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for OptionsWidget");this.type="options";const i=p(e.symbol);if(!i.valid)throw new Error(`Invalid option symbol: ${i.error}`);this.symbol=i.sanitized,this.wsManager=e.wsManager,this.debug=e.debug||!1,this.styled=void 0===e.styled||e.styled,this.data=null,this.isDestroyed=!1,this.unsubscribe=null,this.loadingTimeout=null,this.createWidgetStructure(),this.initializeSymbolEditor(),this.initialize()}createWidgetStructure(){if(this.container.innerHTML='\n <div class="options-widget widget">\n <div class="widget-content">\n \x3c!-- Header Section with Purple Background --\x3e\n <div class="widget-header">\n <div class="symbol-section">\n <h1 class="symbol editable-symbol" \n title="Double-click to edit symbol" \n data-original-symbol="">\n </h1>\n <div class="option-info">\n <span class="underlying-symbol"></span>\n <span class="data-type">OPTIONS</span>\n </div>\n </div>\n <div class="price-section">\n <div class="current-price"></div>\n <div class="price-change">\n <span class="change-value"></span>\n <span class="change-percent"></span>\n </div>\n </div>\n </div>\n\n \x3c!-- Data Grid Section --\x3e\n <div class="data-grid">\n <div class="data-section">\n <div class="section-title">Quote</div>\n <div class="data-row">\n <span class="data-label">Bid</span>\n <span class="data-value bid-ask-value bid-data"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Ask</span>\n <span class="data-value bid-ask-value ask-data"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Trading Activity</div>\n <div class="data-row">\n <span class="data-label">Volume</span>\n <span class="data-value volume"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Open Interest</span>\n <span class="data-value open-interest"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Performance</div>\n <div class="data-row">\n <span class="data-label">Previous Close</span>\n <span class="data-value yesterday-close"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Open</span>\n <span class="data-value open-price"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Day Range</span>\n <span class="data-value range-value day-range"></span>\n </div>\n </div>\n\n <div class="data-section">\n <div class="section-title">Contract Info</div>\n <div class="data-row">\n <span class="data-label">Strike Price</span>\n <span class="data-value strike-price"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Expiration</span>\n <span class="data-value expiry-date"></span>\n </div>\n <div class="data-row">\n <span class="data-label">Prime Share</span>\n <span class="data-value prime-share"></span>\n </div>\n </div>\n </div>\n\n \x3c!-- Footer --\x3e\n <div class="widget-footer">\n <span class="last-update"></span>\n <span class="data-source"></span>\n </div>\n </div>\n \n \x3c!-- Loading Overlay --\x3e\n <div class="widget-loading-overlay">\n <div class="loading-content">\n <div class="loading-spinner"></div>\n <span class="loading-text">Loading options data...</span>\n </div>\n </div>\n </div>\n',this.styled){const t=this.container.querySelector(".options-widget");t&&t.classList.add("widget-styled")}this.addStyles()}addStyles(){if(!document.querySelector("#options-widget-styles")){const t=document.createElement("style");t.id="options-widget-styles",t.textContent=c,document.head.appendChild(t)}}initializeSymbolEditor(){this.symbolEditor=new s(this,{maxLength:20,placeholder:"Enter option contract...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,symbolType:"optionsl1"});const t=this.container.querySelector(".symbol");t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol)}async handleSymbolChange(t){this.debug&&console.log("[OptionsWidget] Symbol change requested:",t);const e=p(t);if(!e.valid)return this.debug&&console.log("[OptionsWidget] Invalid option symbol:",e.error),{success:!1,error:e.error};const n=e.sanitized;if(n===this.symbol)return this.debug&&console.log("[OptionsWidget] Same symbol, no change needed"),{success:!0};try{const t=this.symbol;return this.showLoading(),this.loadingTimeout&&this.clearTimeout(this.loadingTimeout),this.loadingTimeout=this.setTimeout((()=>{this.debug&&console.log("[OptionsWidget] Loading timeout for symbol:",n),this.hideLoading(),this.showError(`No data received for ${n}. Please try again.`)}),1e4),this.unsubscribe&&(this.debug&&console.log(`[OptionsWidget] Unsubscribing from ${t}`),this.wsManager.sendUnsubscribe("queryoptionl1",t),this.unsubscribe(),this.unsubscribe=null),this.symbol=n,this.debug&&console.log(`[OptionsWidget] Subscribing to ${n}`),this.subscribeToData(),this.debug&&console.log(`[OptionsWidget] Successfully changed symbol from ${t} to ${n}`),{success:!0}}catch(t){return console.error("[OptionsWidget] Error changing symbol:",t),this.showError(`Failed to load data for ${n}`),{success:!1,error:`Failed to load ${n}`}}}async initialize(){this.showLoading(),await this.validateInitialSymbol(),this.subscribeToData()}async validateInitialSymbol(){try{const t=this.wsManager.getApiService();if(!t)return void(this.debug&&console.log("[OptionsWidget] API service not available for initial validation"));const e=await t.quoteOptionl1(this.symbol);if(e&&e[0]&&!e[0].error&&!e[0].not_found){const t=e[0];this.initialValidationData=t,this.debug&&console.log("[OptionsWidget] Initial symbol validated:",{symbol:this.symbol,underlying:t.Underlying||t.RootSymbol})}}catch(t){this.debug&&console.warn("[OptionsWidget] Initial symbol validation failed:",t)}}subscribeToData(){this.unsubscribe=this.wsManager.subscribe(this.widgetId,["queryoptionl1"],this.handleMessage.bind(this),this.symbol)}handleData(t){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),"error"!==t.type){if(t.RootSymbol===this.symbol||t.Underlying===this.symbol||t.Symbol?.includes(this.symbol))if(this.debug&&console.log("[OptionsWidget] Found matching options data for",this.symbol,":",t),!0===t.NotFound)this.data?(this.hideLoading(),this.debug&&console.log("[OptionsWidget] No new data, keeping cached data visible")):this.showNoDataState(t);else{const e=new v(t);this.data=e,this.updateWidget(e)}else if(Array.isArray(t)){const e=t.find((t=>t.RootSymbol===this.symbol||t.Underlying===this.symbol||t.Symbol?.includes(this.symbol)));if(e)if(!0===e.NotFound)this.data?(this.hideLoading(),this.debug&&console.log("[OptionsWidget] No new data, keeping cached data visible")):this.showNoDataState(e);else{const t=new v(e);this.data=t,this.updateWidget(t)}else this.debug&&console.log("[OptionsWidget] No matching data in response, keeping cached data"),this.hideLoading()}t._cached&&this.showConnectionQuality()}else{const e=t.message||"Server error";this.data?(this.hideLoading(),this.debug&&console.log("[OptionsWidget] Error received but keeping cached data:",e)):this.showError(e)}}showNoDataState(t){if(!this.isDestroyed)try{this.hideLoading();const e=t.Symbol||this.symbol,n=this.container.querySelector(".symbol");n&&(n.textContent=e);const i=this.container.querySelector(".underlying-symbol");i&&(i.textContent=t.RootSymbol||this.symbol);const s=this.container.querySelector(".current-price");s&&(s.textContent="$0.00");const o=this.container.querySelector(".price-change");if(o){const t=o.querySelector(".change-value");t&&(t.textContent="+0.00");const e=o.querySelector(".change-percent");e&&(e.textContent=" (0.00%)"),o.classList.remove("positive","negative")}const a=this.container.querySelector(".widget-header");a&&a.classList.add("dimmed"),this.showNoDataMessage(e);const r=f(),c=this.container.querySelector(".last-update");c&&(c.textContent=`Checked: ${r}`)}catch(t){console.error("Error showing no data state:",t),this.showError("Error displaying no data state")}}showNoDataMessage(t){const e=this.container.querySelector(".data-grid"),n=this.container.querySelector(".option-details-section");e&&(e.style.display="none"),n&&(n.style.display="none");const i=this.container.querySelector(".no-data-state");i&&i.remove();const s=l("div","","no-data-state"),o=l("div","","no-data-content"),a=document.createElement("div");a.className="no-data-icon",a.innerHTML='<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#9ca3af" stroke-width="2"/>\n <path d="M12 6v6l4 2" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>',o.appendChild(a);const r=l("h3","No Data Available","no-data-title");o.appendChild(r);const c=l("p","","no-data-description");c.appendChild(document.createTextNode("Data for "));const d=l("strong",String(t));c.appendChild(d),c.appendChild(document.createTextNode(" was not found")),o.appendChild(c);const h=l("p","Please check the symbol or try again later","no-data-guidance");o.appendChild(h),s.appendChild(o);const u=this.container.querySelector(".widget-footer");u&&u.parentNode?u.parentNode.insertBefore(s,u):this.container.appendChild(s)}updateWidget(t){if(!this.isDestroyed)try{this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.hideLoading(),this.clearError();const e=this.container.querySelector(".widget-header");e&&e.classList.remove("dimmed");const n=this.container.querySelector(".data-grid");n&&(n.style.display="grid");const i=this.container.querySelector(".no-data-state");i&&i.remove();const s=this.container.querySelector(".symbol");s&&(s.textContent=t.symbol,s.dataset.originalSymbol=t.symbol);const o=this.container.querySelector(".underlying-symbol");o&&(o.textContent=t.rootSymbol||t.underlying||"QQQ");const a=this.container.querySelector(".current-price");a&&(a.textContent=t.formatLastPrice());const r=t.getChange(),c=this.container.querySelector(".price-change");if(c){const e=c.querySelector(".change-value"),n=c.querySelector(".change-percent");e&&(e.textContent=t.formatChange()),n&&(n.textContent=` (${t.formatChangePercent()})`),c.classList.remove("positive","negative","neutral"),r>0?c.classList.add("positive"):r<0?c.classList.add("negative"):c.classList.add("neutral")}const d=this.container.querySelector(".bid-data");if(d){u(d),d.appendChild(document.createTextNode(t.formatBid()));const e=l("span",`× ${t.bidSz}`,"size-info");d.appendChild(e)}const h=this.container.querySelector(".ask-data");if(h){u(h),h.appendChild(document.createTextNode(t.formatAsk()));const e=l("span",`× ${t.askSz}`,"size-info");h.appendChild(e)}const g=this.container.querySelector(".volume");g&&(g.textContent=t.formatVolume());const p=this.container.querySelector(".open-interest");p&&(p.textContent=t.formatOpenInterest());const m=this.container.querySelector(".day-range");m&&(m.textContent=t.formatDayRange());const b=this.container.querySelector(".open-price");b&&(b.textContent=t.formatOpenPx());const y=this.container.querySelector(".yesterday-close");y&&(y.textContent=t.formatYestClosePx());const x=this.container.querySelector(".strike-price");x&&(x.textContent=t.formatStrike());const v=this.container.querySelector(".expiry-date");v&&(v.textContent=t.formatExpirationDate());const w=this.container.querySelector(".prime-share");w&&(w.textContent=t.formatPrimeShare());const S=f(t.quoteTime||Date.now()),k=this.container.querySelector(".last-update");k&&(k.textContent=`Last update: ${S}`);const M=this.container.querySelector(".data-source");M&&(M.textContent=`Source: ${t.getDataSource()}`)}catch(t){console.error("Error updating options widget:",t),this.showError("Error updating data")}}formatPrice(t){return t?t.toFixed(2):"0.00"}formatPriceChange(t){return`${t>=0?"+":""}${t.toFixed(2)}`}showLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.remove("hidden")}hideLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.add("hidden")}showError(t){this.hideLoading(),this.clearError();const e=document.createElement("div");e.className="widget-error",e.textContent=`Error: ${t}`,e.style.cssText="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 15px 20px;\n background: #fee;\n border: 1px solid #f5c6cb;\n border-radius: 6px;\n color: #721c24;\n text-align: center;\n font-size: 14px;\n z-index: 100;\n ",this.container.appendChild(e)}clearError(){const t=this.container.querySelector(".widget-error"),e=this.container.querySelector(".widget-error-container");t&&t.remove(),e&&e.remove()}destroy(){this.isDestroyed=!0,this.loadingTimeout&&(this.clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.symbolEditor&&(this.symbolEditor.destroy(),this.symbolEditor=null),super.destroy()}}class S{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.openInterest=t.OpenInst||0,this.volume=t.Vol||0,this.askSize=t.AskSz||0,this.bidSize=t.BidSz||0,this.strike=t.Strike||0,this.askPrice=t.AskPx||0,this.bidPrice=t.BidPx||0,this.lastChange=t.LastChg||0,this.lastPrice=t.LastPx||0,this.dataSource=t.DataSource||"",this.expire=t.Expire||"",this.symbol=t.Symbol||""}}const k=`\n${o}\n\n/* Base styles remain the same until responsive section */\n.option-chain-widget {\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n background: white;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 13px;\n max-width: 100%;\n overflow: hidden;\n position: relative;\n}\n\n.option-chain-widget .widget-header {\n background: #f9fafb;\n padding: 16px;\n border-bottom: 1px solid #e5e7eb;\n}\n\n.option-chain-widget .input-section {\n display: flex;\n gap: 12px;\n align-items: center;\n flex-wrap: wrap;\n}\n\n.option-chain-widget .symbol-input,\n.option-chain-widget .date-select {\n padding: 8px 12px;\n border: 1px solid #d1d5db;\n border-radius: 4px;\n font-size: 14px;\n font-family: inherit;\n min-width: 0;\n flex: 1;\n}\n\n.option-chain-widget .symbol-input {\n text-transform: uppercase;\n min-width: 100px;\n max-width: 120px;\n}\n\n.option-chain-widget .date-select {\n min-width: 140px;\n background: white;\n}\n\n.option-chain-widget .fetch-button {\n padding: 8px 16px;\n background: #3b82f6;\n color: white;\n border: none;\n border-radius: 4px;\n font-size: 14px;\n cursor: pointer;\n font-family: inherit;\n white-space: nowrap;\n}\n\n.option-chain-widget .fetch-button:hover {\n background: #2563eb;\n}\n\n.option-chain-widget .fetch-button:disabled {\n background: #9ca3af;\n cursor: not-allowed;\n}\n\n.option-chain-widget .date-select:disabled {\n background: #f3f4f6;\n color: #9ca3af;\n cursor: not-allowed;\n}\n\n.option-chain-widget .option-chain-table {\n max-height: 800px;\n overflow-x: auto; /* Enable horizontal scrolling */\n overflow-y: auto; /* Enable vertical scrolling if needed */\n -webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */\n width: 100%; /* Ensure full width */\n position: relative; /* Establish positioning context */\n box-sizing: border-box; /* Include padding and border in the element's total width and height */\n}\n\n.option-chain-widget .table-header {\n background: #f3f4f6;\n font-weight: 600;\n border-bottom: 2px solid #d1d5db;\n position: sticky;\n top: 0;\n z-index: 10;\n width: fit-content; /* Ensure it spans the full width */\n box-sizing: border-box; /* Include padding and border in the element's total width and height */\n}\n\n.option-chain-widget .price-change.positive::before,\n.option-chain-widget .price-change.negative::before {\n content: none;\n}\n \n\n/* Better approach - make section headers align with actual column structure */\n.option-chain-widget .section-headers,\n.option-chain-widget .column-headers {\n\n display: grid;\n grid-template-columns: minmax(160px, 1fr) repeat(6, minmax(80px, 1fr)) 80px minmax(160px, 1fr) repeat(6, minmax(80px, 1fr));\n border-bottom: 1px solid #d1d5db;\n background: #f3f4f6;\n position: sticky;\n top: 0;\n z-index: 12;\n box-sizing: border-box; /* Include padding and border in the element's total width and height */\n margin: 0;\n padding: 0;\n min-width: fit-content;\n}\n\n.option-chain-widget .calls-header {\n grid-column: 1 / 8; /* Span across first 7 columns */\n padding: 8px;\n text-align: center;\n font-weight: 600;\n font-size: 14px;\n color: #374151;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f3f4f6;\n}\n\n.option-chain-widget .strike-header {\n grid-column: 8 / 9; /* Strike column */\n background: #e5e7eb;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.option-chain-widget .puts-header {\n grid-column: 9 / -1; /* Span from column 9 to the end */\n padding: 8px;\n text-align: center;\n font-weight: 600;\n font-size: 14px;\n color: #374151;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f3f4f6;\n}\n\n.option-chain-widget .column-headers {\n display: grid;\n grid-template-columns: minmax(160px, 1fr) repeat(6, minmax(80px, 1fr)) 80px minmax(160px, 1fr) repeat(6, minmax(80px, 1fr));\n position: sticky;\n top: 40px;\n z-index: 11;\n background: #f3f4f6;\n}\n\n.option-chain-widget .calls-columns,\n.option-chain-widget .puts-columns {\n display: contents; /* Remove their own grid, use parent grid */\n}\n\n.option-chain-widget .strike-column {\n display: flex;\n align-items: center;\n justify-content: center;\n background: #e5e7eb !important;\n font-weight: bold;\n color: #374151;\n font-size: 11px;\n text-transform: uppercase;\n}\n\n.option-chain-widget .strike-column span {\n background: transparent !important; /* Ensure span doesn't override parent background */\n}\n\n.option-chain-widget .column-headers span {\n padding: 6px 2px; /* Reduce padding to prevent text overflow */\n text-align: center;\n font-size: 11px; /* Reduce font size */\n text-transform: uppercase;\n color: #6b7280;\n font-weight: 600;\n letter-spacing: 0.3px; /* Reduce letter spacing */\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n background: #f3f4f6;\n}\n\n.option-chain-widget .option-row {\n display: grid;\n grid-template-columns: minmax(160px, 1fr) repeat(6, minmax(80px, 1fr)) 80px minmax(160px, 1fr) repeat(6, minmax(80px, 1fr));\n border-bottom: 1px solid #f3f4f6;\n min-height: 32px;\n}\n\n.option-chain-widget .option-row:hover {\n background: #f9fafb;\n}\n .option-chain-widget .option-row:hover .calls-data span,\n.option-chain-widget .option-row:hover .puts-data span,\n.option-chain-widget .option-row:hover .strike-data {\n background: #f9fafb;\n}\n\n.option-chain-widget .calls-data,\n.option-chain-widget .puts-data {\n display: contents; /* Make children participate in parent grid */\n}\n\n.option-chain-widget .strike-data {\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f9fafb;\n font-weight: 600; /* Changed from bold to 600 for consistency */\n color: #6b7280; /* Changed from #1f2937 to match other widgets */\n}\n\n.option-chain-widget .calls-data span,\n.option-chain-widget .puts-data span {\n padding: 6px 4px;\n text-align: center;\n font-size: 12px; /* Changed from 12px to 14px */\n font-weight: 400; /* Added to match data values in other widgets */\n color: #111827; /* Changed from #374151 to match other widgets */\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.option-chain-widget .contract-cell {\n font-size: 10px;\n color: #6b7280;\n text-align: left;\n padding: 4px 6px;\n font-family: monospace;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.option-chain-widget .positive {\n color: #059669;\n}\n\n.option-chain-widget .negative {\n color: #dc2626;\n}\n\n.option-chain-widget .neutral {\n color: #6b7280;\n}\n\n.option-chain-widget .widget-footer {\n background: #f9fafb;\n padding: 8px 16px;\n border-top: 1px solid #e5e7eb;\n text-align: center;\n font-size: 11px;\n color: #6b7280;\n}\n\n.option-chain-widget .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.9);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 20;\n}\n\n.option-chain-widget .widget-loading-overlay.hidden {\n display: none;\n}\n\n.option-chain-widget .loading-content {\n text-align: center;\n}\n\n.option-chain-widget .loading-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid #e5e7eb;\n border-top: 3px solid #3b82f6;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin: 0 auto 12px;\n}\n\n@keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n.option-chain-widget .no-data-state {\n padding: 40px;\n text-align: center;\n color: #6b7280;\n}\n\n.option-chain-widget .widget-error {\n padding: 20px;\n background: #fef2f2;\n color: #dc2626;\n border: 1px solid #fecaca;\n margin: 16px;\n border-radius: 4px;\n text-align: center;\n}\n\n/* RESPONSIVE STYLES */\n\n/* Tablet styles (768px and down) */\n@media (max-width: 768px) {\n .option-chain-widget .input-section {\n flex-direction: column;\n align-items: stretch;\n gap: 8px;\n }\n \n .option-chain-widget .symbol-input,\n .option-chain-widget .date-select {\n width: 100%;\n max-width: none;\n }\n \n .option-chain-widget .fetch-button {\n width: 100%;\n }\n \n .option-chain-widget .calls-columns,\n .option-chain-widget .puts-columns {\n grid-template-columns: 120px repeat(6, minmax(50px, 1fr));\n }\n \n .option-chain-widget .calls-data,\n .option-chain-widget .puts-data {\n grid-template-columns: 120px repeat(6, minmax(50px, 1fr));\n }\n \n .option-chain-widget .column-headers span {\n font-size: 10px;\n padding: 6px 2px;\n }\n \n .option-chain-widget .calls-data span,\n .option-chain-widget .puts-data span {\n font-size: 11px;\n padding: 4px 2px;\n }\n \n .option-chain-widget .contract-cell {\n font-size: 9px;\n padding: 4px 4px;\n }\n \n .option-chain-widget .strike-data {\n font-size: 12px;\n }\n\n /* Update responsive breakpoints to match */\n .option-chain-widget .section-headers {\n grid-template-columns: 1fr 80px 1fr;\n background: #f3f4f6;\n width: 100%;\n\n }\n}\n\n/* Mobile styles (480px and down) */\n@media (max-width: 480px) {\n .option-chain-widget {\n font-size: 12px;\n }\n \n .option-chain-widget .widget-header {\n padding: 12px;\n }\n \n .option-chain-widget .section-headers {\n grid-template-columns: 1fr 60px 1fr;\n background: #f3f4f6;\n }\n \n .option-chain-widget .column-headers {\n grid-template-columns: 1fr 60px 1fr;\n }\n \n .option-chain-widget .option-row {\n grid-template-columns: 1fr 60px 1fr;\n min-height: 28px;\n }\n \n .option-chain-widget .calls-columns,\n .option-chain-widget .puts-columns {\n grid-template-columns: 100px repeat(6, minmax(40px, 1fr));\n }\n \n .option-chain-widget .calls-data,\n .option-chain-widget .puts-data {\n grid-template-columns: 100px repeat(6, minmax(40px, 1fr));\n }\n \n .option-chain-widget .column-headers span {\n font-size: 9px;\n padding: 4px 1px;\n }\n \n .option-chain-widget .calls-data span,\n .option-chain-widget .puts-data span {\n font-size: 10px;\n padding: 3px 1px;\n }\n \n .option-chain-widget .contract-cell {\n font-size: 8px;\n padding: 3px 2px;\n }\n \n .option-chain-widget .strike-data {\n font-size: 11px;\n padding: 3px;\n }\n \n \n .option-chain-widget .option-chain-table {\n max-height: 400px;\n }\n \n .option-chain-widget .no-data-state {\n padding: 20px;\n font-size: 12px;\n }\n \n .option-chain-widget .widget-error {\n margin: 8px;\n padding: 12px;\n font-size: 12px;\n }\n}\n\n/* Very small mobile (320px and down) */\n@media (max-width: 320px) {\n .option-chain-widget .section-headers {\n grid-template-columns: 1fr 50px 1fr;\n }\n \n .option-chain-widget .column-headers {\n grid-template-columns: 1fr 50px 1fr;\n }\n \n .option-chain-widget .option-row {\n grid-template-columns: 1fr 50px 1fr;\n }\n \n .option-chain-widget .calls-columns,\n .option-chain-widget .puts-columns {\n grid-template-columns: 80px repeat(6, minmax(35px, 1fr));\n }\n \n .option-chain-widget .calls-data,\n .option-chain-widget .puts-data {\n grid-template-columns: 80px repeat(6, minmax(35px, 1fr));\n }\n \n .option-chain-widget .column-headers span {\n font-size: 8px;\n padding: 3px 1px;\n }\n \n .option-chain-widget .calls-data span,\n .option-chain-widget .puts-data span {\n font-size: 9px;\n padding: 2px 1px;\n }\n \n .option-chain-widget .contract-cell {\n font-size: 7px;\n padding: 2px 1px;\n }\n}\n`;class M extends n{constructor(t,e,n){if(super(t,e,n),!e.wsManager)throw new Error("WebSocketManager is required for OptionChainWidget");if(this.type="optionchain",this.wsManager=e.wsManager,this.debug=e.debug||!1,this.data=null,this.isDestroyed=!1,this.unsubscribe=null,this.loadingTimeout=null,e.symbol){const t=g(e.symbol);t.valid?this.symbol=t.sanitized:(console.warn("[OptionChainWidget] Invalid initial symbol:",t.error),this.symbol="")}else this.symbol="";this.date="",this.availableDates={},this.fetchRateLimiter=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:500,e=0;return()=>{const n=Date.now(),i=n-e;return i<t?{valid:!1,error:`Please wait ${Math.ceil((t-i)/1e3)} seconds`,remainingMs:t-i}:(e=n,{valid:!0})}}(1e3),this.createWidgetStructure(),this.initialize()}createWidgetStructure(){this.container.innerHTML='\n <div class="option-chain-widget widget">\n <div class="widget-content">\n \x3c!-- Header Section --\x3e\n <div class="widget-header">\n <div class="input-section">\n <input type="text" class="symbol-input" placeholder="Enter Symbol" value="" />\n <select class="date-select" disabled>\n <option value="">Select a date...</option>\n </select>\n <button class="fetch-button" disabled>Search</button>\n </div>\n </div>\n\n \x3c!-- Data Grid Section --\x3e\n <div class="option-chain-table">\n <div class="table-header">\n <div class="section-headers">\n <div class="calls-header">Calls</div>\n <div class="strike-header"></div>\n <div class="puts-header">Puts</div>\n </div>\n <div class="column-headers">\n <div class="calls-columns">\n <span>Contract</span>\n <span>Last</span>\n <span>Change</span>\n <span>Bid</span>\n <span>Ask</span>\n <span>Volume</span>\n <span>Open Int.</span>\n </div>\n <div class="strike-column">\n <span>Strike</span>\n </div>\n <div class="puts-columns">\n <span>Contract</span>\n <span>Last</span>\n <span>Change</span>\n <span>Bid</span>\n <span>Ask</span>\n <span>Volume</span>\n <span>Open Int.</span>\n </div>\n </div>\n </div>\n <div class="option-chain-data-grid">\n \x3c!-- Option chain data will be populated here --\x3e\n </div>\n </div>\n\n \x3c!-- Footer --\x3e\n <div class="widget-footer">\n <span class="last-update"></span>\n <span class="data-source"></span>\n </div>\n </div>\n \n \x3c!-- Loading Overlay --\x3e\n <div class="widget-loading-overlay hidden">\n <div class="loading-content">\n <div class="loading-spinner"></div>\n <span class="loading-text">Loading option chain data...</span>\n </div>\n </div>\n </div>\n',this.addStyles(),this.setupEventListeners()}addStyles(){if(!document.querySelector("#option-chain-styles")){const t=document.createElement("style");t.id="option-chain-styles",t.textContent=k,document.head.appendChild(t)}}setupEventListeners(){this.symbolInput=this.container.querySelector(".symbol-input"),this.dateSelect=this.container.querySelector(".date-select"),this.fetchButton=this.container.querySelector(".fetch-button"),this.dataGrid=this.container.querySelector(".option-chain-data-grid"),this.symbol&&(this.symbolInput.value=this.symbol,this.fetchButton.disabled=!1,this.dateSelect.innerHTML='<option value="">Click search to load dates...</option>'),this.addEventListener(this.symbolInput,"input",(t=>{const e=t.target.value,n=g(e);this.clearInputError(this.symbolInput),n.valid?(this.symbol=n.sanitized,this.symbolInput.value=n.sanitized,this.dateSelect.innerHTML='<option value="">Click search to load dates...</option>',this.dateSelect.disabled=!0,this.fetchButton.disabled=!1,this.availableDates={},this.date=""):""!==e.trim()?(this.showInputError(this.symbolInput,n.error),this.fetchButton.disabled=!0):this.clearDateOptions()})),this.addEventListener(this.dateSelect,"change",(t=>{const e=t.target.value;if(!e)return void(this.date="");const n=function(t){if(!t||"string"!=typeof t)return{valid:!1,error:"Date is required",normalized:""};const e=t.trim();let n;if(/^\d{4}-\d{2}-\d{2}$/.test(e))n=new Date(e);else if(/^\d{2}\/\d{2}\/\d{4}$/.test(e)){const[t,i,s]=e.split("/");n=new Date(s,parseInt(t,10)-1,parseInt(i,10))}else{if(!/^\d+$/.test(e))return{valid:!1,error:"Invalid date format. Use YYYY-MM-DD or MM/DD/YYYY",normalized:""};n=new Date(parseInt(e,10))}return isNaN(n.getTime())?{valid:!1,error:"Invalid date",normalized:""}:{valid:!0,normalized:n.toISOString().split("T")[0]}}(e);n.valid?(this.date=n.normalized,this.symbol&&this.fetchOptionChain()):(this.showError(n.error),this.date="")})),this.addEventListener(this.fetchButton,"click",(()=>{const t=g(this.symbol);if(!t.valid)return void this.showError(t.error||"Please enter a valid symbol first");const e=this.fetchRateLimiter();e.valid?this.loadAvailableDates(t.sanitized):this.showError(e.error)}))}async loadAvailableDates(t){if(t)try{this.symbol=t,this.symbolInput.value=t,this.dateSelect.disabled=!0,this.dateSelect.innerHTML='<option value="">Loading dates...</option>',this.fetchButton.disabled=!0;const e=this.wsManager.getApiService(),n=await e.getOptionChainDates(t);console.log("Available dates:",n.dates_dictionary),this.availableDates=n.dates_dictionary||{},this.populateDateOptions()}catch(e){console.error("[OptionChainWidget] Error loading dates:",e),this.dateSelect.innerHTML='<option value="">Error loading dates</option>',this.fetchButton.disabled=!1,this.showError(`Failed to load dates for ${t}: ${e.message}`)}}populateDateOptions(){this.dateSelect.innerHTML='<option value="">Select expiration date...</option>';const t=Object.keys(this.availableDates).sort();if(0===t.length)return this.dateSelect.innerHTML='<option value="">No dates available</option>',void(this.fetchButton.disabled=!1);t.forEach((t=>{const e=document.createElement("option");e.value=t;const n=this.availableDates[t],i=function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{timezone:n="ET",format:i="short"}=e;if(!t||"string"!=typeof t)return"";const s=t.split("-");if(3!==s.length)return t;const[o,a,r]=s,c=parseInt(a,10)-1,l=parseInt(r,10),d=("long"===i?["January","February","March","April","May","June","July","August","September","October","November","December"]:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"])[c];return`${d} ${l}, ${o}`}(t,{timezone:m(t),format:"short"});e.textContent=i,e.className=`date-option ${n}`,this.dateSelect.appendChild(e)})),this.dateSelect.disabled=!1,this.fetchButton.disabled=!1,t.length>0&&(this.dateSelect.value=t[0],this.date=t[0],this.fetchOptionChain())}clearDateOptions(){this.dateSelect.innerHTML='<option value="">Click fetch to load dates...</option>',this.dateSelect.disabled=!0,this.fetchButton.disabled=!this.symbol,this.symbol=this.symbolInput.value.trim().toUpperCase()||"",this.date="",this.availableDates={}}initialize(){this.hideLoading(),this.symbol&&this.loadAvailableDates(this.symbol)}fetchOptionChain(){if(this.symbol&&this.date)try{this.showLoading(),this.loadingTimeout&&this.clearTimeout(this.loadingTimeout),this.loadingTimeout=this.setTimeout((()=>{this.debug&&console.log("[OptionChainWidget] Loading timeout for:",this.symbol,this.date),this.hideLoading(),this.showError(`No data received for ${this.symbol} on ${this.date}. Please try again.`)}),1e4),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.subscribeToData()}catch(t){console.error("[OptionChainWidget] Error fetching option chain:",t),this.showError(`Failed to load data for ${this.symbol}`)}else console.warn("[OptionChainWidget] Missing symbol or date")}subscribeToData(){this.unsubscribe=this.wsManager.subscribe(this.widgetId,["queryoptionchain"],this.handleMessage.bind(this),this.symbol,{date:this.date})}handleData(t){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),"error"!==t.type)Array.isArray(t)?0===t.length?this.data?(this.hideLoading(),this.debug&&console.log("[OptionChainWidget] No new data, keeping cached data visible")):this.showNoDataState():(this.data=t,this.displayOptionChain(t)):"queryoptionchain"===t.type&&Array.isArray(t.data)&&(0===t.data.length?this.data?(this.hideLoading(),this.debug&&console.log("[OptionChainWidget] No new data, keeping cached data visible")):this.showNoDataState():(this.data=t.data,this.displayOptionChain(t.data))),t._cached&&this.showConnectionQuality();else{const e=t.message||"Server error";this.data?(this.hideLoading(),this.debug&&console.log("[OptionChainWidget] Error received but keeping cached data:",e)):this.showError(e)}}displayOptionChain(t){if(!this.isDestroyed)try{this.hideLoading(),this.clearError(),this.dataGrid.innerHTML="";const e={};t.forEach((t=>{const n=new S(t),i=n.strike;e[i]||(e[i]={call:null,put:null});"call"===C(n.symbol)?e[i].call=n:e[i].put=n}));if(Object.keys(e).sort(((t,e)=>parseFloat(t)-parseFloat(e))).forEach((t=>{const{call:n,put:i}=e[t],s=document.createElement("div");s.classList.add("option-row");const o=t=>{const e=document.createElement("span");if(!t||0===t)return e.className="neutral",e.textContent="0.00",e;const n=parseFloat(t).toFixed(2),i=t>0?"positive":t<0?"negative":"neutral",s=t>0?"+":"";return e.className=`option-chain-${i}`,e.textContent=`${s}${n}`,e},a=t=>d(t,2,"0.00"),r=document.createElement("div");r.className="calls-data",r.appendChild(l("span",n?h(n.symbol):"--","contract-cell")),r.appendChild(l("span",a(n?.lastPrice)));const c=document.createElement("span");n?c.appendChild(o(n.lastChange)):(c.textContent="0.00",c.className="neutral"),r.appendChild(c),r.appendChild(l("span",a(n?.bidPrice))),r.appendChild(l("span",a(n?.askPrice))),r.appendChild(l("span",n?String(n.volume):"0")),r.appendChild(l("span",n?String(n.openInterest):"0"));const u=document.createElement("div");u.className="strike-data",u.textContent=t;const g=document.createElement("div");g.className="puts-data",g.appendChild(l("span",i?h(i.symbol):"","contract-cell")),g.appendChild(l("span",a(i?.lastPrice)));const p=document.createElement("span");i?p.appendChild(o(i.lastChange)):(p.textContent="0.00",p.className="neutral"),g.appendChild(p),g.appendChild(l("span",a(i?.bidPrice))),g.appendChild(l("span",a(i?.askPrice))),g.appendChild(l("span",i?String(i.volume):"0")),g.appendChild(l("span",i?String(i.openInterest):"0")),s.appendChild(r),s.appendChild(u),s.appendChild(g),this.dataGrid.appendChild(s)})),t&&t.length>0){const e=f(t[0].quoteTime||Date.now()),n=this.container.querySelector(".last-update");n&&(n.textContent=`Last update: ${e}`);const i="OPRA-D"===t[0].DataSource?"20 mins delayed":"Real-time",s=this.container.querySelector(".data-source");s&&(s.textContent=`Source: ${i}`)}}catch(t){console.error("Error displaying option chain:",t),this.showError("Error displaying data")}}showLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.remove("hidden")}hideLoading(){const t=this.container.querySelector(".widget-loading-overlay");t&&t.classList.add("hidden")}showError(t){this.hideLoading(),this.clearError();const e=document.createElement("div");e.className="widget-error",e.textContent=`Error: ${t}`,e.style.cssText="\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n padding: 15px 20px;\n background: #fee;\n border: 1px solid #f5c6cb;\n border-radius: 6px;\n color: #721c24;\n text-align: center;\n font-size: 14px;\n z-index: 100;\n ",this.container.appendChild(e)}clearError(){const t=this.container.querySelector(".widget-error"),e=this.container.querySelector(".widget-error-container");t&&t.remove(),e&&e.remove()}showNoDataState(){this.hideLoading(),this.clearError();const t=l("div","","no-data-state"),e=l("div","","no-data-content"),n=document.createElement("div");n.className="no-data-icon",n.innerHTML='<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#9ca3af" stroke-width="2"/>\n <path d="M12 6v6l4 2" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>',e.appendChild(n);const i=l("h3","No Option Chain Data","no-data-title");e.appendChild(i);const s=l("p","","no-data-description");s.appendChild(document.createTextNode("Option chain data for "));const o=l("strong",h(this.symbol));s.appendChild(o),s.appendChild(document.createTextNode(" on "));const a=l("strong",String(this.date));s.appendChild(a),s.appendChild(document.createTextNode(" was not found")),e.appendChild(s);const r=l("p","Please check the symbol and date or try again later","no-data-guidance");e.appendChild(r),t.appendChild(e);const c=this.container.querySelector(".widget-footer");c&&c.parentNode?c.parentNode.insertBefore(t,c):this.container.appendChild(t)}destroy(){this.isDestroyed=!0,this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null),this.loadingTimeout&&(this.clearTimeout(this.loadingTimeout),this.loadingTimeout=null),super.destroy()}}const C=t=>{const e=t.match(/^(.+?)(\d{6})([CP])(\d{8})$/);if(e){const[,t,n,i,s]=e;return"C"===i?"call":"put"}return null};class _ extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for DataWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for DataWidget");this.type="data",this.symbol=(e.symbol||"").toString().toUpperCase(),this.wsManager=e.wsManager,this.dataSource=e.dataSource||"queryl1",this.debug=e.debug||!1,this.data=null,this.isDestroyed=!1,this.unsubscribe=null,this.loadingTimeout=null,this.createWidgetStructure(),this.initializeSymbolEditor(),this.initialize()}createWidgetStructure(){this.container.innerHTML='\n <div class="data-widget widget">\n <div class="widget-content">\n \x3c!-- Header Section --\x3e\n <div class="widget-header">\n <div class="symbol-section">\n <h1 class="symbol editable-symbol" \n title="Double-click to edit symbol" \n data-original-symbol="">\n </h1>\n <div class="company-info">\n <span class="data-type">Stock</span>\n <span class="company-name"></span>\n </div>\n </div>\n </div>\n\n \x3c!-- Price Section --\x3e\n <div class="price-section">\n <div class="current-price"></div>\n <div class="price-change">\n <span class="change-value"></span>\n <span class="change-percent"></span>\n </div>\n </div>\n\n \x3c!-- Bid/Ask Section --\x3e\n <div class="bid-ask-section">\n <div class="ask">\n <span class="label">Ask</span>\n <span class="value ask-price"></span>\n <span class="size ask-size"></span>\n </div>\n <div class="bid">\n <span class="label">Bid</span>\n <span class="value bid-price"></span>\n <span class="size bid-size"></span>\n </div>\n </div>\n\n \x3c!-- Footer --\x3e\n <div class="widget-footer">\n <span class="last-size">Last Size: <span class="trade-size"></span></span>\n <span class="source">Market Data Powered by MDAS</span>\n </div>\n </div>\n\n \n </div>\n',this.addStyles()}addStyles(){if(!document.querySelector("#data-styles")){const t=document.createElement("style");t.id="data-styles",t.textContent="\n .data-widget {\n position: relative;\n border: 1px solid #ddd;\n border-radius: 8px;\n overflow: hidden;\n background-color: #fff;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n padding: 10px;\n font-family: Arial, sans-serif;\n }\n\n .widget-header {\n display: flex;\n flex-direction: column;\n margin-bottom: 10px;\n }\n\n .symbol {\n font-size: 24px;\n font-weight: bold;\n margin-right: 10px;\n }\n\n .company-info {\n font-size: 12px;\n color: #666;\n }\n\n .trading-info {\n font-size: 12px;\n color: #999;\n }\n\n .price-section {\n display: flex;\n align-items: baseline;\n margin-bottom: 10px;\n }\n\n .current-price {\n font-size: 36px;\n font-weight: bold;\n margin-right: 10px;\n }\n\n .price-change {\n font-size: 18px;\n }\n\n .price-change.positive {\n color: green;\n }\n\n .price-change.negative {\n color: red;\n }\n\n .bid-ask-section {\n display: flex;\n justify-content: space-between;\n margin-bottom: 10px;\n }\n\n .ask, .bid {\n display: flex;\n align-items: center;\n }\n\n .label {\n font-size: 14px;\n margin-right: 5px;\n }\n\n .value {\n font-size: 16px;\n font-weight: bold;\n margin-right: 5px;\n }\n\n .size {\n font-size: 14px;\n color: red;\n }\n\n .widget-footer {\n font-size: 12px;\n color: #333;\n }\n",document.head.appendChild(t)}}initializeSymbolEditor(){this.symbolEditor=new s(this,{maxLength:10,placeholder:"Enter symbol...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,symbolType:"quotel1"});const t=this.container.querySelector(".symbol");t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol)}async handleSymbolChange(t){this.debug&&console.log("[DataWidget] Symbol change requested:",t);const e=t.toUpperCase();if(e===this.symbol)return this.debug&&console.log("[DataWidget] Same symbol, no change needed"),{success:!0};try{const t=this.symbol;return this.showLoading(),this.loadingTimeout&&clearTimeout(this.loadingTimeout),this.loadingTimeout=setTimeout((()=>{this.debug&&console.log("[DataWidget] Loading timeout for symbol:",e),this.hideLoading(),this.showError(`No data received for ${e}. Please try again.`)}),1e4),this.unsubscribe&&(this.debug&&console.log(`[DataWidget] Unsubscribing from ${t}`),this.wsManager.sendUnsubscribe(this.dataSource,t),this.unsubscribe(),this.unsubscribe=null),this.symbol=e,this.debug&&console.log(`[DataWidget] Subscribing to ${e}`),this.subscribeToData(),this.debug&&console.log(`[DataWidget] Successfully changed symbol from ${t} to ${e}`),{success:!0}}catch(t){return console.error("[DataWidget] Error changing symbol:",t),this.hideLoading(),this.showError(`Failed to load data for ${e}`),{success:!1,error:`Failed to load ${e}`}}}initialize(){this.showLoading(),this.subscribeToData()}subscribeToData(){this.unsubscribe=this.wsManager.subscribe(this.widgetId,[this.dataSource],this.handleMessage.bind(this),this.symbol)}showNoDataState(t){let{Symbol:e,NotFound:n,message:i}=t;this.clearError(),this.hideLoading();const s=document.createElement("div");s.className="no-data-state",s.innerHTML=`\n <div class="no-data-content">\n <div class="no-data-icon">\n <svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n <circle cx="12" cy="12" r="10" stroke="#9ca3af" stroke-width="2"/>\n <path d="M12 6v6l4 2" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n </svg>\n </div>\n <h3 class="no-data-title">No Data Available</h3>\n <p class="no-data-description">Data for <strong>${e}</strong> was not found.</p>\n <p class="no-data-guidance">${i||"Please check the symbol or try again later."}</p>\n </div>\n `,this.container.appendChild(s)}handleData(t){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),"error"===t.type&&t.noData)return this.debug&&console.log("[DataWidget] Received no data message:",t.message),void this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:t.message});if("error"!==t.type){if(t.Data&&t.Data.length>0){const e=t.Data.find((t=>t.Symbol===this.symbol));if(e){const t=new i(e);this.updateWidget(t)}else console.log("hereee"),this.showNoDataState({Symbol:this.symbol,NotFound:!0,message:`No data found for ${this.symbol}`})}t._cached&&this.showConnectionQuality()}else{const e=t.message||"Server error";this.showError(e)}}updateWidget(t){if(!this.isDestroyed)try{this.clearError(),this.hideLoading();const e=this.container.querySelector(".symbol");e&&(e.textContent=t.symbol,e.dataset.originalSymbol=t.symbol);const n=this.container.querySelector(".current-price");n&&(n.textContent=t.formatPrice());const i=this.container.querySelector(".price-change"),s=i.querySelector(".change-value"),o=i.querySelector(".change-percent");s.textContent=t.formatPriceChange(),o.textContent=` (${t.formatPriceChangePercent()})`,i.classList.remove("positive","negative","neutral"),t.getPriceChange()>0?i.classList.add("positive"):t.getPriceChange()<0?i.classList.add("negative"):i.classList.add("neutral");const a=this.container.querySelector(".bid-price");a&&(a.textContent=t.bidPrice.toFixed(2));const r=this.container.querySelector(".ask-price");r&&(r.textContent=t.askPrice.toFixed(2));const c=this.container.querySelector(".bid-size");c&&(c.textContent=`× ${t.bidSize}`);const l=this.container.querySelector(".ask-size");l&&(l.textContent=`× ${t.askSize}`);const d=this.container.querySelector(".trade-size");d&&(d.textContent=t.tradeSize);const h=this.container.querySelector(".last-update");if(h){const e=f(t.quoteTime);h.textContent=`Last update: ${e}`}const u=this.container.querySelector(".data-source");u&&(u.textContent=`Source: ${t.getDataSource()}`)}catch(t){console.error("Error updating widget:",t),this.showError("Error updating data")}}}class D extends n{constructor(t,e,n){if(super(t,e,n),!e.symbol)throw new Error("Symbol is required for CombinedMarketWidget");if(!e.wsManager)throw new Error("WebSocketManager is required for CombinedMarketWidget");const i=g(e.symbol);if(!i.valid)throw new Error(`Invalid symbol: ${i.error}`);this.type="combined-market",this.wsManager=e.wsManager,this.symbol=i.sanitized,this.debug=e.debug||!1,this.removeNightSession=!0,this.marketData=null,this.nightSessionData=null,this.unsubscribeMarket=null,this.unsubscribeNight=null,this.createWidgetStructure(),this.initializeSymbolEditor(),this.subscribeToData()}createWidgetStructure(){this.container.innerHTML='\n <div class="combined-market-widget">\n <div class="combined-market-header">\n <div class="combined-symbol-section">\n <h1 class="combined-symbol editable-symbol"\n title="Double-click to edit symbol"\n data-original-symbol="">\n </h1>\n <div class="combined-company-info">\n <span class="company-name">\n </span>\n </div>\n </div>\n </div>\n\n <div class="combined-data-container">\n \x3c!-- Regular Market Data (Left) --\x3e\n <div class="combined-market-section regular-market">\n <div class="combined-price-info">\n <div class="combined-current-price">--</div>\n <div class="combined-price-change neutral">\n <span class="combined-change-value">--</span>\n <span class="combined-change-percent">(--)</span>\n </div>\n </div>\n <div class="combined-market-status"><span class="market-status-label"></span> <span class="combined-timestamp">--</span></div>\n </div>\n\n \x3c!-- Night Session Data (Right) --\x3e\n <div class="combined-market-section night-session">\n\n <div class="combined-price-info">\n <div class="combined-current-price">--</div>\n <div class="combined-price-change neutral">\n <span class="combined-change-value">--</span>\n <span class="combined-change-percent">(--)</span>\n </div>\n </div>\n <div class="combined-market-status">Overnight: <span class="combined-timestamp">--</span></div>\n </div>\n </div>\n\n <div class="combined-loading-overlay hidden">\n <div class="combined-loading-content">\n <div class="combined-loading-spinner"></div>\n <span class="combined-loading-text">Loading market data...</span>\n </div>\n </div>\n </div>\n';const t=this.container.querySelector(".combined-symbol");if(t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol),this.removeNightSession&&!this.isNightSessionActive()){const t=this.container.querySelector(".night-session");t&&(t.style.display="none")}this.addStyles()}addStyles(){if(!document.querySelector("#combined-market-styles")){const t=document.createElement("style");t.id="combined-market-styles",t.textContent='\n .combined-market-widget {\n background: #ffffff;\n color: #111827;\n padding: 24px;\n border-radius: 12px;\n border: 1px solid #e5e7eb;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\n position: relative;\n }\n\n .combined-market-widget .combined-market-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n padding-bottom: 16px;\n border-bottom: 1px solid #e5e7eb;\n }\n\n .combined-market-widget .combined-symbol-section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .combined-market-widget .combined-symbol {\n font-size: 24px;\n font-weight: 700;\n margin: 0;\n color: #111827;\n cursor: pointer;\n transition: all 0.2s ease;\n position: relative;\n }\n\n .combined-market-widget .combined-symbol:hover {\n opacity: 0.8;\n transform: scale(1.02);\n }\n\n .combined-market-widget .combined-symbol:hover::after {\n content: "✎";\n position: absolute;\n top: -8px;\n right: -8px;\n background: #3b82f6;\n color: white;\n font-size: 10px;\n padding: 2px 4px;\n border-radius: 4px;\n opacity: 0.8;\n pointer-events: none;\n }\n\n .combined-market-widget .follow-btn {\n background: #ffffff;\n border: 1px solid #d1d5db;\n color: #374151;\n padding: 8px 16px;\n border-radius: 20px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n transition: all 0.2s;\n }\n\n .combined-market-widget .follow-btn:hover {\n border-color: #9ca3af;\n background: #f9fafb;\n }\n\n .combined-market-widget .combined-data-container {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 40px;\n }\n\n .combined-market-widget .combined-market-section {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .combined-market-widget .combined-price-info {\n display: flex;\n flex-direction: column;\n min-height: 90px;\n }\n\n .combined-market-widget .session-label {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n color: #6b7280;\n margin-bottom: 4px;\n font-weight: 500;\n }\n\n .combined-market-widget .session-label .icon {\n font-size: 16px;\n }\n\n .combined-market-widget .combined-current-price {\n font-size: 48px;\n font-weight: 700;\n line-height: 1;\n color: #111827;\n }\n\n .combined-market-widget .combined-price-change {\n display: flex;\n gap: 8px;\n font-size: 20px;\n font-weight: 600;\n margin-top: 4px;\n }\n\n .combined-market-widget .combined-price-change.positive {\n color: #059669;\n }\n\n .combined-market-widget .combined-price-change.negative {\n color: #dc2626;\n }\n\n .combined-market-widget .combined-price-change.neutral {\n color: #6b7280;\n }\n\n .combined-market-widget .combined-market-status {\n font-size: 14px;\n color: #6b7280;\n margin-top: 8px;\n }\n\n .combined-market-widget .combined-timestamp {\n color: #9ca3af;\n font-weight: 400;\n }\n\n /* Loading overlay */\n .combined-market-widget .combined-loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(255, 255, 255, 0.95);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 12px;\n z-index: 10;\n }\n\n .combined-market-widget .combined-loading-overlay.hidden {\n display: none;\n }\n\n .combined-market-widget .combined-loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n }\n\n .combined-market-widget .combined-loading-spinner {\n width: 40px;\n height: 40px;\n border: 3px solid #e5e7eb;\n border-top-color: #3b82f6;\n border-radius: 50%;\n animation: combined-market-spin 1s linear infinite;\n }\n\n .combined-market-widget .combined-loading-text {\n color: #6b7280;\n font-size: 14px;\n font-weight: 500;\n }\n\n @keyframes combined-market-spin {\n to { transform: rotate(360deg); }\n }\n\n /* Responsive */\n @media (max-width: 768px) {\n .combined-market-widget .combined-data-container {\n grid-template-columns: 1fr;\n gap: 30px;\n }\n\n .combined-market-widget .combined-current-price {\n font-size: 36px;\n }\n\n .combined-market-widget .combined-price-change {\n font-size: 16px;\n }\n }\n',document.head.appendChild(t)}}initializeSymbolEditor(){this.symbolEditor=new s(this,{maxLength:10,placeholder:"Enter symbol...",onSymbolChange:this.handleSymbolChange.bind(this),debug:this.debug,autoUppercase:!0,symbolType:"quotel1"});const t=this.container.querySelector(".combined-symbol");t&&(t.textContent=this.symbol,t.dataset.originalSymbol=this.symbol)}async handleSymbolChange(t){this.debug&&console.log("[CombinedMarketWidget] Symbol change requested:",t);const e=g(t);if(!e.valid)return this.debug&&console.log("[CombinedMarketWidget] Invalid symbol:",e.error),{success:!1,error:e.error};const n=e.sanitized;if(n===this.symbol)return this.debug&&console.log("[CombinedMarketWidget] Same symbol, no change needed"),{success:!0};try{const t=this.symbol;return this.showLoading(),this.unsubscribeMarket&&(this.debug&&console.log(`[CombinedMarketWidget] Unsubscribing from market data: ${t}`),this.wsManager.sendUnsubscribe("queryl1",t),this.unsubscribeMarket(),this.unsubscribeMarket=null),this.unsubscribeNight&&(this.debug&&console.log(`[CombinedMarketWidget] Unsubscribing from night session: ${t}`),this.wsManager.sendUnsubscribe("queryblueoceanl1",t),this.unsubscribeNight(),this.unsubscribeNight=null),this.symbol=n,this.marketData=null,this.nightSessionData=null,this.debug&&console.log(`[CombinedMarketWidget] Subscribing to new symbol: ${n}`),this.subscribeToData(),this.debug&&console.log(`[CombinedMarketWidget] Successfully changed symbol from ${t} to ${n}`),{success:!0}}catch(t){return console.error("[CombinedMarketWidget] Error changing symbol:",t),this.hideLoading(),{success:!1,error:`Failed to load ${n}`}}}subscribeToData(){this.showLoading(),this.unsubscribeMarket=this.wsManager.subscribe(`${this.widgetId}-market`,["queryl1"],(t=>{const{event:e,data:n}=t;"connection"!==e?"data"===e&&(n._dataType="market",this.handleMessage({event:e,data:n})):this.handleConnectionStatus(n)}),this.symbol),this.unsubscribeNight=this.wsManager.subscribe(`${this.widgetId}-night`,["queryblueoceanl1"],(t=>{const{event:e,data:n}=t;"connection"!==e&&"data"===e&&(n._dataType="night",this.handleMessage({event:e,data:n}))}),this.symbol)}handleData(t){const e=t._dataType;if(this.debug&&console.log(`[CombinedMarketWidget] handleData called with type: ${e}`,t),e){if("error"===t.type&&t.noData)return this.debug&&console.log(`[CombinedMarketWidget] Received no data message for ${e}:`,t.message),"market"!==e||this.marketData?"night"!==e||this.nightSessionData||this.debug&&console.log("[CombinedMarketWidget] No night session data available"):this.debug&&console.log("[CombinedMarketWidget] No market data available"),void this.hideLoading();if("error"===t.type){const n=t.message||"Server error";return this.debug&&console.log(`[CombinedMarketWidget] Error received for ${e}:`,n),void this.hideLoading()}if("object"==typeof t&&t.Message){const n=t.Message.toLowerCase();if(n.includes("no data")||n.includes("not found"))return this.debug&&console.log(`[CombinedMarketWidget] No data message for ${e}:`,t.Message),void this.hideLoading()}if("market"===e){if(t.Data&&Array.isArray(t.Data)){const e=t.Data.find((t=>t.Symbol===this.symbol));e?(this.debug&&console.log("[CombinedMarketWidget] Market data received:",e),this.marketData=new i(e),this.updateMarketSection(),this.hideLoading()):(this.debug&&console.log("[CombinedMarketWidget] No market data for symbol in response, keeping cached data"),this.hideLoading())}delete t._dataType}if("night"===e){if(Array.isArray(t)){const e=t.find((t=>t.Symbol===this.symbol));e?!0!==e.NotFound&&e.MarketName&&"BLUE"===e.MarketName?(this.debug&&console.log("[CombinedMarketWidget] Night session data received:",e),this.nightSessionData=new y(e),this.updateNightSessionSection(),this.hideLoading()):(this.debug&&console.log("[CombinedMarketWidget] Night session data not found or not available"),this.hideLoading()):(this.debug&&console.log("[CombinedMarketWidget] No night session data for symbol in response, keeping cached data"),this.hideLoading())}else"queryblueoceanl1"!==t.type&&"querybrucel1"!==t.type||(t[0]?.Symbol===this.symbol?!0!==t[0].NotFound&&t[0].MarketName&&"BLUE"===t[0].MarketName?(this.debug&&console.log("[CombinedMarketWidget] Night session data received (wrapped format):",t[0]),this.nightSessionData=new y(t[0]),this.updateNightSessionSection(),this.hideLoading()):(this.debug&&console.log("[CombinedMarketWidget] Night session data not found or not available (wrapped format)"),this.hideLoading()):(this.debug&&console.log("[CombinedMarketWidget] No matching symbol in wrapped response, keeping cached data"),this.hideLoading()));delete t._dataType}t._cached&&this.showConnectionQuality()}else this.debug&&console.warn("[CombinedMarketWidget] No data type specified, attempting to infer from structure")}updateMarketSection(){if(!this.marketData)return;const t=this.container.querySelector(".regular-market"),e=this.container.querySelector(".company-name");if(e){const t=this.marketData.companyName||`${this.marketData.symbol} Inc.`,n=this.marketData.companyDescription||`${this.marketData.symbol}`;e.textContent=t,e.setAttribute("title",n),e.setAttribute("data-tooltip",n)}t.querySelector(".combined-current-price").textContent=this.marketData.formatPrice();const n=t.querySelector(".combined-price-change"),i=t.querySelector(".combined-change-value"),s=t.querySelector(".combined-change-percent"),o=this.marketData.change,a=o>0?"positive":o<0?"negative":"neutral";n.className=`combined-price-change ${a}`,i.textContent=this.marketData.formatPriceChange(),s.textContent=`(${this.marketData.formatPriceChangePercent()})`;const r=t.querySelector(".market-status-label");r&&(r.textContent=`${this.getSessionType()}:`);t.querySelector(".combined-timestamp").textContent=f(this.marketData.quoteTime,{format:"long"}),this.debug&&console.log("[CombinedMarketWidget] Updated market section:",this.marketData)}updateNightSessionSection(){if(!this.nightSessionData)return;const t=this.container.querySelector(".night-session");if(this.removeNightSession&&!this.isNightSessionActive())return t.style.display="none",void(this.debug&&console.log("[CombinedMarketWidget] Night session hidden (market closed)"));t.style.display="";const e=t.querySelector(".combined-current-price");e&&(e.textContent=this.nightSessionData.lastPrice?this.nightSessionData.formatPrice():"--");const n=t.querySelector(".combined-price-change"),i=t.querySelector(".combined-change-value"),s=t.querySelector(".combined-change-percent"),o=this.nightSessionData.change,a=o>0?"positive":o<0?"negative":"neutral";n.className=`combined-price-change ${a}`,i.textContent=null!==this.nightSessionData.change?this.nightSessionData.formatPriceChange():"--",s.textContent=`(${this.nightSessionData.formatPriceChangePercent()})`;t.querySelector(".combined-timestamp").textContent=f(this.nightSessionData.quoteTime,{format:"long"}),this.debug&&(console.log("connection quality:",this.connectionQuality),console.log("[CombinedMarketWidget] Updated night session section:",this.nightSessionData))}getSessionType(){const t=(new Date).toLocaleString("en-US",{timeZone:"America/New_York"}),e=new Date(t),n=e.getHours(),i=e.getMinutes(),s=60*n+i;return console.log("Current EST time:",n+":"+(i<10?"0":"")+i),s>=240&&s<570?"Pre-Market":s>=570&&s<960?"Market Hours":s>=960&&s<1200?"Post-Market":"Market Closed"}isNightSessionActive(){return"Market Closed"===this.getSessionType()}destroy(){this.unsubscribeMarket&&(this.unsubscribeMarket(),this.unsubscribeMarket=null),this.unsubscribeNight&&(this.unsubscribeNight(),this.unsubscribeNight=null),this.symbolEditor&&(this.symbolEditor.destroy(),this.symbolEditor=null),super.destroy()}}class T{constructor(t){Array.isArray(t)?(console.log("[IntradayChartModel] Processing",t.length,"raw data points"),t.length>0&&console.log("[IntradayChartModel] Sample raw point:",t[0]),this.dataPoints=t.map(((t,e)=>{const n={symbol:t.symbol||t.Symbol,time:t.time||t.Time||t.timestamp||t.Timestamp,open:parseFloat(t.open||t.Open)||0,high:parseFloat(t.high||t.High)||0,low:parseFloat(t.low||t.Low)||0,close:parseFloat(t.close||t.Close)||0,volume:parseInt(t.volume||t.Volume)||0,source:t.source||t.Source};return e<2&&console.log(`[IntradayChartModel] Processed point ${e}:`,n),n.time||console.warn(`[IntradayChartModel] Point ${e} missing time:`,t),0===n.close&&console.warn(`[IntradayChartModel] Point ${e} has zero close price:`,t),n})).filter((t=>{if(!t.time||t.close<=0)return!1;const e=new Date(t.time).getTime();return!(isNaN(e)||e<9466848e5)||(console.warn("[IntradayChartModel] Invalid timestamp:",t.time),!1)})),this.dataPoints.sort(((t,e)=>new Date(t.time).getTime()-new Date(e.time).getTime())),console.log("[IntradayChartModel] Valid data points after filtering:",this.dataPoints.length),this.dataPoints.length>0&&console.log("[IntradayChartModel] Time range:",this.dataPoints[0].time,"to",this.dataPoints[this.dataPoints.length-1].time)):(console.warn("[IntradayChartModel] Expected array of data points, got:",typeof t),this.dataPoints=[]),this.symbol=this.dataPoints.length>0?this.dataPoints[0].symbol:null,this.source=this.dataPoints.length>0?this.dataPoints[0].source:null}getTimeLabels(){return this.dataPoints.map((t=>{const e=new Date(t.time);let n=e.getHours();const i=n>=12?"PM":"AM";n%=12,n=n||12;return[`${n}:${e.getMinutes().toString().padStart(2,"0")} ${i}`,`${(e.getMonth()+1).toString().padStart(2,"0")}/${e.getDate().toString().padStart(2,"0")}`]}))}getOHLCData(){return this.dataPoints.map((t=>({x:new Date(t.time).getTime(),o:t.open,h:t.high,l:t.low,c:t.close})))}getClosePrices(){return this.dataPoints.map((t=>t.close))}getVolumeData(){return this.dataPoints.map((t=>t.volume))}getHighPrices(){return this.dataPoints.map((t=>t.high))}getLowPrices(){return this.dataPoints.map((t=>t.low))}getOpenPrices(){return this.dataPoints.map((t=>t.open))}getStats(){if(0===this.dataPoints.length)return{high:0,low:0,open:0,close:0,change:0,changePercent:0,volume:0};this.getClosePrices();const t=this.getHighPrices(),e=this.getLowPrices(),n=this.getVolumeData(),i=this.dataPoints[0],s=this.dataPoints[this.dataPoints.length-1],o=s.close-i.open,a=0!==i.open?o/i.open*100:0;return{high:Math.max(...t),low:Math.min(...e),open:i.open,close:s.close,change:o,changePercent:a,volume:n.reduce(((t,e)=>t+e),0),dataPoints:this.dataPoints.length}}formatPrice(t){return t.toFixed(2)}formatVolume(t){return t>=1e6?(t/1e6).toFixed(2)+"M":t>=1e3?(t/1e3).toFixed(2)+"K":t.toString()}}
2
2
  /*!
3
3
  * @kurkle/color v0.3.4
4
4
  * https://github.com/kurkle/color#readme
@@ -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