protected sealed override void FinishAjaxRequest()
        {
            // ajax request
            OSJSONResponse resp = CreateJSONResponse();

            if (!AjaxRedirectLocation.IsEmpty())
            {
                // ajax redirect...
                WriteAJAXClientRedirectResponse(Request, Response, AjaxRedirectLocation);
                return;
            }

            if (_partialViewStateBuckets.Count > 0)
            {
                // include in JSON response the updated viewstate buckets that is
                // at this moment stored in _partialViewStateBuckets
                foreach (string bucketName in _partialViewStateBuckets.Keys)
                {
                    resp.AddToHidden(bucketName, _partialViewStateBuckets[bucketName].ToString());
                }
            }
            // send the end request signal
            resp.AddToJs("OsEndRequest()");
            WriteJavascriptResponse(FormatJSONResponseString(resp), /*flush*/ false);

            return;
        }
        protected override void SavePageStateToPersistenceMedium(object viewState)
        {
            string name;
            string value;

            ParseViewStateForCachedObjects(viewState);

            // clear previous partial viewstate buckets
            _partialViewStateBuckets.Clear();

            // save main viewstate bucket
            name = "__OSVSTATE";
            string hash;

            value = SerializeViewState(viewState, out hash);


            if (IsAjaxRequest)
            {
                RegisterPartialViewState(name, value, hash);
            }
            else
            {
                // register the viewstate field
                ClientScript.RegisterHiddenField(name, value);

                // Set VIEWSTATE = "" so that Page::RenderViewStateFields() doesn't go through the buggy code branch that renders a CR inside the element's ID...
                this.GetType().GetProperty("ClientState", BindingFlags.Instance | BindingFlags.NonPublic).GetSetMethod(true).Invoke(this, new object[] { "" });

                if (_resetViewStateField)
                {
                    // #110767 - Add queued javascript to run when document is ready and set the viewstate with the proper value
                    // This will override input field cache for the viewstate field.
                    OSJSONResponse resp = new OSJSONResponse();
                    resp.AddToHidden(name, value);
                    // Ensure that this is the first queued javascript to be executed!
                    //_javascriptQueueResponse.InsertToJs(0, FormatJSONResponseString(resp));
                    //_javascriptQueueResponse.AddToHidden(name, value);
                    OnloadVSUpdate = value;
                }
            }
        }
        public void AjaxRefresh(Control c, int rowIndex, string animationName, string listOperation, bool isTableRecord, object dataItem, StoreViewStateDelegate storeViewStateDelegate, string clientIdOverride)
        {
            // don't do nothing if not in a partial request or if the control is not accessible (e.g.: refreshing widgets inside table records from outside)
            if (!IsAjaxRequest || c == null)
            {
                return;
            }

            // mark that we are refreshing a widget, so that possible queued javascripts happening in the databind are queued
            // to be sent immediately after the control json (for web block preparations or user defined functions execution)
            _isRefreshingWidget = true;

            try {
                LocalState stackBackup = null;
#if JAVA
                HashSet <IAbstractTopLevelComponentWithMandatory> previousVisibleComponentsWithValidation = null,
                                                                  newVisibleComponentsWithValidation      = null;
#endif


                if (c is OSUserControl)
                {
                    OSUserControl blk = (OSUserControl)c;
                    stackBackup = ((IWebScreen)blk.Page).PushStack();
                }
                else
                {
                    stackBackup = ((IWebScreen)c.Page).PushStack();
                }

                if (listOperation == "")
                {
#if JAVA
                    previousVisibleComponentsWithValidation = GetVisibleComponentsWithValidation(c);
#endif

                    // databind the control
                    c.DataBind();

#if JAVA
                    newVisibleComponentsWithValidation = GetVisibleComponentsWithValidation(c);
#endif
                }
                else
                {
                    // some list operation

                    if (!isTableRecord)
                    {
                        // list records require the prerender phase before list append, insert and remove operations, since prerender creates the current child controls hierarchy
                        MethodInfo preRenderRecursiveInternal = typeof(Page).GetMethod("PreRenderRecursiveInternal", BindingFlags.NonPublic | BindingFlags.Instance);
                        preRenderRecursiveInternal.Invoke(c, null);
                    }

                    IListRefresh listWidget = (IListRefresh)c;
                    // execute the list refresh databind to restore LineCount and StartIndex runtime properties in the table / list record
                    listWidget.DoListRefreshDataBind();
                    // execute the operation in the list
                    switch (listOperation)
                    {
                    case "Append":
                        listWidget.AppendItem(dataItem);
                        break;

                    case "Insert":
                        listWidget.InsertItem(rowIndex, dataItem);
                        break;

                    case "Remove":
                        listWidget.RemoveItem(rowIndex);
                        break;

                    case "Refresh":
                        listWidget.RefreshItem(rowIndex);
                        break;

                    default:
                        System.Diagnostics.Debug.Assert(false, "unknown list operation: " + listOperation);
                        break;
                    }
                }

                // call store viewstate in the calling page / block

                storeViewStateDelegate();

                if (stackBackup != null)
                {
                    Debugger.Pop(stackBackup, true);
                }

                // save partial viewstate and get modified buckets

                Hashtable modifiedBuckets = PartialSaveAllState();

                OSJSONResponse resp = CreateJSONResponse();

                // build JSON response with the control rendering and modified buckets

                foreach (string bucketName in modifiedBuckets.Keys)
                {
                    resp.AddToHidden(bucketName, modifiedBuckets[bucketName].ToString());
                }

                string html = "";

                if (listOperation == "")
                {
                    // regular ajax refresh
                    // render control and add it to outers
                    resp.AddToOuter(c.ClientID, RenderControl(c));

#if JAVA
                    ProcessComponentsWithValidationTurnedInvisible(previousVisibleComponentsWithValidation,
                                                                   newVisibleComponentsWithValidation);
#endif
                }
                else
                {
                    // implicit ajax refresh via ListAppend, ListInsert or ListRemove
                    if (listOperation == "Append")
                    {
                        html = RenderAjaxListOperationRow(c);
                    }
                    else if (listOperation == "Insert")
                    {
#if JAVA
                        html = RenderAjaxListOperationRow(c, rowIndex);
#else
                        html = RenderAjaxListOperationRow(c);
#endif
                    }
                    else if (listOperation == "Remove")
                    {
                        // need to render the empty message?
                        if (((IListRefresh)c).IsEmpty)
                        {
                            html = RenderAjaxListOperationRow(c, true);
                        }
                    }
                    else if (listOperation == "Refresh")
                    {
                        // row refresh operation
                        html = RenderAjaxListOperationRow(c, rowIndex);
                    }

                    string oddLineStyle  = "";
                    string evenLineStyle = "";
                    bool   useBullets    = false;

                    if (c is OSDataGrid)
                    {
                        // get table record information to be sent to the client js
                        OSDataGrid dg = (OSDataGrid)c;
                        oddLineStyle  = dg.OddLineStyle ?? oddLineStyle;
                        evenLineStyle = dg.EvenLineStyle ?? evenLineStyle;
                    }
                    else
                    {
                        // get list record information to be sent to the client js
                        Iterator it = (Iterator)c;
                        useBullets = it.UseBullets;
                    }

                    resp.AddToList(clientIdOverride ?? c.ClientID, html, listOperation, rowIndex, isTableRecord, oddLineStyle, evenLineStyle, useBullets);
                }


                // Add call to animation registration
                if (animationName != null && animationName != "None")
                {
                    if (listOperation == "Remove" && animationName == "Highlight")
                    {
                        animationName = "ListRemoveHighlight";
                    }
                    resp.AddToJs("OsRegisterEffect" + animationName.Replace(" ", "") + "();");
                }

                // write and flush the response
                WriteJavascriptResponse(FormatJSONResponseString(resp), /*flush*/ true);

                // send all the queued javascript gathered in this refresh
                if (_javascriptQueueResponse.Js.Count > 0)
                {
                    resp = CreateJSONResponse();
                    resp.AddToJs("outsystems.internal.$(document).ready(function() {" + FormatJSONResponseString(_javascriptQueueResponse) + "});");
                    // clear it for next executions
                    _javascriptQueueResponse.ClearJs();
                    // write the response
                    WriteJavascriptResponse(FormatJSONResponseString(resp), /*flush*/ true);
                }

                // cleanup JavaScript includes so the next refresh will not re-include it
                BlocksJavaScript.CleanupAlreadyIncludedBlocksWithJavaScript();
            } finally {
                // we're no longer refreshing a widget
                _isRefreshingWidget = false;
            }
        }