gokatz
5/31/2017 - 7:30 PM

This is a sample "index.html" to for use with Ember. It moves the loading of the compiled "vendor.js" into an animation frame and instead re

This is a sample "index.html" to for use with Ember. It moves the loading of the compiled "vendor.js" into an animation frame and instead renders a simple splash screen first with a simple CSS loading indicator. Then, once the splash screen is on screen, it starts loading the generated by Ember to boot the app. This should be generic enough you should just be able to drop in place, tweak with your branding, and go.

<!DOCTYPE html>
<html style='height:100%;overflow:none'>
	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<title>My App</title>
		<meta name="description" content="My App by My Company, Inc.">

		<!-- http://www.favicon-generator.org/ generates a good starting manifest that I tweak by hand -->
    <link rel="manifest" href="/manifest.json">
    
    <!-- Set background here to prevent any flashing before the loading screen CSS is interpreted.
         I don't set it inline on <body style=""> because that would take precedence over other 
         stylesheets loaded "in the future" without !important on that background attribute -->
		<style>body{background:#0275D8};/* just for loading, overridden by following stylesheets*/</style>

		<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />

		<meta name="apple-mobile-web-app-capable" content="yes" />
		<meta name="mobile-web-app-capable" content="yes">
		<!-- Apple will use this as the default title of the shortcut when added to home -->
		<meta name="apple-mobile-web-app-title" content="My App Name">

    <!-- Insert your branding icons here -->
		<link rel="apple-touch-icon" sizes="57x57"   href="/assets/app-icons/light-bluebg-std/apple-icon-57x57.png">
		<link rel="apple-touch-icon" sizes="60x60"   href="/assets/app-icons/light-bluebg-std/apple-icon-60x60.png">
		<link rel="apple-touch-icon" sizes="72x72"   href="/assets/app-icons/light-bluebg-std/apple-icon-72x72.png">
		<link rel="apple-touch-icon" sizes="76x76"   href="/assets/app-icons/light-bluebg-std/apple-icon-76x76.png">
		<link rel="apple-touch-icon" sizes="114x114" href="/assets/app-icons/light-bluebg-std/apple-icon-114x114.png">
		<link rel="apple-touch-icon" sizes="120x120" href="/assets/app-icons/light-bluebg-std/apple-icon-120x120.png">
		<link rel="apple-touch-icon" sizes="144x144" href="/assets/app-icons/light-bluebg-std/apple-icon-144x144.png">
		<link rel="apple-touch-icon" sizes="152x152" href="/assets/app-icons/light-bluebg-std/apple-icon-152x152.png">
		<link rel="apple-touch-icon" sizes="180x180" href="/assets/app-icons/light-bluebg-std/apple-icon-180x180.png">
		<link rel="icon" type="image/png" sizes="16x16"   href="/assets/app-icons/light-bluebg/favicon-16x16.png">
		<link rel="icon" type="image/png" sizes="32x32"   href="/assets/app-icons/light-bluebg/favicon-32x32.png">
		<link rel="icon" type="image/png" sizes="96x96"   href="/assets/app-icons/light-bluebg-std/favicon-96x96.png">
		<link rel="icon" type="image/png" sizes="192x192" href="/assets/app-icons/light-bluebg-std/android-icon-192x192.png">
		<meta name="msapplication-TileColor" content="#0275D8">
		<meta name="msapplication-TileImage" content="/assets/app-icons/light-bluebg-std/ms-icon-144x144.png">

		<meta name="theme-color" content="#0275D8">
		<!-- Windows Phone -->
		<meta name="msapplication-navbutton-color" content="#0275D8">
		<!-- iOS Safari -->
		<meta name="apple-mobile-web-app-capable" content="yes">
		<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

    <!-- Note: I include the CSS inline here in my index.html because I want the page to render as 
         fast as possible with as few network requests as possible

         In production, I use 'ember-cli-html-minifier' to minify the index.html to reduce network
         transfer even further.
    -->
    
		<!-- Simple loader CSS from https://css-tricks.com/snippets/css/bouncy-animated-loading-animation/
			NOTE: Tried using GIF data URIs for more compat, but the browser couldn't render them at all
			before the next animation frame kicked off loading scripts and froze the DOM again
		-->
		<style>
		.loader {
			text-align: center;
			height: 72px;
		}
		.loader span {
			display: inline-block;
			vertical-align: middle;
			width: 10px;
			height: 10px;
			margin: 50px auto;
			background: white;
			border-radius: 50px;
			-webkit-animation: loader 0.9s infinite alternate;
			-moz-animation: loader 0.9s infinite alternate;
		}
		.loader span:nth-of-type(2) {
			-webkit-animation-delay: 0.3s;
			-moz-animation-delay: 0.3s;
		}
		.loader span:nth-of-type(3) {
			-webkit-animation-delay: 0.6s;
			-moz-animation-delay: 0.6s;
		}
		@-webkit-keyframes loader {
			0% {
				width: 10px;
				height: 10px;
				opacity: 0.9;
				-webkit-transform: translateY(0);
			}
			100% {
				width: 24px;
				height: 24px;
				opacity: 0.1;
				-webkit-transform: translateY(-21px);
			}
		}
		@-moz-keyframes loader {
			0% {
				width: 10px;
				height: 10px;
				opacity: 0.9;
				-moz-transform: translateY(0);
			}
			100% {
				width: 24px;
				height: 24px;
				opacity: 0.1;
				-moz-transform: translateY(-21px);
			}
		}
		</style>
	</head>
	<body style='height:100%'>
		<div style='position: relative;width:100%;height:100%;background:#0275D8' id='app-splash-screen'>
			<div style='position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);text-align:center'>
				<div>
          <!-- Your brand Icon here -->
					<img src="/assets/app-icons/light-bluebg-std/android-icon-192x192.png">
				</div>
				<div class="loader">
					<span></span>
					<span></span>
					<span></span>
				</div>

        <!-- You could also add brand name here if desired. This is a system font stack I found somewhere. I ultimately decided I didn't like to clutter the splash screen with it and that my logo as enough. -->
				<!-- <div style='font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-style: normal;font-weight:800;color:white;font-size:48px'>
					Brand Name
				</div>
				<div style='font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-style: normal;font-weight:600;color:rgba(255,255,255,0.8);margin-top:1em'>
					Loading awesomeness ...
					<span id='app-loading-spinner'>
						<div class="loader">
							<span></span>
							<span></span>
							<span></span>
						</div>
					</span>
				</div>

				 -->
			</div>
		</div>
		<textarea id="post-splash-head-content" style='display:none'>
			{{content-for "head"}}

			<!-- Insert other global CSS here, like Google Fonts API links, etc -->

			<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
			<link rel="stylesheet" href="{{rootURL}}assets/filos.css">

			{{content-for "head-footer"}}

			<!-- Insert other global scripts here, like Google Maps or Font Awesome -->

			<script src="{{rootURL}}assets/vendor.js"></script>
			<script src="{{rootURL}}assets/filos.js"></script>
		</textarea>
		<textarea id="post-splash-body-content" style='display:none'>
			{{content-for "body"}}

			{{content-for "body-footer"}}
		</textarea>

		<script>

		// Don't start loading the scripts
		window.requestAnimationFrame(function() {
      // Instead of the CSS spinner above, you could use an ASCII spinner...
      // I decided not to use it because it took too long to start animating.
			// // Spinners from https://jsfiddle.net/mnpenner/gm86u2jv/
			// var spinner = "⣾⣽⣻⢿⡿⣟⣯⣷";
			// var el = document.querySelector('#app-loading-spinner');
			// (function(spinner,el) {
			// 	var i = 0;
			// 	el.innerHTML = spinner[i];
			// 	setInterval(function() {
			// 		el.innerHTML = spinner[i];
			// 		i = (i + 1) % spinner.length;
			// 	}, 60);
			// })(spinner,el);

			// Let the spinner get rendered, then start our app bootup
			window.requestAnimationFrame(function() {

				// Now that the spinner is started, boot up the app
				var head     = document.getElementsByTagName('head')[0];
				var appBoot  = document.querySelector('#post-splash-head-content').value;

				// Parse the content 'hidden' in the textarea above
				var fragment = document.createElement('div');
				fragment.id = 'app-boot-container';
				fragment.innerHTML = appBoot;

				// Simply appending the fragment to the head is enough to get the <meta> and the stylesheets
				// recognized and loaded by the browser.
				// However, for <script> tags, we have to do a bit more work
				head.appendChild(fragment);

				// Before we boot the scripts, put our body content on the body
				var bodyContent = document.querySelector('#post-splash-body-content').value;
				var bodyFragment = document.createElement('div');
				bodyFragment.id = 'app-boot-body-extra';
				bodyFragment.innerHTML = bodyContent;
				document.body.appendChild(bodyFragment);

				// Since adding the fragment to head did NOT init scripts (likey due to browser security
				// and the browser not wanting to start scripts from raw html), then we have to start
				// scripts explicitly ourselves.

				// First, we find the scripts in the fragment and make a native javascript array out of them
				// because we will use shift() below (which querySelectorAll()'s return of NodeList does not have)
				// to load scripts in sequence
				var scriptTags = fragment.querySelectorAll('script');
				var scripts = [];
				// 'forEach' is not present on scriptTags in some browsers, e.g. Mozilla, etc,
				// so we loop the old-fashioned way
				for(var i=0; i<scriptTags.length; i++) {
					scripts.push(scriptTags[i].src);
				};

				// Load scripts one at a time for predictable execution order.
				// If we DON'T wait for one to finish before the other starts to load,
				// we potentially (likely) will have errors, because ember and other scripts
				// rely on the default nature of browsers loading scripts syncroniously.
				// If we just inserted all the scripts all at once into the body,
				// the scripts would be loaded asyncroniously, which means smaller scripts
				// would finish before big ones, even if loaded after - which causes
				// dependency errors (such as define not being defined!)
				function loadScript(list) {
					if(!list || !list.length)
						return;

					var src     = list.shift();
					var script  = document.createElement ("script");
					script.type = "text/javascript";
					document.body.appendChild(script);
					script.onload = function() {
						loadScript(list);
					};
					script.src  = src;
				}

				loadScript(scripts);
			});
		});
		</script>


	</body>
</html>