Imports Microsoft.VisualBasic
''' <summary>
''' Our common class file
''' </summary>
''' <remarks></remarks>
Public Class Common
''' <summary>
''' Let's cache this stuff
''' </summary>
''' <remarks></remarks>
Public Shared Sub CacheIt()
' Allow the browser to store resposes in the 'History' folder
HttpContext.Current.Response.Cache.SetAllowResponseInBrowserHistory(True)
' Set our cacheability to Public
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public)
' Resource is valid until the expiration has passed
HttpContext.Current.Response.Cache.SetValidUntilExpires(True)
' Set out last modified date to last year
HttpContext.Current.Response.Cache.SetLastModified(Date.Now.AddDays(-366))
' We want to store and cache the resource
HttpContext.Current.Response.AddHeader("Cache-Control", "store, cache")
' Set the Pragma to cache
HttpContext.Current.Response.AddHeader("pragma", "cache")
' Not sure if this one really works, but it doesn't throw an error, and Google likes resources served from a cookie-less domain... eh... worth a shot
HttpContext.Current.Response.AddHeader("Set-Cookie", "false")
' Make sure our cache control is Public
HttpContext.Current.Response.CacheControl = "public" '
'Set the expiration date of the resource until next year
HttpContext.Current.Response.Expires = 24 * 60 * 366
HttpContext.Current.Response.ExpiresAbsolute = DateAdd(DateInterval.Hour, 24 * 366, Date.Now)
End Sub
''' <summary>
''' Let's check to see if the browser accepts GZip encoding
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function IsGZipEnabled() As Boolean
Dim accEncoding As String = HttpContext.Current.Request.Headers("Accept-Encoding")
'Does the browser accept content encoding?
If (Not accEncoding Is Nothing) Then
If (accEncoding.Contains("gzip") Or accEncoding.Contains("deflate")) Then
Return True
Else
Return False
End If
Else
Return False
End If
End Function
End Class
Imports System.IO.Compression
Imports System.Web
Imports System.Text.RegularExpressions
Imports System.Text
Imports System.IO
Imports System.Web.Compilation
''' <summary>
''' Dynamic compression mechanism for some of the sites content
''' </summary>
''' <remarks></remarks>
Public Class DynamicCompression
Implements IHttpModule
''' <summary>
''' Initialize our module
''' </summary>
''' <param name="application"></param>
''' <remarks></remarks>
Public Sub Init(ByVal application As HttpApplication) _
Implements IHttpModule.Init
'Add the BeginRequest Handler
AddHandler application.BeginRequest, _
AddressOf Me.Application_BeginRequest
Dim context As HttpContext = application.Context
'Let's see if the browser accepts GZip encoding, if it does add the header to the response
If Common.IsGZipEnabled() Then
Dim accEncoding As String = context.Request.Headers("Accept-Encoding")
If accEncoding.Contains("gzip") Then
context.Response.Filter = New GZipStream(context.Response.Filter, CompressionMode.Compress)
context.Response.AppendHeader("Content-Encoding", "gzip")
Else
context.Response.Filter = New DeflateStream(context.Response.Filter, CompressionMode.Compress)
context.Response.AppendHeader("Content-Encoding", "deflate")
End If
End If
End Sub
''' <summary>
''' Begin the request and process our filter
''' </summary>
''' <param name="source"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub Application_BeginRequest(ByVal source As Object, ByVal e As EventArgs)
Dim application As HttpApplication = DirectCast(source, HttpApplication)
Dim context As HttpContext = application.Context
Dim filePath As String = context.Request.FilePath
Dim fileExtension As String = VirtualPathUtility.GetExtension(filePath)
If fileExtension.Equals(".aspx") Then
application.Response.Filter = New Worker(application.Response.Filter, source)
End If
End Sub
''' <summary>
''' Unneeded for our purposes
''' </summary>
''' <remarks></remarks>
Public Sub Dispose() Implements System.Web.IHttpModule.Dispose
End Sub
''' <summary>
''' Let's do the actual work
''' </summary>
''' <remarks></remarks>
Private Class Worker
Inherits Stream
''' <summary>
''' Get our application context and file stream
''' </summary>
''' <param name="sink"></param>
''' <param name="source"></param>
''' <remarks></remarks>
Public Sub New(ByVal sink As Stream, ByVal source As Object)
_sink = sink
Dim application As HttpApplication = DirectCast(source, HttpApplication)
context = application.Context
End Sub
#Region "Properites"
''' <summary>
''' These properties are a necessary implementation when we inherit IO.Stream
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Overrides ReadOnly Property CanRead() As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property CanSeek() As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property CanWrite() As Boolean
Get
Return True
End Get
End Property
Public Overrides Sub Flush()
_sink.Flush()
End Sub
Public Overrides ReadOnly Property Length() As Long
Get
Return 0
End Get
End Property
Private _position As Long
Public Overrides Property Position() As Long
Get
Return _position
End Get
Set(ByVal value As Long)
_position = value
End Set
End Property
Private _sink As Stream
Private context As HttpContext
Private html As String, FileCSS As String, FileJS As String
#End Region
#Region "Methods"
''' <summary>
''' Most of these methods are here because they are necessary for inheriting IO.Stream
''' They will make sure we are handling the stream correctly
''' </summary>
''' <param name="buffer"></param>
''' <param name="offset"></param>
''' <param name="count"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Overrides Function Read(ByVal buffer As Byte(), ByVal offset As Integer, ByVal count As Integer) As Integer
Return _sink.Read(buffer, offset, count)
End Function
Public Overrides Function Seek(ByVal offset As Long, ByVal origin As SeekOrigin) As Long
Return _sink.Seek(offset, origin)
End Function
Public Overrides Sub SetLength(ByVal value As Long)
_sink.SetLength(value)
End Sub
Public Overrides Sub Close()
_sink.Close()
End Sub
''' <summary>
''' Overwride the default response write, so we can pass the stream back to the browser
''' </summary>
''' <param name="buffer__1"></param>
''' <param name="offset"></param>
''' <param name="count"></param>
''' <remarks></remarks>
Public Overrides Sub Write(ByVal buffer__1 As Byte(), ByVal offset As Integer, ByVal count As Integer)
Dim data As Byte() = New Byte(count - 1) {}
Buffer.BlockCopy(buffer__1, offset, data, 0, count)
html = System.Text.Encoding.[Default].GetString(buffer__1)
'CSS
CompressStyles(html)
'JS
CompressJavascript(html)
Dim outdata As Byte() = System.Text.Encoding.[Default].GetBytes(html)
_sink.Write(outdata, 0, outdata.GetLength(0))
End Sub
''' <summary>
''' Grab our stylesheets from the file stream string
''' concatenate it, minify it, and pass back the originating string while replacing all the stylesheet references
''' with a single refernce to our new stylesheet file
''' </summary>
''' <param name="StrFile"></param>
''' <remarks></remarks>
Private Sub CompressStyles(ByVal StrFile As String)
Dim FilesM As MatchCollection, FileName As String
FilesM = Regex.Matches(StrFile, "<link.*?href=""(.*?)"".*? />")
Dim M(FilesM.Count - 1) As String
For i As Long = 0 To M.Length - 1
'This is the file
M(i) = FilesM(i).Groups(1).Value
FileName = HttpContext.Current.Server.MapPath(M(i).Replace("/", "\"))
FileName = FileName.Replace("\\\", "\")
If File.Exists(FileName) Then
Using objFile As New StreamReader(FileName)
FileCSS += objFile.ReadToEnd
objFile.Close()
End Using
FileCSS = Regex.Replace(FileCSS, "/\*.+?\*/", "", RegexOptions.Singleline)
FileCSS = FileCSS.Replace(" ", "")
FileCSS = FileCSS.Replace(vbCr, "")
FileCSS = FileCSS.Replace(vbLf, "")
FileCSS = FileCSS.Replace(vbCrLf, "")
FileCSS = FileCSS.Replace(vbTab, " ")
FileCSS = FileCSS.Replace("\t", "")
FileCSS = FileCSS.Replace(" {", "{")
FileCSS = FileCSS.Replace("{ ", "{")
FileCSS = FileCSS.Replace(" }", "}")
FileCSS = FileCSS.Replace("} ", "}")
FileCSS = FileCSS.Replace(" :", ":")
FileCSS = FileCSS.Replace(": ", ":")
FileCSS = FileCSS.Replace(", ", ",")
FileCSS = FileCSS.Replace("; ", ";")
FileCSS = FileCSS.Replace(";}", "}")
FileCSS = Regex.Replace(FileCSS, "/\*[^\*]*\*+([^/\*]*\*+)*/", "$1")
End If
Next
FilesM = Nothing
If context.Cache("CSS") Is Nothing Then
context.Cache.Add("CSS", FileCSS, Nothing, DateTime.Now.AddDays(31), Cache.NoSlidingExpiration, CacheItemPriority.High, Nothing)
End If
Dim tmp As MatchCollection
tmp = Regex.Matches(StrFile, "<link.*?href=""(.*?)"".*? />")
Dim tmpCt As Long = 0
For Each tmpS As Match In tmp
tmpCt += 1
If tmpCt = tmp.Count Then
StrFile = StrFile.Replace(tmpS.Groups(0).ToString(), "<link type=""text/css"" rel=""stylesheet"" href=""/Style.aspx"" />")
Else
StrFile = StrFile.Replace(tmpS.Groups(0).ToString(), String.Empty)
End If
Next
tmp = Nothing
html = StrFile
End Sub
''' <summary>
''' Grab our scripts from the file stream string
''' concatenate it, minify it, and pass back the originating string while replacing all the scripts references
''' with a single refernce to our new scripts file
''' </summary>
''' <param name="StrFile"></param>
''' <remarks></remarks>
Private Sub CompressJavascript(ByVal StrFile As String)
Dim FilesM1 As MatchCollection, FileName As String
FilesM1 = Regex.Matches(StrFile, "<script.*?src=""(.*?)"".*?></script>")
Dim M1(FilesM1.Count - 1) As String
For j As Long = 0 To M1.Length - 1
'This is the file
M1(j) = FilesM1(j).Groups(1).Value
FileName = HttpContext.Current.Server.MapPath(M1(j).Replace("/", "\"))
FileName = FileName.Replace("\\\", "\")
If File.Exists(FileName) Then
Using objFile1 As New StreamReader(FileName)
FileJS += objFile1.ReadToEnd
objFile1.Close()
End Using
FileJS = Regex.Replace(FileJS, "(// .*?$)", "", RegexOptions.Multiline)
FileJS = Regex.Replace(FileJS, "(/\*.*?\*/)", "", RegexOptions.Multiline)
FileJS = FileJS.Replace(" ", " ")
FileJS = FileJS.Replace(vbCr, vbLf)
FileJS = FileJS.Replace(vbCrLf, vbLf)
FileJS = FileJS.Replace(vbTab, " ")
End If
Next
If context.Cache("JS") Is Nothing Then
context.Cache.Add("JS", FileJS, Nothing, DateTime.Now.AddDays(31), Cache.NoSlidingExpiration, CacheItemPriority.High, Nothing)
End If
FilesM1 = Nothing
Dim tmp1 As MatchCollection
tmp1 = Regex.Matches(StrFile, "<script.*?src=""(.*?)"".*?></script>")
Dim tmpCt1 As Long = 0
For Each tmpS As Match In tmp1
tmpCt1 += 1
If tmpCt1 = tmp1.Count Then
StrFile = StrFile.Replace(tmpS.Groups(0).ToString(), "<script type=""text/javascript"" src=""/Script.aspx""></script>")
Else
StrFile = StrFile.Replace(tmpS.Groups(0).ToString(), String.Empty)
End If
Next
tmp1 = Nothing
html = StrFile
End Sub
#End Region
End Class
End Class
<add name="DynamicCompression" type="DynamicCompression"/>
<%@ Page Language="VB" %>
<%
'Cache our response in the browser
Common.CacheIt()
'Set the content type to output true CSS
Response.ContentType = "text/css"
If Not (Cache("CSS") Is Nothing) Then
'Write it out from the cache
Response.Write(Cache("CSS").ToString())
End If
%>
<%@ Page Language="VB" %>
<%
'Cache our response in the browser
Common.CacheIt()
'Set the content type to output true javascript
Response.ContentType = "text/javascript"
If Not (Cache("JS") Is Nothing) Then
'write out the javascript string from our cache
Response.Write(Cache("JS").ToString())
End If
%>
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" Debug="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Untitled Page</title>
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<link href="/styles/layout1.css" rel="stylesheet" type="text/css" />
<link href="/styles/layout2.css" rel="stylesheet" type="text/css" />
</head>
<body>
Just a test to make sure the CSS and JS works
<a href="javascript:;" onclick="alert($('body').html());">Click Here</a>
<script type="text/javascript" src="/scripts/jquery.js"></script>
<script type="text/javascript" src="/scripts/jquery.pngFix.pack.js"></script>
<script type="text/javascript" src="/scripts/jquery.preloadcssimages.js"></script>
<script type="text/javascript" src="/scripts/custom.js"></script>
</body>
</html>
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (2)
Author
Commented:"I have one question about "App_Code". That folder is usually only seen with "Web Site" model web sites, which don't have a project file. I don't see any mention of this, which might confuse someone who has a "Web Application" model, which won't normally have that folder, unless it is created manually."
This project was initially built as a 'Web Site', and not a 'Web Application'. Most of the projects I build, are 'Web Applications', and thus (as TheLearnedOne states) does not automagically pop in the 'App_Code' folder.
So, there are 2 solutions to this quandry. One is to manually create the 'App_Code' folder, if you do not have one, and pop in the code as I state above... or, if most are like me. Put your class files in your Class Assembly.
Author
Commented:It appears Master Pages are rendered prior to the page content being rendered. I do have an idea of how a work around may be accomplished, but I will save that for a later post (in this article), I just need to build and test it before I can say one way or another.