Foliotek Development Blog

A couple of handy helper razor scripts for Umbraco CMS

Thursday, September 1st, 2011

I’ve recently started a project for a new marketing site for our projects. ?One of the biggest issues with our current marketing site is that it was developed as an application – so changes need to go through a developer and be deployed. ?This basically resulted in the site being stagnant for a couple of years. ?This is bad – there are lots of things we offer now that we didn’t 2 years ago – and there are many opportunities where a page that speaks to a particular system, market, or feature could be very good for business/SEO.

I decided to use a CMS to allow anyone in our business edit and add content. ?Since we were a web consulting firm before we built products – we’ve had a number of custom solutions over the years for managing site content. ?I didn’t want to create that maintenance pain again, so I looked for another maintained solution I could use. ?I settled on Umbraco – it’s free, open source, has a great plugin architecture using the .NET/Razor architecture we already know – so it was a perfect fit.

I wanted to share a couple of handy scripts I came up with to make Umbraco even better:

Add “/edit” redirect to all pages:

To use this, add a couple of rewrite rules to UrlRewriting.config:

<add name="editrewrite"
          virtualUrl="^~/(.*)/edit/?"
          rewriteUrlParameter="ExcludeFromClientQueryString"
          destinationUrl="~/editredirect?path=foliotek/$1"
          ignoreCase="true" />
        <add name="edithomerewrite"
          virtualUrl="^~/edit/?"
          rewriteUrlParameter="ExcludeFromClientQueryString"
          destinationUrl="~/editredirect?path=foliotek"
          ignoreCase="true" />

Add a new scripting file (and macro) called “EditRedirect”

@using uComponents.Core
   @using umbraco.presentation.nodeFactory
   @using uComponents.Core.uQueryExtensions
   @{

   Node n = null;

   var path = Request.QueryString["path"].Split("/".ToCharArray());

   for(int i=0; i<path.Length; i++)
   {
     if(n==null)
     {
       n = uQuery.GetRootNode().GetChildNodes().Where(x=>x.Name.ToLower()== path[i].Trim().ToLower()).First();
     }
     else
     {
       var name = path[i].Trim();
       if(i==path.Length-1 && name.IndexOf(".")>0)
       {
         name=name.Substring(0,name.LastIndexOf("."));
       }
       n = n.GetDescendantNodes().First(c=>c.Name.ToLower()==name.ToLower());
     }
    }

   Response.Redirect("/umbraco/actions/editContent.aspx?id="+n.Id);

   }

Add a new “EditRedirect” template that uses the macro:

<%@ Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" %>

<asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
  <umbraco:Macro runat="server"   Alias="EditRedirect" />
</asp:Content>

And, finally, add a new page called EditRedirect to the root of your site that uses the macro. ? Now you can hit http://site/anydirectory/anypage/edit and it will take you to the editor screen of that page.

 

Simple path to access media by url and in css files

The abstraction umbraco allows for media is great – your editors can create a media item and replace the file later to their hearts content. ?Unfortunately, it’s not very simple to pull this media in scripts, templates, simple html, and especially css files. ?I wrote a macro/script to handle this too. ?Set up is pretty much like the edit redirect:

The script:

 

