energy-visualization-sankey 1.0.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.
- package/README.md +497 -0
- package/babel.config.cjs +28 -0
- package/coverage/clover.xml +6 -0
- package/coverage/coverage-final.json +1 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +101 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +0 -0
- package/demo-caching.js +68 -0
- package/dist/core/Sankey.d.ts +294 -0
- package/dist/core/Sankey.d.ts.map +1 -0
- package/dist/core/events/EventBus.d.ts +195 -0
- package/dist/core/events/EventBus.d.ts.map +1 -0
- package/dist/core/types/events.d.ts +42 -0
- package/dist/core/types/events.d.ts.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/sankey.esm.js +5212 -0
- package/dist/sankey.esm.js.map +1 -0
- package/dist/sankey.standalone.esm.js +9111 -0
- package/dist/sankey.standalone.esm.js.map +1 -0
- package/dist/sankey.standalone.min.js +2 -0
- package/dist/sankey.standalone.min.js.map +1 -0
- package/dist/sankey.standalone.umd.js +9119 -0
- package/dist/sankey.standalone.umd.js.map +1 -0
- package/dist/sankey.umd.js +5237 -0
- package/dist/sankey.umd.js.map +1 -0
- package/dist/sankey.umd.min.js +2 -0
- package/dist/sankey.umd.min.js.map +1 -0
- package/dist/services/AnimationService.d.ts +229 -0
- package/dist/services/AnimationService.d.ts.map +1 -0
- package/dist/services/ConfigurationService.d.ts +173 -0
- package/dist/services/ConfigurationService.d.ts.map +1 -0
- package/dist/services/InteractionService.d.ts +377 -0
- package/dist/services/InteractionService.d.ts.map +1 -0
- package/dist/services/RenderingService.d.ts +152 -0
- package/dist/services/RenderingService.d.ts.map +1 -0
- package/dist/services/calculation/GraphService.d.ts +111 -0
- package/dist/services/calculation/GraphService.d.ts.map +1 -0
- package/dist/services/calculation/SummaryService.d.ts +149 -0
- package/dist/services/calculation/SummaryService.d.ts.map +1 -0
- package/dist/services/data/DataService.d.ts +167 -0
- package/dist/services/data/DataService.d.ts.map +1 -0
- package/dist/services/data/DataValidationService.d.ts +48 -0
- package/dist/services/data/DataValidationService.d.ts.map +1 -0
- package/dist/types/index.d.ts +189 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/Logger.d.ts +88 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/jest.config.cjs +20 -0
- package/package.json +68 -0
- package/rollup.config.js +131 -0
- package/scripts/performance-validation-real.js +411 -0
- package/scripts/validate-optimization.sh +147 -0
- package/scripts/visual-validation-real-data.js +374 -0
- package/src/core/Sankey.ts +1039 -0
- package/src/core/events/EventBus.ts +488 -0
- package/src/core/types/events.ts +80 -0
- package/src/index.ts +35 -0
- package/src/services/AnimationService.ts +983 -0
- package/src/services/ConfigurationService.ts +497 -0
- package/src/services/InteractionService.ts +920 -0
- package/src/services/RenderingService.ts +484 -0
- package/src/services/calculation/GraphService.ts +616 -0
- package/src/services/calculation/SummaryService.ts +394 -0
- package/src/services/data/DataService.ts +380 -0
- package/src/services/data/DataValidationService.ts +155 -0
- package/src/styles/controls.css +184 -0
- package/src/styles/sankey.css +211 -0
- package/src/types/index.ts +220 -0
- package/src/utils/Logger.ts +105 -0
- package/tests/numerical-validation.test.js +575 -0
- package/tests/setup.js +53 -0
- package/tsconfig.json +54 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("d3")):"function"==typeof define&&define.amd?define(["d3"],e):(t="undefined"!=typeof globalThis?globalThis:t||self)["energy-visualization-sankey"]=e(t.d3)}(this,function(t){"use strict";function e(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(i){if("default"!==i){var s=Object.getOwnPropertyDescriptor(t,i);Object.defineProperty(e,i,s.get?s:{enumerable:!0,get:function(){return t[i]}})}}),e.default=t,Object.freeze(e)}var i=e(t);class s{constructor(t){this.logger=t,this.handlers=new Map,this.subscriptions=new Map,this.stats={totalSubscriptions:0,subscriptionsByType:{},totalEventsEmitted:0,eventsByType:{},averageHandlerTime:0,errorCount:0},this.handlerTimes=[],this.MAX_HANDLER_TIMES=100}emit(t){const e=this.handlers.get(t.type);e&&0!==e.size&&(this.stats.totalEventsEmitted++,this.stats.eventsByType[t.type]=(this.stats.eventsByType[t.type]||0)+1,Promise.resolve().then(()=>{const i=performance.now();let s=0,a=0;e.forEach(e=>{try{const i=performance.now(),a=e(t);a&&"function"==typeof a.catch&&a.catch(i=>{this.handleError(i,t,e)});const n=performance.now()-i;this.recordHandlerTime(n),s++}catch(i){this.handleError(i,t,e),a++}});const n=performance.now()-i;this.logger.log(`EventBus: ${t.type} → ${s} handlers (${a} errors) in ${n.toFixed(2)}ms`)}))}subscribe(t,e){this.handlers.has(t)||this.handlers.set(t,new Set),this.handlers.get(t).add(e);const i={id:this.generateSubscriptionId(),eventType:t,handler:e,createdAt:Date.now()};return this.subscriptions.set(i.id,i),this.stats.totalSubscriptions++,this.stats.subscriptionsByType[t]=(this.stats.subscriptionsByType[t]||0)+1,this.logger.debug(`EventBus: Subscribed to ${t} (${i.id}) - ${this.handlers.get(t).size} total handlers`),i}unsubscribe(t){var e;const i=this.handlers.get(t.eventType);i&&(i.delete(t.handler),0===i.size&&(this.handlers.delete(t.eventType),this.logger.debug(`EventBus: Removed empty handler set for ${t.eventType}`)));if(this.subscriptions.delete(t.id)){this.stats.totalSubscriptions=Math.max(0,this.stats.totalSubscriptions-1);const i=this.stats.subscriptionsByType[t.eventType]||0;this.stats.subscriptionsByType[t.eventType]=Math.max(0,i-1);const s=(null===(e=this.handlers.get(t.eventType))||void 0===e?void 0:e.size)||0;this.logger.debug(`EventBus: Unsubscribed from ${t.eventType} (${t.id}) - ${s} handlers remain`)}else this.logger.warn(`EventBus: Attempted to unsubscribe non-existent subscription ${t.id}`)}getStats(){return{...this.stats,averageHandlerTime:this.calculateAverageHandlerTime(),subscriptionsByType:{...this.stats.subscriptionsByType},eventsByType:{...this.stats.eventsByType}}}clear(){const t=this.subscriptions.size,e=this.handlers.size;this.handlers.clear(),this.subscriptions.clear(),this.stats={totalSubscriptions:0,subscriptionsByType:{},totalEventsEmitted:0,eventsByType:{},averageHandlerTime:0,errorCount:0},this.handlerTimes=[],this.logger.debug(`EventBus: Cleared ${t} subscriptions across ${e} event types`)}generateSubscriptionId(){return`sub_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}handleError(t,e,i){this.stats.errorCount++;const s={error:t instanceof Error?t.message:String(t),errorType:t instanceof Error?t.constructor.name:typeof t,stack:t instanceof Error?t.stack:void 0,eventType:e.type,eventSource:e.source,eventTimestamp:e.timestamp,handlerPreview:i.toString().substring(0,100)+"...",handlerLength:i.toString().length,totalErrors:this.stats.errorCount,totalEvents:this.stats.totalEventsEmitted,errorRate:this.stats.totalEventsEmitted>0?(this.stats.errorCount/this.stats.totalEventsEmitted*100).toFixed(2)+"%":"0%"};console.error(`EventBus: Handler error in ${e.type} (error #${this.stats.errorCount}):`,s),this.logger.warn("EventBus: Failing handler source preview:",s.handlerPreview),this.stats.errorCount>0&&this.stats.errorCount%10==0&&this.logger.warn(`EventBus: Error count reached ${this.stats.errorCount} - consider investigating handler stability`)}recordHandlerTime(t){this.handlerTimes.push(t),this.handlerTimes.length>this.MAX_HANDLER_TIMES&&this.handlerTimes.shift(),t>10&&this.logger.warn(`EventBus: Slow handler detected: ${t.toFixed(2)}ms execution time`)}calculateAverageHandlerTime(){if(0===this.handlerTimes.length)return 0;const t=this.handlerTimes.reduce((t,e)=>t+e,0)/this.handlerTimes.length;return Math.round(1e3*t)/1e3}getDebugInfo(){const t=Date.now(),e=Array.from(this.subscriptions.values()).map(e=>({id:e.id,eventType:e.eventType,createdAt:e.createdAt,age:t-e.createdAt})),i={};return this.handlers.forEach((t,e)=>{i[e]=t.size}),{activeSubscriptions:e,handlerCounts:i,recentPerformance:{averageHandlerTime:this.calculateAverageHandlerTime(),recentHandlerTimes:[...this.handlerTimes],totalEvents:this.stats.totalEventsEmitted,errorRate:this.stats.totalEventsEmitted>0?this.stats.errorCount/this.stats.totalEventsEmitted:0}}}}class a extends Error{constructor(t,e){super(t),this.code=e,this.name="SankeyError"}}class n extends a{constructor(t,e){super(t,"DATA_VALIDATION"),this.field=e,this.name="DataValidationError"}}class r{constructor(t){this.options=t}log(t,...e){this.options.debugLogging&&console.log(t,...e)}debug(t,...e){this.options.debugLogging&&console.debug(t,...e)}warn(t,...e){this.options.debugLogging&&console.warn(t,...e)}}class o{constructor(t,e,i,s){this.container=t,this.options=e,this.eventBus=i,this.logger=s,this.hasHeatData=!1,this.HEIGHT=620,this.BOX_WIDTH=120,this.BOX_HEIGHT=30,this.LEFT_X=10,this.TOP_Y=100,this.SCALE=.02,this.ELEC_BOX_X=300,this.ELEC_BOX_Y=120,this.HEAT_BOX_X=750,this.HEAT_BOX_Y=200,this.LEFT_GAP=30,this.BLEED=.5,this.SR3=Math.sqrt(3),this.HSR3=Math.sqrt(3)/2,this.PATH_GAP=20,this.ELEC_GAP=19,this.FUELS=[{fuel:"elec",name:"Electricity",color:"#e49942"},{fuel:"heat",name:"Heat",color:"#98002e"},{fuel:"solar",name:"Solar",color:"#fed530"},{fuel:"nuclear",name:"Nuclear",color:"#ca0813"},{fuel:"hydro",name:"Hydro",color:"#0b24fb"},{fuel:"wind",name:"Wind",color:"#901d8f"},{fuel:"geo",name:"Geothermal",color:"#905a1c"},{fuel:"gas",name:"Natural Gas",color:"#4cabf2"},{fuel:"coal",name:"Coal",color:"#000000"},{fuel:"bio",name:"Biomass",color:"#46be48"},{fuel:"petro",name:"Petroleum",color:"#095f0b"}],this.BOXES=[{box:"elec",name:"Electricity",color:"#cccccc"},{box:"res",name:"Residential/Commercial",color:"#cccccc"},{box:"ag",name:"Agricultural",color:"#cccccc"},{box:"indus",name:"Industrial",color:"#cccccc"},{box:"trans",name:"Transportation",color:"#cccccc"},{box:"heat",name:"Heat",color:"#cccccc"}],this.BOXES_DEFAULT_FLOW_PATHS_ORDER={elec:1,res:2,ag:3,indus:4,trans:5,heat:6},this.HEAT_BOX_FIRST_FLOW_PATHS_ORDER={elec:1,heat:2,res:3,ag:4,indus:5,trans:6},this.FLOW_PATHS_ORDER={},this.BOX_NAMES=this.BOXES.map(t=>t.box),this.FUEL_NAMES=this.FUELS.map(t=>t.fuel),this.RIGHT_GAP=2.1*this.LEFT_GAP,this.SPEED=e.animationSpeed||200,this.FLOW_PATHS_ORDER={elec:this.BOXES_DEFAULT_FLOW_PATHS_ORDER,heat:this.BOXES_DEFAULT_FLOW_PATHS_ORDER,solar:this.BOXES_DEFAULT_FLOW_PATHS_ORDER,nuclear:this.BOXES_DEFAULT_FLOW_PATHS_ORDER,hydro:this.BOXES_DEFAULT_FLOW_PATHS_ORDER,wind:this.BOXES_DEFAULT_FLOW_PATHS_ORDER,geo:this.BOXES_DEFAULT_FLOW_PATHS_ORDER,gas:this.HEAT_BOX_FIRST_FLOW_PATHS_ORDER,coal:this.HEAT_BOX_FIRST_FLOW_PATHS_ORDER,bio:this.BOXES_DEFAULT_FLOW_PATHS_ORDER,petro:this.HEAT_BOX_FIRST_FLOW_PATHS_ORDER},this.validateConfiguration(),this.eventBus.emit({type:"system.initialized",timestamp:Date.now(),source:"ConfigurationService",data:{fuelsCount:this.FUELS.length,sectorsCount:this.BOXES.length,dimensions:{width:this.WIDTH,height:this.HEIGHT}}})}getFuelDisplayName(t){const e=this.FUELS.find(e=>e.fuel===t);if(e)return e.name;return{elec:"Electricity",waste:"Waste Heat"}[t]||t.charAt(0).toUpperCase()+t.slice(1)}getBoxDisplayName(t){const e=this.BOXES.find(e=>e.box===t);if(e)return e.name;return{res:"Residential",ag:"Agriculture",indus:"Industrial",trans:"Transportation",elec:"Electricity"}[t]||t.charAt(0).toUpperCase()+t.slice(1)}getFuelColor(t){const e=this.FUELS.find(e=>e.fuel===t);return(null==e?void 0:e.color)||"#CCCCCC"}getSectorColor(t){const e=this.BOXES.find(e=>e.box===t);return(null==e?void 0:e.color)||"#CCCCCC"}get WIDTH(){if(this.options.width)return this.options.width;{let t=this.container.getBoundingClientRect().width||this.getDefaultWidth();return t=Math.max(t,400),this.applyResponsiveAdjustments(t)}}getDefaultWidth(){return"undefined"!=typeof window&&window.innerWidth?Math.min(.9*window.innerWidth,1200):1e3}applyResponsiveAdjustments(t){if("undefined"!=typeof window){const e=window.innerWidth;e<768?t=Math.min(t,e-40):e<1024&&(t=Math.min(t,e-80))}return t}calculateRightBoxX(){return this.WIDTH-this.BOX_WIDTH}validateConfiguration(){if(this.WIDTH<=0||this.HEIGHT<=0)throw new Error("ConfigurationService: Invalid dimensions");if(this.BOX_WIDTH<=0||this.BOX_HEIGHT<=0)throw new Error("ConfigurationService: Invalid box dimensions");if(this.SCALE<=0)throw new Error("ConfigurationService: Invalid scale factor");if(this.ELEC_BOX_X<=0||this.ELEC_BOX_X<=0)throw new Error("ConfigurationService: Invalid electricity box position");if(0===this.FUELS.length)throw new Error("ConfigurationService: No fuels defined");if(0===this.BOXES.length)throw new Error("ConfigurationService: No sectors defined");const t=["elec","solar","nuclear","hydro","wind","geo","gas","coal","bio","petro"];for(const e of t)this.FUEL_NAMES.includes(e)||console.warn(`ConfigurationService: Missing required fuel: ${e}`);const e=["elec","res","ag","indus","trans"];for(const t of e)this.BOX_NAMES.includes(t)||console.warn(`ConfigurationService: Missing required sector: ${t}`);this.logger.log("ConfigurationService: All configuration validated successfully")}isValidFuel(t){return this.FUEL_NAMES.includes(t)}isValidSector(t){return this.BOX_NAMES.includes(t)}getAllFuels(){return this.FUELS}getAllSectors(){return this.BOXES}getFuelConfig(t){return this.FUELS.find(e=>e.fuel===t)}getSectorConfig(t){return this.BOXES.find(e=>e.box===t)}calculateResponsiveScale(t,e){this.WIDTH,this.HEIGHT;const i=t/this.WIDTH,s=e/this.HEIGHT,a=Math.min(i,s),n=this.WIDTH*a,r=this.HEIGHT*a;return{scale:a,width:n,height:r,offsetX:(t-n)/2,offsetY:(e-r)/2}}getMobileConfiguration(){return{BOX_WIDTH:Math.max(40,.8*this.BOX_WIDTH),LEFT_GAP:Math.max(5,.7*this.LEFT_GAP),RIGHT_GAP:Math.max(10,.7*this.RIGHT_GAP),PATH_GAP:Math.max(4,.7*this.PATH_GAP),SPEED:1.2*this.SPEED}}isMobileViewport(){return"undefined"!=typeof window&&(window.innerWidth<=768||window.innerHeight<=600)}}class l{constructor(t,e,i){this.configurationService=t,this.eventBus=e,this.logger=i}validateData(t){const e=performance.now();try{if(!Array.isArray(t))throw new n("Data must be an array","data");if(0===t.length)throw new n("Data array cannot be empty","data");const i=["elec","waste","solar","nuclear","hydro","wind","geo","gas","coal","bio","petro"],s=["elec","res","ag","indus","trans"];let a=!1;for(let e=0;e<t.length;e++){const r=t[e];if(!r.year)throw new n(`Invalid year at index ${e}`,"year");for(const t of i){if(!(t in r))throw new n(`Missing sector '${t}' in data point for year ${r.year}`,t);const e=r[t];if(!e||"object"!=typeof e)throw new n(`Invalid sector data for '${t}' in year ${r.year}`,t);let i=[...s];"heat"in r&&(a=!0,i.push("heat"));for(const s of i)if(!(s in e)||"number"!=typeof e[s])throw new n(`Invalid breakdown '${s}' for sector '${t}' in year ${r.year}`,`${t}.${s}`)}}a&&(this.configurationService.hasHeatData=a);const r=performance.now();this.eventBus.emit({type:"data.validated",timestamp:Date.now(),source:"DataValidationService",data:{dataPointCount:t.length,yearRange:t.length>0?[t[0].year,t[t.length-1].year]:[0,0],validationTime:r-e}}),this.logger.log(`DataValidationService: ${t.length} data points validated in ${(r-e).toFixed(2)}ms`)}catch(t){throw this.eventBus.emit({type:"system.error",timestamp:Date.now(),source:"DataValidationService",data:{error:t instanceof Error?t:new Error(String(t)),context:"data_validation",recoverable:!1}}),t}}}class c{constructor(t,e,i,s){this.validationService=e,this.eventBus=i,this.logger=s;const a=performance.now();this.validationService.validateData(t);const n=[...t].sort((t,e)=>t.year-e.year);this.data=Object.freeze(n),this.years=Object.freeze(this.data.map(t=>t.year)),this.yearsLength=this.years.length,this.firstYear=this.years[0],this.lastYear=this.years[this.yearsLength-1];const r=performance.now();this.eventBus.emit({type:"data.loaded",timestamp:Date.now(),source:"DataService",data:{dataPoints:[...this.data],yearCount:this.yearsLength,yearRange:[this.firstYear,this.lastYear]}}),this.logger.log(`DataService: ${this.data.length} data points loaded and sorted in ${(r-a).toFixed(2)}ms`),this.logger.log(`DataService: Year range ${this.firstYear}-${this.lastYear}`)}getYearData(t){return this.data.find(e=>e.year===t)}getYearIndex(t){return this.years.indexOf(t)}hasYear(t){return this.years.includes(t)}getYearDataByIndex(t){if(!(t<0||t>=this.data.length))return this.data[t]}isValidYear(t){return t>=this.firstYear&&t<=this.lastYear&&this.hasYear(t)}getYearRange(){return[this.firstYear,this.lastYear]}getDataCount(){return this.data.length}getNextYear(t){const e=this.getYearIndex(t);return-1===e||e===this.yearsLength-1?null:this.years[e+1]}getPreviousYear(t){const e=this.getYearIndex(t);return e<=0?null:this.years[e-1]}getTotalForYear(t,e,i){const s=this.getYearData(t);if(!s)return 0;const a=s[e];return a&&a[i]||0}getYearTotalForFuel(t,e){const i=this.getYearData(t);if(!i)return 0;const s=i[e];return s?(s.elec||0)+(s.res||0)+(s.ag||0)+(s.indus||0)+(s.trans||0):0}getYearTotalForSector(t,e){const i=this.getYearData(t);if(!i)return 0;let s=0;const a=["solar","nuclear","hydro","wind","geo","gas","coal","bio","petro"];for(const t of a){const a=i[t];a&&a[e]&&(s+=a[e])}return"elec"!==e&&i.elec[e]&&(s+=i.elec[e]),s}getMilestoneForYear(t){const e=this.getYearData(t);return null==e?void 0:e.milestone}getYearsWithMilestones(){return this.data.filter(t=>t.milestone).map(t=>t.year)}getAvailableFuels(){if(0===this.data.length)return[];const t=this.data[0];return Object.keys(t).filter(e=>"year"!==e&&"milestone"!==e&&"object"==typeof t[e])}getAvailableSectors(){if(0===this.data.length)return[];const t=this.data[0].elec;return t?Object.keys(t):[]}getDataStatistics(){const t=this.getYearsWithMilestones().length,e=this.getAvailableFuels().length,i=this.getAvailableSectors().length;let s=0;for(let t=1;t<this.yearsLength;t++)s+=this.years[t]-this.years[t-1];const a=this.yearsLength>1?s/(this.yearsLength-1):0;return{totalDataPoints:this.data.length,yearRange:[this.firstYear,this.lastYear],averageYearGap:a,milestonesCount:t,fuelsCount:e,sectorsCount:i}}}class h{constructor(t,e){this.dataService=t,this.configService=e,this.summary=null,this.totals=[],this.flows=[],this.labels=[],this.yearSums={},this.maxes={},this.boxTops=null,this.buildSummary()}buildSummary(){this.buildTotals(),this.buildMaxes(),this.buildBoxTops(),this.summary={totals:this.totals,flows:this.flows,labels:this.labels,maxes:this.maxes,boxTops:this.boxTops,yearSums:this.yearSums}}buildTotals(){const t=this.configService.FUELS,e=this.configService.BOX_NAMES,i=this.configService.ELEC_BOX_Y,s=this.configService.HEAT_BOX_Y,a=this.configService.TOP_Y,n=this.configService.SCALE,r=this.configService.LEFT_GAP;for(let o=0;o<this.dataService.data.length;++o){const l=this.dataService.data[o],c={year:l.year,elec:0,res:0,ag:0,indus:0,trans:0,solar:0,nuclear:0,hydro:0,wind:0,geo:0,gas:0,coal:0,bio:0,petro:0,fuel_height:0,waste:0};this.configService.hasHeatData&&(c.heat=0);const h={year:l.year,elec:i,res:0,ag:0,indus:0,trans:0,solar:0,nuclear:0,hydro:0,wind:0,geo:0,gas:0,coal:0,bio:0,petro:0};this.configService.hasHeatData&&(h.heat=s);const d={year:l.year,elec:0,res:0,ag:0,indus:0,trans:0};this.configService.hasHeatData&&(d.heat=0);for(let o=2;o<t.length;++o){const u=t[o].fuel,g=l[u];if(this.configService.hasHeatData||"heat"!=u){for(let t=0;t<e.length;++t){const i=e[t];(this.configService.hasHeatData||"heat"!=i)&&(g[i]>0&&d[i]++,c[i]+=g[i],c[u]+=g[i],2===o&&"elec"!==i&&(c[i]+=l.elec[i],c[i]+=l.waste[i]),2===o&&"heat"!==i&&this.configService.hasHeatData&&(c[i]+=l.heat[i]))}h[u]=a+c.fuel_height-5,h.elec=i-c.elec*n,this.configService.hasHeatData&&(h.heat=s-c.heat*n),c.fuel_height+=c[u]*n+r}}c.waste=l.waste.res+l.waste.ag+l.waste.indus+l.waste.trans,this.configService.hasHeatData&&(c.waste+=l.waste.heat),"milestone"in l&&(c.milestone=l.milestone),this.yearSums[l.year]=c.bio+c.coal+c.gas+c.geo+c.hydro+c.nuclear+c.petro+c.solar+c.wind,this.totals.push(c),this.flows.push(d),this.labels.push(h)}}buildMaxes(){for(let t=0;t<this.configService.BOXES.length;++t){const e=this.configService.BOXES[t].box;this.maxes[e]=Math.max(...this.totals.map(t=>t[e]))}}buildBoxTops(){this.boxTops={res:this.configService.ELEC_BOX_Y+50,heat:this.configService.HEAT_BOX_Y+50,ag:0,indus:0,trans:0},this.boxTops.ag=this.boxTops.res+this.maxes.res*this.configService.SCALE+this.configService.RIGHT_GAP,this.boxTops.indus=this.boxTops.ag+this.maxes.ag*this.configService.SCALE+this.configService.RIGHT_GAP,this.boxTops.trans=this.boxTops.indus+this.maxes.indus*this.configService.SCALE+this.configService.RIGHT_GAP}}class d{constructor(t,e,i){this.configService=t,this.dataService=e,this.summaryCalculationService=i,this.graphs=[],this.buildGraphs()}buildGraphs(){this.calculateGraphY(),console.log("calculateGraphY",JSON.stringify(this.graphs)),this.calculateGraphX(),console.log("calculateGraphX",JSON.stringify(this.graphs)),this.spaceUpsAndDowns(),console.log("spaceUpsAndDowns",JSON.stringify(this.graphs)),this.processWasteHeatFlows(),console.log("processWasteHeatFlows",JSON.stringify(this.graphs))}calculateGraphY(){const t=this.configService.SCALE,e=this.configService.ELEC_BOX_X,i=this.configService.ELEC_BOX_Y,s=this.configService.HEAT_BOX_X,a=this.configService.HEAT_BOX_Y,n=this.configService.BOX_WIDTH,r=this.configService.LEFT_X,o=this.configService.TOP_Y,l=this.configService.SR3,c=this.configService.PATH_GAP,h=this.configService.LEFT_GAP,d=this.configService.FUELS,u=this.configService.BOX_NAMES,g=this.configService.WIDTH,v=this.configService.FLOW_PATHS_ORDER,p=this.summaryCalculationService.summary;for(let f=0;f<this.dataService.data.length;++f){let y,m=[],S=o,b=i-p.totals[f].elec*t;this.configService.hasHeatData&&(y=a-p.totals[f].heat*t);let E={x:{solar:0,nuclear:0,hydro:0,wind:0,geo:0,gas:0,coal:0,bio:0,petro:0},y:{elec:0,res:0,ag:0,indus:0,trans:0}};this.configService.hasHeatData&&(E.y.heat=0);const x=this.dataService.data[f].year;let w=p.totals.filter(t=>t.year===x)[0],T=p.flows.filter(t=>t.year===x)[0],_=null,C=null,A=this.dataService.data[f].waste;for(let o=0;o<d.length;++o){let x=d[o].fuel;if(!this.configService.hasHeatData&&"heat"==x)continue;let T=this.dataService.data[f][x];T.total=0;const D=[...u].sort((t,e)=>v[x][t]-v[x][e]);for(let h=0;h<D.length;++h){const u=D[h];if(x===u)continue;if(!this.configService.hasHeatData&&"heat"==u)continue;let v={fuel:x,box:u,stroke:0,value:0,a:{x:0,y:0},b:{x:0,y:0},c:{x:0,y:0},cc:{x:0,y:0},d:{x:0,y:0}};if(_=T[D[h]]*t/2,v.value=T[D[h]],0===o?(b+=_,v.a.y=b,v.a.x=e+n,b+=_):1===o?(y+=_,v.a.y=y,v.a.x=s+n,y+=_):(S+=_,v.a.y=S,v.a.x=r),E.y[u]+=_,v.stroke=2*_,v.b.y=v.a.y,"elec"===u?(v.d.x=e,v.d.y=i-w.elec*t+E.y.elec,v.c.x=e-20-(w.elec*t-E.y.elec)/l-(d.length-o)*c,v.b.x=v.c.x-Math.abs(v.a.y-v.d.y)/l):"heat"===u?(v.d.x=s,v.d.y=a-w.heat*t+E.y.heat,v.c.x=s-20-(w.heat*t-E.y.heat)/l-(d.length-o)*c,v.b.x=v.c.x-Math.abs(v.a.y-v.d.y)/l):(v.d.x=g-n,v.d.y=p.boxTops[D[h]]+E.y[u]),v.c.y=v.d.y,E.y[u]+=_,o>1&&(S+=_),C=u,m.push(v),0===o){let e=JSON.parse(JSON.stringify(v));("elec"===x&&"res"===C||"elec"===x&&"ag"===C||"elec"===x&&"indus"===C||"elec"===x&&"trans"===C||"elec"===x&&"heat"===C&&this.configService.hasHeatData)&&(_=A[C]*t/2,b+=2*_,e.stroke=2*_,E.y[C]+=2*_,e.value=A[C]),m.push(e)}}o>1&&(S+=h)}this.graphs.push({graph:m,offsets:E,year:this.dataService.data[f].year,totals:w,flows:T})}}calculateGraphX(){this.calculateGraphXUps(),this.calculateGraphXDowns()}calculateGraphXUps(){const t=this.configService.WIDTH,e=this.configService.BOX_WIDTH,i=this.configService.SCALE,s=this.configService.SR3,a=this.configService.ELEC_GAP,n=this.configService.HSR3;for(let r=0;r<this.graphs.length;++r){let o=null;this.graphs[r].graph.filter(function(t){return t.a.y>t.d.y&&!["elec","heat"].includes(t.box)}).sort(this.sortGraphUp.bind(this)).forEach((l,c)=>{l.box!==o?(this.graphs[r].offsets.y[l.box]=l.stroke/2,l.c.x=t-e-20-l.stroke/2):l.c.x=t-e-20-(this.graphs[r].totals[l.box]*i-this.graphs[r].offsets.y[l.box])/s-c*a*n,l.b.x=l.c.x-Math.abs(l.a.y-l.c.y)/s,l.cc.x=l.c.x-Math.abs(this.graphs[r].totals.fuel_height-l.c.y)/s,o=l.box})}}calculateGraphXDowns(){const t=this.configService.WIDTH,e=this.configService.BOX_WIDTH,i=this.configService.SR3,s=this.configService.HSR3,a=this.configService.ELEC_GAP,n=this.configService.ELEC_BOX_Y;for(let r=0;r<this.graphs.length;++r){let o=null;this.graphs[r].graph.filter(function(t){return t.a.y<t.d.y&&!["elec","heat"].includes(t.box)}).sort(this.sortGraphDown.bind(this)).forEach((l,c)=>{l.box!==o?(this.graphs[r].offsets.y[l.box]=l.stroke/2,l.c.x=t-e-20-l.stroke/2):l.c.x=t-e-20-this.graphs[r].offsets.y[l.box]/i-c*a*s,l.b.x=l.c.x-Math.abs(l.a.y-l.c.y)/i,l.cc.x=l.c.x-Math.abs(n-l.c.y)/i,o=l.box})}}spaceUpsAndDowns(){const t=this.configService.PATH_GAP,e=this.configService.HSR3,i=this.configService.WIDTH,s=this.configService.BOX_WIDTH;let a=null,n=null;for(let r=0;r<this.graphs.length;++r){this.graphs[r].graph.sort(function(t,e){return e.cc.x-t.cc.x}),this.graphs[r].graph.filter(function(t){return!["elec","heat"].includes(t.box)}).forEach((i,s)=>{if(0===s)return void(a=i);let r=t*e;0===i.stroke&&(r=0),n=r-(a.cc.x-a.stroke/2-(i.cc.x+i.stroke/2)),i.cc.x-=n,i.c.x-=n,i.b.x-=n,a=i});let o=Math.max.apply(Math,this.graphs[r].graph.map(function(t){return t.cc.x}));this.graphs[r].graph.filter(function(t){return!["elec","heat"].includes(t.box)}).forEach(t=>{let e=o-(i-s-50);t.c.x-=e,t.b.x-=e})}}processWasteHeatFlows(){for(let t=0;t<this.graphs.length;++t){let e=null;this.graphs[t].graph.filter(t=>"elec"===t.fuel).sort(this.sortGraphDown.bind(this)).forEach((t,i)=>{if(i%2!=0){if(t.fuel="waste",e){const i=Math.abs(e.stroke+t.stroke);t.a.y=e.a.y+i/2,t.b.y=t.a.y,t.b.x=e.b.x-i/3.5,t.c.x=e.c.x-i/3.5,t.c.y=e.c.y+i/2,t.d.y=t.c.y}}else e=t})}}sortGraphUp(t,e){const i=this.configService.BOX_NAMES,s=this.configService.FUEL_NAMES;return i.indexOf(t.box)<i.indexOf(e.box)?1:i.indexOf(t.box)>i.indexOf(e.box)?-1:s.indexOf(t.fuel)<s.indexOf(e.fuel)?1:s.indexOf(t.fuel)>s.indexOf(e.fuel)?-1:0}sortGraphDown(t,e){const i=this.configService.BOX_NAMES,s=this.configService.FUEL_NAMES;return i.indexOf(t.box)<i.indexOf(e.box)?-1:i.indexOf(t.box)>i.indexOf(e.box)?1:s.indexOf(t.fuel)<s.indexOf(e.fuel)?-1:s.indexOf(t.fuel)>s.indexOf(e.fuel)?1:0}sigfig2(t){if(null==t||"string"==typeof t&&""===t.trim())return console.warn("sigfig2 received invalid input:",t),0;let e;if("string"==typeof t){if(e=parseFloat(t),isNaN(e))return console.warn("sigfig2 could not parse string to number:",t),0}else e=t;return isNaN(e)?(console.warn("sigfig2 received NaN:",t),0):e>1&&e<10?Number.parseFloat(e.toPrecision(1)):Number.parseFloat(e.toPrecision(2))}createLine(){return i.line().x(function(t){return t.x}).y(function(t){return t.y})}}class u{constructor(t,e,s,a,n){this.configService=t,this.summaryCalculationService=e,this.graphCalculationService=s,this.dataService=a,this.eventBus=n,this.lineGenerator=i.line().x(t=>t.x).y(t=>t.y)}drawHeader(){const t=i.select(".title_container").style("height","58px").append("svg").attr("id","title").attr("width",this.configService.WIDTH).attr("height",58),e=this.configService.options.country,s=t.append("text").text(`${e} energy usage`).attr("text-anchor","end").attr("x",this.configService.ELEC_BOX_X-10).attr("y","1.5em").attr("class","title");s.append("tspan").text("0 W/capita").attr("text-anchor","end").attr("x",this.configService.ELEC_BOX_X-13).attr("dy","1.4em").attr("class","unit year-total animate title-bottom").attr("data-incr","0").attr("data-value","0");const a=this.dataService.firstYear,n=this.dataService.lastYear;s.append("tspan").text(a.toString()).attr("text-anchor","start").attr("x",this.configService.ELEC_BOX_X).attr("dy","0em").attr("class","year animate").attr("data-incr","0").attr("data-value",a),t.append("text").text(`Energy Transitions in ${e} History, ${a}-${n}`).attr("x",this.configService.ELEC_BOX_X+this.configService.BOX_WIDTH+25).attr("y","1.5em").attr("class","affiliation").append("tspan").text("Suits, Matteson, and Moyer (2020)").attr("class","affiliation-bottom").attr("x",this.configService.ELEC_BOX_X+this.configService.BOX_WIDTH+25).attr("dy","1.4em");const r=this.configService.ELEC_BOX_X+this.configService.BOX_WIDTH+(this.configService.WIDTH-(this.configService.ELEC_BOX_X+this.configService.BOX_WIDTH))/2+50;t.append("text").text("Center for Robust Decision-making on").attr("x",r).attr("y","1.5em").attr("class","affiliation").append("tspan").text("Climate and Energy Policy, UChicago").attr("class","affiliation-bottom").attr("x",r).attr("dy","1.4em")}drawLeftLabels(t,e){const i=this.configService.FUELS.slice(2);t.selectAll(".fuel-label-left").data(i).join("text").attr("class",t=>`label animate fuel ${t.fuel} fuel-label-left`).text(t=>t.name).attr("x",this.configService.LEFT_X).attr("y",(t,s)=>{let a=this.configService.TOP_Y;for(let t=0;t<s;t++){const s=i[t];a+=(e[s.fuel]||0)*this.configService.SCALE+this.configService.LEFT_GAP}return a-5}).attr("data-incr","0").attr("data-fuel",t=>t.fuel).attr("data-value",t=>{const i=e[t.fuel]||0;return this.graphCalculationService.sigfig2(i)}).classed("hidden",t=>0===(e[t.fuel]||0))}drawBoxes(t,e){const i=this.summaryCalculationService.summary.boxTops;for(let s=0;s<this.configService.BOXES.length;s++){const a=this.configService.BOXES[s];let n,r;if(!this.configService.hasHeatData&&"heat"==a.box)continue;"elec"===a.box?(n=this.configService.ELEC_BOX_X,r=this.configService.ELEC_BOX_Y-e.elec*this.configService.SCALE):"heat"===a.box?(n=this.configService.HEAT_BOX_X,r=this.configService.HEAT_BOX_Y-e.heat*this.configService.SCALE):(n=this.configService.WIDTH-this.configService.BOX_WIDTH,r=i[a.box]||0);const o=e[a.box]||0;t.append("rect").attr("x",n).attr("y",r).attr("width",this.configService.BOX_WIDTH).attr("height",o>0?o*this.configService.SCALE+this.configService.BLEED:0).attr("class",`box sector animate ${a.box}`).classed("fuel",["elec","heat"].includes(a.box)).attr("data-sector",a.box).attr("data-fuel",["elec","heat"].includes(a.box)?a.box:"").attr("data-incr","0");const l=t.append("text").text("res"===a.box?"Residential":a.name).attr("x",n).attr("y",r-5).attr("dy","res"===a.box?"-1.8em":"-0.8em").attr("data-sector",a.box).attr("data-fuel",["elec","heat"].includes(a.box)?a.box:"").attr("class",`label sector animate ${a.box}`).classed("hidden",0===o).classed("fuel",["elec","heat"].includes(a.box));"res"===a.box&&l.append("tspan").text("/Commercial").attr("x",n).attr("dy","1em").attr("data-incr","0"),l.append("tspan").attr("class",`total sector animate ${a.box}`).attr("data-sector",a.box).attr("data-value",o).text(this.graphCalculationService.sigfig2(o)).attr("x",n).attr("dy","1.2em").attr("data-incr","0"),l.append("tspan").attr("class",`total waste-level sector animate ${a.box}`).attr("data-sector",a.box).attr("data-value","0").text(this.graphCalculationService.sigfig2(0)).attr("x",n+this.configService.BOX_WIDTH).attr("dy","0").attr("text-anchor","end").attr("data-incr","0")}}drawFlows(t,e,i){const s=this.graphCalculationService.graphs[e];console.log("graphData.graph",s.graph);const a=s.graph.filter(t=>null!==t.b.x&&void 0!==t.b.x).reduce((t,e)=>(t[e.fuel]||(t[e.fuel]=[]),t[e.fuel].push(e),t),{});console.log("strokesByFuel",a),Object.entries(a).forEach(([e,s])=>{t.select(`.fuel.${e}`).selectAll(".flow-path").data(s).join("path").attr("class",t=>`flow animate ${t.fuel} ${t.box} flow-path`).attr("d",t=>this.parseLineData(t)).attr("stroke-width",t=>t.stroke>0?t.stroke+this.configService.BLEED:0).attr("data-fuel",t=>t.fuel).attr("data-sector",t=>t.box).attr("data-incr","0").attr("stroke-linejoin",t=>"waste"!==t.fuel?"round":"").on("mouseover",(e,s)=>{i.attr("style",""),i.transition().duration(200).style("opacity",.9);const a=this.configService.getFuelDisplayName(s.fuel),n=this.configService.getBoxDisplayName(s.box),r=this.graphCalculationService.sigfig2(s.value),o=(null==e?void 0:e.pageX)||0,l=(null==e?void 0:e.pageY)||0;i.html(`${a} → ${n}<div class='fuel_value'>${r}</div>`).style("left",`${o}px`).style("top",l-28+"px"),this.highlightFuel(t,a)}).on("mouseout",()=>{i.transition().duration(500).style("opacity",0),this.resetHighlight(t)})}),this.eventBus.emit({type:"rendering.completed",timestamp:Date.now(),source:"ChartRenderingService",data:{type:"flows",year:s.year,flowCount:s.graph.length}})}clearChart(t){t.selectAll("*").remove()}drawInitialChart(t,e){const i=this.graphCalculationService.graphs;for(const e of this.configService.FUELS)t.append("g").attr("class",`fuel ${e.fuel}`);return t.append("g").attr("class","fuel waste"),this.drawHeader(),this.drawFlows(t,0,e),this.drawLeftLabels(t,i[0].totals),this.drawBoxes(t,i[0].totals),!0}highlightFuel(t,e){t.selectAll(".flow").style("opacity",function(){return i.select(this).attr("data-fuel")===e?1:.3})}resetHighlight(t){t.selectAll(".flow").style("opacity",function(){return"waste"===i.select(this).attr("data-fuel")?.6:.8})}parseLineData(t){const e=[t.a,t.b,t.c,t.d];return this.lineGenerator(e)||""}}var g=Object.freeze({__proto__:null,AnimationService:class{constructor(t,e,i,s,a,n,r){this.configService=t,this.summaryCalculationService=e,this.graphCalculationService=i,this.dataService=s,this.options=a,this.eventBus=n,this.logger=r,this.state={currentYearIndex:0,isAnimating:!1,animationTimer:null,speed:200},this.svg=null,this.tooltip=null,this.graphs=[],this.graphNest=null,this.sliderWidth=null,this.state.speed=this.configService.SPEED}setupAnimation(t,e,i,s){this.logger.log("AnimationService: Setting up animation controls..."),this.state.currentYearIndex=0,this.svg=i,this.tooltip=s,this.graphs=t,this.graphNest=e,this.setupTimelineControls(),this.setYear(this.dataService.firstYear),this.logger.log(`AnimationControlService: Animation setup complete for ${this.dataService.yearsLength} years`)}setupTimelineControls(){const t=this;i.select("#rangeSlider").on("input",function(){const e=parseFloat(this.value);t.setYear(e),t.updateSliderIndicator()});const e=document.getElementById("rangeSlider");this.sliderWidth=e?e.getBoundingClientRect().width:1200;const s=i.select("#axisTop").style("margin","-5px").style("margin-left","5px").append("svg").attr("id","sliderYear").attr("width",this.sliderWidth).attr("height",40).attr("preserveAspectRatio","xMinYMin meet").attr("viewBox",`0 0 ${this.sliderWidth} 40`),a=i.select("#testTick").style("height","15px").style("margin","-5px").style("margin-top","-7px").style("margin-left","5px").append("svg").attr("id","slider").attr("width",this.sliderWidth).attr("height",50).attr("preserveAspectRatio","xMinYMin meet").attr("viewBox",`0 0 ${this.sliderWidth} 50`);this.setupMilestones(s,a);const n=document.getElementById("rangeSlider");n&&(n.min=this.dataService.firstYear.toString(),n.max=this.dataService.lastYear.toString(),n.value=this.dataService.firstYear.toString(),this.updateSliderIndicator())}setupMilestones(t,e){const s=i.scaleLinear().range([0,this.sliderWidth-this.configService.LEFT_X]).domain([this.dataService.firstYear,this.dataService.lastYear]);let a=15;this.dataService.yearsLength<50&&(a=5);const n=[];for(let t=5;t<this.dataService.yearsLength;t+=a)n.push(this.dataService.years[t]);const r=i.axisTop(s).tickValues(n).tickFormat(t=>Math.floor(t).toString());t.append("g").attr("transform","translate(0, 53)").call(r).call(t=>t.select(".domain").remove()).call(t=>t.selectAll("line").remove()).selectAll(".tick text").attr("y",-25);const o=this.dataService.getYearsWithMilestones(),l=i.axisBottom(s).tickValues(o).tickFormat(()=>"●"),c=e.append("g").attr("transform","translate(4, 0)").call(l).call(t=>t.select(".domain").remove());c.selectAll(".tick text").attr("data-toggle","dialog").style("font-size","20px").attr("y",3),this.setupMilestoneDialogs(c)}setupMilestoneDialogs(t){const e=this.createMilestoneDialog();setTimeout(()=>{document.addEventListener("click",t=>{const i=document.getElementById("dialog");if(!i||!e.isOpen)return;const s=i.contains(t.target),a=t.target.closest(".tick");s||null!==a||e.close()},!0)},100);const s=this;t.selectAll(".tick").select("text").on("click",function(t,a){const n=a;t&&t.stopPropagation(),s.setYear(n);const r=i.select("#rangeSlider").node();r&&(r.focus(),r.value=n.toString()),s.updateSliderIndicator(),s.state.animationTimer&&s.pause(),i.select("#play-button").classed("playbutton",!0);const o=s.dataService.getYearData(n);if(!(null==o?void 0:o.milestone))return;const l=o.year>=1800&&o.year<=1862,c=(o.year>=1877&&o.year,o.year>=1947&&o.year<=2019),h=`<b>${o.year}: </b>${o.milestone}`,d=Math.ceil(s.sliderWidth/2),u=this.getBoundingClientRect(),g=window.pageYOffset||document.documentElement.scrollTop,v=window.pageXOffset||document.documentElement.scrollLeft;let p;p=l?u.left+v:c?u.right+v-d:u.left+v+u.width/2-d/2,p=Math.max(10,Math.min(p,window.innerWidth-d-10)),e.isOpen&&e.close(),setTimeout(()=>{e.html(h);const t=u.bottom+g+5;e.dialog&&(e.dialog.style.width=`${d}px`,e.dialog.style.left=`${p}px`,e.dialog.style.top=`${t}px`),e.open(),window.innerWidth<768&&(e.dialog.style.width=window.innerWidth-20+"px")},20)}).style("cursor","pointer").attr("title",function(t){const e=t,i=s.dataService.getYearData(e);return(null==i?void 0:i.milestone)?`Click to see ${e} milestone`:""})}createMilestoneDialog(){let t=document.getElementById("dialog");return t||(t=document.createElement("div"),t.id="dialog",t.style.opacity="0",document.body.appendChild(t)),{isOpen:!1,dialog:t,open(){this.dialog.style.display="block",this.dialog.style.visibility="visible",this.dialog.style.position="absolute",this.dialog.style.background="#fff",this.dialog.style.border="1px solid #ddd",this.dialog.style.borderRadius="4px",this.dialog.style.boxShadow="0 4px 12px rgba(0,0,0,0.15)",this.dialog.style.padding="15px",this.dialog.style.zIndex="1000",this.dialog.style.fontSize="14px",this.dialog.style.lineHeight="1.4",this.dialog.style.color="#333",this.dialog.style.maxWidth="90vw",this.dialog.style.opacity="1",this.isOpen=!0},close(){this.dialog.style.opacity="0",this.dialog.style.display="none",this.isOpen=!1},html(t){this.dialog.innerHTML=t},option(t){t.width&&(this.dialog.style.width="number"==typeof t.width?`${t.width}px`:t.width)}}}setupPlayControls(){const t=document.getElementById("play-button");t&&(t.addEventListener("click",()=>{this.state.isAnimating?this.pause():this.play()}),t.className="playbutton",this.state.isAnimating=!1)}setupYearDisplay(){const t=document.getElementById("dynamicYear");t&&(t.textContent=this.dataService.years[0].toString())}setYear(t){const e=this.dataService.getYearIndex(t);if(-1===e)return void console.warn(`AnimationControlService: Year ${t} not found in available years`);const i=this.getCurrentYear();this.state.currentYearIndex=e;const s=document.getElementById("rangeSlider");s&&(s.value=t.toString()),this.animatePeriod(e),this.updateYearDisplay(t),this.eventBus.emit({type:"year.changed",timestamp:Date.now(),source:"AnimationControlService",data:{year:t,previousYear:i,yearIndex:e,isAnimating:this.state.isAnimating}})}animatePeriod(t){if(!this.svg||!this.graphs||!this.graphNest)return;const e=this.svg,s=this.tooltip,a=this.graphs,n=this.graphNest;e.selectAll(".label").classed("hidden",function(){var e,s;const n=i.select(this);if(n.classed("sector")){const i=n.attr("data-sector");return(null===(e=a[t])||void 0===e?void 0:e.totals[i])<=0}if(n.classed("fuel")){const e=n.attr("data-fuel");return(null===(s=a[t])||void 0===s?void 0:s.totals[e])<=0}return!1});const r=this.configService,o=this.graphCalculationService,l=this.summaryCalculationService,c=this.dataService.years;i.selectAll(".animate").on("mouseover",function(e){var n;if(!s)return;const l=i.select(this);if(l.classed("flow")){const i=l.attr("data-fuel"),c=l.attr("data-sector"),h=null===(n=a[t])||void 0===n?void 0:n.graph.find(t=>t.fuel===i&&t.box===c);if(h){s.attr("style",""),s.transition().duration(200).style("opacity",1);const t=r.getFuelDisplayName(h.fuel),i=r.getBoxDisplayName(h.box),a=o.sigfig2(h.value),n=(null==e?void 0:e.pageX)||0,l=(null==e?void 0:e.pageY)||0;s.html(`${t} → ${i}<div class='fuel_value'>${a}</div>`).style("left",`${n}px`).style("top",l-35+"px")}}else if(l.classed("fuel")&&!l.classed("elec")&&!l.classed("heat")){if(!s)return;s.attr("style",""),s.transition().duration(200).style("opacity",1);const t=l.attr("data-fuel"),i=parseFloat(l.attr("data-value")||"0"),a=r.getFuelDisplayName(t),n=(null==e?void 0:e.pageX)||0,c=(null==e?void 0:e.pageY)||0;s.html(`${a} → ${o.sigfig2(i)}`).style("left",`${n}px`).style("top",c-35+"px")}}).on("mouseout",()=>{s&&s.transition().duration(500).style("opacity",0)}).transition().duration(5*this.configService.SPEED).ease(i.easeLinear).on("start",function(){const e=i.select(this),s=i.active(this);s&&s.attr("d",function(){var i;if(e.classed("flow")){const s=e.attr("data-fuel"),n=e.attr("data-sector"),r=null===(i=a[t])||void 0===i?void 0:i.graph.find(t=>t.fuel===s&&t.box===n);if(r){return o.createLine()([r.a,r.b,r.c,r.d])}}return e.attr("d")}).attr("stroke-width",function(){if(e.classed("flow")){let i=n.strokes[c[t]][e.attr("data-fuel")][e.attr("data-sector")];return i>0?i+r.BLEED:0}return e.attr("stroke-width")}).attr("y",function(){return e.classed("box")&&e.classed("fuel")?n.tops[c[t]][e.attr("data-fuel")]:e.classed("label")&&e.classed("fuel")?n.tops[c[t]][e.attr("data-fuel")]-5:e.attr("y")}).attr("height",function(){return e.classed("box")&&e.classed("sector")?n.heights[c[t]][e.attr("data-sector")]:e.attr("height")}).attr("data-value",function(){return e.classed("label")&&e.classed("fuel")&&!e.classed("elec")&&!e.classed("heat")?a[t].totals[e.attr("data-fuel")]:e.attr("data-value")}).tween("text",function(){var i;const s=this;if(e.classed("year")){const e=parseInt(s.textContent||"0"),i=c[t];return function(t){const a=e+(i-e)*t;s.setAttribute("data-value",a.toString()),s.textContent=Math.round(a).toString()}}if(e.classed("year-total"))return function(e){const i=l.yearSums,a=Math.floor(i[c[t]]||0);s.setAttribute("data-value",a.toString()),s.textContent=`${Math.round(a)} W/capita`};if(e.classed("waste-level")){const e=parseFloat(s.getAttribute("data-value")||"0"),a=(null===(i=n.waste[c[t]])||void 0===i?void 0:i[s.getAttribute("data-sector")||""])||0;return function(t){const i=e+(a-e)*t;s.setAttribute("data-value",i.toString()),s.textContent=(o.sigfig2(i)||0).toString()}}if(e.classed("total")){const e=parseFloat(s.getAttribute("data-value")||"0"),i=a[t].totals[s.getAttribute("data-sector")||""]||0;return function(t){const a=e+(i-e)*t;s.setAttribute("data-value",a.toString()),s.textContent=o.sigfig2(a).toString()}}return null})}),this.updateSliderIndicator()}updateSliderIndicator(){const t=document.getElementById("rangeSlider"),e=document.getElementById("dynamicYear");if(!t||!e)return;const i=this.getCurrentYear(),s=this.dataService.firstYear,a=this.dataService.lastYear,n=this.calculateIndicatorPosition(t,i,s,a);this.applyIndicatorPosition(e,n,i)}calculateIndicatorPosition(t,e,i,s){return 5.5+(e-i)/(s-i)*(t.getBoundingClientRect().width-11)-26}applyIndicatorPosition(t,e,i){t.style.left=`${e}px`,t.textContent=i.toString(),this.logger.log(`AnimationControlService: Indicator positioned: ${e.toFixed(1)}px for year ${i}`)}updateYearDisplay(t){const e=document.getElementById("dynamicYear");e&&(e.textContent=t.toString())}play(){if(this.state.isAnimating)return;this.state.isAnimating=!0;const t=document.getElementById("play-button");t&&(t.className="playpaused"),this.state.animationTimer=window.setInterval(()=>{this.nextFrame()},this.state.speed),this.eventBus.emit({type:"animation.started",timestamp:Date.now(),source:"AnimationControlService",data:{isPlaying:!0,currentYear:this.getCurrentYear(),speed:this.state.speed}})}pause(){if(!this.state.isAnimating)return;this.state.isAnimating=!1;const t=document.getElementById("play-button");t&&(t.className="playbutton"),this.state.animationTimer&&(clearInterval(this.state.animationTimer),this.state.animationTimer=null),this.eventBus.emit({type:"animation.stopped",timestamp:Date.now(),source:"AnimationControlService",data:{isPlaying:!1,currentYear:this.getCurrentYear(),speed:this.state.speed}})}nextFrame(){this.state.currentYearIndex+1>=this.dataService.yearsLength&&(this.state.currentYearIndex=0,!this.options.loopAnimation)?this.pause():this.nextYear()}setSpeed(t){if(t<=0)throw new Error("Animation speed must be positive");this.state.speed=t,this.state.isAnimating&&(this.pause(),setTimeout(()=>{this.play()},100)),this.eventBus.emit({type:"speed.changed",timestamp:Date.now(),source:"AnimationControlService",data:{speed:t}})}isPlaying(){return this.state.isAnimating}getCurrentYear(){return this.dataService.years[this.state.currentYearIndex]||this.dataService.firstYear}nextYear(){this.state.currentYearIndex<this.dataService.yearsLength-1&&(this.state.currentYearIndex++,this.setYear(this.dataService.years[this.state.currentYearIndex]))}previousYear(){this.state.currentYearIndex>0&&(this.state.currentYearIndex--,this.setYear(this.dataService.years[this.state.currentYearIndex]))}cleanup(){this.pause(),this.state.currentYearIndex=0,this.state.isAnimating=!1,this.svg=null,this.tooltip=null,this.graphs=[],this.graphNest=null}}});var v=Object.freeze({__proto__:null,InteractionService:class{constructor(t,e,i,s){this.animationControlService=t,this.dataService=e,this.eventBus=i,this.logger=s,this.state={isMouseDown:!1,lastMousePosition:{x:0,y:0},selectedElement:null,isDragging:!1,touchStartTime:0,keyboardShortcutsEnabled:!0},this.handlers={},this.eventListeners=[]}initializeInteractions(t,e){this.logger.log("InteractionService: Initializing comprehensive multi-platform interactions..."),this.setupMouseInteractions(t,e),this.setupTouchInteractions(t,e),this.enableKeyboardNavigation(),this.setupSliderInteractions(),this.setupButtonInteractions(),this.eventBus.emit({type:"system.initialized",timestamp:Date.now(),source:"InteractionService",data:{mouseEnabled:!0,touchEnabled:"ontouchstart"in window,keyboardEnabled:this.state.keyboardShortcutsEnabled}}),this.logger.log("InteractionService: All interaction modalities initialized successfully")}setupMouseInteractions(t,e){const i=t.node();if(!i)return;this.addEventListener(i,"mousemove",t=>{this.state.lastMousePosition={x:t.clientX,y:t.clientY};const e=t.target;e&&(e.classList.contains("flow")||e.classList.contains("box"))&&this.handleElementHover(e,t)}),this.addEventListener(i,"click",t=>{const e=t.target;e&&this.handleElementClick(e,t)}),this.addEventListener(i,"mousedown",t=>{this.state.isMouseDown=!0,this.state.selectedElement=t.target}),this.addEventListener(document,"mouseup",()=>{this.state.isMouseDown=!1,this.state.selectedElement=null,this.state.isDragging=!1}),this.logger.log("InteractionService: Mouse interactions enabled")}setupTouchInteractions(t,e){const i=t.node();if(!i||!("ontouchstart"in window))return;this.addEventListener(i,"touchstart",t=>{this.state.touchStartTime=Date.now();const e=t.touches[0];e&&(this.state.lastMousePosition={x:e.clientX,y:e.clientY})}),this.addEventListener(i,"touchend",t=>{if(Date.now()-this.state.touchStartTime<300){const e=t.changedTouches[0];if(e){const t=document.elementFromPoint(e.clientX,e.clientY);if(t){const i=new MouseEvent("click",{clientX:e.clientX,clientY:e.clientY,bubbles:!0});this.handleElementClick(t,i)}}}}),this.addEventListener(i,"touchmove",t=>{t.preventDefault();const e=t.touches[0];e&&(this.state.lastMousePosition={x:e.clientX,y:e.clientY})}),this.logger.log("InteractionService: Touch interactions enabled")}enableKeyboardNavigation(){if(this.state.keyboardShortcutsEnabled)return;this.addEventListener(document,"keydown",t=>{t.target instanceof HTMLInputElement||t.target instanceof HTMLTextAreaElement||t.target instanceof HTMLSelectElement||this.handleKeyboardNavigation(t)}),this.state.keyboardShortcutsEnabled=!0,this.logger.log("InteractionService: Keyboard navigation enabled")}disableKeyboardNavigation(){this.state.keyboardShortcutsEnabled=!1,this.logger.log("InteractionService: Keyboard navigation disabled")}handleKeyboardNavigation(t){const e=t.key.toLowerCase();switch(e){case" ":case"enter":t.preventDefault(),this.animationControlService.isPlaying()?this.animationControlService.pause():this.animationControlService.play();break;case"arrowleft":t.preventDefault(),this.animationControlService.previousYear();break;case"arrowright":t.preventDefault(),this.animationControlService.nextYear();break;case"home":t.preventDefault(),this.animationControlService.setYear(this.dataService.firstYear);break;case"end":t.preventDefault(),this.animationControlService.setYear(this.dataService.lastYear);break;case"escape":t.preventDefault(),this.animationControlService.pause()}this.eventBus.emit({type:"interaction.keypress",timestamp:Date.now(),source:"InteractionService",data:{key:e,ctrlKey:t.ctrlKey,shiftKey:t.shiftKey,altKey:t.altKey}}),this.handlers.onKeyboardNavigation&&this.handlers.onKeyboardNavigation(e,t)}setupSliderInteractions(){const t=document.getElementById("rangeSlider");if(!t)return;this.addEventListener(t,"input",t=>{const e=t.target,i=parseFloat(e.value);this.animationControlService.setYear(i),this.eventBus.emit({type:"interaction.slider",timestamp:Date.now(),source:"InteractionService",data:{year:i,value:i}}),this.handlers.onSliderInteraction&&this.handlers.onSliderInteraction(i,t)}),this.logger.log("InteractionService: Slider interactions enabled")}setupButtonInteractions(){const t=document.getElementById("play-button");if(t){const e=t=>{t.preventDefault(),this.animationControlService.isPlaying()?this.animationControlService.pause():this.animationControlService.play(),this.eventBus.emit({type:"interaction.button",timestamp:Date.now(),source:"InteractionService",data:{buttonId:"play-button",action:this.animationControlService.isPlaying()?"play":"pause"}})};this.addEventListener(t,"click",e)}document.querySelectorAll("[data-speed]").forEach(t=>{this.addEventListener(t,"click",t=>{const e=t.target,i=parseInt(e.dataset.speed||"200");this.animationControlService.setSpeed(i),this.eventBus.emit({type:"interaction.button",timestamp:Date.now(),source:"InteractionService",data:{buttonId:e.id,action:"speed-change",speed:i}})})}),this.logger.log("InteractionService: Button interactions enabled")}setupAccessibilityFeatures(t){const e=t.node();if(!e)return;e.setAttribute("role","img"),e.setAttribute("aria-label","Interactive U.S. Energy Usage Sankey Diagram"),e.setAttribute("tabindex","0");this.addEventListener(e,"focus",()=>{e.style.outline="2px solid #007cba"}),this.addEventListener(e,"blur",()=>{e.style.outline="none"});const i=document.getElementById("rangeSlider");i&&(i.setAttribute("aria-label","Select year for energy data visualization"),i.setAttribute("role","slider"));const s=document.getElementById("play-button");s&&(s.setAttribute("aria-label","Play or pause animation"),s.setAttribute("role","button")),this.logger.log("InteractionService: Accessibility features enabled")}handleElementHover(t,e){t.classList.add("hovered"),document.querySelectorAll(".hovered").forEach(e=>{e!==t&&e.classList.remove("hovered")}),this.eventBus.emit({type:"interaction.hover",timestamp:Date.now(),source:"InteractionService",data:{elementType:t.classList.contains("flow")?"flow":"box",fuel:t.getAttribute("data-fuel"),sector:t.getAttribute("data-sector"),mousePosition:{x:e.clientX,y:e.clientY}}}),this.handlers.onElementHover&&this.handlers.onElementHover(t,e)}handleElementClick(t,e){this.eventBus.emit({type:"interaction.click",timestamp:Date.now(),source:"InteractionService",data:{elementType:t.classList.contains("flow")?"flow":"box",fuel:t.getAttribute("data-fuel"),sector:t.getAttribute("data-sector"),mousePosition:{x:e.clientX,y:e.clientY}}}),this.handlers.onElementClick&&this.handlers.onElementClick(t,e)}setInteractionHandlers(t){this.handlers={...this.handlers,...t},this.logger.log("InteractionService: Custom handlers updated")}addEventListener(t,e,i){t.addEventListener(e,i),this.eventListeners.push({element:t,event:e,handler:i})}cleanup(){this.eventListeners.forEach(({element:t,event:e,handler:i})=>{t.removeEventListener(e,i)}),this.eventListeners=[],this.state={isMouseDown:!1,lastMousePosition:{x:0,y:0},selectedElement:null,isDragging:!1,touchStartTime:0,keyboardShortcutsEnabled:!1},this.handlers={},document.querySelectorAll(".hovered, .selected").forEach(t=>{t.classList.remove("hovered","selected")}),this.logger.log("InteractionService: Cleanup completed")}getInteractionState(){return{...this.state}}isInteractionEnabled(){return this.eventListeners.length>0}getInteractionStats(){return{eventListeners:this.eventListeners.length,keyboardEnabled:this.state.keyboardShortcutsEnabled,touchSupported:"ontouchstart"in window,lastInteraction:this.state.lastMousePosition}}triggerElementHover(t){if(t){const e=new MouseEvent("mouseover",{bubbles:!0,clientX:this.state.lastMousePosition.x,clientY:this.state.lastMousePosition.y});this.handleElementHover(t,e)}}triggerElementClick(t){if(t){const e=new MouseEvent("click",{bubbles:!0,clientX:this.state.lastMousePosition.x,clientY:this.state.lastMousePosition.y});this.handleElementClick(t,e)}}}});return class{constructor(t,e){this.services={},this.svg=null,this.tooltip=null,this.initialized=!1,this.destroyed=!1,this.subscriptions=[],this.logger=new r(e),this.validateInputs(t,e),this.container=this.resolveContainer(t),this.options=this.mergeOptionsWithDefaults(e),this.wasteHeatVisible=!1!==this.options.showWasteHeat,this.eventBus=new s(this.logger),this.setupSystemEventListeners(),this.initialize()}async initialize(){const t=performance.now();try{this.eventBus.emit({type:"system.initialized",timestamp:Date.now(),source:"SankeyVisualization",data:{version:"7.0.0",services:[],initTime:0}}),this.logger.log("SankeyVisualization: Creating infrastructure services..."),await this.createConfigurationService(),this.logger.log("SankeyVisualization: Creating data processing services..."),await this.createDataServices(),this.logger.log("SankeyVisualization: Creating calculation services..."),await this.createCalculationServices(),this.logger.log("SankeyVisualization: Creating rendering services..."),await this.createRenderingServices(),this.logger.log("SankeyVisualization: Creating animation services..."),await this.createAnimationService(),this.logger.log("SankeyVisualization: Creating interaction services..."),await this.createInteractionServices(),this.logger.log("SankeyVisualization: Initializing DOM structure..."),this.initializeDOMElements(),this.logger.log("SankeyVisualization: Performing initial render..."),await this.performInitialRender();const e=performance.now()-t;this.eventBus.emit({type:"system.ready",timestamp:Date.now(),source:"SankeyVisualization",data:{version:"7.0.0",totalInitTime:e,dataPointCount:this.options.data.length,yearRange:[Math.min(...this.options.data.map(t=>t.year)),Math.max(...this.options.data.map(t=>t.year))]}}),this.initialized=!0,this.logger.log(`SankeyVisualization: Complete initialization in ${e.toFixed(2)}ms`),e>500&&(this.logger.warn(`SankeyVisualization: Slow initialization detected (${e.toFixed(2)}ms) - consider data size optimization`),this.logger.warn("Performance optimization suggestions:"),this.logger.warn(" - Check dataset size: Large datasets (>100 years) may require progressive loading"),this.logger.warn(" - Verify system resources: Low memory or CPU can impact initialization"),this.logger.warn(" - Monitor network performance: Slow dynamic imports affect service loading"),this.logger.warn(" - Consider cache prewarming: Precompute frequently accessed calculations"))}catch(t){this.handleInitializationError(t)}}async createConfigurationService(){this.services.configurationService=new o(this.container,this.options,this.eventBus,this.logger),this.logger.debug("SankeyVisualization: ConfigurationService created")}async createDataServices(){this.services.dataValidationService=new l(this.services.configurationService,this.eventBus,this.logger),this.services.dataService=new c(this.options.data,this.services.dataValidationService,this.eventBus,this.logger),this.logger.debug("SankeyVisualization: Data services created ( services)")}async createCalculationServices(){this.services.summaryService=new h(this.services.dataService,this.services.configurationService),this.services.graphService=new d(this.services.configurationService,this.services.dataService,this.services.summaryService),this.logger.debug("SankeyVisualization: Calculation services created and wired (3 services)")}async createRenderingServices(){this.services.renderingService=new u(this.services.configurationService,this.services.summaryService,this.services.graphService,this.services.dataService,this.eventBus)}async createAnimationService(){const{AnimationService:t}=await Promise.resolve().then(function(){return g});this.services.animationService=new t(this.services.configurationService,this.services.summaryService,this.services.graphService,this.services.dataService,this.options,this.eventBus,this.logger)}async createInteractionServices(){const{InteractionService:t}=await Promise.resolve().then(function(){return v});this.services.interactionService=new t(this.services.animationService,this.services.dataService,this.eventBus,this.logger)}initializeDOMElements(){const t=this.services.configurationService;this.injectHTML(),this.tooltip=i.select("body").append("div").attr("class","tooltip").style("opacity",0),this.svg=i.select(".sankey").append("svg").attr("id","chart").attr("width",this.options.width||t.WIDTH).attr("height",this.options.height||t.HEIGHT)}injectHTML(){let t='\n <div class="us-energy-sankey-wrapper">\n <div class="sankey" style="line-height: 0;"></div>\n ';if(this.options.includeTimeline&&(t+=`\n <div class="range-slider">\n <div id="axisTop"></div>\n <form style="margin: -5px;margin-left: 5px;">\n <input id="rangeSlider" class="range-slider__range" type="range" \n value="${this.services.dataService.firstYear}" min="${this.services.dataService.firstYear}" max="${this.services.dataService.lastYear}" name="foo">\n <output id="dynamicYear" for="foo"></output>\n </form>\n \n <div id="testTick"></div>\n \n <div class="container" style="margin-left: 10px;margin-top: 40px;margin-bottom: 15px;padding: 0;">\n `),this.options.includeControls&&(t+='\n <div class="sidebar" style="width: 90px; float: left;">\n <span id="play-button" class="playbutton" type="button"></span>\n <button id="jButton" style="display:none"></button>\n <button id="kButton" style="display:none"></button>\n </div>\n '),this.options.includeWasteToggle&&(t+='\n <div class="content switch_box box_1" style="float: right;">\n <label id="lbl_waste_hide_show" for="waste_required">Hide electricity waste heat</label>\n <input type="checkbox" id="waste_required" name="waste" class="switch_1">\n </div>\n '),this.options.includeTimeline&&(t+="\n </div>\n </div>\n "),t+="</div>",this.options.includeTimeline&&(t+='<div id="dialog" title="" style="display: none;"></div>'),this.container.innerHTML=t,!document.querySelector(".title_container")){const t=document.createElement("div");t.className="title_container",this.container.insertBefore(t,this.container.firstChild)}const e=this.container.querySelector(".sankey");e&&(this.wasteHeatVisible||e.classList.add("waste-heat-hidden"))}async performInitialRender(){if(!this.svg||!this.tooltip)throw new a("DOM elements not initialized");const t=this.services.summaryService.summary,e=this.services.graphService.graphs;console.log("graphService",JSON.stringify(e));const i=this.buildGraphNest(e,t);console.log("buildGraphNest",JSON.stringify(e)),this.services.renderingService.drawInitialChart(this.svg,this.tooltip),this.services.animationService.setupAnimation(e,i,this.svg,this.tooltip),console.log("setupAnimation",JSON.stringify(e)),this.services.interactionService.initializeInteractions(this.svg,this.tooltip),this.setupEventListeners(),this.options.autoPlay&&this.options.includeControls&&setTimeout(()=>{this.services.animationService.play()},500)}buildGraphNest(t,e){const i=this.services.configurationService.SCALE,s={strokes:{},tops:{},heights:{},waste:{}};for(let a=0;a<t.length;++a){let n=this.services.configurationService.TOP_Y;const r=t[a].year;s.strokes[r]={},s.tops[r]={},s.heights[r]={},s.waste[r]={},s.strokes[r].waste={};for(const o of this.services.configurationService.FUELS){const l=o.fuel;s.strokes[r][l]={},"elec"==l?s.tops[r][l]=this.services.configurationService.ELEC_BOX_Y-e.totals[a].elec*i:"heat"==l?s.tops[r][l]=this.services.configurationService.HEAT_BOX_Y-e.totals[a].heat*i:(s.tops[r][l]=n,n+=e.totals[a][l]*i+this.services.configurationService.LEFT_GAP);for(const n of this.services.configurationService.BOXES){const o=n.box;s.waste[r][o]=this.services.dataService.data[a].waste[o],s.heights[r][o]=e.totals[a][o]*i;const c=t[a].graph.find(t=>t.fuel===l&&t.box===o);c&&(s.strokes[r][l][o]=c.stroke)}const c=t[a].graph.filter(t=>"waste"===t.fuel);for(const t of c)s.strokes[r][t.fuel]||(s.strokes[r][t.fuel]={}),s.strokes[r][t.fuel][t.box]=t.stroke}}return s}setupEventListeners(){if(this.options.includeWasteToggle){const t=document.getElementById("waste_required");t&&(t.checked=this.wasteHeatVisible,t.addEventListener("change",()=>{this.toggleWasteHeat()}))}this.options.includeControls&&document.addEventListener("keypress",t=>{13!==t.which&&32!==t.which||(t.preventDefault(),this.services.animationService.isPlaying()?this.services.animationService.pause():this.services.animationService.play())})}setupSystemEventListeners(){const t=this.eventBus.subscribe("system.error",t=>{console.error("System Error:",t.data)}),e=this.eventBus.subscribe("year.changed",t=>{this.logger.log(`Year changed to ${t.data.year}`)});this.subscriptions.push(t,e)}handleInitializationError(t){throw console.error("SankeyVisualization: Initialization failed:",t),this.eventBus.emit({type:"system.error",timestamp:Date.now(),source:"SankeyVisualization",data:{error:t instanceof Error?t:new Error(String(t)),context:"initialization",recoverable:!1}}),this.destroy(),t}updateWasteLabel(){const t=document.getElementById("lbl_waste_hide_show");t&&(t.textContent=this.wasteHeatVisible?"Hide electricity waste heat":"Show electricity waste heat")}play(){var t;return this.initialized?(null===(t=this.services.animationService)||void 0===t||t.play(),this):(console.warn("SankeyVisualization: Cannot play animation before initialization"),this)}pause(){var t;return this.initialized?(null===(t=this.services.animationService)||void 0===t||t.pause(),this):(console.warn("SankeyVisualization: Cannot pause animation before initialization"),this)}setYear(t){var e;return this.initialized?(null===(e=this.services.animationService)||void 0===e||e.setYear(t),this):(console.warn("SankeyVisualization: Cannot set year before initialization"),this)}getCurrentYear(){var t;return this.services.animationService?this.services.animationService.getCurrentYear():this.services.dataService?this.services.dataService.firstYear:(null===(t=this.options.data[0])||void 0===t?void 0:t.year)||1800}setSpeed(t){var e;return this.initialized?(null===(e=this.services.animationService)||void 0===e||e.setSpeed(t),this):(console.warn("SankeyVisualization: Cannot set speed before initialization"),this)}isPlaying(){var t;return this.initialized&&(null===(t=this.services.animationService)||void 0===t?void 0:t.isPlaying())||!1}isInitialized(){return this.initialized}getYears(){return this.services.dataService.years}getDataService(){return this.services.dataService}toggleWasteHeat(){this.wasteHeatVisible=!this.wasteHeatVisible;const t=document.getElementById("waste_required");t&&(t.checked=this.wasteHeatVisible);const e=this.container.querySelector(".sankey");return e&&(this.wasteHeatVisible?e.classList.remove("waste-heat-hidden"):e.classList.add("waste-heat-hidden")),this.updateWasteLabel(),this}isWasteHeatVisible(){return this.wasteHeatVisible}destroy(){this.destroyed||(this.subscriptions.forEach(t=>this.eventBus.unsubscribe(t)),this.subscriptions=[],this.eventBus.clear(),this.svg&&(this.svg.remove(),this.svg=null),this.tooltip&&(this.tooltip.remove(),this.tooltip=null),this.services={},this.destroyed=!0,this.initialized=!1)}validateInputs(t,e){if(!t)throw new a("Container ID or element is required");if(!e)throw new a("Options are required");if(!e.data||!Array.isArray(e.data)||0===e.data.length)throw new n("Data array is required and must not be empty","data");this.validateDataStructure(e.data)}validateDataStructure(t){for(let e=0;e<t.length;e++){const i=t[e];if(!i.year||"number"!=typeof i.year)throw new n(`Invalid year at index ${e}`,"year");const s=["elec","waste","solar","nuclear","hydro","wind","geo","gas","coal","bio","petro"];for(const t of s)if(!(t in i))throw new n(`Missing sector '${t}' in data point for year ${i.year}`,t)}}resolveContainer(t){if("string"==typeof t){const e=document.getElementById(t);if(!e)throw new a(`Container element not found: ${t}`);return e}if(t instanceof HTMLElement)return t;throw new a("Invalid container: must be string ID or HTMLElement")}mergeOptionsWithDefaults(t){return{data:t.data,country:t.country,includeControls:!1!==t.includeControls,includeTimeline:!1!==t.includeTimeline,includeWasteToggle:!1!==t.includeWasteToggle,autoPlay:t.autoPlay||!1,showWasteHeat:!1!==t.showWasteHeat,animationSpeed:t.animationSpeed||200,width:t.width||null,height:t.height||620,loopAnimation:void 0!==t.loopAnimation&&t.loopAnimation}}}});
|
|
2
|
+
//# sourceMappingURL=sankey.umd.min.js.map
|