// could assetslibrary auto byref those projects ?
        // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150818
        // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150809/chrome-filesystem

        /// <summary>
        /// This is a javascript application.
        /// </summary>
        /// <param name="page">HTML document rendered by the web server which can now be enhanced.</param>
        public Application(IApp page)
        {




            //Native.document.documentElement.style.overflow = IStyle.OverflowEnum.auto;


            // chrome by default has no scrollbar, bowser does
            Native.document.documentElement.style.overflow = IStyle.OverflowEnum.hidden;
            Native.body.style.margin = "0px";
            Native.body.Clear();

            // ipad?
            Native.window.onerror +=
                e =>
                {
                    new IHTMLPre {
                        "error " + new { e.error }
                    }.AttachToDocument();
                };

            // https://www.youtube.com/watch?v=tnS8K0yhmZU
            // http://www.reddit.com/r/oculus/comments/2sv5lk/new_release_of_shadertoy_vr/
            // https://www.shadertoy.com/view/lsSGRz

            // https://sites.google.com/a/jsc-solutions.net/backlog/knowledge-base/2015/201503/20150309
            // https://zproxy.wordpress.com/2015/03/09/project-windstorm/
            // https://github.com/jimbo00000/RiftRay


            #region += Launched chrome.app.window
            dynamic self = Native.self;
            dynamic self_chrome = self.chrome;
            object self_chrome_socket = self_chrome.socket;

            if (self_chrome_socket != null)
            {
                if (!(Native.window.opener == null && Native.window.parent == Native.window.self))
                {
                    Console.WriteLine("chrome.app.window.create, is that you?");

                    // pass thru
                }
                else
                {
                    // should jsc send a copresence udp message?
                    chrome.runtime.UpdateAvailable += delegate
                    {
                        new chrome.Notification(title: "UpdateAvailable");

                    };

                    chrome.app.runtime.Launched += async delegate
                    {
                        // 0:12094ms chrome.app.window.create {{ href = chrome-extension://aemlnmcokphbneegoefdckonejmknohh/_generated_background_page.html }}
                        Console.WriteLine("chrome.app.window.create " + new { Native.document.location.href });

                        new chrome.Notification(title: "ChromeUDPSendAsync");

                        var xappwindow = await chrome.app.window.create(
                               Native.document.location.pathname, options: null
                        );

                        //xappwindow.setAlwaysOnTop

                        xappwindow.show();

                        await xappwindow.contentWindow.async.onload;

                        Console.WriteLine("chrome.app.window loaded!");
                    };


                    return;
                }
            }
            #endregion



            Console.WriteLine("create WebGLRenderingContext");

            var gl = new WebGLRenderingContext(alpha: true);

            if (gl == null)
            {
                Native.body.style.backgroundColor = "yellow";

                new IHTMLPre {
                    // https://code.google.com/p/chromium/issues/detail?id=294207
                    "Rats! WebGL hit a snag.",

                    //new IHTMLAnchor { href = "about:gpu", innerText = "about:gpu" }
                }.AttachToDocument();
                return;
            }


            Native.body.style.backgroundColor = "blue";

            gl.oncontextlost += async e =>
            {
                //  The GPU process hung. Terminating after 10000 ms.

                // GpuProcessHostUIShim: The GPU process crashed!

                Native.body.style.backgroundColor = "red";

                Native.body.Clear();

                new IHTMLPre {
                    // https://code.google.com/p/chromium/issues/detail?id=294207
                    //"The GPU process crashed! (or did the graphics driver crash?)",
                    "The GPU process crashed! (or did the display driver crash?)",

                    //new IHTMLAnchor { href = "about:gpu", innerText = "about:gpu" }
                }.AttachToDocument();

                // reload?

                //if (Native.window.confirm("oncontextlost, retry or refresh later?"))
                //{
                //    Native.body.style.backgroundColor = "yellow";

                //    //e.
                //}

                await new IHTMLButton { "blacklist " + new { Native.document.location.hash } + " shader,  reload tab to reload gpu. (or restart browser)" }.AttachToDocument().async.onclick;

                Native.window.localStorage[new { Native.document.location.hash }] = "blacklisted";

                // reload gpu?
                Native.document.location.reload();
            };

            //gl.canvas.async.oncont

            var combo = new IHTMLSelect().AttachToDocument();

            combo.style.position = IStyle.PositionEnum.absolute;
            combo.style.left = "0px";
            combo.style.top = "0px";
            //combo.style.right = "0px";
            combo.style.width = "100%";

            combo.style.backgroundColor = "rgba(255,255,255,0.5)";
            //combo.style.backgroundColor = "rgba(255,255,0,0.5)";
            //combo.style.background = "linear-gradient(to bottom, rgba(255,255,255,0.5 0%,rgba(255,255,255,0.0 100%))";
            combo.style.border = "0px solid transparent";
            combo.style.fontSize = "large";
            combo.style.paddingLeft = "1em";
            combo.style.fontFamily = IStyle.FontFamilyEnum.Verdana;
            combo.style.cursor = IStyle.CursorEnum.pointer;



            //var mAudioContext = new AudioContext();


            var c = gl.canvas.AttachToDocument();

            #region onresize
            new { }.With(
                async delegate
                {
                    do
                    {
                        c.width = Native.window.Width;
                        c.height = Native.window.Height;
                        c.style.SetSize(c.width, c.height);
                    }
                    while (await Native.window.async.onresize);
                }
            );
            #endregion



            #region CaptureMouse
            var mMouseOriX = 0;
            var mMouseOriY = 0;
            var mMousePosX = 0;
            var mMousePosY = 0;

            c.onmousedown += async ev =>
            {
                mMouseOriX = ev.CursorX;
                //mMouseOriY = ev.CursorY;
                mMouseOriY = c.height - ev.CursorY;

                mMousePosX = mMouseOriX;
                mMousePosY = mMouseOriY;

                // why aint it canvas?
                //ev.Element
                //ev.CaptureMouse();

                // using ?
                ev.Element.requestPointerLock();
                await ev.Element.async.onmouseup;
                Native.document.exitPointerLock();

                mMouseOriX = -Math.Abs(mMouseOriX);
                mMouseOriY = -Math.Abs(mMouseOriY);
            };

            //c.ontouchmove += 

            c.onmousemove += ev =>
            {
                if (ev.MouseButton == IEvent.MouseButtonEnum.Left)
                {
                    mMousePosX += ev.movementX;
                    mMousePosY += ev.movementY;
                }
            };

            c.onmousewheel += ev =>
            {
                ev.preventDefault();
                ev.stopPropagation();

                mMousePosY += 3 * ev.WheelDirection;
            };

            #endregion



            // http://www.wufoo.com/html5/attributes/05-list.html
            // http://www.w3schools.com/tags/att_input_list.asp
            //uiauto.datalist1.EnsureID();
            //uiauto.search.list = uiauto.datalist1.id;
            //uiauto.datalist1.id = "datalist1";
            //uiauto.search.list = "datalist1";
            //new IHTMLPre { new { uiauto.search.list, uiauto.datalist1.id } }.AttachToDocument();

            var sw = Stopwatch.StartNew();


            //new IHTMLOption { value = "", innerText = $"{References.programs.Count} shaders available" }.AttachTo(combo);
            new IHTMLOption { value = "", innerText = b.programs.Count + " shaders available" }.AttachTo(combo);


            // should bind the selection to uri and reload if gpu crashes.

            #region can we have a next button?
            // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150807/shadertoy
            new IHTMLButton { "next" }.AttachToDocument().With(
                next =>
                {
                    new IStyle(next)
                    {
                        position = IStyle.PositionEnum.absolute,
                        right = "1em",
                        top = "2em",
                        bottom = "1em",
                        padding = "4em"
                    };

                    next.onclick += delegate
                    {
                        var n = combo.selectedIndex + 1;
                        Console.WriteLine(new { n });
                        combo.selectedIndex = n;

                        // if we are on a laptop, by clicking the button perhaps now you want to click right arrow?
                        combo.focus();
                    };
                }
            );
            #endregion

            // do not add byref if bypackage is available!

            //            Severity Code    Description Project File Line
            //Error CS0433  The type 'ShaderToy' exists in both 'ChromeShaderToyColumns, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' and 'WebGL ShaderToy, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'    ChromeShaderToyPrograms.b Z:\jsc.svn\examples\javascript\chrome\apps\WebGL\ChromeShaderToyPrograms\ChromeShaderToyPrograms.b\Application.cs   283

            ShaderToy.EffectPass pip = null;

            // http://stackoverflow.com/questions/25289390/html-how-to-make-input-type-list-only-accept-a-list-choice
            //References.programs.Keys.WithEachIndex(
            b.programs.Keys.WithEachIndex(
                async (key, index) =>
                {
                    //var text = (1 + index) + " of " + References.programs.Count + " " + key.SkipUntilIfAny("ChromeShaderToy").Replace("By", " by ");
                    var text = (1 + index) + " of " + b.programs.Count + " " + key.SkipUntilIfAny("ChromeShaderToy").Replace("By", " by ");

                    var blacklisted = Native.window.localStorage[new { hash = "#" + key }] == "blacklisted";
                    if (blacklisted)
                    {
                        var option0 = new IHTMLOption { value = key, innerText = "blacklisted " + text }.AttachTo(combo);
                        return;
                    }


                    var option = new IHTMLOption { value = key, innerText = text }.AttachTo(combo);

                    await Native.window.async.onframe;

                    // um should we preselect it? did we come from a reload? did gpu crash?
                    if (Native.document.location.hash == "#" + key)
                    {
                        // 0 is the header.. should it be a optionheader instead?
                        combo.selectedIndex = 1 + index;
                    }


                    // we are about to create 100 objects. does it have any impact to UI?
                    //var frag = References.programs[key]();
                    var frag = b.programs[key]();

                    // 0ms Error: {{ infoLog = WARNING: 0:13: '=' : global variable initializers should be constant expressions (uniforms and globals are allowed in global initializers for legacy compatibility)
                    // can we detect errors correctly?

                    var len = frag.ToString().Length;

                    option.innerText = text + " " + new
                    {
                        //frame,
                        //load = load.ElapsedMilliseconds + "ms ",

                        frag = len + "bytes ",
                        // a telemetry to track while running on actual hardware
                        //fragGPU = pass0.xCreateShader.fsTranslatedShaderSource.Length + " bytes"
                    };

                    // cant we get it if we manually set it?
                    await option.async.onselect;

                    // first time select. before we try to load the shader, lets make sure we remember what causes the gpu crash
                    // 2278ms select {{ key = InvadersByIapafoto, index = 10, hash = #InputTimeByIq }}
                    await Native.document.location.replace("#" + key);

                    // need two frames to see a change in hash?
                    //await Native.window.async.onframe;
                    //await Native.window.async.onframe;


                    // unless from previous refresh it crashed gpu?
                    Console.WriteLine("select " + new { key, index, Native.document.location.hash });

                    var load = Stopwatch.StartNew();

                    // defined at?
                    // "C:\util\jsc\nuget\WebGL.ShaderToy.1.0.0.0.nupkg"
                    var pass0 = new ShaderToy.EffectPass(
                        gl: gl,
                        precission: ShaderToy.DetermineShaderPrecission(gl),
                        supportDerivatives: gl.getExtension("OES_standard_derivatives") != null
                    );
                    pass0.MakeHeader_Image();
                    pass0.NewShader_Image(frag);

                    load.Stop();

                    new { }.With(
                        async delegate
                        {
                            while (await option.async.ondeselect)
                            {
                                pip = pass0;

                                await option.async.onselect;
                            }
                        }
                    );

                    var framesInSecond = 0;
                    var theSecond = Stopwatch.StartNew();

                    var frame = 0;
                    do
                    {
                        // can we change uri so a refresh would reload the same program?
                        // perhaps its time to review historic api?
                        Native.document.location.replace("#" + key);


                        frame++;
                        framesInSecond++;

                        if (theSecond.ElapsedMilliseconds >= 1000)
                        {
                            //option.innerText = key + new { frame };
                            option.innerText = text + " " + new
                            {
                                //frame,
                                framesInSecond,
                                load = load.ElapsedMilliseconds + "ms ",

                                frag = len + "bytes ",
                                // a telemetry to track while running on actual hardware
                                fragGPU = pass0.xCreateShader.fsTranslatedShaderSource.Length + " bytes"
                            };

                            framesInSecond = 0;
                            //theSecond.Restart();
                            theSecond = Stopwatch.StartNew();
                        }


                        // can we scale?
                        pass0.Paint_Image(
                            sw.ElapsedMilliseconds / 1000.0f,

                            mMouseOriX,
                            mMouseOriY,
                            mMousePosX,
                            mMousePosY,

                            zoom: 1.0f
                        );

                        if (pip != null)
                        {
                            // can we scale?
                            pip.Paint_Image(
                                sw.ElapsedMilliseconds / 1000.0f,

                                mMouseOriX,
                                mMouseOriY,
                                mMousePosX,
                                mMousePosY,

                                zoom: 0.10f
                            );

                        }

                        // what does it do?
                        gl.flush();

                        // wither we are selected or we are pip?
                        await option.async.selected;
                    }
                    while (await Native.window.async.onframe);

                }
            );





        }
        // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150808/equirectangular
        // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150718/shadertoy
        // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150706
        // subst b: X:\jsc.svn\examples\javascript\chrome\apps\WebGL\ChromeShaderToyColumns\ChromeShaderToyColumns\bin\Debug\staging\ChromeShaderToyColumns.Application\web


        public Application(IApp page)
		{
			#region += Launched chrome.app.window
			dynamic self = Native.self;
			dynamic self_chrome = self.chrome;
			object self_chrome_socket = self_chrome.socket;

			if (self_chrome_socket != null)
			{
				if (!(Native.window.opener == null && Native.window.parent == Native.window.self))
				{
					Console.WriteLine("chrome.app.window.create, is that you?");

					// pass thru
				}
				else
				{
					// should jsc send a copresence udp message?
					chrome.runtime.UpdateAvailable += delegate
					{
						new chrome.Notification(title: "UpdateAvailable");

					};

					chrome.app.runtime.Launched += async delegate
					{
						// 0:12094ms chrome.app.window.create {{ href = chrome-extension://aemlnmcokphbneegoefdckonejmknohh/_generated_background_page.html }}
						Console.WriteLine("chrome.app.window.create " + new { Native.document.location.href });

						new chrome.Notification(title: "ChromeUDPSendAsync");

						var xappwindow = await chrome.app.window.create(
							   Native.document.location.pathname, options: null
						);

						//xappwindow.setAlwaysOnTop

						xappwindow.show();

						await xappwindow.contentWindow.async.onload;

						Console.WriteLine("chrome.app.window loaded!");
					};


					return;
				}
			}
			#endregion


			// view-source:https://www.shadertoy.com/view/Xls3WS
			// https://www.shadertoy.com/api

			// https://www.shadertoy.com/view/Xls3WS
			// https://www.shadertoy.com/js/cmRenderUtils.js
			// https://www.shadertoy.com/js/effect.js

			// what does it take to import those nice shaders into jsc world?

			// x:\jsc.svn\examples\javascript\webgl\webglchocolux\webglchocolux\application.cs
			// it looks there are no channels.
			// is it a vert or frag?
			//  fragColor = vec4( col, 1.0 );
			// must be a frag
			// <body onload="watchInit()" 


			//ChromeShaderToyColumns.Library.ShaderToy.AttachToDocument(
			//	new Shaders.ProgramFragmentShader()
			//);

			new { }.With(
				async delegate
				{
					Native.body.style.margin = "0px";
					(Native.body.style as dynamic).webkitUserSelect = "auto";

					var vs = new Shaders.ProgramFragmentShader();

					var mAudioContext = new AudioContext();
					var gl = new WebGLRenderingContext(alpha: true);
					var c = gl.canvas.AttachToDocument();

					c.style.SetSize(460, 237);
					c.width = 460;
					c.height = 237;

					var u = new UIKeepRendering
					{
						animate = true
					}.AttachToDocument();

					//new IHTMLPre { "init..." }.AttachToDocument();

					// function ShaderToy( parentElement, editorParent, passParent )
					// function buildInputsUI( me )

					//  this.mGLContext = createGlContext( this.mCanvas, false, true );
					//  {alpha: useAlpha, depth: false, antialias: false, stencil: true, premultipliedAlpha: false, preserveDrawingBuffer: usePreserveBuffer } 

					var mMouseOriX = 0;
					var mMouseOriY = 0;
					var mMousePosX = 0;
					var mMousePosY = 0;

					// 308
					//var mEffect = new Library.ShaderToy.Effect(
					//	mAudioContext,
					//	gl,

					//	callback: delegate
					//	{
					//		new IHTMLPre { "at callback" }.AttachToDocument();

					//	},
					//	obj: null,
					//	forceMuted: false,
					//	forcePaused: false
					//);


					////mEffect.mPasses[0].NewTexture
					//// EffectPass.prototype.NewTexture = function( wa, gl, slot, url )
					//// this.mPasses[j].Create( rpass.type, this.mAudioContext, this.mGLContext );
					//// EffectPass.prototype.MakeHeader_Image = function( precission, supportDerivatives )
					//mEffect.mPasses[0].MakeHeader_Image();

					//// EffectPass.prototype.NewShader = function( gl, shaderCode )
					//// EffectPass.prototype.NewShader_Image = function( gl, shaderCode )
					//mEffect.mPasses[0].NewShader_Image(vs);

					//// ShaderToy.prototype.resetTime = function()
					// Effect.prototype.ResetTime = function()

					// ShaderToy.prototype.startRendering = function()
					// Effect.prototype.Paint = function(time, mouseOriX, mouseOriY, mousePosX, mousePosY, isPaused)
					// EffectPass.prototype.Paint = function( wa, gl, time, mouseOriX, mouseOriY, mousePosX, mousePosY, xres, yres, isPaused )
					// EffectPass.prototype.Paint_Image = function( wa, gl, time, mouseOriX, mouseOriY, mousePosX, mousePosY, xres, yres )

					var pass = new Library.ShaderToy.EffectPass(
						mAudioContext,
						gl,
						precission: Library.ShaderToy.DetermineShaderPrecission(gl),
						supportDerivatives: gl.getExtension("OES_standard_derivatives") != null,
						callback: null,
						obj: null,
						forceMuted: false,
						forcePaused: false,
						//quadVBO: Library.ShaderToy.createQuadVBO(gl, right: 0, top: 0),
						outputGainNode: null
					);
					pass.MakeHeader_Image();
					pass.NewShader_Image(vs);

					var sw = Stopwatch.StartNew();

					do
					{
						pass.Paint_Image(
							sw.ElapsedMilliseconds / 1000.0f,

							mMouseOriX,
							mMouseOriY,
							mMousePosX,
							mMousePosY
                            //,

							// gl_FragCoord
							// cannot be scaled, and can be referenced directly.
							// need another way to scale
							//zoom: 0.3f
						);

						// what does it do?
                        // need redux build..
						gl.flush();

						await u.animate.async.@checked;
					}
					while (await Native.window.async.onframe);

				}
			);
		}
        // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150706
        // subst b: r:\jsc.svn\examples\glsl\future\GLSLShaderToyPip\GLSLShaderToyPip\bin\Debug\staging\GLSLShaderToyPip.Application\web

        /// <summary>
        /// This is a javascript application.
        /// </summary>
        /// <param name="page">HTML document rendered by the web server which can now be enhanced.</param>
        public Application(IApp page)
		{
			// the idea of this exammple
			// is to look at how multiple shaders can be linked to work together.
			// we need two shaders
			// first we could run them as separate programs in pip mode
			// selected by the host/javascript
			// then repeat the same experiment, but have the shader do the pip in a single program
			// later shader code could be nugeted
			// lets have a copy of
			// X:\jsc.svn\examples\javascript\chrome\apps\WebGL\ChromeShaderToyQuadraticBezierByMattdesl\ChromeShaderToyQuadraticBezierByMattdesl\Shaders\Program.frag
			// locally should we need to modify it..

			// can we change colors?


			// https://www.shadertoy.com/view/lsSGRz

			#region += Launched chrome.app.window
			dynamic self = Native.self;
			dynamic self_chrome = self.chrome;
			object self_chrome_socket = self_chrome.socket;

			if (self_chrome_socket != null)
			{
				if (!(Native.window.opener == null && Native.window.parent == Native.window.self))
				{
					Console.WriteLine("chrome.app.window.create, is that you?");

					// pass thru
				}
				else
				{
					// should jsc send a copresence udp message?
					chrome.runtime.UpdateAvailable += delegate
					{
						new chrome.Notification(title: "UpdateAvailable");

					};

					chrome.app.runtime.Launched += async delegate
					{
						// 0:12094ms chrome.app.window.create {{ href = chrome-extension://aemlnmcokphbneegoefdckonejmknohh/_generated_background_page.html }}
						Console.WriteLine("chrome.app.window.create " + new { Native.document.location.href });

						new chrome.Notification(title: "ChromeUDPSendAsync");

						var xappwindow = await chrome.app.window.create(
							   Native.document.location.pathname, options: null
						);

						//xappwindow.setAlwaysOnTop

						xappwindow.show();

						await xappwindow.contentWindow.async.onload;

						Console.WriteLine("chrome.app.window loaded!");
					};


					return;
				}
			}
			#endregion

			new { }.With(
				async delegate


				//02000047 <module>.SHA1111132814b0387cee18e0fe5efe63eb881cfd505@901285072
				//02000048 GLSLShaderToyPip.Application+<AttachToDocument>d__1+<MoveNext>06000020
				//script: error JSC1000:
				//error:
				//  statement cannot be a load instruction(or is it a bug?)
				//  [0x0000]
				//		ldarg.0    +1 -0

				//public static async void AttachToDocument()
				//public async void AttachToDocument()
				{
					Native.body.style.margin = "0px";
					(Native.body.style as dynamic).webkitUserSelect = "auto";


					var gl = new WebGLRenderingContext(alpha: true);

					#region GPU process was unable to boot
					if (gl == null)
					{

						new IHTMLPre {
							// https://code.google.com/p/chromium/issues/detail?id=294207
							"Rats! WebGL hit a snag. \n WebGL: Unavailable.\n GPU process was unable to boot. \n restart chrome.",

							// chrome sends us to about:blank?
							//new IHTMLAnchor {

							//	target = "_blank",

							//	href = "about:gpu", innerText = "about:gpu",

							//	// http://tirania.org/blog/archive/2009/Jul-27-1.html
							//	//onclick += de
							//}
							//.With(a => {  a.onclick += e => { e.preventDefault();  Native.window.open("about:gpu"); }; } )


						}.AttachToDocument();
						return;
					}
					#endregion



					var c = gl.canvas.AttachToDocument();

					#region oncontextlost
					gl.oncontextlost +=
						e =>
						{
							//[12144:10496:0311 / 120850:ERROR: gpu_watchdog_thread.cc(314)] : The GPU process hung. Terminating after 10000 ms.
							//   GpuProcessHostUIShim: The GPU process crashed!
							gl.canvas.Orphanize();

							new IHTMLPre {
								// https://code.google.com/p/chromium/issues/detail?id=294207
								@"Rats! WebGL hit a snag.
oncontextlost.
The GPU process hung. Terminating. 
check chrome://gpu for log messages.  
do we have a stack trace?

" + new { e.statusMessage } ,

								// chrome sends us to about:blank?
								//new IHTMLAnchor {

								//	target = "_blank",

								//	href = "about:gpu", innerText = "about:gpu",

								//	// http://tirania.org/blog/archive/2009/Jul-27-1.html
								//	//onclick += de
								//}
								//.With(a => {  a.onclick += e => { e.preventDefault();  Native.window.open("about:gpu"); }; } )


							}.AttachToDocument();
						};
					#endregion


					#region onresize
					new { }.With(
						async delegate
						{
							do
							{
								c.width = Native.window.Width;
								c.height = Math.Min(300, Native.window.Height);
								c.style.SetSize(c.width, c.height);
							}
							while (await Native.window.async.onresize);
						}
					);
					#endregion




					#region CaptureMouse
					var mMouseOriX = 0;
					var mMouseOriY = 0;
					var mMousePosX = c.width / 2;
					var mMousePosY = 0;

					c.onmousedown += ev =>
					{
						mMouseOriX = ev.CursorX;
						mMouseOriY = ev.CursorY;
						mMousePosX = mMouseOriX;
						mMousePosY = mMouseOriY;

						ev.CaptureMouse();
					};

					c.onmousemove += ev =>
					{
						if (ev.MouseButton == IEvent.MouseButtonEnum.Left)
						{
							mMousePosX = ev.CursorX;
							mMousePosY = c.height - ev.CursorY;
						}
					};


					c.onmouseup += ev =>
					{
						mMouseOriX = -Math.Abs(mMouseOriX);
						mMouseOriY = -Math.Abs(mMouseOriY);
					};
					#endregion

					var quadVBO = ShaderToy.createQuadVBO(gl);

					var pass1 = new ShaderToy.EffectPass(
						gl: gl,
						precission: ShaderToy.DetermineShaderPrecission(gl),
						supportDerivatives: gl.getExtension("OES_standard_derivatives") != null,
						quadVBO: quadVBO
					);
					pass1.MakeHeader_Image();
					var frag1 = new GLSLShaderToyPip.Shaders.TheColorGradientFragmentShader();
					pass1.NewShader_Image(frag1);

					var pass0 = new ShaderToy.EffectPass(
						gl: gl,
						precission: ShaderToy.DetermineShaderPrecission(gl),
						supportDerivatives: gl.getExtension("OES_standard_derivatives") != null,
						quadVBO: quadVBO
					);
					pass0.MakeHeader_Image();
					var frag0 = new GLSLShaderToyPip.Shaders.ChromeShaderToyQuadraticBezierByMattdeslFragmentShader();
					//var frag = new GLSLShaderToyPip.Shaders.TheColorGradientFragmentShader();
					pass0.NewShader_Image(frag0);

					if (pass0.xCreateShader.mProgram == null)
					{
						gl.Orphanize();
						return;
					}

					new { }.With(
						async delegate
						{
							do
							{
								Native.document.body.style.backgroundColor = "cyan";
								await Task.Delay(500);
								Native.document.body.style.backgroundColor = "yellow";
								await Task.Delay(500);
							} while (await Native.window.async.onframe);
						}
					);



					// https://developer.mozilla.org/en/docs/Web/API/WebGLRenderingContext

					#region Paint_Image
					Paint_ImageDelegate Paint_Image =
					(mProgram, time, mouseOriX, mouseOriY, mousePosX, mousePosY) =>
					{


						var viewportxres = gl.canvas.width;
						var viewportyres = gl.canvas.height;

						#region Paint_Image
						//new IHTMLPre { "enter Paint_Image" }.AttachToDocument();

						// http://www.html5rocks.com/en/tutorials/webgl/webgl_fundamentals/
						//gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
						gl.viewport(0, 0, viewportxres, viewportyres);


						// alpha to zero will only hide the pixel if blending is enabled. 
						gl.useProgram(mProgram);

						// uniform4fv
						var mouse = new[] { mousePosX, mousePosY, mouseOriX, mouseOriY };

						var l2 = gl.getUniformLocation(mProgram, "iGlobalTime"); if (l2 != null) gl.uniform1f(l2, time);
						var l3 = gl.getUniformLocation(mProgram, "iResolution"); if (l3 != null) gl.uniform3f(l3, viewportxres, viewportyres, 1.0f);
						var l4 = gl.getUniformLocation(mProgram, "iMouse"); if (l4 != null) gl.uniform4fv(l4, mouse);
						//var l7 = gl.getUniformLocation(this.mProgram, "iDate"); if (l7 != null) gl.uniform4fv(l7, dates);
						//var l9 = gl.getUniformLocation(this.mProgram, "iSampleRate"); if (l9 != null) gl.uniform1f(l9, this.mSampleRate);

						var ich0 = gl.getUniformLocation(mProgram, "iChannel0"); if (ich0 != null) gl.uniform1i(ich0, 0);
						var ich1 = gl.getUniformLocation(mProgram, "iChannel1"); if (ich1 != null) gl.uniform1i(ich1, 1);
						var ich2 = gl.getUniformLocation(mProgram, "iChannel2"); if (ich2 != null) gl.uniform1i(ich2, 2);
						var ich3 = gl.getUniformLocation(mProgram, "iChannel3"); if (ich3 != null) gl.uniform1i(ich3, 3);




						//for (var i = 0; i < mInputs.Length; i++)
						//{
						//	var inp = mInputs[i];

						//	gl.activeTexture((uint)(gl.TEXTURE0 + i));

						//	if (inp == null)
						//	{
						//		gl.bindTexture(gl.TEXTURE_2D, null);
						//	}
						//}

						var times = new[] { 0.0f, 0.0f, 0.0f, 0.0f };
						var l5 = gl.getUniformLocation(mProgram, "iChannelTime");
						if (l5 != null) gl.uniform1fv(l5, times);

						var resos = new float[12] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
						var l8 = gl.getUniformLocation(mProgram, "iChannelResolution");
						if (l8 != null) gl.uniform3fv(l8, resos);



						// using ?
						var l1 = (uint)gl.getAttribLocation(mProgram, "pos");
						gl.bindBuffer(gl.ARRAY_BUFFER, quadVBO);
						gl.vertexAttribPointer(l1, 2, gl.FLOAT, false, 0, 0);
						gl.enableVertexAttribArray(l1);

						gl.drawArrays(gl.TRIANGLES, 0, 6);
						// first frame is now visible
						gl.disableVertexAttribArray(l1);
						#endregion

						//mFrame++;

					};
					#endregion


					var sw = Stopwatch.StartNew();
					do
					{
						pass1.Paint_Image(
						sw.ElapsedMilliseconds / 1000.0f,

								mMouseOriX,
								mMouseOriY,
								mMousePosX,
								mMousePosY,

								zoom: 1.0f
						);

						pass0.Paint_Image(
						sw.ElapsedMilliseconds / 1000.0f,

									mMouseOriX,
									mMouseOriY,
									mMousePosX,
									mMousePosY,

									//zoom: 0.5f
									zoom: mMousePosX / (float)c.width
								);

						// what does it do?
						gl.flush();

					}
					while (await Native.window.async.onframe);

				}
			);

		}
        // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150807/shadertoy

        /// <summary>
        /// This is a javascript application.
        /// </summary>
        /// <param name="page">HTML document rendered by the web server which can now be enhanced.</param>
        public Application(HTML.Pages.IApp page)
        {
            // show shader based on tab selection?



            //Native.document.documentElement.style.overflow = IStyle.OverflowEnum.auto;


            // chrome by default has no scrollbar, bowser does

            // "C:\util\jsc\bin\ScriptCoreLib.dll"
            Native.document.documentElement.style.overflow = IStyle.OverflowEnum.hidden;
            Native.body.style.margin = "0px";
            Native.body.Clear();

            // ipad?
            Native.window.onerror +=
                e =>
                {
                    new IHTMLPre {
                        "error " + new { e.error }
                    }.AttachToDocument();
                };

            // https://www.youtube.com/watch?v=tnS8K0yhmZU
            // http://www.reddit.com/r/oculus/comments/2sv5lk/new_release_of_shadertoy_vr/
            // https://www.shadertoy.com/view/lsSGRz

            // https://sites.google.com/a/jsc-solutions.net/backlog/knowledge-base/2015/201503/20150309
            // https://zproxy.wordpress.com/2015/03/09/project-windstorm/
            // https://github.com/jimbo00000/RiftRay


            //1>Z:\jsc.svn\examples\javascript\chrome\apps\WebGL\ChromeShaderToyPrograms\ChromeShaderToyPrograms\Application.cs(81,35,81,39): 
            // error CS0656: Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'
            // 

            #region += Launched chrome.app.window
            dynamic self = Native.self;
            dynamic self_chrome = self.chrome;
            object self_chrome_socket = self_chrome.socket;

            if (self_chrome_socket != null)
            {
                if (!(Native.window.opener == null && Native.window.parent == Native.window.self))
                {
                    Console.WriteLine("chrome.app.window.create, is that you?");

                    // pass thru
                }
                else
                {
                    // should jsc send a copresence udp message?
                    chrome.runtime.UpdateAvailable += delegate
                    {
                        new chrome.Notification(title: "UpdateAvailable");

                    };

                    chrome.app.runtime.Launched += async delegate
                    {
                        // 0:12094ms chrome.app.window.create {{ href = chrome-extension://aemlnmcokphbneegoefdckonejmknohh/_generated_background_page.html }}
                        Console.WriteLine("chrome.app.window.create " + new { Native.document.location.href });

                        new chrome.Notification(title: "ChromeUDPSendAsync");

                        var xappwindow = await chrome.app.window.create(
                               Native.document.location.pathname, options: null
                        );

                        //xappwindow.setAlwaysOnTop

                        xappwindow.show();

                        await xappwindow.contentWindow.async.onload;

                        Console.WriteLine("chrome.app.window loaded!");
                    };


                    return;
                }
            }
            #endregion




            var gl = new WebGLRenderingContext(alpha: true);

            if (gl == null)
            {

                new IHTMLPre {
                    // https://code.google.com/p/chromium/issues/detail?id=294207
                    "Rats! WebGL hit a snag.",

                    //new IHTMLAnchor { href = "about:gpu", innerText = "about:gpu" }
                }.AttachToDocument();
                return;
            }


            Native.body.style.backgroundColor = "blue";

            gl.oncontextlost += delegate
            {
                Native.body.style.backgroundColor = "red";

                // reload?
                
            };

            //gl.canvas.async.oncont

            var combo = new IHTMLSelect().AttachToDocument();

            combo.style.position = IStyle.PositionEnum.absolute;
            combo.style.left = "0px";
            combo.style.top = "0px";
            //combo.style.right = "0px";
            combo.style.width = "100%";

            combo.style.backgroundColor = "rgba(255,255,255,0.5)";
            //combo.style.backgroundColor = "rgba(255,255,0,0.5)";
            //combo.style.background = "linear-gradient(to bottom, rgba(255,255,255,0.5 0%,rgba(255,255,255,0.0 100%))";
            combo.style.border = "0px solid transparent";
            combo.style.fontSize = "large";
            combo.style.paddingLeft = "1em";
            combo.style.fontFamily = IStyle.FontFamilyEnum.Verdana;
            combo.style.cursor = IStyle.CursorEnum.pointer;



            //var mAudioContext = new AudioContext();


            var c = gl.canvas.AttachToDocument();

            #region onresize
            new { }.With(
                async delegate
                {
                    do
                    {
                        c.width = Native.window.Width;
                        c.height = Native.window.Height;
                        c.style.SetSize(c.width, c.height);
                    }
                    while (await Native.window.async.onresize);
                }
            );
            #endregion



            #region CaptureMouse
            var mMouseOriX = 0;
            var mMouseOriY = 0;
            var mMousePosX = 0;
            var mMousePosY = 0;

            c.onmousedown += async ev =>
            {
                mMouseOriX = ev.CursorX;
                //mMouseOriY = ev.CursorY;
                mMouseOriY = c.height - ev.CursorY;

                mMousePosX = mMouseOriX;
                mMousePosY = mMouseOriY;

                // why aint it canvas?
                //ev.Element
                //ev.CaptureMouse();

                // using ?
                ev.Element.requestPointerLock();
                await ev.Element.async.onmouseup;
                Native.document.exitPointerLock();

                mMouseOriX = -Math.Abs(mMouseOriX);
                mMouseOriY = -Math.Abs(mMouseOriY);
            };

           //c.ontouchmove += 

            c.onmousemove += ev =>
            {
                if (ev.MouseButton == IEvent.MouseButtonEnum.Left)
                {
                    mMousePosX += ev.movementX;
                    mMousePosY += ev.movementY;
                }
            };

            c.onmousewheel += ev =>
            {
                ev.preventDefault();
                ev.stopPropagation();

                mMousePosY += 3 * ev.WheelDirection;
            };

            #endregion



            // http://www.wufoo.com/html5/attributes/05-list.html
            // http://www.w3schools.com/tags/att_input_list.asp
            //uiauto.datalist1.EnsureID();
            //uiauto.search.list = uiauto.datalist1.id;
            //uiauto.datalist1.id = "datalist1";
            //uiauto.search.list = "datalist1";
            //new IHTMLPre { new { uiauto.search.list, uiauto.datalist1.id } }.AttachToDocument();

            var sw = Stopwatch.StartNew();


            new IHTMLOption { value = "", innerText = $"{References.programs.Count} shaders available" }.AttachTo(combo);


            // should bind the selection to uri and reload if gpu crashes.

            #region can we have a next button?
            // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150807/shadertoy
            new IHTMLButton { "next" }.AttachToDocument().With(
                next =>
                {
                    new IStyle(next)
                    {
                        position = IStyle.PositionEnum.absolute,
                        right = "1em",
                        top = "2em",
                        bottom = "1em",
                        padding = "4em"
                    };

                    next.onclick += delegate
                    {
                        var n = combo.selectedIndex + 1;
                        Console.WriteLine(new { n });
                        combo.selectedIndex = n;
                    };
                }
            );
            #endregion



            ShaderToy.EffectPass pip = null;

            // http://stackoverflow.com/questions/25289390/html-how-to-make-input-type-list-only-accept-a-list-choice
            References.programs.Keys.WithEachIndex(
                async (key, index) =>
                {
                    var text = (1 + index) + " of " + References.programs.Count + " " + key.SkipUntilIfAny("ChromeShaderToy").Replace("By", " by ");

                    var option = new IHTMLOption { value = key, innerText = text }.AttachTo(combo);

                    await Native.window.async.onframe;

                    // we are about to create 100 objects. does it have any impact to UI?
                    var frag = References.programs[key]();
                    var len = frag.ToString().Length;

                    option.innerText = text + " " + new
                    {
                        //frame,
                        //load = load.ElapsedMilliseconds + "ms ",

                        frag = len + "bytes ",
                        // a telemetry to track while running on actual hardware
                        //fragGPU = pass0.xCreateShader.fsTranslatedShaderSource.Length + " bytes"
                    };

                    // cant we get it if we manually set it?
                    await option.async.onselect;
                    await Native.window.async.onframe;

                    var load = Stopwatch.StartNew();

                    var pass0 = new ShaderToy.EffectPass(
                        gl: gl,
                        precission: ShaderToy.DetermineShaderPrecission(gl),
                        supportDerivatives: gl.getExtension("OES_standard_derivatives") != null
                    );
                    pass0.MakeHeader_Image();
                    pass0.NewShader_Image(frag);

                    load.Stop();

                    new { }.With(
                        async delegate
                        {
                            while (await option.async.ondeselect)
                            {
                                pip = pass0;

                                await option.async.onselect;
                            }
                        }
                    );

                    var framesInSecond = 0;
                    var theSecond = Stopwatch.StartNew();

                    var frame = 0;
                    do
                    {
                        frame++;
                        framesInSecond++;

                        if (theSecond.ElapsedMilliseconds >= 1000)
                        {
                            //option.innerText = key + new { frame };
                            option.innerText = text + " " + new
                            {
                                //frame,
                                framesInSecond,
                                load = load.ElapsedMilliseconds + "ms ",

                                frag = len + "bytes ",
                                // a telemetry to track while running on actual hardware
                                fragGPU = pass0.xCreateShader.fsTranslatedShaderSource.Length + " bytes"
                            };

                            framesInSecond = 0;
                            //theSecond.Restart();
                            theSecond = Stopwatch.StartNew();
                        }


                        // can we scale?
                        pass0.Paint_Image(
                            sw.ElapsedMilliseconds / 1000.0f,

                            mMouseOriX,
                            mMouseOriY,
                            mMousePosX,
                            mMousePosY,

                            zoom: 1.0f
                        );

                        if (pip != null)
                        {
                            // can we scale?
                            pip.Paint_Image(
                                sw.ElapsedMilliseconds / 1000.0f,

                                mMouseOriX,
                                mMouseOriY,
                                mMousePosX,
                                mMousePosY,

                                zoom: 0.10f
                            );

                        }

                        // what does it do?
                        gl.flush();

                        // wither we are selected or we are pip?
                        await option.async.selected;
                    }
                    while (await Native.window.async.onframe);

                }
            );





        }