@using uComponents.Core
   @using umbraco.cms.businesslogic.media
   @using uComponents.Core.uQueryExtensions
   @{

   Media m = null;
   if(!( Model.Media is umbraco.MacroEngines.DynamicNull || (Model.Media is string && Model.Media=="")))
   {
     m = uQuery.GetMedia(Model.Media);
    }
  else
   {
     var path = Request.QueryString["path"].Split("/".ToCharArray());

     for(int i=0; i<path.Length; i++)
     {
       if(m==null)
       {
         m = uQuery.GetMediaByName( path[i].Trim()).First();
       }
       else
       {
         var name = path[i].Trim();
         if(i==path.Length-1 && name.IndexOf(".")>0)
         {
           name=name.Substring(0,name.LastIndexOf("."));
         }
         m = m.GetChildMedia().First(c=>c.Text==name);
       }
      }
   }

   Response.Clear();
   Response.ContentType = GetContentType(m.GetProperty<string>("umbracoExtension"));
   Response.TransmitFile(m.GetProperty<string>("umbracoFile"));

   }

   @functions{
     protected string GetContentType(string ext)
     {
   var contentTypes = new Dictionary<string, string>
                               {
                                   {"3dm", "x-world/x-3dmf"},
                                   {"3dmf", "x-world/x-3dmf"},
                                   {"a", "application/octet-stream"},
                                   {"aab", "application/x-authorware-bin"},
                                   {"aam", "application/x-authorware-map"},
                                   {"aas", "application/x-authorware-seg"},
                                   {"abc", "text/vnd.abc"},
                                   {"acgi", "text/html"},
                                   {"afl", "video/animaflex"},
                                   {"ai", "application/postscript"},
                                   {"aif", "audio/aiff"},
                                   {"aifc", "audio/aiff"},
                                   {"aiff", "audio/aiff"},
                                   {"aim", "application/x-aim"},
                                   {"aip", "text/x-audiosoft-intra"},
                                   {"ani", "application/x-navi-animation"},
                                   {"aos", "application/x-nokia-9000-communicator-add-on-software"},
                                   {"aps", "application/mime"},
                                   {"arc", "application/octet-stream"},
                                   {"arj", "application/arj"},
                                   {"art", "image/x-jg"},
                                   {"asf", "video/x-ms-asf"},
                                   {"asm", "text/x-asm"},
                                   {"asp", "text/asp"},
                                   {"asx", "application/x-mplayer2"},
                                   {"au", "audio/basic"},
                                   {"avi", "video/avi"},
                                   {"avs", "video/avs-video"},
                                   {"bcpio", "application/x-bcpio"},
                                   {"bin", "application/octet-stream"},
                                   {"bm", "image/bmp"},
                                   {"bmp", "image/bmp"},
                                   {"boo", "application/book"},
                                   {"book", "application/book"},
                                   {"boz", "application/x-bzip2"},
                                   {"bsh", "application/x-bsh"},
                                   {"bz", "application/x-bzip"},
                                   {"bz2", "application/x-bzip2"},
                                   {"c", "text/plain"},
                                   {"c++", "text/plain"},
                                   {"cat", "application/vnd.ms-pki.seccat"},
                                   {"cc", "text/plain"},
                                   {"ccad", "application/clariscad"},
                                   {"cco", "application/x-cocoa"},
                                   {"cdf", "application/cdf"},
                                   {"cer", "application/pkix-cert"},
                                   {"cha", "application/x-chat"},
                                   {"chat", "application/x-chat"},
                                   {"class", "application/java"},
                                   {"com", "application/octet-stream"},
                                   {"conf", "text/plain"},
                                   {"cpio", "application/x-cpio"},
                                   {"cpp", "text/x-c"},
                                   {"cpt", "application/x-cpt"},
                                   {"crl", "application/pkcs-crl"},
                                   {"css", "text/css"},
                                   {"def", "text/plain"},
                                   {"der", "application/x-x509-ca-cert"},
                                   {"dif", "video/x-dv"},
                                   {"dir", "application/x-director"},
                                   {"dl", "video/dl"},
                                   {"doc", "application/msword"},
                                   {"dot", "application/msword"},
                                   {"dp", "application/commonground"},
                                   {"drw", "application/drafting"},
                                   {"dump", "application/octet-stream"},
                                   {"dv", "video/x-dv"},
                                   {"dvi", "application/x-dvi"},
                                   {"dwf", "drawing/x-dwf (old)"},
                                   {"dwg", "application/acad"},
                                   {"dxf", "application/dxf"},
                                   {"eps", "application/postscript"},
                                   {"es", "application/x-esrehber"},
                                   {"etx", "text/x-setext"},
                                   {"evy", "application/envoy"},
                                   {"exe", "application/octet-stream"},
                                   {"f", "text/plain"},
                                   {"f90", "text/x-fortran"},
                                   {"fdf", "application/vnd.fdf"},
                                   {"fif", "image/fif"},
                                   {"fli", "video/fli"},
                                   {"for", "text/x-fortran"},
                                   {"fpx", "image/vnd.fpx"},
                                   {"g", "text/plain"},
                                   {"g3", "image/g3fax"},
                                   {"gif", "image/gif"},
                                   {"gl", "video/gl"},
                                   {"gsd", "audio/x-gsm"},
                                   {"gtar", "application/x-gtar"},
                                   {"gz", "application/x-compressed"},
                                   {"h", "text/plain"},
                                   {"help", "application/x-helpfile"},
                                   {"hgl", "application/vnd.hp-hpgl"},
                                   {"hh", "text/plain"},
                                   {"hlp", "application/x-winhelp"},
                                   {"htc", "text/x-component"},
                                   {"htm", "text/html"},
                                   {"html", "text/html"},
                                   {"htmls", "text/html"},
                                   {"htt", "text/webviewhtml"},
                                   {"htx", "text/html"},
                                   {"ice", "x-conference/x-cooltalk"},
                                   {"ico", "image/x-icon"},
                                   {"idc", "text/plain"},
                                   {"ief", "image/ief"},
                                   {"iefs", "image/ief"},
                                   {"iges", "application/iges"},
                                   {"igs", "application/iges"},
                                   {"ima", "application/x-ima"},
                                   {"imap", "application/x-httpd-imap"},
                                   {"inf", "application/inf"},
                                   {"ins", "application/x-internett-signup"},
                                   {"ip", "application/x-ip2"},
                                   {"isu", "video/x-isvideo"},
                                   {"it", "audio/it"},
                                   {"iv", "application/x-inventor"},
                                   {"ivr", "i-world/i-vrml"},
                                   {"ivy", "application/x-livescreen"},
                                   {"jam", "audio/x-jam"},
                                   {"jav", "text/plain"},
                                   {"java", "text/plain"},
                                   {"jcm", "application/x-java-commerce"},
                                   {"jfif", "image/jpeg"},
                                   {"jfif-tbnl", "image/jpeg"},
                                   {"jpe", "image/jpeg"},
                                   {"jpeg", "image/jpeg"},
                                   {"jpg", "image/jpeg"},
                                   {"jps", "image/x-jps"},
                                   {"js", "application/x-javascript"},
                                   {"jut", "image/jutvision"},
                                   {"kar", "audio/midi"},
                                   {"ksh", "application/x-ksh"},
                                   {"la", "audio/nspaudio"},
                                   {"lam", "audio/x-liveaudio"},
                                   {"latex", "application/x-latex"},
                                   {"lha", "application/lha"},
                                   {"lhx", "application/octet-stream"},
                                   {"list", "text/plain"},
                                   {"lma", "audio/nspaudio"},
                                   {"log", "text/plain"},
                                   {"lsp", "application/x-lisp"},
                                   {"lst", "text/plain"},
                                   {"lsx", "text/x-la-asf"},
                                   {"ltx", "application/x-latex"},
                                   {"lzh", "application/octet-stream"},
                                   {"lzx", "application/lzx"},
                                   {"m", "text/plain"},
                                   {"m1v", "video/mpeg"},
                                   {"m2a", "audio/mpeg"},
                                   {"m2v", "video/mpeg"},
                                   {"m3u", "audio/x-mpequrl"},
                                   {"man", "application/x-troff-man"},
                                   {"map", "application/x-navimap"},
                                   {"mar", "text/plain"},
                                   {"mbd", "application/mbedlet"},
                                   {"mc$", "application/x-magic-cap-package-1.0"},
                                   {"mcd", "application/mcad"},
                                   {"mcf", "image/vasa"},
                                   {"mcp", "application/netmc"},
                                   {"me", "application/x-troff-me"},
                                   {"mht", "message/rfc822"},
                                   {"mhtml", "message/rfc822"},
                                   {"mid", "audio/midi"},
                                   {"midi", "audio/midi"},
                                   {"mif", "application/x-frame"},
                                   {"mime", "message/rfc822"},
                                   {"mjf", "audio/x-vnd.audioexplosion.mjuicemediafile"},
                                   {"mjpg", "video/x-motion-jpeg"},
                                   {"mm", "application/base64"},
                                   {"mme", "application/base64"},
                                   {"mod", "audio/mod"},
                                   {"moov", "video/quicktime"},
                                   {"mov", "video/quicktime"},
                                   {"movie", "video/x-sgi-movie"},
                                   {"mp2", "audio/mpeg"},
                                   {"mp3", "audio/mpeg3"},
                                   {"mpa", "audio/mpeg"},
                                   {"mpc", "application/x-project"},
                                   {"mpe", "video/mpeg"},
                                   {"mpeg", "video/mpeg"},
                                   {"mpg", "video/mpeg"},
                                   {"mpga", "audio/mpeg"},
                                   {"mpp", "application/vnd.ms-project"},
                                   {"mpt", "application/x-project"},
                                   {"mpv", "application/x-project"},
                                   {"mpx", "application/x-project"},
                                   {"mrc", "application/marc"},
                                   {"ms", "application/x-troff-ms"},
                                   {"mv", "video/x-sgi-movie"},
                                   {"my", "audio/make"},
                                   {"mzz", "application/x-vnd.audioexplosion.mzz"},
                                   {"nap", "image/naplps"},
                                   {"naplps", "image/naplps"},
                                   {"nc", "application/x-netcdf"},
                                   {"ncm", "application/vnd.nokia.configuration-message"},
                                   {"nif", "image/x-niff"},
                                   {"niff", "image/x-niff"},
                                   {"nix", "application/x-mix-transfer"},
                                   {"nsc", "application/x-conference"},
                                   {"nvd", "application/x-navidoc"},
                                   {"o", "application/octet-stream"},
                                   {"oda", "application/oda"},
                                   {"omc", "application/x-omc"},
                                   {"omcd", "application/x-omcdatamaker"},
                                   {"omcr", "application/x-omcregerator"},
                                   {"p", "text/x-pascal"},
                                   {"p10", "application/pkcs10"},
                                   {"p12", "application/pkcs-12"},
                                   {"p7a", "application/x-pkcs7-signature"},
                                   {"p7c", "application/pkcs7-mime"},
                                   {"pas", "text/pascal"},
                                   {"pbm", "image/x-portable-bitmap"},
                                   {"pcl", "application/vnd.hp-pcl"},
                                   {"pct", "image/x-pict"},
                                   {"pcx", "image/x-pcx"},
                                   {"pdf", "application/pdf"},
                                   {"pfunk", "audio/make"},
                                   {"pgm", "image/x-portable-graymap"},
                                   {"pic", "image/pict"},
                                   {"pict", "image/pict"},
                                   {"pkg", "application/x-newton-compatible-pkg"},
                                   {"pko", "application/vnd.ms-pki.pko"},
                                   {"pl", "text/plain"},
                                   {"plx", "application/x-pixclscript"},
                                   {"pm", "image/x-xpixmap"},
                                   {"png", "image/png"},
                                   {"pnm", "application/x-portable-anymap"},
                                   {"pot", "application/mspowerpoint"},
                                   {"pov", "model/x-pov"},
                                   {"ppa", "application/vnd.ms-powerpoint"},
                                   {"ppm", "image/x-portable-pixmap"},
                                   {"pps", "application/mspowerpoint"},
                                   {"ppt", "application/mspowerpoint"},
                                   {"ppz", "application/mspowerpoint"},
                                   {"pre", "application/x-freelance"},
                                   {"prt", "application/pro_eng"},
                                   {"ps", "application/postscript"},
                                   {"psd", "application/octet-stream"},
                                   {"pvu", "paleovu/x-pv"},
                                   {"pwz", "application/vnd.ms-powerpoint"},
                                   {"py", "text/x-script.phyton"},
                                   {"pyc", "applicaiton/x-bytecode.python"},
                                   {"qcp", "audio/vnd.qcelp"},
                                   {"qd3", "x-world/x-3dmf"},
                                   {"qd3d", "x-world/x-3dmf"},
                                   {"qif", "image/x-quicktime"},
                                   {"qt", "video/quicktime"},
                                   {"qtc", "video/x-qtc"},
                                   {"qti", "image/x-quicktime"},
                                   {"qtif", "image/x-quicktime"},
                                   {"ra", "audio/x-pn-realaudio"},
                                   {"ram", "audio/x-pn-realaudio"},
                                   {"ras", "application/x-cmu-raster"},
                                   {"rast", "image/cmu-raster"},
                                   {"rexx", "text/x-script.rexx"},
                                   {"rf", "image/vnd.rn-realflash"},
                                   {"rgb", "image/x-rgb"},
                                   {"rm", "application/vnd.rn-realmedia"},
                                   {"rmi", "audio/mid"},
                                   {"rmm", "audio/x-pn-realaudio"},
                                   {"rmp", "audio/x-pn-realaudio"},
                                   {"rng", "application/ringing-tones"},
                                   {"rnx", "application/vnd.rn-realplayer"},
                                   {"roff", "application/x-troff"},
                                   {"rp", "image/vnd.rn-realpix"},
                                   {"rpm", "audio/x-pn-realaudio-plugin"},
                                   {"rt", "text/richtext"},
                                   {"rtf", "text/richtext"},
                                   {"rtx", "application/rtf"},
                                   {"rv", "video/vnd.rn-realvideo"},
                                   {"s", "text/x-asm"},
                                   {"s3m", "audio/s3m"},
                                   {"saveme", "application/octet-stream"},
                                   {"sbk", "application/x-tbook"},
                                   {"scm", "application/x-lotusscreencam"},
                                   {"sdml", "text/plain"},
                                   {"sdp", "application/sdp"},
                                   {"sdr", "application/sounder"},
                                   {"sea", "application/sea"},
                                   {"set", "application/set"},
                                   {"sgm", "text/sgml"},
                                   {"sgml", "text/sgml"},
                                   {"sh", "application/x-bsh"},
                                   {"shtml", "text/html"},
                                   {"sid", "audio/x-psid"},
                                   {"sit", "application/x-sit"},
                                   {"skd", "application/x-koan"},
                                   {"skm", "application/x-koan"},
                                   {"skp", "application/x-koan"},
                                   {"skt", "application/x-koan"},
                                   {"sl", "application/x-seelogo"},
                                   {"smi", "application/smil"},
                                   {"smil", "application/smil"},
                                   {"snd", "audio/basic"},
                                   {"sol", "application/solids"},
                                   {"spc", "application/x-pkcs7-certificates"},
                                   {"spl", "application/futuresplash"},
                                   {"spr", "application/x-sprite"},
                                   {"sprite", "application/x-sprite"},
                                   {"src", "application/x-wais-source"},
                                   {"ssi", "text/x-server-parsed-html"},
                                   {"ssm", "application/streamingmedia"},
                                   {"sst", "application/vnd.ms-pki.certstore"},
                                   {"step", "application/step"},
                                   {"stl", "application/sla"},
                                   {"stp", "application/step"},
                                   {"sv4cpio", "application/x-sv4cpio"},
                                   {"sv4crc", "application/x-sv4crc"},
                                   {"svf", "image/vnd.dwg"},
                                   {"svr", "application/x-world"},
                                   {"swf", "application/x-shockwave-flash"},
                                   {"t", "application/x-troff"},
                                   {"talk", "text/x-speech"},
                                   {"tar", "application/x-tar"},
                                   {"tbk", "application/toolbook"},
                                   {"tcl", "application/x-tcl"},
                                   {"tcsh", "text/x-script.tcsh"},
                                   {"tex", "application/x-tex"},
                                   {"texi", "application/x-texinfo"},
                                   {"texinfo", "application/x-texinfo"},
                                   {"text", "text/plain"},
                                   {"tgz", "application/x-compressed"},
                                   {"tif", "image/tiff"},
                                   {"tr", "application/x-troff"},
                                   {"tsi", "audio/tsp-audio"},
                                   {"tsp", "audio/tsplayer"},
                                   {"tsv", "text/tab-separated-values"},
                                   {"turbot", "image/florian"},
                                   {"txt", "text/plain"},
                                   {"uil", "text/x-uil"},
                                   {"uni", "text/uri-list"},
                                   {"unis", "text/uri-list"},
                                   {"unv", "application/i-deas"},
                                   {"uri", "text/uri-list"},
                                   {"uris", "text/uri-list"},
                                   {"ustar", "application/x-ustar"},
                                   {"uu", "application/octet-stream"},
                                   {"vcd", "application/x-cdlink"},
                                   {"vcs", "text/x-vcalendar"},
                                   {"vda", "application/vda"},
                                   {"vdo", "video/vdo"},
                                   {"vew", "application/groupwise"},
                                   {"viv", "video/vivo"},
                                   {"vivo", "video/vivo"},
                                   {"vmd", "application/vocaltec-media-desc"},
                                   {"vmf", "application/vocaltec-media-file"},
                                   {"voc", "audio/voc"},
                                   {"vos", "video/vosaic"},
                                   {"vox", "audio/voxware"},
                                   {"vqe", "audio/x-twinvq-plugin"},
                                   {"vqf", "audio/x-twinvq"},
                                   {"vql", "audio/x-twinvq-plugin"},
                                   {"vrml", "application/x-vrml"},
                                   {"vrt", "x-world/x-vrt"},
                                   {"vsd", "application/x-visio"},
                                   {"vst", "application/x-visio"},
                                   {"vsw", "application/x-visio"},
                                   {"w60", "application/wordperfect6.0"},
                                   {"w61", "application/wordperfect6.1"},
                                   {"w6w", "application/msword"},
                                   {"wav", "audio/wav"},
                                   {"wb1", "application/x-qpro"},
                                   {"wbmp", "image/vnd.wap.wbmp"},
                                   {"web", "application/vnd.xara"},
                                   {"wiz", "application/msword"},
                                   {"wk1", "application/x-123"},
                                   {"wmf", "windows/metafile"},
                                   {"wml", "text/vnd.wap.wml"},
                                   {"wmlc", "application/vnd.wap.wmlc"},
                                   {"wmls", "text/vnd.wap.wmlscript"},
                                   {"wmlsc", "application/vnd.wap.wmlscriptc"},
                                   {"word", "application/msword"},
                                   {"wp", "application/wordperfect"},
                                   {"wp5", "application/wordperfect"},
                                   {"wp6", "application/wordperfect"},
                                   {"wpd", "application/wordperfect"},
                                   {"wq1", "application/x-lotus"},
                                   {"wri", "application/mswrite"},
                                   {"wrl", "application/x-world"},
                                   {"wrz", "model/vrml"},
                                   {"wsc", "text/scriplet"},
                                   {"wsrc", "application/x-wais-source"},
                                   {"wtk", "application/x-wintalk"},
                                   {"xbm", "image/x-xbitmap"},
                                   {"xdr", "video/x-amt-demorun"},
                                   {"xgz", "xgl/drawing"},
                                   {"xif", "image/vnd.xiff"},
                                   {"xl", "application/excel"},
                                   {"xla", "application/excel"},
                                   {"xlb", "application/excel"},
                                   {"xlc", "application/excel"},
                                   {"xld", "application/excel"},
                                   {"xlk", "application/excel"},
                                   {"xll", "application/excel"},
                                   {"xlm", "application/excel"},
                                   {"xls", "application/excel"},
                                   {"xlt", "application/excel"},
                                   {"xlv", "application/excel"},
                                   {"xlw", "application/excel"},
                                   {"xm", "audio/xm"},
                                   {"xml", "text/xml"},
                                   {"xmz", "xgl/movie"},
                                   {"xpix", "application/x-vnd.ls-xpix"},
                                   {"xpm", "image/x-xpixmap"},
                                   {"x-png", "image/png"},
                                   {"xsr", "video/x-amt-showrun"},
                                   {"xwd", "image/x-xwd"},
                                   {"xyz", "chemical/x-pdb"},
                                   {"z", "application/x-compress"},
                                   {"zip", "application/x-compressed"},
                                   {"zoo", "application/octet-stream"},
                                   {"zsh", "text/x-script.zsh"}
                               };

   if(contentTypes[ext]==null)
   {
     return "application/octet-stream";

   }
       return contentTypes[ext];
     }
   }

 

