public HUpdate(int cycle, HElement page, object state, TimeSpan elapsed)
 {
     this.Cycle   = cycle;
     this.Page    = page;
     this.State   = state;
     this.Elapsed = elapsed;
 }
 public static void PushUpdate(string frame, int cycle, HElement page, object state, TimeSpan elapsed)
 {
     for (;;)
     {
         HFrame hframe;
         if (Frames.TryGetValue(frame, out hframe))
         {
             var nextFrame = hframe.With(updates: hframe.Updates.Add(cycle, new HUpdate(cycle, page, state, elapsed)));
             if (Frames.TryUpdate(frame, nextFrame, hframe))
             {
                 return;
             }
         }
         else
         {
             if (Frames.TryAdd(frame, new HFrame(cycle, ImmutableDictionary <int, HUpdate> .Empty.Add(cycle, new HUpdate(cycle, page, state, elapsed)))))
             {
                 return;
             }
         }
     }
 }
 static HElement FirstHtmlTransformer(HElement element)
 {
     return(new HElement("html", element.Element("head"), new HElement("body", "")));
 }
        public static TResponse Process <TRequest, TResponse, TState>(TRequest request, IRequestAdapter <TRequest, TResponse> requestAdapter, Func <TState, JsonData[], TRequest, HtmlResult <HElement> > page) where TState : class, new()
        {
            if (requestAdapter.IsGetMethod(request))
            {
                var result      = page(new TState(), Array <JsonData> .Empty, request);
                var rawResponse = requestAdapter.RawResponse(result);
                if (rawResponse != null)
                {
                    return(rawResponse);
                }

                var html = result.Html;
                var head = html.Element("head") ?? new HElement("head");

                var startHead = new HElement(head.Name,
                                             head.Attributes,
                                             Scripts(frame: Guid.NewGuid().ToString(), refreshPeriod: result.RefreshPeriod ?? TimeSpan.FromSeconds(10)),
                                             head.Nodes
                                             );
                var firstHtmlTransformer = result.As <HtmlResult>()?.FirstHtmlTransformer ?? FirstHtmlTransformer;
                html = firstHtmlTransformer(new HElement("html", startHead, html.Nodes.Where(node => node.As <HElement>()?.Name.LocalName != "head")));
                var toHtmlText = result.As <HtmlResult>()?.ToHtmlText ?? ToHtmlText;
                return(requestAdapter.ToResponse(toHtmlText(html), "text/html", result));
            }
            else
            {
                var json = Parse(requestAdapter.Content(request));

                var route = requestAdapter.Frame(request) ?? "<null>";

                var frame = route + ":" + json.JPath("frame")?.ToString();
                var cycle = ConvertHlp.ToInt(json.JPath("cycle")).OrDefault(0);
                var prev  = PopUpdate(frame, cycle);

                var json_commands = (json.JPath("commands").As <JArray>()?.Select(j => new JsonData(j)).ToArray()).OrEmpty();

                HtmlResult <HElement> result = null;
                var watch = System.Diagnostics.Stopwatch.StartNew();
                try
                {
                    result = page(prev?.Item2?.State.As <TState>() ?? new TState(), json_commands, request);
                }
                catch (Exception exc) //HACK ловятся все ошибки для того, чтобы не зациклилась страница. Добавить обработку ошибок на сторону js.
                {
                    result = new HtmlResult <HElement> {
                        State = prev.Item2.State, Html = prev.Item2.Page
                    };
                }
                watch.Stop();
                var rawResponse = requestAdapter.RawResponse(result);
                if (rawResponse != null)
                {
                    return(rawResponse);
                }

                var isPartial = result.Html.Name.LocalName != "html";
                var toBody    = isPartial ? html => html : (Func <HElement, HElement>)(html => html?.Element("body"));

                var js_updates = HtmlJavaScriptDiffer.JsSync(new HElementProvider(), toBody(prev?.Item2?.Page), toBody(result.Html)).ToArray();
                var jupdate    = new Dictionary <string, object>()
                {
                    { "cycle", prev.Item1 }, { "prev_cycle", cycle }, { "processed_commands", json_commands.Length }, { "updates", js_updates }
                };

                PushUpdate(frame, prev.Item1, result.Html, result.State, watch.Elapsed);

                return(requestAdapter.ToResponse(JsonConvert.SerializeObject(jupdate), "application/javascript", result));
            }
        }
 static string ToHtmlText(HElement element)
 {
     return(element?.ToHtmlText());
 }