/// <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)
        {
            // view-source:file:///X:/opensource/github/three.js/examples/css3d_panorama.html

            var camera = new THREE.PerspectiveCamera(75, Native.window.aspect, 1, 1000);
            var scene = new THREE.Scene();

            var renderer = new THREE.CSS3DRenderer();

            #region sides
            var sides = new[]
            {
                new side
                {
                    img=  new cubecamera_px(),
                    //img=  new humus_px(),

                    // glsl, clr46, Vector3?
                    position= new THREE.Vector3( -512, 0, 0 ),
                    rotation= new THREE.Vector3( 0, Math.PI / 2, 0 )
                },
                new side {
                    img=  new cubecamera_nx(),
                    //img=  new humus_nx(),
                    position= new THREE.Vector3( 512, 0, 0 ),
                    rotation= new THREE.Vector3( 0, -Math.PI / 2, 0 )
                },
                new side{
                    img=  new cubecamera_py(),
                    //img=  new humus_py(),
                    position= new THREE.Vector3( 0,  512, 0 ),
                    rotation= new THREE.Vector3( Math.PI / 2, 0, Math.PI )
                },
                new side{
                    img=  new cubecamera_ny(),
                    //img=  new humus_ny(),
                    position= new THREE.Vector3( 0, -512, 0 ),
                    rotation= new THREE.Vector3( - Math.PI / 2, 0, Math.PI )
                },
                new side{
                    img=  new cubecamera_pz(),
                    //img=  new humus_pz(),
                    position= new THREE.Vector3( 0, 0,  512 ),
                    rotation= new THREE.Vector3( 0, Math.PI, 0 )
                },
                new side{
                    img=  new cubecamera_nz(),
                    //img=  new humus_nz(),
                    position= new THREE.Vector3( 0, 0, -512 ),
                    rotation= new THREE.Vector3( 0, 0, 0 )
                }
            };
            #endregion

            for (var i = 0; i < sides.Length; i++)
            {
                var side = sides[i];

                var element = side.img;

                element.style.SetSize(1026, 1026);

                //element.width = 1026; // 2 pixels extra to close the gap.

                var xobject = new THREE.CSS3DObject(element);
                xobject.position.set(side.position.x, side.position.y, side.position.z);
                xobject.rotation.set(side.rotation.x, side.rotation.y, side.rotation.z);
                scene.add(xobject);

            }


            //<div style="-webkit-transform-style: preserve-3d; width: 978px; height: 664px; -webkit-transform: translate3d(0px, 0px, 432.6708237832803px) matrix3d(0.34382355213165283, -0.024581052362918854, -0.938712477684021, 0, 0, -0.9996572732925415, 0.026176948100328445, 0, 0.9390342831611633, 0.00900025200098753, 0.34370577335357666, 0, 0, 0, 0, 0.9999999403953552) translate3d(489px, 332px, 0px);">
            //        <img src="assets/CSS3DPanoramaByHumus/posx.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, -512, 0, 0, 1);"><img src="assets/CSS3DPanoramaByHumus/negx.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 512, 0, 0, 1);"><img src="assets/CSS3DPanoramaByHumus/posy.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 512, 0, 1);"><img src="assets/CSS3DPanoramaByHumus/negy.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, -512, 0, 1);"><img src="assets/CSS3DPanoramaByHumus/posz.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 512, 1);"><img src="assets/CSS3DPanoramaByHumus/negz.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, -512, 1);"></div>
            //<div style="-webkit-transform-style: preserve-3d; width: 978px; height: 664px; -webkit-transform: translate3d(0px, 0px, 432.6708237832803px) matrix3d(-0.4524347484111786, 0, 0.8917974829673767, 0, 0, -1, 0, 0, -0.8917974829673767, 0, -0.4524347484111786, 0, 0, 0, 0, 1) translate3d(489px, 332px, 0px);">
            // <img width="1026" src="textures/cube/Bridge2/posx.jpg"                                             style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(0, 0, -1, 0, 0, -1, 0, 0, 1, 0, 0, 0, -512, 0, 0, 1);"><img width="1026" src="textures/cube/Bridge2/negx.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 512, 0, 0, 1);"><img width="1026" src="textures/cube/Bridge2/posy.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(-1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 512, 0, 1);"><img width="1026" src="textures/cube/Bridge2/negy.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(-1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, -512, 0, 1);"><img width="1026" src="textures/cube/Bridge2/posz.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 512, 1);"><img width="1026" src="textures/cube/Bridge2/negz.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, -512, 1);"></div> 


            renderer.setSize(Native.window.Width, Native.window.Height);
            renderer.domElement.AttachToDocument();

            #region onresize
            Native.window.onresize +=
                delegate
                {
                    camera.aspect = Native.window.aspect;
                    camera.updateProjectionMatrix();

                    renderer.setSize(Native.window.Width, Native.window.Height);
                };
            #endregion

            var target = new THREE.Vector3();

            var lon = 90.0;
            var lat = 0.0;
            var phi = 0.0;
            var theta = 0.0;





            var drag = false;


            Native.window.onframe +=
                delegate
                {
                    if (Native.document.pointerLockElement == Native.document.body)
                        lon += 0.00;
                    else
                        lon += 0.01;

                    lat = Math.Max(-85, Math.Min(85, lat));

                    //Native.document.title = new { lon, lat }.ToString();


                    phi = THREE.Math.degToRad(90 - lat);
                    theta = THREE.Math.degToRad(lon);

                    target.x = Math.Sin(phi) * Math.Cos(theta);
                    target.y = Math.Cos(phi);
                    target.z = Math.Sin(phi) * Math.Sin(theta);

                    camera.lookAt(target);

                    renderer.render(scene, camera);

                };

            #region ontouchmove
            var touchX = 0;
            var touchY = 0;

            Native.document.body.ontouchstart +=
                e =>
                {
                    e.preventDefault();

                    var touch = e.touches[0];

                    touchX = touch.screenX;
                    touchY = touch.screenY;

                };


            Native.document.body.ontouchmove +=
              e =>
              {
                  e.preventDefault();

                  var touch = e.touches[0];

                  lon -= (touch.screenX - touchX) * 0.1;
                  lat += (touch.screenY - touchY) * 0.1;

                  touchX = touch.screenX;
                  touchY = touch.screenY;

              };
            #endregion






            #region camera rotation
            Native.document.body.onmousemove +=
                e =>
                {
                    e.preventDefault();

                    if (Native.document.pointerLockElement == Native.document.body)
                    {
                        lon += e.movementX * 0.1;
                        lat -= e.movementY * 0.1;

                        //Console.WriteLine(new { lon, lat, e.movementX, e.movementY });
                    }
                };


            Native.document.body.onmouseup +=
              e =>
              {
                  drag = false;
                  e.preventDefault();
              };

            Native.document.body.onmousedown +=
                e =>
                {
                    //e.CaptureMouse();

                    drag = true;
                    e.preventDefault();
                    Native.document.body.requestPointerLock();

                };


            Native.document.body.ondblclick +=
                e =>
                {
                    e.preventDefault();

                    Console.WriteLine("requestPointerLock");
                };

            #endregion



            Native.document.body.onmousewheel +=
                e =>
                {
                    camera.fov -= e.WheelDirection * 5.0;
                    camera.updateProjectionMatrix();
                };


        }
        /// <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)
        {
            // http://blog.cuartodejuegos.es/wp-content/uploads/2011/06/Story-cubes-caras.jpg
            // http://css-eblog.com/3d/box.html

            var w = Native.window.Width;
            var h = Native.window.Height;

            //      var world, ground, 
            var timeStep = 1 / 60.0;

            //diceRigid, dice,
            //camera, scene, renderer, floorObj,
            //startDiceNum = 3,
            var cubeSize = 3.0;

            #region Cannonの世界を生成
            var world = new CANNON.World();

            //重力を設定
            world.gravity.set(0, -90.82, 0);
            world.broadphase = new CANNON.NaiveBroadphase();
            world.solver.iterations = 10;
            world.solver.tolerance = 0.001;

            //地面用にPlaneを生成
            var plane = new CANNON.Plane();

            //Planeの剛体を質量0で生成する
            var ground = new CANNON.RigidBody(0, plane);

            //X軸に90度(つまり地面)に回転
            ground.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
            ground.position.set(
                50, 0, 50);

            world.add(ground);
            #endregion


            #region initThree() {


            //var camera = new THREE.OrthographicCamera(-w / 2, w / 2, h / 2, -h / 2, 1, 1000);
            var camera = new THREE.PerspectiveCamera(60, w / (double)h, 0.1, 1000);


            camera.lookAt(new THREE.Vector3(0, 0, 0));

            var scene = new THREE.Scene();
            var renderer = new THREE.CSS3DRenderer();
            renderer.setSize(w, h);

            var textureSize = 800;
            var floorEle = new IHTMLDiv();
            //floorEle.style.width = textureSize + "px";
            //floorEle.style.height = textureSize + "px";

            floorEle.style.width = 100 + "px";
            floorEle.style.height = 100 + "px";

            new WebGLDiceByEBLOG.HTML.Images.FromAssets.background().ToBackground(floorEle.style, true);

            //floorEle.style.background = 'url(http://jsrun.it/assets/d/x/0/w/dx0wl.png) left top repeat';
            //floorEle.style.backgroundSize = textureSize / 20 + "px " + textureSize / 20 + "px";
            (floorEle.style as dynamic).backgroundSize = textureSize / 20 + "px " + textureSize / 20 + "px";

            var floorObj = new THREE.CSS3DObject(floorEle);
            //floorObj.position.fromArray(new double[] { 100, 0, 0 });
            //floorObj.rotation.fromArray(new double[] { Math.PI / 2.0, 0, 0 });
            scene.add(floorObj);

            scene.add(camera);

            renderer.domElement.AttachToDocument();

            renderer.render(scene, camera);
            #endregion }



            #region createDice
            Func<xdice> createDice = delegate
            {


                Console.WriteLine("before array");


                //t = new Array(3);
                //p.rotation = t;

                var zero = 0.0;


                #region boxInfo
                var boxInfo = new[] 
                {
                         new xdiceface {
                        img = (IHTMLImage)new WebGLDiceByEBLOG.HTML.Images.FromAssets.num4(),
                        position= new double [] { 0, 0, -cubeSize },

                        // jsc, please allow 
                        rotation= new double [] { 0, 0, zero }
                    },


                    new  xdiceface{
                        img= (IHTMLImage)new WebGLDiceByEBLOG.HTML.Images.FromAssets.num2(),
                        position = new double [] { -cubeSize, 0, 0 },
                        rotation =  new double [] { 0, Math.PI / 2, 0 }
                    },
                    new xdiceface{
                        img = (IHTMLImage)new WebGLDiceByEBLOG.HTML.Images.FromAssets.num5(),
                        position = new double [] { cubeSize, 0, 0 },
                        rotation= new double [] { 0, -Math.PI / 2, 0 }
                    },
                    new xdiceface{
                        img= (IHTMLImage)new WebGLDiceByEBLOG.HTML.Images.FromAssets.num1(),
                        position= new double [] { 0,  cubeSize, 0 },
                        rotation= new double [] { Math.PI / 2, 0, Math.PI }
                    },
                    new xdiceface {
                        img= (IHTMLImage)new WebGLDiceByEBLOG.HTML.Images.FromAssets.num6(),
                        position= new double [] { 0, -cubeSize, 0 },
                        rotation= new double [] { - Math.PI / 2, 0, Math.PI }
                    },
                    new xdiceface{
                        img= (IHTMLImage)new WebGLDiceByEBLOG.HTML.Images.FromAssets.num3(),
                        position= new double [] { 0, 0,  cubeSize },
                        rotation= new double [] { 0, Math.PI, 0 }
                    },
               
                };
                #endregion
                Console.WriteLine("after array");

                //for three.js
                //var el, dice,
                //    info, img, face;

                var el = new IHTMLDiv();
                el.style.width = cubeSize * 2 + "px";
                el.style.height = cubeSize * 2 + "px";
                var dice = new THREE.CSS3DObject(el);

                for (var j = 0; j < boxInfo.Length; j++)
                {
                    Console.WriteLine("after array " + new { j });

                    var info = boxInfo[j];

                    info.img.style.SetSize(
                        (int)(cubeSize * 2),
                        (int)(cubeSize * 2)
                    );


                    var face = new THREE.CSS3DObject(info.img);

                    face.position.fromArray(info.position);
                    face.rotation.fromArray(info.rotation);
                    dice.add(face);
                }

                //Create physics.
                var mass = 1;
                var box = new CANNON.Box(new CANNON.Vec3(cubeSize, cubeSize, cubeSize));
                var body = new CANNON.RigidBody(mass, box);

                //body.position.set(x, y, z);
                //body.velocity.set(0, 0, Math.random() * -50 - 30);

                //body.angularVelocity.set(10, 10, 10);
                //body.angularDamping = 0.001;

                return new xdice
                {
                    dice = dice,
                    rigid = body
                };
            };
            #endregion


            //world.allowSleep = true;
            var stopped = false;

            Func<double> random = new Random().NextDouble;

            #region initAnimation
            Action<xdice> initAnimation = y =>
            {
                var position = new
                {
                    x = 5 + random() * 50.0,
                    y = 5 + random() * 50.0,
                    z = 5 + random() * 5.0,
                };

                Console.WriteLine(new { position });
                y.rigid.position.set(position.x, position.y, position.z);

                y.rigid.velocity.set(
                    random() * 20 + 0,
                    random() * 20 + 10,
                    random() * 20 + 10
                    );


                y.rigid.angularVelocity.set(10, 10, 10);
                y.rigid.angularDamping = 0.001;
            };
            #endregion

            //create a dice.

            var AllDice = new List<xdice>();

            for (int i = 0; i < 9; i++)
            {

                var y = createDice();

                initAnimation(y);

                scene.add(y.dice);
                world.add(y.rigid);

                AllDice.Add(y);

            }





            var target = new THREE.Vector3();

            var lon = 50.0;
            var lat = -72.0;





            var drag = false;

            #region onframe
            Native.window.onframe +=
                delegate
                {
                    if (Native.document.pointerLockElement == Native.document.body)
                        lon += 0.00;
                    else
                        lon += 0.01;

                    lat = Math.Max(-85, Math.Min(85, lat));

                    Native.document.title = new { lon = (int)lon, lat = (int)lat }.ToString();


                    var phi = THREE.Math.degToRad(90.0 - lat);
                    var theta = THREE.Math.degToRad(lon);

                    target.x = Math.Sin(phi) * Math.Cos(theta);
                    target.y = Math.Cos(phi);
                    target.z = Math.Sin(phi) * Math.Sin(theta);

                    //XInteractiveInt32Form.ToInteractiveInt32Form(
                    camera.position.set(
                        x: 0,
                        y: 50.ToInteractiveInt32Form(),
                        z: 0
                   );

                    #region updatePhysics();
                    //物理エンジンの時間を進める
                    world.step(timeStep);

                    //物理エンジンで計算されたbody(RigidBody)の位置をThree.jsのMeshにコピー
                    AllDice.WithEach(
                        y =>
                        {
                            y.rigid.position.copy(y.dice.position);
                            y.rigid.quaternion.copy(y.dice.quaternion);

                            //y.rigid.position.copy(camera.position);
                        }
                    );

                    if (!drag)
                    {
                        camera.lookAt(
                            new THREE.Vector3(
                                AllDice.Average(i => i.rigid.position.x),
                                AllDice.Average(i => i.rigid.position.y),
                                AllDice.Average(i => i.rigid.position.z)
                            )
                        );



                    }
                    else
                    {
                        camera.lookAt(
                            new THREE.Vector3(
                                target.x + camera.position.x,
                                target.y + camera.position.y,
                                target.z + camera.position.z
                            )
                        );
                    }

                    ground.position.copy(floorObj.position);
                    ground.quaternion.copy(floorObj.quaternion);
                    #endregion

                    renderer.render(scene, camera);
                };
            #endregion




            #region sleepy
            AllDice.Last().With(
                x =>
                {

                    x.rigid.addEventListener("sleepy",

                        IFunction.OfDelegate(
                            new Action(
                                delegate
                                {

                                    var px = new THREE.Vector4(1, 0, 0, 0);
                                    var nx = new THREE.Vector4(-1, 0, 0, 0);
                                    var py = new THREE.Vector4(0, 1, 0, 0);
                                    var ny = new THREE.Vector4(0, -1, 0, 0);
                                    var pz = new THREE.Vector4(0, 0, 1, 0);
                                    var nz = new THREE.Vector4(0, 0, -1, 0);
                                    var UP = 0.99;
                                    //tmp;

                                    #region showNum
                                    Action<int> showNum = num =>
                                    {
                                        new { num }.ToString().ToDocumentTitle();
                                    };

                                    if (px.applyMatrix4(x.dice.matrixWorld).y > UP)
                                    {
                                        showNum(5);
                                    }
                                    else if (nx.applyMatrix4(x.dice.matrixWorld).y > UP)
                                    {
                                        showNum(2);
                                    }
                                    else if (py.applyMatrix4(x.dice.matrixWorld).y > UP)
                                    {
                                        showNum(1);
                                    }
                                    else if (ny.applyMatrix4(x.dice.matrixWorld).y > UP)
                                    {
                                        showNum(6);
                                    }
                                    else if (pz.applyMatrix4(x.dice.matrixWorld).y > UP)
                                    {
                                        showNum(3);
                                    }
                                    else if (nz.applyMatrix4(x.dice.matrixWorld).y > UP)
                                    {
                                        showNum(4);
                                    }
                                    #endregion

                                    stopped = true;
                                }
                            )
                        )
                    );
                }
            );

            #endregion

            #region onresize
            Action AtResize = delegate
            {
                camera.aspect = Native.window.Width / Native.window.Height;
                camera.updateProjectionMatrix();
                renderer.setSize(Native.window.Width, Native.window.Height);
            };
            Native.window.onresize +=
              delegate
              {
                  AtResize();
              };
            #endregion


            #region onclick
            var button = new IHTMLDiv();

            button.style.position = IStyle.PositionEnum.absolute;
            button.style.left = "0px";
            button.style.right = "0px";
            button.style.bottom = "0px";
            button.style.height = "3em";
            button.style.backgroundColor = "rgba(0,0,0,0.5)";

            button.AttachToDocument();



            button.onmousedown +=
                e =>
                {
                    e.stopPropagation();
                };

            button.ontouchstart +=
                e =>
                {
                    e.stopPropagation();
                };

            button.ontouchmove +=
                 e =>
                 {
                     e.stopPropagation();
                 };

            button.onclick +=
                delegate
                {
                    stopped = false;

                    AllDice.WithEach(
                       y =>
                       {
                           initAnimation(y);
                       }
                    );

                };
            #endregion

            #region ontouchmove
            var touchX = 0;
            var touchY = 0;

            Native.document.body.ontouchend +=
               e =>
               {
                   drag = false;
               };


            Native.document.body.ontouchstart +=
                e =>
                {
                    drag = true;
                    e.preventDefault();

                    var touch = e.touches[0];

                    touchX = touch.screenX;
                    touchY = touch.screenY;

                };


            Native.document.body.ontouchmove +=
              e =>
              {
                  e.preventDefault();

                  var touch = e.touches[0];

                  lon -= (touch.screenX - touchX) * 0.1;
                  lat += (touch.screenY - touchY) * 0.1;

                  touchX = touch.screenX;
                  touchY = touch.screenY;

              };
            #endregion






            #region camera rotation
            renderer.domElement.onmousemove +=
                e =>
                {
                    e.preventDefault();

                    if (Native.document.pointerLockElement == renderer.domElement)
                    {
                        drag = true;
                        lon += e.movementX * 0.1;
                        lat -= e.movementY * 0.1;

                        //Console.WriteLine(new { lon, lat, e.movementX, e.movementY });
                    }
                };


            renderer.domElement.onmouseup +=
              e =>
              {
                  drag = Native.document.pointerLockElement != null;
                  e.preventDefault();
              };

            renderer.domElement.onmousedown +=
                e =>
                {
                    //e.CaptureMouse();

                    drag = true;
                    e.preventDefault();
                    renderer.domElement.requestPointerLock();

                };


            renderer.domElement.ondblclick +=
                e =>
                {
                    e.preventDefault();

                    Console.WriteLine("requestPointerLock");
                };

            #endregion






        }
        // example broken by sf data loss?
        // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20151016/azimuthal


        /// <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)
        {
            // chrome 48 opens too many connections to download pngs?
            // Version 48.0.2536.0 canary (64-bit)

            //TCP enter https { ClientCounter = 395 }
            //{ RemoteEndPoint = 192.168.1.199:65133, isPeerProxy = False }
            //{ certificate = , chain =  }

            //Unhandled Exception: OutOfMemoryException.

            // view-source:file:///X:/opensource/github/three.js/examples/css3d_panorama.html

            // http://security.stackexchange.com/questions/53765/router-detecting-port-scan-and-ack-flood-attack
            // what causes the port flood

            ////TCP enter https { ClientCounter = 12 }
            ////{ RemoteEndPoint = 192.168.1.199:65188, isPeerProxy = False }
            //new azi_ny().AttachToDocument();
            //// what happens if we add two of them?

            //var __threejs = new THREE.Vector3(-512, 0, 0);




            //var camera = new THREE.PerspectiveCamera(75, Native.window.aspect, 1, 1000);
            //var scene = new THREE.Scene();

            //var renderer = new THREE.CSS3DRenderer();

            Console.WriteLine("about to init sides...");
            #region sides
            var sides = new[]
            {
                new side
                {
                    img=  new azi_px(),

                    // glsl, clr46, Vector3?
                    position= new THREE.Vector3( -512, 0, 0 ),
                    rotation= new THREE.Vector3( 0, Math.PI / 2, 0 )
                },
                new side {
                    img=  new azi_nx(),
                    position= new THREE.Vector3( 512, 0, 0 ),
                    rotation= new THREE.Vector3( 0, -Math.PI / 2, 0 )
                },
                new side{
                    img=  new azi_py(),
                    position= new THREE.Vector3( 0,  512, 0 ),
                    rotation= new THREE.Vector3( Math.PI / 2, 0, Math.PI )
                },
                new side{
                    img=  new azi_ny(),
                    position= new THREE.Vector3( 0, -512, 0 ),
                    rotation= new THREE.Vector3( - Math.PI / 2, 0, Math.PI )
                },
                new side{
                    img=  new azi_pz(),
                    position= new THREE.Vector3( 0, 0,  512 ),
                    rotation= new THREE.Vector3( 0, Math.PI, 0 )
                },
                new side{
                    img=  new azi_nz(),
                    position= new THREE.Vector3( 0, 0, -512 ),
                    rotation= new THREE.Vector3( 0, 0, 0 )
                }
            };
            #endregion
            Console.WriteLine("about to init CSS3DObject sides... did chrome just abuse TCP ?");

            var camera = new THREE.PerspectiveCamera(75, Native.window.aspect, 1, 1000);
            var scene = new THREE.Scene();

            var renderer = new THREE.CSS3DRenderer();

            for (var i = 0; i < sides.Length; i++)
            {
                var side = sides[i];

                var element = side.img;

                element.style.SetSize(1026, 1026);

                //element.width = 1026; // 2 pixels extra to close the gap.

                var xobject = new THREE.CSS3DObject(element);
                xobject.position.set(side.position.x, side.position.y, side.position.z);
                xobject.rotation.set(side.rotation.x, side.rotation.y, side.rotation.z);
                scene.add(xobject);

            }

            //// c48 floods the ports by now.
            //TCP enter https { ClientCounter = 344 }
            //{ RemoteEndPoint = 192.168.1.199:49221, isPeerProxy = False }

            //Unhandled Exception: OutOfMemoryException.
            //return;


            //<div style="-webkit-transform-style: preserve-3d; width: 978px; height: 664px; -webkit-transform: translate3d(0px, 0px, 432.6708237832803px) matrix3d(0.34382355213165283, -0.024581052362918854, -0.938712477684021, 0, 0, -0.9996572732925415, 0.026176948100328445, 0, 0.9390342831611633, 0.00900025200098753, 0.34370577335357666, 0, 0, 0, 0, 0.9999999403953552) translate3d(489px, 332px, 0px);">
            //        <img src="assets/CSSAzimuthMapViz/posx.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, -512, 0, 0, 1);"><img src="assets/CSSAzimuthMapViz/negx.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 512, 0, 0, 1);"><img src="assets/CSSAzimuthMapViz/posy.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 512, 0, 1);"><img src="assets/CSSAzimuthMapViz/negy.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, -512, 0, 1);"><img src="assets/CSSAzimuthMapViz/posz.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 512, 1);"><img src="assets/CSSAzimuthMapViz/negz.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, -512, 1);"></div>
            //<div style="-webkit-transform-style: preserve-3d; width: 978px; height: 664px; -webkit-transform: translate3d(0px, 0px, 432.6708237832803px) matrix3d(-0.4524347484111786, 0, 0.8917974829673767, 0, 0, -1, 0, 0, -0.8917974829673767, 0, -0.4524347484111786, 0, 0, 0, 0, 1) translate3d(489px, 332px, 0px);">
            // <img width="1026" src="textures/cube/Bridge2/posx.jpg"                                             style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(0, 0, -1, 0, 0, -1, 0, 0, 1, 0, 0, 0, -512, 0, 0, 1);"><img width="1026" src="textures/cube/Bridge2/negx.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 512, 0, 0, 1);"><img width="1026" src="textures/cube/Bridge2/posy.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(-1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 512, 0, 1);"><img width="1026" src="textures/cube/Bridge2/negy.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(-1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, -512, 0, 1);"><img width="1026" src="textures/cube/Bridge2/posz.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 512, 1);"><img width="1026" src="textures/cube/Bridge2/negz.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, -512, 1);"></div> 


            renderer.setSize(Native.window.Width, Native.window.Height);
            renderer.domElement.AttachToDocument();

            #region onresize
            Native.window.onresize +=
                delegate
                {
                    camera.aspect = Native.window.aspect;
                    camera.updateProjectionMatrix();

                    renderer.setSize(Native.window.Width, Native.window.Height);
                };
            #endregion

            var target = new THREE.Vector3();

            var lon = 90.0;
            var lat = 0.0;
            var phi = 0.0;
            var theta = 0.0;





            var drag = false;


            Native.window.onframe +=
                delegate
                {
                    if (Native.document.pointerLockElement == Native.document.body)
                        lon += 0.00;
                    else
                        lon += 0.01;

                    lat = Math.Max(-85, Math.Min(85, lat));

                    //Native.document.title = new { lon, lat }.ToString();


                    phi = THREE.Math.degToRad(90 - lat);
                    theta = THREE.Math.degToRad(lon);

                    target.x = Math.Sin(phi) * Math.Cos(theta);
                    target.y = Math.Cos(phi);
                    target.z = Math.Sin(phi) * Math.Sin(theta);

                    camera.lookAt(target);

                    renderer.render(scene, camera);

                };

            #region ontouchmove
            var touchX = 0;
            var touchY = 0;

            Native.document.body.ontouchstart +=
                e =>
                {
                    e.preventDefault();

                    var touch = e.touches[0];

                    touchX = touch.screenX;
                    touchY = touch.screenY;

                };


            Native.document.body.ontouchmove +=
              e =>
              {
                  e.preventDefault();

                  var touch = e.touches[0];

                  lon -= (touch.screenX - touchX) * 0.1;
                  lat += (touch.screenY - touchY) * 0.1;

                  touchX = touch.screenX;
                  touchY = touch.screenY;

              };
            #endregion






            #region camera rotation
            Native.document.body.onmousemove +=
                e =>
                {
                    e.preventDefault();

                    if (Native.document.pointerLockElement == Native.document.body)
                    {
                        lon += e.movementX * 0.1;
                        lat -= e.movementY * 0.1;

                        //Console.WriteLine(new { lon, lat, e.movementX, e.movementY });
                    }
                };


            Native.document.body.onmouseup +=
              e =>
              {
                  drag = false;
                  e.preventDefault();
              };

            Native.document.body.onmousedown +=
                e =>
                {
                    //e.CaptureMouse();

                    drag = true;
                    e.preventDefault();
                    Native.document.body.requestPointerLock();

                };


            Native.document.body.ondblclick +=
                e =>
                {
                    e.preventDefault();

                    Console.WriteLine("requestPointerLock");
                };

            #endregion



            Native.document.body.onmousewheel +=
                e =>
                {
                    camera.fov -= e.WheelDirection * 5.0;
                    camera.updateProjectionMatrix();
                };


        }
        /// <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)
        {
            // haha this is rather messed up on android.
            // perhaps the css3d runs out of memory?

            //var frame0 = Task.Delay(100);
            //var frame1 = Task.Delay(200);



            var sw = Stopwatch.StartNew();



            //var lon0 = 90.0;
            var lon0 = 0.0;
            var lon1 = 0.0;

            var lon = new sum(
                 () => lon0,
                 () => lon1
             );

            var lat0 = 0.0;
            var lat1 = 0.0;

            // or could we do it with byref or pointers?
            var lat = new sum(
                () => lat0,
                () => lat1
            );

            var phi = 0.0;
            var theta = 0.0;



            var camera_rotation_z = 0.0;


            var drag = false;

            new { }.With(
                async delegate
                {

                    //var f12 = await new airplane().async.oncomplete;

                    // S6 likes 1024 more...
                    var f12 = await new airplane_1024().async.oncomplete;



                    Action<int> draweye = async (int eyeid) =>
                        {
                            // view-source:file:///X:/opensource/github/three.js/examples/css3d_panorama.html

                            //var camera = new THREE.PerspectiveCamera(75, Native.window.aspect, 1, 1000);

                            // wearality lenses?
                            //var camera = new THREE.PerspectiveCamera(90, Native.window.aspect, 1, 1000);
                            //var camera = new THREE.PerspectiveCamera(120, Native.window.aspect, 1, 1000);
                            //var camera = new THREE.PerspectiveCamera(96, Native.window.aspect, 1, 1000);
                            var camera = new THREE.PerspectiveCamera(90, Native.window.aspect, 1, 1000);
                            var scene = new THREE.Scene();

                            var renderer0 = new THREE.CSS3DRenderer();

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


                            // s6 webview wont know?
                            //var f12_height = 1536;
                            // or is s6 memory or size limited?
                            var f12_height = f12.height;

                            //var f12 = await new airplane_low().async.oncomplete;
                            //var f12 = await new airplane_1024().async.oncomplete;
                            //var f12 = await new airplane_729().async.oncomplete;
                            // stop flickering damnet
                            //var f12 = await new airplane_400().async.oncomplete;



                            // ok we got this cool 12 frame stereo map.

                            // what happens if we just pass it?
                            // haha. we get all frames in one.


                            //Func<int, var> f = i =>

                            #region f
                            Func<int, IHTMLCanvas> f = i =>
                            {
                                // we do have a skybox example somewhere...
                                var f1 = new CanvasRenderingContext2D(w: f12_height, h: f12_height);


                                // can we keep animating the stereo ?

                                // if we return canvas it gets messed up. why?
                                // looks to  be a bug?

                                //var stale = new IHTMLImage();

                                if (eyeid == 0)
                                {
                                    // GearVR would have both eyes!
                                    // laptop has to flip between eyes to give similar effect?
                                    // if this were a chrome app. could gearvr request the frames into the photos360 app?

                                    f1.drawImage(f12, i * f12_height, 0, sw: f12_height, sh: f12_height,
                                    dx: 0, dy: 0, dw: f12_height, dh: f12_height);

                                    // whenever we call drawImage ? callsite event monitoring?
                                    // this seems to be slow
                                    //stale.src = f1.canvas.toDataURL();

                                    // can we have a synchronized frame choreo?
                                    //await Task.Delay(1000 / 15);
                                }
                                else
                                {
                                    //await frame0;

                                    // update!

                                    f1.drawImage(f12, (i + 6) * f12_height, 0, sw: f12_height, sh: f12_height,
                                    dx: 0, dy: 0, dw: f12_height, dh: f12_height);

                                    //stale.src = f1.canvas.toDataURL();

                                    //await frame1;
                                    //await Task.Delay(1000 / 15);

                                };

                                // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150812/cssstereo

                                return f1;
                            };
                            #endregion


                            //var f0 = new CanvasRenderingContext2D(w: f12.height, h: f12.height);
                            //f0.drawImage(f12, 0, 0, sw: f12.height, sh: f12.height,
                            //    dx: 0, dy: 0, dw: f12.height, dh: f12.height);

                            #region sides
                            var sides = new Func<side>[]
                    {
                () => new side
                {
                    CSS3DObject_element=  f(0),
                    position= new THREE.Vector3( -512, 0, 0 ),
                    rotation= new THREE.Vector3( 0, Math.PI / 2, 0 )
                },
                () => new side {
                    //img=  new humus_nx(),
                    CSS3DObject_element = f(1),

                    position= new THREE.Vector3( 512, 0, 0 ),
                    rotation= new THREE.Vector3( 0, -Math.PI / 2, 0 )
                },
                () => new side{
                    CSS3DObject_element=  f(2),
                    //img=  new humus_py(),
                    position= new THREE.Vector3( 0,  512, 0 ),
                    rotation= new THREE.Vector3( Math.PI / 2, 0, Math.PI )
                },
                () => new side{
                    //img=  new humus_ny(),
                    CSS3DObject_element=  f(3),
                    position= new THREE.Vector3( 0, -512, 0 ),
                    rotation= new THREE.Vector3( - Math.PI / 2, 0, Math.PI )
                },
                () => new side{
                    CSS3DObject_element=  f(4),
                    //img=  new humus_pz(),
                    position= new THREE.Vector3( 0, 0,  512 ),
                    rotation= new THREE.Vector3( 0, Math.PI, 0 )
                },
                () => new side{
                    CSS3DObject_element=  f(5),
                    //img=  new humus_nz(),
                    position= new THREE.Vector3( 0, 0, -512 ),
                    rotation= new THREE.Vector3( 0, 0, 0 )
                }
            };
                            #endregion

                            for (var i = 0; i < sides.Length; i++)
                            {
                                if (i == 1)
                                {
                                    var side = sides[i]();

                                    var element = side.CSS3DObject_element;

                                    element.style.SetSize(1026, 1026);
                                    //element.style.SetSize(256, 256);

                                    //element.width = 1026; // 2 pixels extra to close the gap.

                                    var xobject = new THREE.CSS3DObject(element);
                                    xobject.position.set(side.position.x, side.position.y, side.position.z);
                                    xobject.rotation.set(side.rotation.x, side.rotation.y, side.rotation.z);

                                    // and lets use max res?
                                    // since everything flicers, lets keep only front CSS thing
                                    scene.add(xobject);
                                }

                            }


                            //<div style="-webkit-transform-style: preserve-3d; width: 978px; height: 664px; -webkit-transform: translate3d(0px, 0px, 432.6708237832803px) matrix3d(0.34382355213165283, -0.024581052362918854, -0.938712477684021, 0, 0, -0.9996572732925415, 0.026176948100328445, 0, 0.9390342831611633, 0.00900025200098753, 0.34370577335357666, 0, 0, 0, 0, 0.9999999403953552) translate3d(489px, 332px, 0px);">
                            //        <img src="assets/CSSStereoCubeMap/posx.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, -512, 0, 0, 1);"><img src="assets/CSSStereoCubeMap/negx.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 512, 0, 0, 1);"><img src="assets/CSSStereoCubeMap/posy.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 512, 0, 1);"><img src="assets/CSSStereoCubeMap/negy.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, -512, 0, 1);"><img src="assets/CSSStereoCubeMap/posz.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 512, 1);"><img src="assets/CSSStereoCubeMap/negz.jpg" width="1026" style="width: 1024px; height: 1024px; position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, -512, 1);"></div>
                            //<div style="-webkit-transform-style: preserve-3d; width: 978px; height: 664px; -webkit-transform: translate3d(0px, 0px, 432.6708237832803px) matrix3d(-0.4524347484111786, 0, 0.8917974829673767, 0, 0, -1, 0, 0, -0.8917974829673767, 0, -0.4524347484111786, 0, 0, 0, 0, 1) translate3d(489px, 332px, 0px);">
                            // <img width="1026" src="textures/cube/Bridge2/posx.jpg"                                             style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(0, 0, -1, 0, 0, -1, 0, 0, 1, 0, 0, 0, -512, 0, 0, 1);"><img width="1026" src="textures/cube/Bridge2/negx.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 512, 0, 0, 1);"><img width="1026" src="textures/cube/Bridge2/posy.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(-1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 512, 0, 1);"><img width="1026" src="textures/cube/Bridge2/negy.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(-1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, -512, 0, 1);"><img width="1026" src="textures/cube/Bridge2/posz.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 512, 1);"><img width="1026" src="textures/cube/Bridge2/negz.jpg" style="position: absolute; -webkit-transform-style: preserve-3d; -webkit-transform: translate3d(-50%, -50%, 0px) matrix3d(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, -512, 1);"></div> 


                            renderer0.domElement.AttachToDocument();

                            #region onresize
                            new { }.With(
                                async delegate
                                {
                                    do
                                    {

                                        //camera.aspect = Native.window.aspect;
                                        camera.aspect = Native.window.Width / 2.0 / Native.window.Height;
                                        camera.updateProjectionMatrix();

                                        //renderer0.setSize(Native.window.Width / 2, Native.window.Height);
                                        renderer0.setSize(Native.window.Width / 2, Native.window.Height);
                                        //renderer0.domElement.style.SetLocation(Native.window.Width / 2 * eyeid, 0);
                                        renderer0.domElement.style.SetLocation(Native.window.Width / 2 * (1 - eyeid), 0);

                                    }
                                    while (await Native.window.async.onresize);
                                });
                            #endregion

                            var target = new THREE.Vector3();


                            Native.window.onframe +=
                                delegate
                                {
                                    //if (Native.document.pointerLockElement == Native.document.body)
                                    //    lon += 0.00;
                                    //else
                                    //    lon += 0.01;

                                    //lat = Math.Max(-85, Math.Min(85, lat));

                                    Native.document.title = new { lon, lat }.ToString();


                                    //phi = THREE.Math.degToRad(90 - lat);
                                    //theta = THREE.Math.degToRad(lon);

                                    //target.x = Math.Sin(phi) * Math.Cos(theta);
                                    //target.y = Math.Cos(phi);
                                    //target.z = Math.Sin(phi) * Math.Sin(theta);

                                    //camera.lookAt(target);


                                    phi = THREE.Math.degToRad(90 - lat);
                                    theta = THREE.Math.degToRad(lon);

                                    target.x = 500 * Math.Sin(phi) * Math.Cos(theta);
                                    target.y = 500 * Math.Cos(phi);
                                    target.z = 500 * Math.Sin(phi) * Math.Sin(theta);

                                    camera.lookAt(target);
                                    camera.rotation.z += camera_rotation_z;


                                    renderer0.render(scene, camera);
                                };


                        };

                    draweye(1);
                    draweye(0);


                    var compassHeadingOffset = 0.0;
                    var compassHeadingInitialized = 0;
                    var compassHeadingInitializedsw = Stopwatch.StartNew();

                    #region compassHeading
                    // X:\jsc.svn\examples\javascript\android\Test\TestCompassHeading\TestCompassHeading\Application.cs
                    Native.window.ondeviceorientation +=
                      dataValues =>
                      {
                          // Convert degrees to radians
                          var alphaRad = dataValues.alpha * (Math.PI / 180);
                          var betaRad = dataValues.beta * (Math.PI / 180);
                          var gammaRad = dataValues.gamma * (Math.PI / 180);

                          // Calculate equation components
                          var cA = Math.Cos(alphaRad);
                          var sA = Math.Sin(alphaRad);
                          var cB = Math.Cos(betaRad);
                          var sB = Math.Sin(betaRad);
                          var cG = Math.Cos(gammaRad);
                          var sG = Math.Sin(gammaRad);

                          // Calculate A, B, C rotation components
                          var rA = -cA * sG - sA * sB * cG;
                          var rB = -sA * sG + cA * sB * cG;
                          var rC = -cB * cG;

                          // Calculate compass heading
                          var compassHeading = Math.Atan(rA / rB);

                          // Convert from half unit circle to whole unit circle
                          if (rB < 0)
                          {
                              compassHeading += Math.PI;
                          }
                          else if (rA < 0)
                          {
                              compassHeading += 2 * Math.PI;
                          }

                          /*
                          Alternative calculation (replacing lines 99-107 above):

                            var compassHeading = Math.atan2(rA, rB);

                            if(rA < 0) {
                              compassHeading += 2 * Math.PI;
                            }
                          */

                          // Convert radians to degrees
                          compassHeading *= 180 / Math.PI;

                          // Compass heading can only be derived if returned values are 'absolute'

                          // X:\jsc.svn\examples\javascript\android\Test\TestCompassHeadingWithReset\TestCompassHeadingWithReset\Application.cs

                          //Native.document.body.innerText = new { compassHeading }.ToString();
                          //if (compassHeadingInitialized > 0)
                          if (compassHeadingInitializedsw.ElapsedMilliseconds > 5000)
                          {
                              lon1 = compassHeading - compassHeadingOffset;
                          }
                          else
                          {

                              compassHeadingOffset = compassHeading;
                              compassHeadingInitialized++;
                              //compassHeadingInitialized.
                          }

                      };
                    #endregion

                    #region gamma
                    Native.window.ondeviceorientation +=
                        //e => Native.body.innerText = new { e.alpha, e.beta, e.gamma }.ToString();
                        //e => lon = e.gamma;
                        e =>
                        {
                            lat1 = e.gamma;

                            // after servicing a running instance would be nice
                            // either by patching or just re running the whole iteration in the backgrou
                            //camera_rotation_z = e.beta * 0.02;
                            camera_rotation_z = e.beta * -0.01;
                        };
                    #endregion



                    #region camera rotation
                    var old = new { clientX = 0, clientY = 0 };

                    Native.document.body.ontouchstart +=
                        e =>
                        {
                            var n = new { e.touches[0].clientX, e.touches[0].clientY };
                            old = n;
                        };

                    Native.document.body.ontouchmove +=
                            e =>
                            {
                                var n = new { e.touches[0].clientX, e.touches[0].clientY };

                                e.preventDefault();

                                lon0 += (n.clientX - old.clientX) * 0.2;
                                lat0 -= (n.clientY - old.clientY) * 0.2;

                                old = n;
                            };


                    Native.document.body.onmousemove +=
                        e =>
                        {
                            e.preventDefault();

                            if (Native.document.pointerLockElement == Native.document.body)
                            {
                                lon0 += e.movementX * 0.1;
                                lat0 -= e.movementY * 0.1;

                                //Console.WriteLine(new { lon, lat, e.movementX, e.movementY });
                            }
                        };


                    Native.document.body.onmouseup +=
                      e =>
                      {
                          //drag = false;
                          e.preventDefault();
                      };

                    Native.document.body.onmousedown +=
                        e =>
                        {
                            //e.CaptureMouse();

                            //drag = true;
                            e.preventDefault();
                            Native.document.body.requestPointerLock();

                        };



                    #endregion



                }
            );

        }