The template:

 

<%@ Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" %>

<asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
  <umbraco:Macro runat="server"  MediaID="[#media]" Alias="getMedia" />
</asp:Content>

 

The redirect rule:

 

        <add name="cssmediarewrite" 
          virtualUrl="^~/css/media/(.*)" 
          rewriteUrlParameter="ExcludeFromClientQueryString" 
          destinationUrl="~/cssmedia?path=$1" 
          ignoreCase="true" />

 

Finally, add a page called CssMedia that uses the template. ?Now, you can reference /css/media/mediapath/medianame.fileextension ?to get the item. ?This also means that your css files can use media/mediapath/medianame.fileextension to reference Umbraco media.

 

 

Scrollable DataGrid table with Fixed Header with jQuery

Thursday, August 25th, 2011

Problem

Implement scrolling on a table, but make the header fixed.

I had a really long table and wanted to make it scrollable, but keep the header in place. I saw a few examples of this, but with very little explanation to go with it. So, I just did it myself.

Sample Markup


<div>

  <table id="MyDataRows">

    <thead>
      <tr class="grid_header">
        <th>Col1</th>
        <th>Col2</th>
        <th>Col3</th>
      </tr<>
    </thead>

    <tbody>
      <tr>
        <td>Data</td>
        <td>Data</td>
         <td>Data</td>
      </tr>
    </tbody>
  </table>

