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

Fast ASP.NET Web page loading by downloading multiple JavaScripts after visible content and in batch

Rate me:
Please Sign up or sign in to vote.
4.93/5 (60 votes)
3 Aug 2008CPOL15 min read 381.2K   2.1K   261  
Download all external scripts on your Web page after the visible content is loaded for faster perceived speed and donwload multiple JavaScript in batch for better actual speed
using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml;
using System.IO;

/// <summary>
/// Summary description for CombineScripts
/// </summary>
namespace Dropthings.Web.Util
{
    public class CombineScripts
    {
        private static Regex _FindScriptTags = new Regex(@"<script\s*src\s*=\s*""(?<url>.[^""]+)"".[^>]*>\s*</script>", RegexOptions.Compiled);
        private static readonly string SCRIPT_VERSION_NO = ConfigurationManager.AppSettings["ScriptVersionNo"];
         

        /// <summary>
        /// Combine script references using file sets defined in a configuration file.
        /// It will replace multiple script references using one 
        /// </summary>
        public static string CombineScriptBlocks(string scripts)
        {
            List<UrlMapSet> sets = LoadSets();
            string output = scripts;

            foreach (UrlMapSet mapSet in sets)
            {
                int setStartPos = -1;
                List<string> names = new List<string>();

                output = _FindScriptTags.Replace(output, new MatchEvaluator(delegate(Match match)
                {
                    string url = match.Groups["url"].Value;

                    UrlMap urlMatch = mapSet.Urls.Find(
                        new Predicate<UrlMap>(
                            delegate(UrlMap map)
                            {
                                return map.Url == url;
                            }));

                    if (null != urlMatch)
                    {
                        // Remember the first script tag that matched in this UrlMapSet because
                        // this is where the combined script tag will be inserted
                        if (setStartPos < 0) setStartPos = match.Index;
                        
                        names.Add(urlMatch.Name);
                        return string.Empty;
                    }
                    else
                    {
                        return match.Value;
                    }

                }));

                if (setStartPos >= 0)
                {
                    string setName = string.Empty;
                    // if the set says always include all urls within it whenever a single match is found,
                    // then generate the full set
                    if (mapSet.IsIncludeAll)
                    {
                        // No need send the individual url names when the full set needs to be included
                        setName = string.Empty;
                    }
                    else
                    {
                        names.Sort();
                        setName = string.Join(",", names.ToArray());
                    }
                    
                    string urlPrefix = HttpContext.Current.Request.Path.Substring(0, HttpContext.Current.Request.Path.LastIndexOf('/') + 1);
                    string newScriptTag = "<script type=\"text/javascript\" src=\"Scripts.ashx?" + HttpUtility.UrlEncode(mapSet.Name) + "=" + HttpUtility.UrlEncode(setName) + "&" + HttpUtility.UrlEncode(urlPrefix) + "&" + HttpUtility.UrlEncode(SCRIPT_VERSION_NO) + "\"></script>";

                    output = output.Insert(setStartPos, newScriptTag);
                }
            }

            return output;
        }

        public static List<UrlMapSet> LoadSets()
        {
            List<UrlMapSet> sets = new List<UrlMapSet>();

            using (XmlReader reader = new XmlTextReader(new StreamReader(HttpContext.Current.Server.MapPath("~/App_Data/FileSets.xml"))))
            {
                reader.MoveToContent();
                while (reader.Read())
                {
                    if ("set" == reader.Name)
                    {
                        string setName = reader.GetAttribute("name");
                        string isIncludeAll = reader.GetAttribute("includeAll");

                        UrlMapSet mapSet = new UrlMapSet();
                        mapSet.Name = setName;
                        if (isIncludeAll == "true") 
                            mapSet.IsIncludeAll = true;

                        while (reader.Read())
                        {
                            if ("url" == reader.Name)
                            {
                                string urlName = reader.GetAttribute("name");
                                string url = reader.ReadElementContentAsString();
                                mapSet.Urls.Add(new UrlMap(urlName, url));
                            }
                            else if ("set" == reader.Name)
                                break;
                        }

                        sets.Add(mapSet);
                    }
                }
            }

            return sets;
        }
    }

    public class UrlMapSet
    {
        public string Name;
        public bool IsIncludeAll;
        public List<UrlMap> Urls = new List<UrlMap>();
    }

    public class UrlMap
    {
        public string Name;
        public string Url;

        public UrlMap(string name, string url)
        {
            this.Name = name;
            this.Url = url;
        }
    }
}

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 Code Project Open License (CPOL)


Written By
Architect BT, UK (ex British Telecom)
United Kingdom United Kingdom

Comments and Discussions