Click here to Skip to main content
15,886,799 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 379.9K   2.1K   262  
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
<%@ WebHandler Language="C#" Class="Scripts" %>

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web;
using System.Net;

using Dropthings.Web.Util;

public class Scripts : IHttpHandler {
    
    public void ProcessRequest (HttpContext context) {

        
        string queryString = HttpUtility.UrlDecode(context.Request.QueryString.ToString());

        string[] urlSplit = queryString.Split('&');

        string setInfo = urlSplit[0];
        string urlPrefix = urlSplit[1];

        string[] tokens = setInfo.Split('=');
        string setName = tokens[0];
        string[] urlMaps = tokens[1].Split(',');

        byte[] encodedBytes;

        if (context.Cache[setInfo] == null)
        {
            // Find the set
            UrlMapSet set = CombineScripts.LoadSets().Find(
                new Predicate<UrlMapSet>(delegate(UrlMapSet match)
                    {
                        return match.Name == setName;
                    }));

            // Find the URLs requested to be rendered            
            List<UrlMap> maps = string.IsNullOrEmpty(tokens[1]) ? set.Urls 
                : set.Urls.FindAll(new Predicate<UrlMap>(delegate(UrlMap map)
                    {
                        return Array.BinarySearch<string>(urlMaps, map.Name) >= 0;
                    }));

            string urlScheme = context.Request.Url.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped);

            StringBuilder buffer = new StringBuilder();
            foreach (UrlMap map in maps)
            {
                /*
                 * Urls can be in one of the following formats:
                 * a) Relative url to the page
                 * b) Relative url starting with application path e.g. /Dropthings/....
                 * c) Absolute url with http:// prefix
                 */

                string fullUrl = map.Url;
                if (map.Url.StartsWith("http://")) fullUrl = map.Url;
                else if (map.Url.StartsWith(context.Request.ApplicationPath)) fullUrl = urlScheme + map.Url;
                else fullUrl = urlScheme + urlPrefix + map.Url;

                string mapUrlForJS = HttpUtility.HtmlDecode(map.Url).Replace("'", "\'");
                HttpWebRequest request = this.CreateHttpWebRequest(fullUrl);
                try
                {
                    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
                    {
                        if (response.StatusCode == HttpStatusCode.OK)
                        {
                            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                            {
                                string responseContent = reader.ReadToEnd();
                                buffer.Append(responseContent);
                                buffer.Append(Environment.NewLine);
                                buffer.Append(@"
                            if(typeof(Sys)!=='undefined') Array.add(Sys._ScriptLoader._getLoadedScripts(), '" + mapUrlForJS + @"'); 
                            if( !window._combinedScripts ) { window._combinedScripts = []; } 
                            window._combinedScripts.push('" + mapUrlForJS + @"');");
                                buffer.Append(Environment.NewLine);
                            }
                        }
                        else
                        {
                            buffer.Append("alert(\"Cannot load script from:" + mapUrlForJS + ". Please correct mapping for '" + map.Name + "' in App_Data\\\\FileSets.xml\");");
                            buffer.Append(Environment.NewLine);
                        }
                    }
                }
                catch (Exception x)
                {
                    buffer.Append("alert(\"Cannot load script from:" + mapUrlForJS + ". Please correct mapping for '" + map.Name + "' in App_Data\\\\FileSets.xml\");");
                    buffer.Append(Environment.NewLine);
                }
            }

            buffer.Append(@"
                if(typeof(Sys)!=='undefined')             
                {                
                    if(typeof(Sys._ScriptLoader) !== 'undefined')
                    {                                    
                        Sys._ScriptLoader.isScriptLoaded = function Sys$_ScriptLoader$isScriptLoaded(scriptSrc) 
                        {                                                    
                            var dummyScript = document.createElement('script');
                            dummyScript.src = scriptSrc;
                            var result = Array.contains(Sys._ScriptLoader._getLoadedScripts(), scriptSrc);
                            if( result === true ) return true;
                            result = Array.contains( window._combinedScripts, scriptSrc );
                            if( result === true ) return true;                            
                            var scriptTags = document.getElementsByTagName('script');
                            for(var i = 0; i < scriptTags.length; i ++ ) if( scriptTags[i].src == dummyScript.src ) return true;
                            return false;
                        }
                    }                    
                }");

            string responseString = buffer.ToString();
            encodedBytes = context.Request.ContentEncoding.GetBytes(responseString);
            //context.Cache.Add(setInfo, encodedBytes, null, DateTime.MaxValue,
            //    TimeSpan.FromDays(1), System.Web.Caching.CacheItemPriority.Normal, null);
        }
        else
        {
            encodedBytes = context.Cache[setInfo] as byte[];
        }
        
        context.Response.ContentType = "text/javascript";
        context.Response.ContentEncoding = context.Request.ContentEncoding;
        context.Response.Cache.SetMaxAge(TimeSpan.FromDays(30));
        context.Response.Cache.SetExpires(DateTime.Now.AddDays(30));
        context.Response.Cache.SetCacheability(HttpCacheability.Private);
        context.Response.AppendHeader("Content-Length", encodedBytes.Length.ToString());
        
        context.Response.OutputStream.Write(encodedBytes, 0, encodedBytes.Length);
        context.Response.Flush();
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }

    private HttpWebRequest CreateHttpWebRequest(string url)
    {
        HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
        request.Headers.Add("Accept-Encoding", "gzip");
        request.AutomaticDecompression = DecompressionMethods.GZip;
        request.MaximumAutomaticRedirections = 2;
        request.MaximumResponseHeadersLength = 4 * 1024;
        request.ReadWriteTimeout = 1 * 1000;
        request.Timeout = 5 * 1000;

        return request;
    }
}

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