</div>

First Attempt

So, the first option I saw online was to add “overflow-y: auto; display: block; height: 330px;” to the tbody tag. I saw somewhere where this could work with a single column, but with multiple columns, the header is pushed to the right, and the table rows are pushed to the left. I played with this awhile, but never could get it to work.

Solution

While not as elegant as I would like, this solution does work. Of course, if you isolate the header and then wrap the data rows in a div with the overflow-y stuff, that would work. However, a datagrid populates the header automatically. You could build another datagrid and mess with that, but jQuery quickly solves the issue. Here’s the plan:

  • Add a place for the header to be moved outside of the table with the rows where the overflow-y will be used
  • Move the header
  • Realign the columns

New Markup

This is pretty basic. ?I added a table just before the table with data in it. ?This will hold the header. ?I also styled the div to allow for scrolling.


<table id="MyHeader"></table>

<div style="overflow-y: auto; height: 330px;">

  <table id="MyDataRows">

    <thead>
      <tr class="grid_header">
        <th>Col1</th>
        <th>Col2</th>
        <th>Col3</th>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td>Data</td>
        <td>Data</td>
        <td>Data</td>
      </tr>
    </tbody>
  </table>

</div>


jQuery Code


<script type="text/javascript">// <![CDATA[
$(function () {
    /* move the table header from the datagrid outside, so it doesn't scroll" */
    $("#MyHeader").append($("#StudentData thead"));
    $("#MyDataRows").css("margin-bottom", "0");

    var ths = $("#MyHeader th");
    var tds = $("#MyDataRows tr:first td");

    /* Reset the padding of the th's to match the td's */
    ths.css("padding-left", tds.first().css("padding-left"));
    ths.css("padding-right", tds.first().css("padding-right"));

    /* Set the widths of all the th's (except the last to acccount for scroll bar) to the corresponding td width */
    for (var i = 0; i < tds.length - 1; i++) { 		     
        ths.eq(i).width(tds.eq(i).width()); 		    
    } 		        
});
// ]]></script>


