Reformat your CSS on the fly

Audience

Everyone

Have you ever, when developing a website, been frustrated when you get a different result when you deploy your application to http://site/ and http://site/app/.

iStock_000001095647XSmall

I don’t know about you, but it annoys the hell out of me. All that work in CSS and none of your images work when you run it in a different location. This would be especially frustrating when you are running multiple feature branches and need Kerberos interaction. The amount of work required to get Kerberos to work (SPN’s, URL’s, Delegation and AD) limits the number of sites you can have in an enterprise environment.

I have just two. I have a site for the latest candidate release and one to host all of my feature branches. As I am using TFS I identify my Feature with either a Requirement or Change Request Id and name everything after this.

Thus I have http://site/ and http://site-dev/1345. This makes it easy to find and test, and my CI build at the ends of the day just overwrites the previous version.

This means that all of your css like this…

.down {  
   padding-right:14px;  
   background: url('/UI/Resources/Images/arrow_down.gif') no-repeat 100% 50%;   
}

…will not work in one of your locations 🙁 This makes me sad…

So, in order to cheer up your CSS, you can give it a little bit of .NET Omph…

The first thing you need to do is get .NET to handle ALL of your requests, and not just for the ASP.NET pages.

image

Add a “Wildcard application mapping” to the “aspnet_isapi.dll” and you are good to go…

To process the css we need an HttpHandler, this is dead easy to implement and action so:

Imports System.Web
Imports System.Text.RegularExpressions


Public Class CssHttpHandler
    Implements IHttpHandler

    Public ReadOnly Property IsReusable() As Boolean Implements System.Web.IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

    Public Sub ProcessRequest(ByVal context As System.Web.HttpContext) Implements System.Web.IHttpHandler.ProcessRequest
        context.Response.ContentType = "text/css"

        'Get the file from the query stirng
        Dim File As String = context.Request.FilePath

        ' Find the actual path
        Dim Path As String = context.Server.MapPath(File)

        'Limit to only css files
        If Not System.IO.Path.GetExtension(Path) = ".css" Then
            context.Response.End()
        End If

        'Make sure file exists
        If Not System.IO.File.Exists(Path) Then
            context.Response.End()
        End If


        ' Open the file, read the contents and replace the variables
        Using css As System.IO.StreamReader = New System.IO.StreamReader(Path)
            Dim cssText As String = css.ReadToEnd()

            ' Replace url's
            Dim rximg As New Regex("url('*(.+?)'*)")
            For Each m As Match In rximg.Matches(cssText)
                cssText = cssText.Replace(m.Groups(1).Value, HandleRootOperator(m.Groups(1).Value))
            Next

            context.Response.Write("/* Please use the ~ operator in front of all URL's. e.g. url('~/UI/Resources/Images/Image.gif') will be converted at runtime to point at the root of the application. */" & vbCrLf & cssText)
        End Using

    End Sub

    ' Methods
    Public Function HandleRootOperator(ByVal virtualUrl As String) As String
        If Not String.IsNullOrEmpty(virtualUrl) Then
            If virtualUrl.StartsWith("^~/", StringComparison.OrdinalIgnoreCase) Then
                Return ("^" & Me.applicationPath & virtualUrl.Substring(2))
            End If
            If virtualUrl.StartsWith("~/", StringComparison.OrdinalIgnoreCase) Then
                Return (Me.applicationPath & virtualUrl.Substring(1))
            End If
        End If
        Return virtualUrl
    End Function




    ' Fields
    Private applicationPath As String = IIf((HttpRuntime.AppDomainAppVirtualPath.Length > 1), HttpRuntime.AppDomainAppVirtualPath, String.Empty)


End Class

Now add the Handler to you web.config

<httpHandlers>
  <add verb="*" path="*.css" type="Company.System.Product.CssHttpHandler, Company.System.Product" />
</httpHandlers>

 

And you are done 🙂

Technorati Tags:    

Create a conversation around this article

Share on Facebook
Share on Twitter
Share on Linkdin

Read more

Martin Hinshelwood
In organizational development and team dynamics, Agile (as the Agile Manifesto delineates) and Scrum (as the Scrum Guide outlines) guide teams not by solving their problems but by illuminating the issues that demand attention. These frameworks aim to identify and spotlight the challenges within a team or organization’s processes, effectively …
Martin Hinshelwood
This week, I participated in a Scrum.org Webinar hosted by Sabrina Love (Scrum.org Product Owner) as well as my colleagues, Joanna Płaskonka, Ph.D. and Alex Ballarin to discuss the state of learning and how immersive learning is the future of training. You can watch the video below to hear what …
Martin Hinshelwood
For a long time now I have been searching for that perfect domain that epitomised the vision, the why, of what I am trying to achieve with my customers and the industry at large. Now I have found it in http://nkdagility.com
Martin Hinshelwood
At the MVP Summit I was appalled by the number of people who asked questions about new features for supporting hierarchical tasks! I shared a disgusted look with Peter Provost and we had a quick (and I mean really quick) conversation that resulted in this post. it really comes down …