context: /*
   * Magix - A Managed Ajax Library for ASP.NET
   * Copyright 2010 - 2011 - Ra-Software, Inc. - thomas.hansen@winergyinc.com
   * Magix is licensed as GPLv3, or Commercially for Proprietary Projects through Ra-Software.
   */
  
  using System;
  using System.IO;
  using System.Web;
  using System.Text;
  using System.Web.UI;
  using System.Reflection;
  using System.Globalization;
  using System.Collections.Generic;
  using System.Text.RegularExpressions;
  using Magix.UX.Core;
  using Magix.UX.Widgets;
  using Magix.UX.Widgets.Core;
  
  namespace Magix.UX
  {
      public sealed class AjaxManager
      {
          private List<BaseControl> _raControls = new List<BaseControl>();
          private List<string> _scriptIncludes = new List<string>();
          private List<string> _dynamicScriptIncludes = new List<string>();
          private string _redirectUrl;
          private MemoryStream _memStream;
          private HtmlTextWriter _writer;
          private MemoryStream _memStreamBack;
          private HtmlTextWriter _writerBack;
          private string _requestViewState;
  
          /**
           * Public accessor, only way to access instance of AjaxManager. The AjaxManager is
           * the "glue" that ties everything together. Use this property to retrieve the only
           * existing object of type AjaxManager.
           */
          public static AjaxManager Instance
          {
              get
              {
                  if (((Page)HttpContext.Current.CurrentHandler).Items["_AjaxManagerInstance"] == null)
                      ((Page)HttpContext.Current.CurrentHandler).Items["_AjaxManagerInstance"] = new AjaxManager();
                  return (AjaxManager)((Page)HttpContext.Current.CurrentHandler).Items["_AjaxManagerInstance"];
              }
          }
  
          private List<BaseControl> RaControls
          {
              get { return _raControls; }
          }
  
          /**
           * Returns true if this is a Ra-Ajax callback. A Ra-Ajax callback is also a "subset" of
           * a normal Postback, which means that if this property is true, then the IsPostBack from 
           * ASP.NET's System.Web.UI.Page will also be true.
           */
          public bool IsCallback
          {
              get { return HttpContext.Current.Request.Params["__RA_CALLBACK"] == "true" && HttpContext.Current.Request.Params["HTTP_X_MICROSOFTAJAX"] == null; }
          }
  
          internal HtmlTextWriter Writer
          {
              get
              {
                  if (_writer == null)
                  {
                      _memStream = new MemoryStream();
                      TextWriter tw = new StreamWriter(_memStream);
                      _writer = new HtmlTextWriter(tw);
                  }
                  return _writer;
              }
          }
  
          /**
           * Use this method to append you own script and/or HTML or other output which will be executed 
           * on the client when the request returns. Notice though that the JavaScript Ajax engine of
           * Ra-Ajax will expect whatever is being returned to the client to be JavaScript. This means that
           * if you want to return other types of values and objects back to the client you need to wrap this
           * somehow inside of JavaScript by using e.g. JSON or some similar mechanism. Also you should always
           * end your JavaScript statements with semicolon (;) since otherwise other parts of the JavaScript
           * returned probably will fissle.
           */
          public HtmlTextWriter WriterAtBack
          {
              get
              {
                  if (_writerBack == null)
                  {
                      _memStreamBack = new MemoryStream();
                      TextWriter tw = new StreamWriter(_memStreamBack);
                      _writerBack = new HtmlTextWriter(tw);
                  }
                  return _writerBack;
              }
          }
  
          internal void InitializeControl(BaseControl ctrl)
          {
              // We store all Ra Controls in a list for easily access later down the road...
              RaControls.Add(ctrl);
  
              // Making sure we only run the initialization logic ONCE...!!
              if (RaControls.Count == 1)
              {
                  if (((Page)HttpContext.Current.CurrentHandler).Request.Params["HTTP_X_MICROSOFTAJAX"] != null)
                      return;
  
                  if (IsCallback)
                  {
                      // This is a Ra-Ajax callback, we need to wait until the Page Load 
                      // events are finished loading and then find the control which
                      // wants to fire an event and do so...
                      ((Page)HttpContext.Current.CurrentHandler).LoadComplete += CurrentPage_LoadComplete;
  
                      // Checking to see if the Filtering logic has been supressed
                      ((Page)HttpContext.Current.CurrentHandler).Response.Filter = new CallbackFilter(((Page)HttpContext.Current.CurrentHandler).Response.Filter);
                  }
                  else
                  {
                      ((Page)HttpContext.Current.CurrentHandler).LoadComplete += CurrentPage_LoadComplete_NO_AJAX;
  
                      // Checking to see if the Filtering logic has been supressed
                      ((Page)HttpContext.Current.CurrentHandler).Response.Filter = new PostbackFilter(((Page)HttpContext.Current.CurrentHandler).Response.Filter);
                  }
              }
          }
  
          void CurrentPage_LoadComplete_NO_AJAX(object sender, EventArgs e)
          {
              string browser = 
                  ((Page) HttpContext.Current.CurrentHandler).Request.Browser.Browser.ToLower();
              if (browser == "firefox" || 
                  browser == "iceweasel" ||
                  browser == "netscape" ||
                  browser == "applewebkit" ||
                  browser.Contains("mozilla"))
                  ((Page)HttpContext.Current.CurrentHandler).Form.Attributes.Add("autocomplete", "off");
          }
  
          private void CurrentPage_LoadComplete(object sender, EventArgs e)
          {
              // Storing request viewstate in order to be able to "diff" 
              // them in rendering of response
              _requestViewState = ((Page)HttpContext.Current.CurrentHandler).Request.Params["__VIEWSTATE"];
  
              // Finding the Control which initiated the request
              string idOfControl = ((Page)HttpContext.Current.CurrentHandler).Request.Params["__MUX_CONTROL_CALLBACK"];
  
              // Setting Response Content-Type to JavaScript
              ((Page)HttpContext.Current.CurrentHandler).Response.ContentType = "text/javascript";
  
              // Checking to see if this is a "non-Control" callback...
              if (string.IsNullOrEmpty(idOfControl))
              {
                  string functionName = ((Page)HttpContext.Current.CurrentHandler).Request.Params["__FUNCTION_NAME"];
                  
                  if (string.IsNullOrEmpty(functionName))
                      return;
  
                  Control ctrlToCallFor = ((Page)HttpContext.Current.CurrentHandler);
                  MethodInfo webMethod = ExtractMethod(functionName, ref ctrlToCallFor);
  
                  if (webMethod == null || webMethod.GetCustomAttributes(typeof(Magix.UX.Core.WebMethod), false).Length == 0)
                      throw new Exception("Cannot call a method without a WebMethod attribute");
  
                  ParameterInfo[] parameters = webMethod.GetParameters();
  
                  object[] args = new object[parameters.Length];
                  for (int idx = 0; idx < parameters.Length && ((Page)HttpContext.Current.CurrentHandler).Request.Params["__ARG" + idx] != null; idx++)
                  {
                      args[idx] = Convert.ChangeType(((Page)HttpContext.Current.CurrentHandler).Request.Params["__ARG" + idx], parameters[idx].ParameterType);
                  }
  
                  object retVal = webMethod.Invoke(ctrlToCallFor, args);
                  
                  if (retVal != null)
                      WriterAtBack.Write("MUX.Control._methodReturnValue='{0}';", 
                          string.Format(CultureInfo.InvariantCulture, "{0}", retVal).Replace("\\", "\\\\").Replace("'", "\\'"));
                  return;
              }
  
              BaseControl ctrl = RaControls.Find(
                  delegate(BaseControl idx)
                  {
                      return idx.ClientID == idOfControl;
                  });
  
              if (ctrl == null)
                  throw new ApplicationException("There seems to be something wrong with the way you [re-] load controls in your page. The control that raised this event, doesn't exist, which means it was never re-created on the server on the second request ...");
  
              // Getting the name of the event the control raised
              string eventName = ((Page)HttpContext.Current.CurrentHandler).Request.Params["__MUX_EVENT"];
  
              // Dispatching the event to our Ra Control...
              ctrl.RaiseEvent(eventName);
          }
  
          private MethodInfo ExtractMethod(string functionName, ref Control ctrlToCallFor)
          {
              MethodInfo webMethod = null;
              if (functionName.IndexOf(".") == -1)
              {
                  // No UserControls in the picture here...
                  webMethod = ((Page)HttpContext.Current.CurrentHandler).GetType().BaseType.GetMethod(functionName,
                      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                  if (webMethod != null)
                  {
                      ctrlToCallFor = ((Page)HttpContext.Current.CurrentHandler);
                  }
                  else
                  {
                      // Couldn't find method in Page, looking in MasterPage
                      webMethod = ((Page)HttpContext.Current.CurrentHandler).Master.GetType().BaseType.GetMethod(functionName,
                          BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                      ctrlToCallFor = ((Page)HttpContext.Current.CurrentHandler).Master;
                  }
              }
              else
              {
                  // A "." means there's a UserControl hosting this method
                  string[] entities = functionName.Split('.');
                  Control ctrl = Selector.SelectFirst<UserControl>(
                      ((Page)HttpContext.Current.CurrentHandler),
                      delegate(Control idx)
                      {
  
gt:                         return entities[0] == idx.ClientID;