Final Markup

So, the end result is two tables whose column widths match but which function as a scrollable datagrid table with a fixed header.


<table id="MyHeader">
  <thead>
    <tr class="grid_header">
      <th>Col1</th>
      <th>Col2</th>
      <th>Col3</th>
    </tr>
  </thead>
</table>

<div style="overflow-y: auto; height: 330px;">

  <table id="MyDataRows">

    <tbody>
      <tr>
        <td>Data</td>
        <td>Data</td>
        <td>Data</td>
      </tr>
    </tbody>

  </table>

</div>


Aligning a List of Checkboxes with Text that Wraps

Friday, June 17th, 2011

Summary

I ran into a problem where there was a list construct and each item was a checkbox. When the text of the checkbox wrapped, the alignment was all messed up, so I came up a way to make it look better. ?I also had some additional items I wanted to be aligned as well, so I put those things in a div.

Screenshots

Here’s how it looks with and without the formatting:

Without the formatting (may need to zoom in)

With Formatting

HTML



<div class="checkboxlist">
    <ul>
        <li>
            <asp:CheckBox ID="CheckBox1" runat="server" Text="This is a checkbox option in an unordered list that is pretty long and ends u wrapping onto another line, but maintains alignment" />
        </li>
        <li>
            <asp:CheckBox ID="CheckBox2" runat="server" Text="This is yet another checkbox option in an unordered list that is pretty long and ends u wrapping onto another line, but maintains alignment" />
            <div>
                <br /> Here's some other info as well that isn't a part of the checkbox. The alignment for this works as well.
            </div>
        </li>
    </ul>
