/// <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(IDefault page)
        {
            // http://blog.teamtreehouse.com/accessing-the-device-camera-with-getusermedia
            // http://stackoverflow.com/questions/11539689/why-localstreams-contains-localmediastream-and-remotestreams-contains-mediastrea
            // https://developer.mozilla.org/en-US/docs/Web/API/Navigator.getUserMedia
            // http://neave.github.io/face-detection/


            var navigator = (NavigatorUserMedia)(object)Native.window.navigator;

            var successCallback =
                new Action<LocalMediaStream>(
                    localMediaStream =>
                    {
                        Console.WriteLine("got video");

                        //var src = (string)new IFunction("return window.URL.createObjectURL(this);").apply(localMediaStream);

                        var v = new IHTMLVideo { src = localMediaStream.ToObjectURL() }.AttachToDocument();

                        v.play();
                    }
                );

            var errorCallback =
                new Action<NavigatorUserMediaError>(
                    e =>
                    {
                        // restart on error? :P
                        Native.window.alert("no video: " + new { e.code });
                    }
                );

            //var a = 1;

            //var c = new MediaStreamConstraints { video = a == 1, audio = a == 0 };

            //var o = new IFunction("return {video: true};").apply(null);


            navigator.webkitGetUserMedia(
                new { video = true, audio = false },
                successCallback: IFunction.OfDelegate(
                    successCallback
                ),
                errorCallback: IFunction.OfDelegate(
                    errorCallback
                )
            );

            @"Hello world".ToDocumentTitle();
            // Send data from JavaScript to the server tier
            service.WebMethod2(
                @"A string from JavaScript.",
                value => value.ToDocumentTitle()
            );
        }
        /// <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://www.brucelawson.co.uk/2012/specifying-which-camera-in-getusermedia/
            // https://groups.google.com/forum/#!topic/discuss-webrtc/xLBdsLz_Mbw
            // http://stackoverflow.com/questions/15322622/using-multiple-usb-cameras-with-web-rtc

            // dont we have async available?
            Native.window.navigator.getUserMedia(
                stream =>
                {
                    // getUserMedia error { code = , message = , name = DevicesNotFoundError }


                    var v = new IHTMLVideo { src = stream.ToObjectURL() };

                    v.AttachToDocument();

                    v.play();

                    new IHTMLButton { innerText = "record frame" }.AttachToDocument().onclick +=
                        delegate
                        {
                            // Uncaught IndexSizeError: Index or size was negative, or greater than the allowed value. 
                            var c = new CanvasRenderingContext2D(v.clientWidth, v.clientHeight);

                            c.drawImage(
                                v, 0, 0, c.canvas.width, c.canvas.height
                            );

                            var bytes = c.bytes;

                            c.canvas.AttachToDocument();
                        };

                    new IHTMLButton { innerText = "record gif" }.AttachToDocument().WhenClicked(

                       async btn =>
                       {
                           btn.disabled = true;


                           var frames = new List<byte[]>();
                           var x = v.clientWidth;
                           var y = v.clientHeight;

                           //new Timer(
                           //    async tt =>
                           //    {


                           btn.innerText = "rec " + new { frames.Count }.ToString();
                           //while (frames.Count < 60)

                           //while (frames.Count < 20)
                           while (frames.Count < 2)

                               // works with two frames.
                               // are we logging too much data?

                               // cant we hop to another thread in bytes instead yet?
                           // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20160108
                               // are we encoding grame bytes as base64?

                           {
                               await Task.Delay(1000 / 5);

                               btn.innerText = new { frames.Count }.ToString();

                               var bytes = v.bytes;

                               Console.WriteLine(new { bytes.Length });

                               // script: error JSC1000: No implementation found for this native method, please implement
                               // [System.Threading.Tasks.TaskFactory`1.
                               // StartNew(, System.Object, System.Threading.CancellationToken, System.Threading.Tasks.TaskCreationOptions, System.Threading.Tasks.TaskScheduler)]

                               frames.Add(bytes);

                           }


                           Console.WriteLine("encoding!");

                           var e = new Stopwatch();
                           e.Start();


                           // crashes the browser why???
                           var src = await new GIFEncoderWorker(
                               x,
                               y,
                                   delay: 1000 / 10,
                               frames: frames,
                               AtFrame:
                                async index =>
                                {
                                    btn.innerText = new { index, frames.Count }.ToString();

                                    if (index == frames.Count - 1)
                                    {
                                        btn.style.color = "blue";
                                        await Task.Delay(800);
                                        btn.style.color = "";

                                        btn.innerText = "record gif";

                                    }
                                }


                           );


                           Console.WriteLine("done! " + new { e.Elapsed });

                           new IHTMLImage { src = src }.AttachToDocument();


                           btn.disabled = false;


                           btn.title = new { e.Elapsed, src.Length }.ToString();

                       }
                       );
                }
            );

        }
        /// <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)
        {

            // X:\jsc.svn\examples\javascript\WebCamToGIFAnimation\WebCamToGIFAnimation\Application.cs

            control.nfc.onnfc +=
                nfcid =>
                {
                    page.nfcid.innerText = new { nfcid }.ToString();
                };

            Native.window.navigator.getUserMedia(
               stream =>
               {

                   var v = new IHTMLVideo { src = stream.ToObjectURL() };

                   v.AttachToDocument();

                   v.play();



                   control.nfc.onnfc +=
                        nfcid =>
                        {
                            var c = new CanvasRenderingContext2D(v.clientWidth, v.clientHeight);

                            c.drawImage(
                                v, 0, 0, c.canvas.width, c.canvas.height
                            );

                            c.fillText(
                                new { nfcid }.ToString()
                                , 4, 32, 400
                            );

                            var bytes = c.bytes;

                            c.canvas.AttachToDocument();
                        };

                   new IHTMLButton { innerText = "record frame" }.AttachToDocument().onclick +=
                       delegate
                       {
                           var c = new CanvasRenderingContext2D(v.clientWidth, v.clientHeight);

                           c.drawImage(
                               v, 0, 0, c.canvas.width, c.canvas.height
                           );

                           c.fillText(
                              new { xnfcid = "?", page.nfcid }.ToString()
                              , 4, 32, 400
                          );

                           var bytes = c.bytes;

                           c.canvas.AttachToDocument();
                       };
               }
           );

        }