Click here to Skip to main content
15,893,904 members
Articles / Web Development / XHTML
Article

AJAX UpdatePanel With Inline Client Scripts Parser

Rate me:
Please Sign up or sign in to vote.
3.29/5 (5 votes)
9 Oct 2008CPOL 60.8K   24   8
AJAX UpdatePanel with inline client scripts parser.

Introduction

This control solves the problem when you need inline client scripts within an ASP.NET UpdatePanel to work. Inline scripts, to my knowledge, are not registered to the Script Manager when the "AJAX call" is made. The problem occurs if you have a UserControl, containing an inline script that is dynamically added into an UpdatePanel after an AJAX postback.

Background

If you, like me, already have (had) an existing Web application that's cluttered with inline scripts, and want to take advantage of the features that ASP.NET AJAX provides, you might find yourself with a tedious refactoring job. I managed to solve 99% (1% was invalid markup) of all my inline script problems by using this control.

Hopefully, it can do the same for you!

Using the Code

The control inherits from the original UpdatePanel and uses a Regex to parse for client scripts. You use it just like the original UpdatePanel, and can switch on/off the parsing for inline scripts by setting the property RegisterInlineClientScripts to false.

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Text.RegularExpressions;
using System.IO;

namespace Tuna.Web.Controls
{
public class TunaUpdatePanel : UpdatePanel
{
    private static readonly Regex REGEX_CLIENTSCRIPTS = new Regex(
    "<script\\s((?<aname>[-\\w]+)=[\"'](?<avalue>.*?)[\"']\\s?)*\\s*>(?<script>.*?)</script>",
    RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled |
    RegexOptions.ExplicitCapture);
    private bool m_RegisterInlineClientScripts = true;

    /// <summary>
    /// If the updatepanel shall parse and append inline scripts, default true
    /// </summary>
    public bool RegisterInlineClientScripts
    {
        get
        {
            return this.m_RegisterInlineClientScripts;
        }
        set
        {
            this.m_RegisterInlineClientScripts = value;
        }
    }

    protected virtual string AppendInlineClientScripts(string htmlsource)
    {
        if (this.ContentTemplate != null && htmlsource.IndexOf(
            "<script", StringComparison.CurrentCultureIgnoreCase) > -1)
        {
            MatchCollection matches = REGEX_CLIENTSCRIPTS.Matches(htmlsource);
            if (matches.Count > 0)
            {
                for (int i = 0; i < matches.Count; i++)
                {
                    string script = matches[i].Groups["script"].Value;
                    string scriptID = script.GetHashCode().ToString();
                    string scriptSrc = "";

                    CaptureCollection aname = matches[i].Groups["aname"].Captures;
                    CaptureCollection avalue = matches[i].Groups["avalue"].Captures;
                    for (int u = 0; u < aname.Count; u++)
                    {
                        if (aname[u].Value.IndexOf("src",
                            StringComparison.CurrentCultureIgnoreCase) == 0)
                        {
                            scriptSrc = avalue[u].Value;
                            break;
                        }
                    }

                    if (scriptSrc.Length > 0)
                    {
                        ScriptManager.RegisterClientScriptInclude(this,
                            this.GetType(), scriptID, scriptSrc);
                    }
                    else
                    {
                        ScriptManager.RegisterClientScriptBlock(this, this.GetType(),
                            scriptID, script, true);
                    }

                    htmlsource = htmlsource.Replace(matches[i].Value, "");
                }

            }
        }
        return htmlsource;
    }

    protected override void RenderChildren(HtmlTextWriter writer)
    {
        ScriptManager sm = ScriptManager.GetCurrent(Page);
        if (this.RegisterInlineClientScripts && sm != null && sm.IsInAsyncPostBack)
        {
            using (HtmlTextWriter htmlwriter = new HtmlTextWriter(new StringWriter()))
            {
                base.RenderChildren(htmlwriter);

                string html;
                int outputSize;

                //Get the actual rendering and size
                html = htmlwriter.InnerWriter.ToString();
                outputSize = html.Length;

                //Append inlinescripts and fetch the new markup and size
                html = this.AppendInlineClientScripts(html);
                outputSize -= html.Length;

                //Replace ContentSize if there are any gains
                if (outputSize > 0)
                {
                    html = this.SetOutputContentSize(html, outputSize);
                }

                writer.Write(html);
            }
        }
        else
        {
            base.RenderChildren(writer);
        }
    }

    private string SetOutputContentSize(string html, int difference)
    {
        string[] split = html.Split('|');
        int size = int.Parse(split[0]);
        split[0] = (size - difference).ToString();
        return string.Join("|", split);
    }
}
}

Optimization suggestions are welcome!