</div>


CSS



div.checkboxlist ul li { margin: 7px 0px; }
div.checkboxlist ul li input { width: 15px; display: block; float:left;}
div.checkboxlist ul li label { padding-left: 28px; display: block; }
div.checkboxlist ul li div { margin-left: 28px; clear: both; font-size: .9em; }


Plupload Custom File Upload UI

Monday, March 14th, 2011

Plupload is a tool that allows you to upload files using HTML5 Gears, Silverlight, Flash, BrowserPlus, normal forms, and provides features such as upload progress, image resizing and chunked uploads.

I’m going to discuss the process I went through to implement a custom Plupload UI.

Here is a video of it in action:

http://www.youtube.com/watch?v=cYxjlHXXXe4&w=480&h=390

Here is the simplified source:

http://dl.dropbox.com/u/249320/CustomPluploadExample.zip

Now I’ll go into details on how it was built using the Plupload framework.
I modeled my version of the plupload custom example here.

First of all I init an instance of plupload and set the options. I set the drop area, and the container that the file progress will be displayed in.
Note: Only FF and Chrome support drag’n drop in Plupload.


$(document).ready(function(){
var uploader = new plupload.Uploader({
// General settings
runtimes: 'html5,flash,gears,browserplus,silverlight,html4',url:<%= SaveUrl %>,
browse_button : "pickfiles",
button_browse_hover : true,
drop_element : "dropArea",
autostart : true,
max_file_size: '100mb',
container: "FileContainer",
chunk_size: '1mb',
unique_names: true,
// Flash settings
flash_swf_url: <%= "'"+  Page.ResolveUrl("~/plugins/plupload/plupload.flash.swf") +"'" %>,
// Silverlight settings
silverlight_xap_url: <%= '"'+ Page.ResolveUrl("~/plugins/plupload/plupload.silverlight.xap") +'"' %>
});

Plupload doesn’t automatically hide the drop area when the current browser doesn’t support it so I bind some custom logic to the plupload “Init” event below. Drag and drop support requires that the browser support FileReader and will not work under the flash or silverlight runtimes.


uploader.bind('Init', function(up, params){
try{
if(!!FileReader &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; !((params.runtime == "flash") || (params.runtime == "silverlight")))
$("#dropArea").show();
$("#fileSelectMsg").hide();
}
catch(err){}});
uploader.init();

So now we need to be able to display files in our “FileContainer” div the script below fires on the FilesAdded event. It is called when a file is added or dropped in the drop area. When this event is fired I create a div that contains the elements we will need to display upload progress and the name of the file.


uploader.bind('FilesAdded', function(up, files) {
$.each(files, function(i, file) {
$('#filelist').append(
'
<div id="' + file.id + '" class="<span class=">fileItem">
<div class="name">' +</div>
</div>
file.name + '</div><a href="#" id="cancel'+file.id+'" class="cancel">cancel</a><div class="fileInfo"><span class="size">' + plupload.formatSize(file.size) + '</span>' +
'<div class="plupload_progress"><div class="plupload_progress_container"><div class="plupload_progress_bar"></div></div></div>'+
'<span class="<span class=">percentComplete</span>">');

//Fire Upload Event
up.refresh(); // Reposition Flash/Silverlight
uploader.start();

//Bind cancel click event
$('#cancel'+file.id).click(function(){
$fileItem = $('#' + file.id);
$fileItem.addClass("cancelled");
uploader.removeFile(file);
$(this).unbind().remove();
});

//Set ico_Ok to Uploading
$confirmNext.attr("disabled", "disabled");
$confirmNext.html("Uploading..");
$confirmNext.unbind("click").click(function(e){e.preventDefault();});
});
});

Now that we have files uploading we need to update the progress bars for each file div in the “FileContainer”. So yet again Plupload conveniently has an event called “UploadProgress” that fires after each chunk has been successfully uploaded.


uploader.bind('UploadProgress', function(up, file) {
var  $fileWrapper = $('#' + file.id);
$fileWrapper.find(".plupload_progress").show();
$fileWrapper.find(".plupload_progress_bar").attr("style", "width:"+ file.percent + "%");
$fileWrapper.find(".percentComplete").html(file.percent+"%");
$fileWrapper.find('#cancel'+file.id).remove();
});

So now we have files uploading and being added to the container and updating the upload progress in real-time. So now I want to grey out the div once the file has been completed. In order to do this I add a class to that file’s div. The “FileUploaded” event gets fired after each file is successfully uploaded.


uploader.bind('FileUploaded', function(up, file) {
$fileItem = $('#' + file.id);
$fileItem.addClass("completed");

$confirmNext.removeAttr("disabled");
$confirmNext.html("Ok");
$confirmNext.unbind().click(function(e){window.location.href = window.location.href;});

$('#cancel'+file.id).unbind().remove();
});

Last but not least I’ll show you how to display errors that happen server-side when a chunk is uploaded. On “ChunkUploaded” event I check the response message if it contains “Error:” in the response string. I kill the current upload and display that error message to the user and also log the error in my application. The following snippet performs just this.


uploader.bind("ChunkUploaded", function(up, file, response){
//Should return a status 200 if the chunk was uploaded successfully
if(response.status != null)
{
if(response.status != "200" || (response.response.indexOf("Error") != -1))
{
if(response.response.indexOf("Error") != -1)
{
//Prompt the user with the custom error message
$("div.error:first").show().html('<p>'+response.response+'</p>');
}
else
{
//Log this as an error
//Custom line of code to log error on server would go here

//Notify user of error
$("div.error:first").show().html('<p>There was an error uploading your file '+ file.name +' Support has been notified.</p>');
}
$('#' + file.id).addClass("cancelled");
uploader.stop();
}
}
});

That’s how I built my custom UI wrapper around the Plupload core. Hopefully that will help get you going in the right direction.

Loading images last with jQuery

Tuesday, February 9th, 2010

There are lots of ways to make your webpages faster and more responsive. YSlow is a great tool to help you find many great ways to make a particular page faster.

One of the best things you can do is reduce the number of requests (css/js/images/etc) to the server. Typically, this would mean that you would combine files – merge all of your JS and CSS (and minify while you are at it), and use CSS Sprites to combine images.

One major problem of using CSS Sprites is that it can be quite painful to maintain. Over time, if you want to add or change some of your images – you basically need to rebuild and replace the combined images and all of the CSS rules specifying coordinates. Sometimes, this makes the CSS Sprite technique unreasonable to implement.

In one such case, we had about 50 images in one application that were causing the page to take a long time to load. These images were previews of some different design choices that the user could make. The design choices themselves (and their previews) were database driven so that we can add new designs through an admin interface. So, CSS Spriteing the previews would seriously hamper that flexibility.

One other design consideration was that the previews weren’t that important – the page was fully functional and usable without the images. In fact, the designs weren’t even visible until you toggled the design menu.

There is a lazy loader plugin for jQuery already available here – but it didn’t fit our needs. Instead of skipping images in order to get the page working as soon as possible (and initiate the load once the page is usable) – it is made to skip loading offscreen images until they are scrolled into view. It might have somewhat worked for our needs – but I thought it was better to load the images as soon as possible, instead of waiting for the design menu to be expanded to initiate the load. That way, most of the time the designs would be visible by the time they open the menu – but it wouldn’t interfere with the rest of the interface.

My solution was to set the src for all of the previews to a single animated loading image – like one you can get here. Then, I set a custom attribute on the image for the real preview’s url. Finally, some jQuery code runs after the page is done loading which replaces each src attribute with the url in the custom attribute,which will load the real image.

Sample HTML:


<ul>
    <li templateid="7bcf8f23-fdd0-45c5-a429-d2ffb59e47f0" class="selected"><span>3D Dots
        Dark</span>
        <img src="/static/img/ajax-loader-small.gif" deferredsrc="/resources/7bcf8f23-fdd0-45c5-a429-d2ffb59e47f0/preview.jpg" class="deferredLoad" alt="3D Dots Dark">
    </li>
    <li templateid="b1a09e28-629e-472a-966e-fc98fc269607"><span>3D Dots Lite</span>
        <img src="/static/img/ajax-loader-small.gif" deferredsrc="/resources/b1a09e28-629e-472a-966e-fc98fc269607/preview.jpg" class="deferredLoad" alt="3D Dots Lite">
    </li>
    <li templateid="e121d26a-9c8f-466f-acc7-9a79d5e8cfa9"><span>Beauty</span>
        <img src="/static/img/ajax-loader-small.gif" deferredsrc="/resources/e121d26a-9c8f-466f-acc7-9a79d5e8cfa9/preview.jpg" class="deferredLoad" alt="Beauty">
    </li>
    <li templateid="322e4c7a-33e7-4e05-bb72-c4076a83a3d0"><span>Black and White</span>
        <img src="/static/img/ajax-loader-small.gif" deferredsrc="/resources/322e4c7a-33e7-4e05-bb72-c4076a83a3d0/preview.jpg" class="deferredLoad" alt="Black and White">
    </li>
    <li templateid="57716da9-91ef-4cf0-82f1-722d0770ad7f"><span>Blank</span>
        <img src="/static/img/ajax-loader-small.gif" deferredsrc="/resources/57716da9-91ef-4cf0-82f1-722d0770ad7f/preview.jpg" class="deferredLoad" alt="Blank">
    </li>
    <li templateid="a79e1136-db47-4acd-be3e-2daf4522796d"><span>Blue Leaves</span>
        <img src="/static/img/ajax-loader-small.gif" deferredsrc="/resources/a79e1136-db47-4acd-be3e-2daf4522796d/preview.jpg" class="deferredLoad" alt="Blue Leaves">
    </li>
    <li templateid="03cb737d-4da7-46d5-b4e4-5ad4b4a3aaf4"><span>Blue Open</span>
        <img src="/static/img/ajax-loader-small.gif" deferredsrc="/resources/03cb737d-4da7-46d5-b4e4-5ad4b4a3aaf4/preview.jpg" class="deferredLoad" alt="Blue Open">
    </li>
    <li templateid="899dff2f-38ba-44f7-9fe2-af66e62674a4"><span>Compass</span>
        <img src="/static/img/ajax-loader-small.gif" deferredsrc="/resources/899dff2f-38ba-44f7-9fe2-af66e62674a4/preview.jpg" class="deferredLoad" alt="Compass">
    </li>
</ul>

Sample javascript:


$(function(){
        $("img.deferredLoad").each(function() {
            var $this = $(this);
            $this.attr("src",$this.attr("deferredSrc")).removeClass("deferredLoad");
        });
});