Click here to Skip to main content
Click here to Skip to main content
Articles » Web Development » Ajax » General » Downloads
 
Add your own
alternative version

Magic AJAX: Applying AJAX to your existing Web Pages

, 28 May 2007 MIT
How to apply AJAX technologies to your web pages without replacing ASP.NET controls and/or writing JavaScript code.
magicajax-030-net11.zip
magicajax
Examples
ExampleSite (.NET 2.0 only)
Web.sitemap
webparts
images
first.GIF
last.GIF
next.gif
pow_by_aspnet2.0.gif
prev.GIF
examples
App_Data
App_Code
Docs
Core
script
bin
MagicAjax.dll
magicajax-030-net20.zip
MagicAjax.dll
Web.sitemap
first.GIF
last.GIF
next.gif
pow_by_aspnet2.0.gif
prev.GIF
magicajax-030-source.zip
Web.sitemap
CVS
Root
Repository
Entries.Old
Entries
Entries.Extra.Old
Entries.Extra
first.GIF
last.GIF
next.gif
pow_by_aspnet2.0.gif
prev.GIF
CVS
Root
Repository
Entries.Old
Entries
Entries.Extra.Old
Entries.Extra
CVS
Root
Repository
Entries.Old
Entries
Entries.Extra.Old
Entries.Extra
CVS
Root
Repository
Entries.Old
Entries
Entries.Extra.Old
Entries.Extra
CVS
Root
Repository
Entries.Old
Entries
Entries.Extra.Old
Entries.Extra
CVS
Root
Repository
Entries
Entries.Extra
Entries.Old
Entries.Extra.Old
CVS
Root
Repository
Entries.Old
Entries.Extra.Old
Entries
Entries.Extra
CVS
Root
Repository
Entries
Entries.Extra
Entries.Old
Entries.Extra.Old
MagicAjax.snk
CVS
Root
Repository
Entries.Old
Entries.Extra.Old
Entries
Entries.Extra
UI
Design
CVS
Root
Repository
Entries.Old
Entries.Extra.Old
Entries
Entries.Extra
Controls
ClientEventControls
CVS
Root
Repository
Entries.Old
Entries
Entries.Extra.Old
Entries.Extra
CVS
Root
Repository
Entries.Extra
Entries.Old
Entries
Entries.Extra.Old
CVS
Root
Repository
Entries.Old
Entries.Extra.Old
Entries
Entries.Extra
Interfaces
CVS
Root
Repository
Entries.Old
Entries.Extra.Old
Entries
Entries.Extra
Configuration
CVS
Root
Repository
Entries.Old
Entries.Extra.Old
Entries
Entries.Extra
CVS
Root
Repository
Entries
Entries.Extra
Entries.Old
Entries.Extra.Old
CVS
Root
Repository
Entries.Old
Entries.Extra.Old
Entries
Entries.Extra
magicajax.zip
AJAXTest
AJAXTest.csproj.webinfo
Global.asax
Ajax
Ajax.csproj.user
Controls
Design
script
#region LGPL License
/*
MagicAjax.NET Framework
Copyright (C) 2005  MagicAjax Project Team

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#endregion

using System;
using System.Configuration;
using System.Web;
using System.Globalization;
using System.Web.UI;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Specialized;
using System.Text;

namespace MagicAjax
{
	/// <summary>
	/// Helper functions for AjaxCall handling.
	/// </summary>
	public sealed class AjaxCallHelper
	{
		#region Private Fields/Methods
		private AjaxCallHelper() { }

		private static ArrayList _sbWritingLevels
		{
			get
			{
				ArrayList levels = HttpContext.Current.Items["_sbWritingLevels"] as ArrayList;
				if (levels == null)
				{
					throw new MagicAjaxException("Using the AjaxCallHelper write methods outside of an AjaxCall is not allowed.");
				}
				return levels;
			}
			set
			{
				HttpContext.Current.Items["_sbWritingLevels"] = value;
			}
		}
		private static int _writingLevel
		{
			get
			{
				if (HttpContext.Current.Items.Contains("_writingLevel"))
				{
					return (int)(HttpContext.Current.Items["_writingLevel"]);
				}
				return 0;
			}
			set
			{
				HttpContext.Current.Items["_writingLevel"] = value;
			}
		}

		private static void MergeNextWritingLevelRecursive(int writingLevel)
		{
			int nextLevel = writingLevel + 1;
			if (_sbWritingLevels.Count == nextLevel)
				return;

			MergeNextWritingLevelRecursive(nextLevel);
			(_sbWritingLevels[writingLevel] as StringBuilder).Append((_sbWritingLevels[nextLevel] as StringBuilder).ToString());
			_sbWritingLevels.RemoveAt(nextLevel);
		}
		#endregion

		#region Static Methods

		/// <summary>
		/// Gets the name of the javascript function that is used for AjaxCalls from
		/// the client.
		/// </summary>
		public static string AjaxCallClientFunction
		{
			get { return "AJAXCbo.DoAjaxCall"; }
		}

		/// <summary>
		/// Increases the script writing level.
		/// </summary>
		/// <remarks>
		/// There must be a matching DecreaseWritingLevel call.
		/// See remarks of MergeUpperWritingLevelsWithCurrent method.
		/// </remarks>
		public static void IncreaseWritingLevel()
		{
			_writingLevel++;
			if (_writingLevel == _sbWritingLevels.Count)
				_sbWritingLevels.Add(new StringBuilder());
		}

		/// <summary>
		/// Decreases the script writing level.
		/// </summary>
		/// <remarks>
		/// There must be a matching IncreaseWritingLevel call.
		/// See remarks of MergeUpperWritingLevelsWithCurrent method.
		/// </remarks>
		public static void DecreaseWritingLevel()
		{
			if (_writingLevel == 0)
				throw new MagicAjaxException("Script writing level cannot be negative.");

			_writingLevel--;
		}

		/// <summary>
		/// Merges the script rendering from upper leves into the current one.
		/// </summary>
		/// <remarks>
		/// Writing levels are used to control the ordering of script commands,
		/// especially for recursive methods.
		/// For example:
		/// AjaxCallHelper.IncreaseWritingLevel();
		/// WriteMyScript();
		/// AjaxCallHelper.DecreaseWritingLevel();
		/// 
		/// AjaxCallHelper.Write("MyScript rendering will be after this");
		/// AjaxCallHelper.MergeUpperWritingLevelsWithCurrent()
		/// 
		/// This will produce the output:
		/// MyScript rendering will be after this
		/// [MyScript method rendering]
		/// </remarks>
		public static void MergeUpperWritingLevelsWithCurrent()
		{
			MergeNextWritingLevelRecursive(_writingLevel);
		}

		/// <summary>
		/// Obtains a reference to a client-side script function that causes, when invoked,
		/// the control to raise an AjaxCall event to the server. This method also passes
		/// a parameter to the server control that performs the post-back processing on
		/// the server.
		/// </summary>
		/// <example>
		/// // Replaces the submit function of the button with the AjaxCall invoking function.
		/// btnSend.Attributes.Add ("onclick", AjaxCallHelper.GetAjaxCallEventReference(btnSend) + " return false;");
		/// </example>
		/// <param name="control"></param>
		/// <param name="argument"></param>
		/// <returns></returns>
		public static string GetAjaxCallEventReference(Control control, string argument, bool isAsynchronous)
		{
			if (control == null)
				throw new ArgumentNullException("control");

			return String.Format("{0}('{1}','{2}','{3}');", AjaxCallClientFunction, control.UniqueID, argument, (isAsynchronous) ? "async" : "sync");
		}

		/// <summary>
		/// Obtains a reference to a client-side script function that causes, when invoked,
		/// the control to raise an AjaxCall event to the server. This method also passes
		/// a parameter to the server control that performs the post-back processing on
		/// the server.
		/// </summary>
		/// <example>
		/// // Replaces the submit function of the button with the AjaxCall invoking function.
		/// btnSend.Attributes.Add ("onclick", AjaxCallHelper.GetAjaxCallEventReference(btnSend) + " return false;");
		/// </example>
		/// <param name="control"></param>
		/// <param name="argument"></param>
		/// <returns></returns>
		public static string GetAjaxCallEventReference(Control control, string argument)
		{
			if (control == null)
				throw new ArgumentNullException("control");

			return GetAjaxCallEventReference(control, argument, true);
		}

		/// <summary>
		/// Obtains a reference to a client-side script function that causes, when invoked,
		/// the control to raise an AjaxCall event to the server.
		/// </summary>
		/// <example>
		/// // Replaces the submit function of the button with the AjaxCall invoking function.
		/// btnSend.Attributes.Add ("onclick", AjaxCallHelper.GetAjaxCallEventReference(btnSend) + " return false;");
		/// </example>
		/// <param name="control"></param>
		/// <returns></returns>
		public static string GetAjaxCallEventReference(Control control)
		{
			return GetAjaxCallEventReference(control, "", true);
		}

		/// <summary>
		/// Appends javascript: to the beginning of the return from a
		/// GetAjaxCallEventReference call to allow hyperlink AjaxCall processing on the
		/// server.
		/// </summary>
		/// <param name="control"></param>
		/// <param name="argument"></param>
		/// <returns></returns>
		public static string GetAjaxCallClientHyperlink(Control control, string argument)
		{
			return String.Format("javascript:{0}", GetAjaxCallEventReference(control, argument));
		}

		/// <summary>
		/// Defines the time between repeated automatic AjaxCalls of the page.
		/// </summary>
		/// <param name="milliSeconds">Set it to zero in order to disable AjaxCallTimer</param>
		public static void SetAjaxCallTimerInterval(int milliSeconds)
		{
			if (milliSeconds > 0)
			{
				if (MagicAjaxContext.Current.IsAjaxCall)
				{
					Write(String.Format("AJAXCbo.SetIntervalForAjaxCall({0});", milliSeconds));
				}
				else if (MagicAjaxContext.Current.IsBrowserSupported)
				{
					//EnableAjaxOnPage();
					Page page = (Page)HttpContext.Current.Handler;
					page.RegisterStartupScript("AJAX_AjaxCallTimer_SCRIPT", String.Format("<script language=\"javascript\">AJAXCbo.SetIntervalForAjaxCall({0});</script>", milliSeconds));
				}
			}
			else
			{
				if (MagicAjaxContext.Current.IsAjaxCall)
					Write("AJAXCbo.ClearIntervalForAjaxCall();");
				else
					throw new MagicAjaxException("AjaxCallTimer can be cleared only during an AjaxCall.");
			}
		}
		#endregion

		#region Static Methods for javascript writing
		/// <summary>
		/// Produces the javascript that redirects to a url.
		/// </summary>
		/// <param name="url"></param>
		public static void Redirect(string url)
		{
			Init();
			Write(String.Format("window.location.href=\"{0}\";\r\n", Util.ResolveUrl(url)));
			End();
		}

		/// <summary>
		/// Produces the javascript that will set the attributes of a control.
		/// </summary>
		/// <remarks>
		/// The attributes must be of this format:
		/// "attrib1=value1|attrib2=value2|attrib3=value3"
		/// </remarks>
		/// <param name="clientID">ClientID of the control</param>
		/// <param name="attributes">Formatted list of attributes</param>
		public static void WriteSetAttributesOfControl(string clientID, string attributes)
		{
			Write(String.Format("AJAXCbo.SetAttributesOfControl(\"{0}\",\"{1}\");\r\n", clientID, attributes));
		}

		/// <summary>
		/// Produces the javascript that will a new element on the page.
		/// </summary>
		/// <param name="parentID">The id of the element that will contain the new element</param>
		/// <param name="elementID">The id of the new element</param>
		/// <param name="html">The innerHTML of the new element</param>
		/// <param name="beforeElemID">The id of the element that the new element must be inserted before it. Use null to append the new element at the end of the parent element</param>
		public static void WriteAddElementScript(string parentID, string tagName, string elementID, string html, string beforeElemID)
		{
			string before = (beforeElemID != null) ? String.Format("\"{0}\"", beforeElemID) : "null";
			Write(String.Format("AJAXCbo.AddElement(\"{0}\",\"{1}\",\"{2}\",{3},{4});\r\n", parentID, tagName, elementID, EncodeString(html), before));
		}

		/// <summary>
		/// Provides the javascript that will remove an existing element from the page.
		/// </summary>
		/// <param name="parentID">The id of the element that contains the element to be removed</param>
		/// <param name="elementID">The id of the element to be removed</param>
		public static void WriteRemoveElementScript(string parentID, string elementID)
		{
			Write(String.Format("AJAXCbo.RemoveElement(\"{0}\",\"{1}\");\r\n", parentID, elementID));
		}

		/// <summary>
		/// Provides the javascript that will set the value of a field on the page.
		/// </summary>
		/// <remarks>
		/// Use this if you want to manipulate, during AjaxCall, a hidden field previously
		/// registered by the Page.RegisterHiddenField method.
		/// </remarks>
		/// <param name="fieldName"></param>
		/// <param name="fieldValue"></param>
		public static void WriteSetFieldScript(string fieldName, string fieldValue)
		{
			Write(String.Format("AJAXCbo.SetField(\"{0}\",{1});\r\n", fieldName, EncodeString(fieldValue)));
		}

		public static void WriteAddHeaderElementScript(string tagName, string innerText, NameValueCollection attributes)
		{
			StringBuilder sbuilder = new StringBuilder("new Array(");
			if (attributes != null)
			{
				for (int i = 0; i < attributes.Count; i++)
				{
					if (i > 0)
						sbuilder.Append(",");
					sbuilder.AppendFormat("\"{0}\",\"{1}\"", attributes.Keys[i], attributes[i]);
				}
			}
			sbuilder.Append(")");

			Write(String.Format("AJAXCbo.AddHeaderElement(\"{0}\",{1},{2});\r\n", tagName, EncodeString(innerText), sbuilder.ToString()));
		}

		/// <summary>
		/// Produces the javascript that will add a script element to the page.
		/// </summary>
		/// <param name="scriptText">The script text</param>
		/// <param name="scriptText">The script attributes</param>
		public static void WriteAddScriptElementScript(string scriptText, NameValueCollection scriptAttributes)
		{
			StringBuilder sbuilder = new StringBuilder("new Array(");
			for (int i = 0; i < scriptAttributes.Count; i++)
			{
				if (i > 0)
					sbuilder.Append(",");
				sbuilder.AppendFormat("\"{0}\",\"{1}\"", scriptAttributes.Keys[i], scriptAttributes[i]);
			}
			sbuilder.Append(")");
			Write(String.Format("AJAXCbo.AddScript({0},{1});\r\n", EncodeString(scriptText), sbuilder.ToString()));
		}

		public static void WriteAddHiddenFieldScript(string fieldName, string fieldValue)
		{
			Write(String.Format("AJAXCbo.AddHiddenField(\"{0}\",{1});\r\n", fieldName, EncodeString(fieldValue)));
		}

		/// <summary>
		/// Provides the javascript that will se the innerHTML of an element of the page.
		/// </summary>
		/// <param name="html"></param>
		/// <param name="elementID">The id of the element of the page</param>
		public static void WriteSetHtmlOfElementScript(string html, string elementID)
		{
			Write(String.Format("AJAXCbo.SetHtmlOfElement({0},\"{1}\");\r\n", EncodeString(html), elementID));
		}

		public static void WriteSetHtmlOfPageScript(string html)
		{
			Write(String.Format("AJAXCbo.SetHtmlOfPage({0});\r\n", EncodeString(html)));
		}

		public static void WriteSetVisibilityOfElementScript(string elementID, bool visible)
		{
			Write(String.Format("AJAXCbo.SetVisibilityOfElement(\"{0}\",{1});\r\n", elementID, visible.ToString().ToLower(System.Globalization.CultureInfo.InvariantCulture)));
		}

		/// <summary>
		/// Provides the javascript that will invoke an alert message box.
		/// </summary>
		/// <param name="message"></param>
		public static void WriteAlert(string message)
		{
			Write(String.Format("AJAXCbo.Alert({0});\r\n", EncodeString(message)));
		}

		/// <summary>
		/// It appends a "'AJAX_LOADING_OK';" string to the end, so that the client
		/// knows that the loading of the AjaxCall javascript data wasn't aborted,
		/// but they were fully loaded.
		/// </summary>
		/// <remarks>
		/// Called by the End method.
		/// </remarks>
		internal static void WriteEndSignature()
		{
			Write("'AJAX_LOADING_OK';");
		}

		/// <summary>
		/// Converts a string to a javascript string.
		/// </summary>
		/// <example>
		/// AjaxCallHelper.Write (String.Format("alert({0});", AjaxCallHelper.EncodeString("Alert invoked by javascript.")));
		/// </example>
		/// <param name="str"></param>
		/// <returns></returns>
		public static string EncodeString(string str)
		{
			//TODO: use 1 regular expression (faster)
			System.Text.StringBuilder sb = new System.Text.StringBuilder(str);
			sb.Replace("\\", "\\\\");
			sb.Replace("\"", "\\\"");
			sb.Replace("\r", "\\r");
			sb.Replace("\n", "\\n");
			sb.Replace("\t", "\\t");
			sb.Insert(0, '\"');
			sb.Append('\"');
			return sb.ToString();
		}

		public static void WriteFormat(string format, params object[] args)
		{
			Write(String.Format(format, args));
		}

		public static void WriteLine(string text)
		{
			Write(text + "\r\n");
		}

		/// <summary>
		/// Writes javascript to Response during an AjaxCall.
		/// </summary>
		/// <remarks>
		/// Use this method if you want to send custom javascript code for the client to execute.
		/// Use EncodeString if you want to append a string for your javascript code.
		/// </remarks>
		/// <example>
		/// AjaxCallHelper.Write (String.Format("var span = document.getElementById('{0}');", label.ClientID));
		/// AjaxCallHelper.Write (String.Format("span.innerHTML = {0});", AjaxCallHelper.EncodeString("Changed by javascript")));
		/// </example>
		/// <param name="text"></param>
		public static void Write(string text)
		{
			// Use the string builder
			(_sbWritingLevels[_writingLevel] as StringBuilder).Append(text);
		}

		internal static void Init()
		{
			HttpResponse hr = HttpContext.Current.Response;
			hr.StatusCode = 200;
			hr.StatusDescription = "OK";

			_sbWritingLevels = new ArrayList();
			_sbWritingLevels.Add(new StringBuilder());
			_writingLevel = 0;
		}

		/// <summary>
		/// Use this method instead of Response.End during an AjaxCall.
		/// </summary>
		/// <remarks>
		/// Writes the End signature, ends the writing to the Response and causes
		/// CompleteRequest on the application.
		/// </remarks>
		public static void End()
		{
			if (_writingLevel > 0)
				throw new MagicAjaxException("Script writing level should be 0 at the end of AjaxCall. IncreaseWritingLevel calls do not match DecreaseWritingLevel calls.");

			WriteEndSignature();

			HttpResponse hr = HttpContext.Current.Response;

			hr.Clear();
			MergeNextWritingLevelRecursive(0);
			hr.Write((_sbWritingLevels[0] as StringBuilder).ToString());

			MagicAjaxContext.Current.CompletedAjaxCall = true;
			hr.End();
		}
		#endregion


		#region Methods for reflecting scripts, hidden fields and stylesheets

		/// <summary> List of previous fingerprints of all script blocks </summary>
		internal static ArrayList _previousScriptFPs
		{
			get
			{
				if (!HttpContext.Current.Items.Contains("_previousScriptFPs"))
					HttpContext.Current.Items.Add("_previousScriptFPs", new ArrayList());
				return (ArrayList)HttpContext.Current.Items["_previousScriptFPs"];
			}
			set
			{
				HttpContext.Current.Items["_previousScriptFPs"] = value;
			}
		}

		/// <summary> List of current fingerprints of all script blocks </summary>
        internal static ArrayList _currentScriptFPs
		{
			get
			{
				if (!HttpContext.Current.Items.Contains("_currentScriptFPs"))
					HttpContext.Current.Items.Add("_currentScriptFPs", new ArrayList());
				return (ArrayList)HttpContext.Current.Items["_currentScriptFPs"];
			}
			set
			{
				HttpContext.Current.Items["_currentScriptFPs"] = value;
			}
		}
		
		/// <summary>
		/// Reflects registered Hidden fields if added/changed on callback
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		internal static void Page_PreRender(object sender, EventArgs e)
		{
			Page page = (Page)sender;

#if NET_2_0
			// Reflect head content (title and stylesheets)
            if (page.Header != null)
			    page.Header.SetRenderMethodDelegate(new RenderMethod(RenderHead));
#endif
			
			if (MagicAjaxContext.Current.IsAjaxCall)
			{
				// Get the fingerprints of the scripts sent previous time
				if (page.Request.Form["__MAGICAJAX_SCRIPT_FINGERPRINTS"] != null)
				{
					_previousScriptFPs.AddRange(page.Request.Form["__MAGICAJAX_SCRIPT_FINGERPRINTS"].Split(';'));
				}
				HandleHiddenFields(page);
				HandleClientScriptBlocks(page, true);
				HandleOnSubmitStatements(page, true);
			}
			else
			{
				// Not in Ajax call, so create fingerprints of all script blocks
				HandleClientScriptBlocks(page, false);
				HandleOnSubmitStatements(page, false);

				// Create hidden field to store scriptblock fingerprints later on (see Render method of RenderedByScriptControl.cs)
				page.RegisterHiddenField("__MAGICAJAX_SCRIPT_FINGERPRINTS", String.Empty);
			}
		}

#if NET_2_0
		///<summary>
		/// Handles style,title and link tags (inside HEAD) that were added/changed during callback.
		/// For first page-request, only stores hidden field containing fingerprints
		/// of the tags inside the html head section.
		/// When reflectToClient is true, also reflects the changed tags inside head.
		///</summary>
		private static void RenderHead(HtmlTextWriter writer, Control ctl)
		{
			if (ctl != null)
			{
				bool reflectToClient = MagicAjaxContext.Current.IsAjaxCall;
				
				ArrayList _previousHeaderFPs = new ArrayList();
				ArrayList _currentHeaderFPs = new ArrayList();

				// Get the fingerprints of the head-tags sent previous time
				if (HttpContext.Current.Request.Form["__MAGICAJAX_HEAD_FINGERPRINTS"] != null)
				{
					_previousHeaderFPs.AddRange(HttpContext.Current.Request.Form["__MAGICAJAX_HEAD_FINGERPRINTS"].Split(';'));
				}

				StringBuilder sbHeader = new StringBuilder();
				HtmlTextWriter headerWriter = new HtmlTextWriter(new System.IO.StringWriter(sbHeader));
				
				System.Web.UI.HtmlControls.HtmlHead h = new System.Web.UI.HtmlControls.HtmlHead();
				ctl.SetRenderMethodDelegate(null);
				ctl.RenderControl(headerWriter);

				//strip <header> tag
				string header = sbHeader.ToString();

				//write to HtmlTextWriter
				writer.Write(header);
				
				//reflect title
				if (ctl.Page.Title != null)
				{
					string titleFP = Util.GetFingerprint(ctl.Page.Title);
					_currentHeaderFPs.Add(titleFP);
					if (reflectToClient && !_previousHeaderFPs.Contains(titleFP))
					{
						AjaxCallHelper.WriteAddHeaderElementScript("title", ctl.Page.Title, null);
					}
				}

				MatchCollection matches = Util.HeaderStyleTagsRegEx.Matches(header);
				for (int i = 0; i < matches.Count; i++)
				{
					Match match = matches[i];

					string matchFP = Util.GetFingerprint(match.Value);
					_currentHeaderFPs.Add(matchFP);

					if (reflectToClient && !_previousHeaderFPs.Contains(matchFP))
					{
						string tagName = match.Groups["tag"].Value.ToLower(CultureInfo.InvariantCulture);
						string innerText = match.Groups["inner"].Success ? match.Groups["inner"].Value : String.Empty;

						NameValueCollection attrNameValues = new NameValueCollection();

						if (tagName == "link")
						{
							CaptureCollection attributes = match.Groups["attribute"].Captures;
							CaptureCollection attrnames = match.Groups["attrname"].Captures;
							CaptureCollection attrvalues = match.Groups["attrvalue"].Captures;

							for (int j = 0; j < attributes.Count; j++)
							{
								string attrname = attrnames[j].Value.ToLower(System.Globalization.CultureInfo.InvariantCulture);
								attrNameValues.Add(attrname, attrvalues[j].Value);
							}
						}

						AjaxCallHelper.WriteAddHeaderElementScript(tagName, innerText, attrNameValues);
					}
				}

				string allHeadFPs = string.Join(";", (string[])_currentHeaderFPs.ToArray(typeof(string)));

				// Store head-tag fingerprints in hidden field
				if (reflectToClient && HttpContext.Current.Request.Form["__MAGICAJAX_HEAD_FINGERPRINTS"] != allHeadFPs)
					AjaxCallHelper.WriteSetFieldScript("__MAGICAJAX_HEAD_FINGERPRINTS", allHeadFPs);
				else if (!reflectToClient)
					ctl.Page.RegisterHiddenField("__MAGICAJAX_HEAD_FINGERPRINTS", allHeadFPs);
			}
		}
#endif

		/// <summary>
		/// Reflects registered Hidden fields if added/changed on callback
		/// </summary>
		/// <param name="page"></param>
		/// <param name="reflectToClient"></param>
		private static void HandleHiddenFields(Page page)
		{
			IDictionary regFields = GetPageHiddenDictionary(page, "_registeredHiddenFields");
			if (regFields != null)
			{
				foreach (DictionaryEntry fld in regFields)
				{
					string key = (string)fld.Key;
					string value = (string)fld.Value;
					// For now, don't reflect system hidden fields (starting with "__").
					if (key != null && !key.StartsWith("__") && value != page.Request.Form[key])
					{
						if (page.Request.Form[key] == null)
							AjaxCallHelper.WriteAddHiddenFieldScript(key, value);
						else
							AjaxCallHelper.WriteSetFieldScript(key, value);
					}
				}
			}
		}

		/// <summary>
		/// Handle registered ClientScriptBlocks to see if some where added/changed on callback/postback
		/// </summary>
        private static void HandleClientScriptBlocks(Page page, bool reflectToClient)
		{
			IDictionary regScripts = GetPageHiddenDictionary(page, "_registeredClientScriptBlocks");
			SetScriptDictionaryFingerPrints(regScripts, reflectToClient);
		}

		/// <summary>
		/// Handle registered OnSubmitStatements to see if some where added/changed on callback/postback
		/// </summary>
		/// <param name="reflectToClient"></param>
        private static void HandleOnSubmitStatements(Page page, bool reflectToClient)
		{
			IDictionary regScripts = GetPageHiddenDictionary(page, "_registeredOnSubmitStatements");
			if (regScripts != null)
			{
				ListDictionary onSubmitStatementItem = new ListDictionary();
				StringBuilder onSubmitStringBuilder = new StringBuilder();
				onSubmitStringBuilder.Append("\r\n<script type=\"text/javascript\">\r\n<!--\r\n");
				onSubmitStringBuilder.Append("function WebForm_OnSubmit() {\r\n");

				foreach (string value in regScripts.Values)
					onSubmitStringBuilder.Append(value);

				onSubmitStringBuilder.Append("\r\nreturn true;\r\n}");
				onSubmitStringBuilder.Append("// -->\r\n</script>\r\n");

				onSubmitStatementItem.Add("_registeredOnSubmitStatements", onSubmitStringBuilder.ToString());

				SetScriptDictionaryFingerPrints(onSubmitStatementItem, reflectToClient);
			}
		}

		/// <summary>
		/// Handle registered ClientStartupScripts to see if some where added/changed on callback/postback
		/// </summary>
		/// <param name="reflectToClient"></param>
        internal static void HandleClientStartupScripts(Page page, bool reflectToClient)
		{
			IDictionary regScripts = GetPageHiddenDictionary(page, "_registeredClientStartupScripts");
			SetScriptDictionaryFingerPrints(regScripts, reflectToClient);
		}

		/// <summary>
		/// Handle declared client-Arrays to see if some where added/changed on callback/postback
		/// </summary>
		/// <param name="reflectToClient"></param>
        internal static void HandleArrayDeclares(Page page, bool reflectToClient)
		{
			IDictionary regArrays = GetPageHiddenDictionary(page, "_registeredArrayDeclares");
			SetArrayDictionaryFingerPrints(regArrays, reflectToClient);
		}

		/// <summary>
		/// Sets fingerprints for declared Arrays.
		/// When reflectToClient is set to true, reflects added/changed Array-declarations
		/// back to the client.
		/// </summary>
		/// <param name="regArrays"></param>
		/// <param name="reflectToClient"></param>
		private static void SetArrayDictionaryFingerPrints(IDictionary regArrays, bool reflectToClient)
		{
			if (regArrays != null)
			{
				foreach (DictionaryEntry fld in regArrays)
				{
					StringBuilder sb = new StringBuilder();

					string key = (string)fld.Key;
					ArrayList values = (ArrayList)fld.Value;

					sb.Append("var ");
					sb.Append(key);
					sb.Append(" =  new Array(");

					for (int i = 0; i < values.Count; i++)
					{
						if (i > 0)
							sb.Append(", ");
						sb.Append((string)values[i]);
					}
					sb.Append(");");

					string script = sb.ToString();
					string scriptFP = Util.GetFingerprint(script);

					_currentScriptFPs.Add(scriptFP);

					if (reflectToClient && !_previousScriptFPs.Contains(scriptFP))
					{
						NameValueCollection scriptAttributes = new NameValueCollection();
						scriptAttributes.Add("type", "text/javascript");
						AjaxCallHelper.WriteAddScriptElementScript(script, scriptAttributes);
					}
				}
			}
		}

		/// <summary>
		/// Sets fingerprints for registered Script blocks.
		/// When reflectToClient is set to true, reflects added/changed scripts
		/// back to the client.
		/// </summary>
		/// <param name="regScripts"></param>
		/// <param name="reflectToClient"></param>
		private static void SetScriptDictionaryFingerPrints(IDictionary regScripts, bool reflectToClient)
		{
			if (regScripts != null)
			{
				foreach (DictionaryEntry fld in regScripts)
				{
					int keyHash = -1;
					try 
					{
						keyHash = fld.Key.GetHashCode();
					}
					catch (NullReferenceException) {}
					string script = (string)fld.Value;

#if NET_2_0			
					//don't handle WebPartManager scripts (we're handling them seperately later on)
					if (script != null && (script.Contains("__wpm = new WebPartManager") || script.Contains("var menuWebPart_")))
						continue;
#endif		
					string scriptFP = Util.GetFingerprint(string.Format("{0}:{1}", keyHash, script));

					_currentScriptFPs.Add(scriptFP);

					if (reflectToClient && !_previousScriptFPs.Contains(scriptFP))
					{
						if (script.IndexOf("<script") == -1)
						{
							NameValueCollection scriptAttributes = new NameValueCollection();
							scriptAttributes.Add("type", "text/javascript");
							AjaxCallHelper.WriteAddScriptElementScript(script, scriptAttributes);
						}
						else
						{
							Match match = Util.ScriptTagsRegEx.Match(script);
							if (match.Success)
							{
								CaptureCollection attrnamesOption = match.Groups["attrname"].Captures;
								CaptureCollection attrvaluesOption = match.Groups["attrvalue"].Captures;

								NameValueCollection scriptAttributes = new NameValueCollection();
								for (int j = 0; j < attrnamesOption.Count; j++)
								{
									scriptAttributes.Add(attrnamesOption[j].Value, attrvaluesOption[j].Value);
								}

								string scriptText = match.Groups["script"].Value;

								AjaxCallHelper.WriteAddScriptElementScript(scriptText, scriptAttributes);
							}
						}
					}
				}
			}
		}

		/// <summary>
		/// Uses reflection to get the private dictionary field 'fieldName'.
		/// Uses Page.ClientScript for .NET 2.0, and the Page-object for .NET 1.1
		/// Note: if MEDIUM_TRUST flag is set, reflection is not allowed, so return null;
		/// </summary>
		/// <param name="fieldName"></param>
		/// <returns></returns>
		private static IDictionary GetPageHiddenDictionary(Page page, string fieldName)
		{
#if NET_2_0
			return (IDictionary)(Util.GetPrivateField(page.ClientScript, typeof(ClientScriptManager), fieldName));
#else
			return (IDictionary)(Util.GetPrivateField(page, typeof(Page), fieldName));
#endif
		}
		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Argiris Kirtzidis
Web Developer
Greece Greece
No Biography provided

| Advertise | Privacy | Mobile
Web02 | 2.8.141022.2 | Last Updated 28 May 2007
Article Copyright 2005 by Argiris Kirtzidis
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid