<!--  [if IE]><meta http-equiv="X-UA-Compatible" content="IE=5" ><![endif]-->
<!DOCTYPE html>
<html>
<head>
    <!-- Resource Hints for Performance -->
    <link rel="preconnect" href="https://js.stripe.com" crossorigin>
    <link rel="preconnect" href="https://unpkg.com" crossorigin>
    <link rel="dns-prefetch" href="https://web-sdk.smartlook.com">
    <link rel="preload" href="js/app.min.js" as="script">
    <link rel="preload" href="js/ag-grid-community.min.js" as="script">

    <link rel="stylesheet" type="text/css" href="mxgraph/css/common.css">
    <link rel="stylesheet" type="text/css" href="styles/resizable-dialogs.css">
    <!-- Leaflet for Map Editor - loaded on demand when Map Editor opens (see showMapEditorDialog) -->
    <script type="text/javascript">
        window.loadLeafletForMapEditor = function() {
            if (window._leafletLoaded) return Promise.resolve();
            if (window._leafletLoading) return window._leafletLoading;
            window._leafletLoading = new Promise(function(resolve, reject) {
                var link = document.createElement('link');
                link.rel = 'stylesheet';
                link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
                link.integrity = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=';
                link.crossOrigin = '';
                link.onload = function() {
                    var link2 = document.createElement('link');
                    link2.rel = 'stylesheet';
                    link2.href = 'https://ppete2.github.io/Leaflet.PolylineMeasure/Leaflet.PolylineMeasure.css';
                    document.head.appendChild(link2);
                    var s1 = document.createElement('script');
                    s1.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
                    s1.integrity = 'sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=';
                    s1.crossOrigin = '';
                    s1.onload = function() {
                        var s2 = document.createElement('script');
                        s2.src = 'https://ppete2.github.io/Leaflet.PolylineMeasure/Leaflet.PolylineMeasure.js';
                        s2.onload = function() { window._leafletLoaded = true; resolve(); };
                        s2.onerror = reject;
                        document.head.appendChild(s2);
                    };
                    s1.onerror = reject;
                    document.head.appendChild(s1);
                };
                link.onerror = reject;
                document.head.appendChild(link);
            });
            return window._leafletLoading;
        };
        window.showMapEditorDialog = window.showMapEditorDialog || function(retryCount) {
            retryCount = retryCount || 0;
            var ui = (window.App && (window.App._editorUi || window.App._instance)) ||
                (window.App && window.App._instance && window.App._instance.editor && window.App._instance.editor.editorUi) ||
                (window.App && window.App.main && typeof window.App.main === 'object' && window.App.main.editor && window.App.main.editor.editorUi);
            if (!ui) {
                if (retryCount < 3) {
                    setTimeout(function() { window.showMapEditorDialog(retryCount + 1); }, 500);
                    return;
                }
                alert('Please wait for the diagram to load, then try again.');
                return;
            }
            window.loadLeafletForMapEditor().then(function() {
                return import('./js/electrisim/dialogs/MapEditorDialog.js');
            }).then(function(m) {
                new m.MapEditorDialog(ui).show();
            }).catch(function(e) {
                console.error('Map Editor load error:', e);
                alert('Map Editor failed to load. Check the browser console for details.');
            });
        };
    </script>
    
    <script type="text/javascript">
        window.mxBasePath = 'mxgraph';
        window.mxClient = { basePath: 'mxgraph' };
    </script>
    

    <script type="module" src="js/electrisim/config/environment.js"></script>
    <!-- Removed duplicate scripts (domCache, batchDOM, performanceTracker, performance-optimizer) - loaded once in electrisimScripts -->

    <!-- Defer Smartlook analytics - load after page interactive to improve initial load -->
    <script type='text/javascript'>
        window.addEventListener('load', function() {
            setTimeout(function() {
                window.smartlook||(function(d) {
                    var o=smartlook=function(){ o.api.push(arguments)},h=d.getElementsByTagName('head')[0];
                    var c=d.createElement('script');o.api=new Array();c.async=true;c.type='text/javascript';
                    c.charset='utf-8';c.src='https://web-sdk.smartlook.com/recorder.js';h.appendChild(c);
                })(document);
                smartlook('init', '31f78a73918844d1c038266ca853b5b9bd7da408', {
                    region: 'eu',
                    interceptors: {
                        error: (data, context) => {
                            if (data && data.message && /MetaMask|metamask|Failed to connect to MetaMask/i.test(data.message)) {
                                return false;
                            }
                            return true;
                        }
                    }
                });
            }, 3000);
        });
    </script>
    <script type="text/javascript">
        // Suppress MetaMask extension errors (extension injects into every page; failures are not from our app)
        window.addEventListener('unhandledrejection', function(e) {
            var msg = (e.reason && (e.reason.message || String(e.reason))) || '';
            if (/MetaMask|metamask|Failed to connect to MetaMask/i.test(msg)) {
                e.preventDefault();
                e.stopPropagation();
            }
        });
    </script>

    <title>Electrisim - Model, Simulate And Analyse Power System With the Free Cloud App</title>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="Description" content="open-source online application for modelling, simulation and analysis of power system">
    <meta name="Keywords" content="electrical analysis, power system, simulation, load flow, short circuit">
    <meta itemprop="name" content="app.electrisim.com - open-source online application for electrical system">
    <meta itemprop="description" content="open-source online application for modelling, simlulation and analysis of power system">
    <meta itemprop="image" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta name="msapplication-config" content="images/browserconfig.xml">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="theme-color" content="#48d800">

    <script type="text/javascript">
        // URL Parameters handling
        var urlParams = (function() {
            var result = new Object();
            var params = window.location.search.slice(1).split('&');
            for (var i = 0; i < params.length; i++) {
                idx = params[i].indexOf('=');
                if (idx > 0) {
                    result[params[i].substring(0, idx)] = params[i].substring(idx + 1);
                }
            }
            return result;
        })();

        // Forces CDN caches by passing URL parameters via URL hash
        if (window.location.hash != null && window.location.hash.substring(0, 2) == '#P') {
            try {
                urlParams = JSON.parse(decodeURIComponent(window.location.hash.substring(2)));
                if (urlParams.hash != null) {
                    window.location.hash = urlParams.hash;
                }
            } catch (e) {
                // ignore
            }
        }

        // Global variables
        var mxIsElectron = window && window.process && window.process.type;

		// Redirects page if required
		if (urlParams['dev'] != '1')
		{
			(function()
			{
				var proto = window.location.protocol;
				
				if (!mxIsElectron)
				{
					var host = window.location.host;
		
					// Redirects apex, drive and rt to www
					if (host === 'draw.io' || host === 'rt.draw.io' || host === 'drive.draw.io')
					{
						host = 'www.draw.io';
					}
					
					var href = proto + '//' + host + window.location.href.substring(
							window.location.protocol.length +
							window.location.host.length + 2);
		
					// Redirects if href changes
					if (href != window.location.href)
					{
						window.location.href = href;
					}
				}
			})();
		}


		/**
		 * Adds meta tag to the page.
		 */
		 function mxmeta(name, content, httpEquiv)
		{
			try
			{
				var s = document.createElement('meta');
				
				if (name != null) 
				{
					s.setAttribute('name', name);
				}

				s.setAttribute('content', content);
				
				if (httpEquiv != null) 
				{
					s.setAttribute('http-equiv', httpEquiv);
				}

			  	var t = document.getElementsByTagName('meta')[0];
			  	t.parentNode.insertBefore(s, t);
			}
			catch (e)
			{
				// ignore
			}
		};      

        // Define mxGraph core dependencies
        const mxGraphDeps = [
            { src: 'mxgraph/mxClient.js' }
        ];

        // Define initialization scripts
        const initScripts = [
            { src: 'js/PreConfig.js' },
            { src: 'js/diagramly/Init.js' }
        ];

        // Define core scripts
        const coreScripts = [
            { src: 'js/json/json2.min.js' },
            { src: 'js/jscolor/jscolor.js' },
            { src: 'js/sanitizer/sanitizer.min.js' }
        ];

        // Define utility scripts
        const utilScripts = [
            { src: 'js/ag-grid-community.min.js' }
        ];

        // Define auth and subscription scripts (ES modules)
        const authScripts = [
            { src: 'https://js.stripe.com/v3/' },
            { src: 'js/electrisim/loginsubscription/stripe.js', type: 'module', async: true },
            { src: 'js/electrisim/loginsubscription/auth-handler.js', type: 'module', async: true },
            { src: 'js/electrisim/loginsubscription/stripe-subscription.js', type: 'module', async: true }
        ];

        // Add auth fallback functions for app.min.js compatibility
        window.isAuthenticated = window.isAuthenticated || function() {
            return !!localStorage.getItem('token');
        };
        
        window.getCurrentUser = window.getCurrentUser || function() {
            const userStr = localStorage.getItem('user');
            return userStr ? JSON.parse(userStr) : null;
        };

        // Define mxGraph scripts (dependent on mxClient)
        const mxGraphScripts = [
        //    { src: 'js/mxgraph/Init.js' }
        //    { src: 'js/mxgraph/Editor.js' },
         //   { src: 'js/mxgraph/Graph.js' },
        //    { src: 'js/mxgraph/Shapes.js' },
        //    { src: 'js/mxgraph/EditorUi.js' },
        //    { src: 'js/mxgraph/Actions.js' },
        //    { src: 'js/mxgraph/Toolbar.js' },
        //    { src: 'js/mxgraph/Menus.js' },
        //    { src: 'js/mxgraph/Format.js' },
        //    { src: 'js/mxgraph/Sidebar.js' },
        //    { src: 'js/mxgraph/Dialogs.js' }
        ];

        // Define application scripts (ES modules)
        const appScripts = [
            { src: 'js/electrisim/auth-globals.js' },
            { src: 'js/app.min.js' },
           // { src: 'js/extensions.min.js' },
           // { src: 'js/shapes.min.js' },
            { src: 'js/electrisim/dialogs/EditDataDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/dialogs/ComponentsDataDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/utils/cellUtils.js', type: 'module', async: true },
            { src: 'js/electrisim/ensureSubscriptionFunctions.js', type: 'module', async: true },
            { src: 'js/electrisim/dialogs/LoadFlowDialog.js?v=1.0', type: 'module', async: true },
            { src: 'js/electrisim/dialogs/BessSizingDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/dialogs/RPCDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/dialogs/RPCResultsDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/dialogs/OptimalPowerFlowDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/dialogs/ContingencyDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/dialogs/ShortCircuitDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/lineBaseDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/LibraryDialogManager.js', type: 'module', async: true },
            { src: 'js/electrisim/lineLibraryDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/externalGridDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/generatorDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/staticGeneratorDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/asymmetricStaticGeneratorDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/busDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/transformerBaseDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/transformerLibraryDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/threeWindingTransformerBaseDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/threeWindingTransformerLibraryDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/shuntReactorDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/capacitorDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/loadDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/asymmetricLoadDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/shortCircuit.js', type: 'module', async: true },
            { src: 'js/electrisim/impedanceDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/wardDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/extendedWardDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/motorDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/storageDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/SSCDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/SVCDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/TCSCDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/dcLineDialog.js', type: 'module', async: true },
            { src: 'js/electrisim/configureAttributes.js', type: 'module', async: true },
            { src: 'js/electrisim/supportingFunctions.js', type: 'module', async: true },
            { src: 'js/electrisim/loadFlow.js', type: 'module', async: true },
            { src: 'js/electrisim/loadflowOpenDss.js', type: 'module', async: true },
            { src: 'js/electrisim/bessSizing.js', type: 'module', async: true },
            { src: 'js/electrisim/rpcAnalysis.js', type: 'module', async: true },
            { src: 'js/electrisim/optimalPowerFlow.js', type: 'module', async: true },
            { src: 'js/electrisim/shortCircuit.js', type: 'module', async: true },
            { src: 'js/electrisim/contingencyAnalysis.js', type: 'module', async: true }
        ];

        function checkAllLoaded() {
            if (mxScriptsLoaded && mxWinLoaded) {
                try {
                    // Remove loading screen first
                    var geInfo = document.getElementById('geInfo');
                    if (geInfo != null) {
                        geInfo.parentNode.removeChild(geInfo);
                    }
                    
                    if (typeof App !== 'undefined' && App.main) {
                        App.main(function(app) {
                            window.App._instance = app;
                            window.App._editorUi = app;
                            if (app && app.editor) {
                                window.App.main.editor = app.editor;
                            }
                            if (typeof window.initializePerformanceOptimizers === 'function' &&
                                app && app.editor && app.editor.graph) {
                                window.initializePerformanceOptimizers(app.editor.graph);
                            }
                        });
                    }
                } catch (e) {
                    console.error('Error in checkAllLoaded:', e);
                }
            }
        }

        function mxscript(src, onLoad, id, dataAppKey, noWrite, type) {
            type = type || 'text/javascript';
            var defer = onLoad == null && !noWrite;
            
            if ((urlParams['dev'] != '1' && typeof document.createElement('canvas').getContext === "function") ||
                onLoad != null || noWrite) {
                var s = document.createElement('script');
                s.setAttribute('type', type);
                s.setAttribute('src', src);
                if (defer) s.setAttribute('defer', 'true');
                if (id != null) s.setAttribute('id', id);
                if (dataAppKey != null) s.setAttribute('data-app-key', dataAppKey);
                
                if (onLoad != null) {
                    var r = false;
                    s.onload = s.onreadystatechange = function() {
                        if (!r && (!this.readyState || this.readyState == 'complete')) {
                            r = true;
                            onLoad();
                        }
                    };
                }
                
                var t = document.getElementsByTagName('script')[0];
                t.parentNode.insertBefore(s, t);
            } else {
                document.write('<script src="' + src + '"' + 
                    ((id != null) ? ' id="' + id +'" ' : '') +
                    ((dataAppKey != null) ? ' data-app-key="' + dataAppKey +'" ' : '') + 
                    '></scr' + 'ipt>');
            }
        }

		/**
		 * Asynchronously adds scripts to the page.
		 */
		 function mxinclude(src)
		{
			var g = document.createElement('script');
			g.type = 'text/javascript';
			g.async = true;
			g.src = src;
			
		    var s = document.getElementsByTagName('script')[0];
		    s.parentNode.insertBefore(g, s);
		};
        // Sequential script loading function
        function loadScriptSequence(scripts, onComplete) {
            let index = 0;
            function loadNext() {
                if (index < scripts.length) {
                    const script = scripts[index];
                    mxscript(script.src, function() {
                        index++;
                        loadNext();
                    }, null, null, false, script.type || 'text/javascript');
                } else if (onComplete) {
                    onComplete();
                }
            }
            loadNext();
        }

        // Custom loading system disabled to prevent conflicts with original draw.io loading
        // The original draw.io loading system below will handle all script loading
        /*
        // Start loading scripts
        (function() {
            // Load XLSX library first
            mxscript('https://unpkg.com/xlsx-style@0.8.13/dist/xlsx.full.min.js');

            // Load core dependencies
            loadScriptSequence(mxGraphDeps, function() {
                // Load initialization scripts
                loadScriptSequence(initScripts, function() {
                    // Load core scripts
                    loadScriptSequence(coreScripts, function() {
                        // Load utility scripts
                        loadScriptSequence(utilScripts, function() {
                            // Load auth scripts
                            loadScriptSequence(authScripts, function() {
                                // Load mxGraph scripts
                                loadScriptSequence(mxGraphScripts, function() {
                                    // Load application scripts
                                    loadScriptSequence(appScripts, function() {


                                        mxScriptsLoaded = true;
                                        checkAllLoaded();
                                    });
                                });
                            });
                        });
                    });
                });
            });

            var name = 'electrisim.com';
            mxmeta('apple-mobile-web-app-title', name);
            mxmeta('application-name', name);

            if (mxIsElectron) {
                mxmeta(null, 'default-src \'self\' \'unsafe-inline\'; connect-src \'self\' https://*.draw.io https://fonts.googleapis.com https://fonts.gstatic.com; img-src * data:; media-src *; font-src *; style-src-elem \'self\' \'unsafe-inline\' https://fonts.googleapis.com', 'Content-Security-Policy');
            }
        })();
        */
        
        // Meta tags for app
        var name = 'electrisim.com';
        mxmeta('apple-mobile-web-app-title', name);
        mxmeta('application-name', name);

        if (mxIsElectron) {
            mxmeta(null, 'default-src \'self\' \'unsafe-inline\'; connect-src \'self\' https://*.draw.io https://fonts.googleapis.com https://fonts.gstatic.com; img-src * data:; media-src *; font-src *; style-src-elem \'self\' \'unsafe-inline\' https://fonts.googleapis.com', 'Content-Security-Policy');
        }

		// Checks for local storage
		var isLocalStorage = false;
		
		try
		{
			isLocalStorage = urlParams['local'] != '1' && typeof(localStorage) != 'undefined';
		}
		catch (e)
		{
			// ignored
		}

		var mxScriptsLoaded = false, mxWinLoaded = false;
		
		window.onload = function() {
            mxWinLoaded = true;
            checkAllLoaded();
        };
		
		var t0 = new Date();

		// Changes paths for local development environment
		if (urlParams['dev'] == '1')
		{
			// Used to request grapheditor/mxgraph sources in dev mode
			var mxDevUrl = document.location.protocol + '//devhost.jgraph.com/mxgraph2';
			
			// Used to request draw.io sources in dev mode
			var drawDevUrl = document.location.protocol + '//devhost.jgraph.com/drawio/src/main/webapp/';
			
			if (document.location.protocol == 'file:')
			{
				mxDevUrl = '../../../../../mxgraph2';
				drawDevUrl = './';
				
				// Forces includes for dev environment in node.js
				mxForceIncludes = true;
			}
			

			var geBasePath = mxDevUrl + '/javascript/examples/grapheditor/www/js';
			var mxBasePath = mxDevUrl + '/javascript/src';
			
			mxscript(drawDevUrl + 'js/PreConfig.js');
			mxscript(drawDevUrl + 'js/diagramly/Init.js');
			mxscript(geBasePath + '/Init.js');
			mxscript(mxDevUrl + '/javascript/src/js/mxClient.js');
			
			// Adds all JS code that depends on mxClient. This indirection via Devel.js is
			// required in some browsers to make sure mxClient.js (and the files that it
			// loads asynchronously) are available when the code loaded in Devel.js runs.
			mxscript(drawDevUrl + 'js/diagramly/Devel.js');
			
			// Electron
			if (mxIsElectron)
			{
				mxscript('js/diagramly/DesktopLibrary.js');
				mxscript('js/diagramly/ElectronApp.js');
			}
			
			mxscript(drawDevUrl + 'js/PostConfig.js');
		}
		else
		{
			(function()
			{
				var hostName = window.location.hostname;
				
				// Supported domains are *.draw.io and the packaged version in Quip
				var supportedDomain = (hostName.substring(hostName.length - 8, hostName.length) === '.draw.io') ||
					(hostName.substring(hostName.length - 13, hostName.length) === '.diagrams.net');
					(hostName.substring(hostName.length - 17, hostName.length) === '.quipelements.com');

									
				function loadAppJS()				
				{
					// XLSX deferred - loaded on demand via requestIdleCallback (see end of loadElectrisimScripts)

					// Load AG Grid library before app.min.js
					mxscript('js/ag-grid-community.min.js', function()
					{
                        // Load auth-globals.js BEFORE app.min.js
                    mxscript('js/electrisim/auth-globals.js', function()
                    {
						// PERFORMANCE: Patches temporarily disabled due to rendering issues
						// TODO: Need safer, more conservative performance optimizations
						// mxscript('js/electrisim/core-performance-patch.js', function() {
						(function() {
							console.log('⚠️ Core performance patches disabled - using original rendering');
							
							// Use async loading for app.min.js to prevent blocking
							var appScript = document.createElement('script');
							appScript.src = 'js/app.min.js';
							appScript.async = false; // Maintain execution order but don't block parsing
							appScript.defer = true;
							appScript.onload = function() {
							// Use browser print dialog for PDF export (exp.draw.io is deprecated; no external server needed)
							if (typeof EditorUi !== 'undefined') {
								EditorUi.prototype.printPdfExport = true;
							}
							console.log('✅ app.min.js loaded, starting to load Electrisim scripts');
							
							function appendDrawioGlobalsBridge() {
							// Bridge mx* from classic global scope to window for ES modules (transformer terminal labels, etc.)
							var bridgeScript = document.createElement('script');
							bridgeScript.src = 'js/electrisim/utils/drawioGlobalsBridge.js';
							bridgeScript.async = false;
							bridgeScript.onload = function () {
								startElectrisimModules();
							};
							bridgeScript.onerror = function () {
								console.warn('drawioGlobalsBridge.js failed; ES modules may miss mxShape');
								startElectrisimModules();
							};
							document.head.appendChild(bridgeScript);
							}
							var emptyImageFix = document.createElement('script');
							emptyImageFix.src = 'js/electrisim/utils/mxSvgCanvasEmptyImageFix.js';
							emptyImageFix.async = false;
							emptyImageFix.onload = appendDrawioGlobalsBridge;
							emptyImageFix.onerror = function () {
								console.warn('mxSvgCanvasEmptyImageFix.js failed; empty SVG image hrefs may error');
								appendDrawioGlobalsBridge();
							};
							document.head.appendChild(emptyImageFix);
							
							function startElectrisimModules() {
							
							// Terminal labels: hook draw.io redraw in classic scope (mxShape is a bare global here).
							// ES module assigns window.__electrisimPaintTrafoLabels when dialogInitializer loads.
							(function electrisimTrafoRedrawHook() {
								if (window.__electrisimTrafoRedrawHooked) {
									return;
								}
								var S = typeof mxShape !== 'undefined' ? mxShape : window.__drawio_mxShape;
								var R = typeof mxCellRenderer !== 'undefined' ? mxCellRenderer : window.__drawio_mxCellRenderer;
								if (!S || !S.prototype || !S.prototype.redraw) {
									console.warn('Electrisim: mxShape not found; transformer terminal labels hook skipped');
									return;
								}
								window.__electrisimTrafoRedrawHooked = true;
								var oShape = S.prototype.redraw;
								S.prototype.redraw = function () {
									oShape.apply(this, arguments);
									var fn = window.__electrisimPaintTrafoLabels;
									if (fn && this.state) {
										try {
											fn(this.state);
										} catch (err) {
											console.warn('Electrisim trafo labels (mxShape):', err);
										}
									}
								};
								if (R && R.prototype && R.prototype.redraw) {
									var oCell = R.prototype.redraw;
									R.prototype.redraw = function (state, force, rendering) {
										oCell.apply(this, arguments);
										var fn = window.__electrisimPaintTrafoLabels;
										if (fn && state) {
											try {
												fn(state);
											} catch (err2) {
												console.warn('Electrisim trafo labels (mxCellRenderer):', err2);
											}
										}
									};
								}
							})();
							
							// Load Electrisim custom scripts after app.min.js
							// PERFORMANCE: Load performance utils first, then other modules
                            // NOTE: For local dev use `npm run minify:restore` so the .js files contain unminified code; avoid loading .backup files directly to prevent MIME/404 issues.
                            var electrisimScripts = [
								// Load SAFE performance optimizer FIRST
								'js/electrisim/safe-performance-optimizer.js',
								'js/electrisim/resizableDialogs.js',
								'js/electrisim/lazy-loader.js',
								// Then load performance utilities
								'js/electrisim/utils/domCache.js',
								'js/electrisim/utils/batchDOM.js',
								'js/electrisim/utils/performanceTracker.js',
								'js/electrisim/utils/performanceUtils.js',
								'js/electrisim/utils/pageVisibilityManager.js',
								'js/electrisim/utils/performanceMonitor.js',
								'js/electrisim/utils/autoInitOptimizers.js',
								// Then load other utilities and dialogs
								'js/electrisim/dialogs/EditDataDialog.js',
                                'js/electrisim/results/resultBoxes.js?v=4',
                                'js/electrisim/dialogs/ComponentsDataDialog.js',
								'js/electrisim/utils/cellUtils.js',
								'js/electrisim/utils/networkPreparation.js',
                                'js/electrisim/ensureSubscriptionFunctions.js',
                                'js/electrisim/dialogs/dialogInitializer.js',
								'js/electrisim/dialogs/BessSizingDialog.js',
								'js/electrisim/dialogs/RPCDialog.js',
								'js/electrisim/dialogs/RPCResultsDialog.js',
								'js/electrisim/dialogs/DiagnosticReportDialog.js',
								'js/electrisim/dialogs/OptimalPowerFlowDialog.js',
								'js/electrisim/dialogs/OptimalPowerFlowResultsDialog.js',
								'js/electrisim/dialogs/ControllerSimulationDialog.js',
								'js/electrisim/dialogs/ControllerSimulationResultsDialog.js',
								'js/electrisim/dialogs/TimeSeriesSimulationDialog.js',
								'js/electrisim/dialogs/TimeSeriesSimulationResultsDialog.js',
								'js/electrisim/dialogs/ContingencyDialog.js',
								'js/electrisim/dialogs/ShortCircuitDialog.js',
								'js/electrisim/lineBaseDialog.js',
								'js/electrisim/LibraryDialogManager.js',
								'js/electrisim/lineLibraryDialog.js',
								'js/electrisim/externalGridDialog.js',
								'js/electrisim/generatorDialog.js',
								'js/electrisim/staticGeneratorDialog.js',
								'js/electrisim/asymmetricStaticGeneratorDialog.js',
								'js/electrisim/busDialog.js',
								'js/electrisim/transformerBaseDialog.js',
								'js/electrisim/transformerLibraryDialog.js',
								'js/electrisim/threeWindingTransformerBaseDialog.js',
								'js/electrisim/threeWindingTransformerLibraryDialog.js',
								'js/electrisim/shuntReactorDialog.js',
								'js/electrisim/capacitorDialog.js',
								'js/electrisim/loadDialog.js',
								'js/electrisim/asymmetricLoadDialog.js',
								'js/electrisim/shortCircuit.js',
								'js/electrisim/impedanceDialog.js',
								'js/electrisim/wardDialog.js',
								'js/electrisim/extendedWardDialog.js',
								'js/electrisim/motorDialog.js',
								'js/electrisim/storageDialog.js',
								'js/electrisim/SSCDialog.js',
								'js/electrisim/SVCDialog.js',
								'js/electrisim/TCSCDialog.js',
								'js/electrisim/dcLineDialog.js',
								'js/electrisim/configureAttributes.js',
								'js/electrisim/dialogs/MapEditorDialog.js',
								'js/electrisim/map/mapInitializer.js',
								'js/electrisim/supportingFunctions.js',
								'js/electrisim/loadFlow.js',
								'js/electrisim/loadflowOpenDss.js',
								'js/electrisim/bessSizing.js',
								'js/electrisim/rpcAnalysis.js',
								'js/electrisim/optimalPowerFlow.js',
								'js/electrisim/controllerSimulation.js',
								'js/electrisim/timeSeriesSimulation.js',
								'js/electrisim/contingencyAnalysis.js'
                            ];
							
							// Auth scripts (Stripe, etc.) - deferred until after Electrisim scripts for faster initial load
							var authScripts = [
								'https://js.stripe.com/v3/',
								'js/electrisim/loginsubscription/stripe.js',
								'js/electrisim/loginsubscription/auth-handler.js',
								'js/electrisim/loginsubscription/stripe-subscription.js'
							];
							
							function loadAuthScripts(index) {
								if (index < authScripts.length) {
									var script = document.createElement('script');
									if (index > 0) script.type = 'module';
									script.src = authScripts[index];
									script.onload = function() {
										loadAuthScripts(index + 1);
									};
									script.onerror = function() {
										if (index === 0) {
											console.warn('Stripe SDK script failed to load (e.g. ad blocker or network). Subscription/checkout will use redirect flow.');
											window._stripeScriptFailed = true;
										}
										loadAuthScripts(index + 1);
									};
									document.head.appendChild(script);
								} else {
									// Defer XLSX - load during idle (Excel import/export)
									var loadXLSXDeferred = function() {
										if (window.XLSX) return;
										var s = document.createElement('script');
										s.src = 'https://unpkg.com/xlsx-style@0.8.13/dist/xlsx.full.min.js';
										s.async = true;
										document.head.appendChild(s);
									};
									if (typeof requestIdleCallback !== 'undefined') {
										requestIdleCallback(function() { loadXLSXDeferred(); }, { timeout: 3000 });
									} else {
										setTimeout(loadXLSXDeferred, 3000);
									}
								}
							}
							
							// Improved Electrisim script loading with better error handling and batching
							// Load scripts in parallel batches to prevent UI blocking
							function loadElectrisimScripts(index) {
								if (index < electrisimScripts.length) {
									// Load critical scripts first (performance utils, then others)
									const batchSize = index === 0 ? 1 : (index === 1 ? 2 : 5); // Load performanceUtils first, then visibility manager and monitor together
									const batch = electrisimScripts.slice(index, index + batchSize);
									let loaded = 0;
									
									batch.forEach(function(scriptSrc) {
										var script = document.createElement('script');
										script.type = 'module';
										script.src = scriptSrc;
										script.async = true; // Allow parallel loading
										
										script.onload = function() {
											loaded++;
											if (loaded === batch.length) {
												// Use setTimeout to yield to UI thread between batches
												setTimeout(function() {
													loadElectrisimScripts(index + batch.length);
												}, 0);
											}
										};
										
										script.onerror = function(e) {
											console.warn('Failed to load script:', scriptSrc, e);
											loaded++;
											if (loaded === batch.length) {
												setTimeout(function() {
													loadElectrisimScripts(index + batch.length);
												}, 0);
											}
										};
										
										document.head.appendChild(script);
									});
								} else {
									// Electrisim scripts done - now load deferred auth (Stripe) for faster initial load
									loadAuthScripts(0);
									mxScriptsLoaded = true;
									checkAllLoaded();
								}
							}
							
							// Start loading Electrisim scripts first (Stripe deferred until after)
							loadElectrisimScripts(0);
							
							} // end startElectrisimModules
							
							}; // Close app.min.js onload function
							
							document.head.appendChild(appScript);
							console.log('🚀 app.min.js loading started');
							
						})(); // Close immediate function
					}); // Close auth-globals.js callback
					}); // Close ag-grid callback
							
					if (!supportedDomain)
					{
						mxscript('js/PostConfig.js');
					}
					
					// Electron
					if (mxIsElectron)
					{
						mxscript('js/diagramly/DesktopLibrary.js', function()
						{
							mxscript('js/diagramly/ElectronApp.js', function()
							{
								mxscript('js/extensions.min.js', function()
								{
									mxscript('js/stencils.min.js', function()
									{
										mxscript('js/shapes.min.js', function()
										{
											mxscript('js/PostConfig.js');
										});
									});
								});
							});
						});
					}
				};
				
				if (!supportedDomain || mxIsElectron)
				{
					mxscript('js/PreConfig.js', loadAppJS);
				}
				else
				{
					loadAppJS();
				}
			})();
		}

        // Basic error handling
        window.onerror = function() {
            var status = document.getElementById('geStatus');
            if (status != null) {
                status.innerHTML = 'Page is loading.';
            }
        };
    </script>

    <link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/plgmlhohecdddhbmmkncjdmlhcmaachm">
    <link rel="apple-touch-icon" sizes="180x180" href="images/apple-touch-icon.png">
    <link rel="icon" type="image/png" sizes="32x32" href="images/logo_Electrisim_removebg-preview.png">
    <link rel="icon" type="image/png" sizes="16x16" href="images/logo_Electrisim_removebg-preview.png">
    <link rel="mask-icon" href="images/safari-pinned-tab.svg" color="#d89000">
    <link rel="stylesheet" type="text/css" href="js/croppie/croppie.min.css">
    <link rel="stylesheet" type="text/css" href="mxgraph/css/common.css">
    <link rel="stylesheet" type="text/css" href="styles/grapheditor.css">
    <link rel="stylesheet" type="text/css" href="styles/mobile.css">
    <link rel="preconnect" href="https://storage.googleapis.com">
    <link rel="canonical" href="https://app.electrisim.com">
    <link rel="manifest" href="images/manifest.json">
    <link rel="shortcut icon" href="images/logo_Electrisim_removebg-preview.png">
    <style type="text/css">
        html, body { height: 100%; margin: 0; padding: 0; }
        body { overflow: hidden; }
        div.picker { z-index: 10007; }
        .geSidebarContainer .geTitle input {
            font-size:8pt;
            color:#606060;
        }
        .geBlock {
            z-index:-3;
            margin:100px;
            margin-top:40px;
            margin-bottom:30px;
            padding:20px;
        }
        .geBlock h1, .geBlock h2 {
            margin-top:0px;
            padding-top:0px;
        }
        
        .geEditor ::-webkit-scrollbar {
            width:14px;
            height:14px;
        }
        .geEditor ::-webkit-scrollbar-track {
            background-clip:padding-box;
            border:solid transparent;
            border-width:1px;
        }
        .geEditor ::-webkit-scrollbar-corner {
            background-color:transparent;
        }
        .geEditor ::-webkit-scrollbar-thumb {
            background-color:rgba(0,0,0,.1);
            background-clip:padding-box;
            border:solid transparent;
            border-radius:10px;
        }
        .geEditor ::-webkit-scrollbar-thumb:hover{
            background-color:rgba(0,0,0,.4);
        }
    </style>
    <!--[if (IE 9)|(IE 10)]><!-->
    <script type="text/vbscript">
        Function mxUtilsBinaryToArray(Binary)
            Dim i
            ReDim byteArray(LenB(Binary))
            For i = 1 To LenB(Binary)
                byteArray(i-1) = AscB(MidB(Binary, i, 1))
            Next
            mxUtilsBinaryToArray = byteArray
        End Function
    </script>
    <!--<![endif]-->
</head>
<body class="geEditor">
    <div id="geInfo">
        <div class="geBlock" style="text-align:center;min-width:50%;">
            <h1>Electrisim - open-source app for power system modelling, simulation and analysis</h1>
            <h2 id="geStatus">Loading...</h2>
            <p>
                app.electrisim.com is open-source online application for modelling, simulation and analysis of power system.
            </p>
            <p>
                Loading...
            </p>
            <p>
                Please ensure JavaScript is enabled. Works on desktop and mobile browsers.
            </p>
        </div>
    </div>
</body>
</html>