Enjoy!

Thanks to Rogic for solving the issue I hade with replacing the response context to opitmize traffic!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Sweden Sweden
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Member 825063916-Mar-14 23:31
Member 825063916-Mar-14 23:31 
GeneralMy vote of 5 Pin
CyberLight28-Aug-12 1:55
CyberLight28-Aug-12 1:55 
Questionthanks Pin
parsamiga31-Oct-11 21:12
parsamiga31-Oct-11 21:12 
GeneralSome improvements Pin
Rogic22-Sep-08 1:37
Rogic22-Sep-08 1:37 
Hello, I've used your modified panel with success on current project and found it very useful since we have a lot of SWFObject wrappers that emit inline javascript. This kind of UpdatePanel was exactly what I was looking for. Here are some improvements to the code:

1) Modifying the response context to remove inline js: The problem is to determine the exact size of the new HTML that is sent to the browser, and to modify first number (length of html string) in original UdpatePanel response. For this purpose, RenderChildren method has to be overriden. Render method doesn't need to be overriden.
2) Combine all inline javascripts into one script that is going to be registered via RegisterClientScriptBlock - so we avoid multiple registrations (script tags) and execute this one combined script - this also speeds up javascript execution.

The modified code was tested in scenarios with multiple panels, with UpdateMode set to both Always/Conditional and I've noticed no problems, but I cannot say it's 100% bug free Smile | :)

private readonly StringBuilder CombinedScripts = new StringBuilder();

protected virtual string RegisterAndRemoveScripts(string htmlsource)
{
    if (this.ContentTemplate != null && htmlsource.IndexOf("<script",> -1)
    {
        MatchCollection matches = REGEX_CLIENTSCRIPTS.Matches(htmlsource);
        for (int i = 0; i < matches.Count; i++)
        {
            string script = matches[i].Groups["script"].Value;
            string scriptSrc = "";
            CaptureCollection aname = matches[i].Groups["aname"].Captures;
            CaptureCollection avalue = matches[i].Groups["avalue"].Captures;
            for (int u = 0; u < aname.Count; u++)
            {
                if (aname[u].Value.IndexOf("src", StringComparison.CurrentCultureIgnoreCase) == 0)
                {
                    scriptSrc = avalue[u].Value;
                    break;
                }
            }

			if (scriptSrc.Length > 0)
				ScriptManager.RegisterClientScriptInclude(this, this.GetType(), scriptSrc.GetHashCode().ToString(), scriptSrc);
			else
				this.CombinedScripts.Append(script);

			htmlsource = htmlsource.Replace(matches[i].Value, "");
        }
    }
    return htmlsource;
}

private bool _rendered;
protected override void RenderChildren(HtmlTextWriter writer)
{
    ScriptManager sm = ScriptManager.GetCurrent(Page);
    if (sm != null && sm.IsInAsyncPostBack)
    {
    	if (this._rendered)
    		return;

    	using (HtmlTextWriter writer2 = new HtmlTextWriter(new StringWriter(CultureInfo.CurrentCulture)))
    	{
			base.RenderChildren(writer2);
			string output = writer2.InnerWriter.ToString();
            
			// remove scripts:
			if (this.RegisterInlineClientScripts)
			{
    			int lengthBefore = output.Length;
				// modify output
				output = RegisterAndRemoveScripts(output);
				// register client block
				string combined = CombinedScripts.ToString();
				if (combined.Length > 0)
					ScriptManager.RegisterClientScriptBlock(this, this.GetType(), combined.GetHashCode().ToString(), combined, true);
				int lengthAfter = output.Length;
    			int difference = lengthBefore - lengthAfter;
				// replace content only if there are some gains:
				if(difference > 0)
					output = ReplaceRenderedOutputContentSize(output, difference);
			}

			writer.Write(output);
    	}
    }
    else
    {
		base.RenderChildren(writer);
    }
    this._rendered = true;
}

private static string ReplaceRenderedOutputContentSize(string output, int difference)
{
	string[] split = output.Split('|');
	int size = int.Parse(split[0]);
	split[0] = (size - difference).ToString();
	return string.Join("|",split);
}


Hope this helps,
Rogić
GeneralRe: Some improvements Pin
Baxterboom7-Oct-08 3:00
Baxterboom7-Oct-08 3:00 
GeneralRe: Some improvements [modified] Pin
Baxterboom8-Oct-08 5:34
Baxterboom8-Oct-08 5:34 
AnswerRe: Some improvements Pin
Rogic21-Oct-08 0:49
Rogic21-Oct-08 0:49 
GeneralSo close..... Pin
Owen3713-Dec-07 7:26
Owen3713-Dec-07 7:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.