One of the new, cool capabilities of .Net is built-in compression and bundling of CSS and javascript (Gunner Peipman has written an excellent introductory article here). BlogEngine.NET 2.7 uses this infrastructure. However, there are a couple of gotchas when integrating an existing site's styles and scripts into BlogEngine.NET.
What Gets Bundled in BlogEngine.NET?
BlogEngine.NET sets up its bundles in the Global.asax.cs file, like this:
static void Init_BundleTable()
{
BundleTable.Bundles.EnableDefaultBundles();
BundleTable.Bundles.Clear();
var jsTransform = new JsMinify();
var cssTransform = new CssMinify();
// scripts added to header and NOT deferred
var hdrjs = new Bundle("~/Scripts/Header/js", jsTransform);
hdrjs.AddDirectory("~/Scripts/Header", "*.js", false);
BundleTable.Bundles.Add(hdrjs);
// for anonymous users
var css = new Bundle("~/Styles/css", cssTransform);
css.AddDirectory("~/Styles", "*.css", false);
BundleTable.Bundles.Add(css);
var js = new Bundle("~/Scripts/js", jsTransform);
js.AddDirectory("~/Scripts", "*.js", false);
BundleTable.Bundles.Add(js);
...
The AddDirectory()
method will add every file that matches the specified pattern in the directory to the bundle in alphabetical order. For the fragment above, all the *.css files in the Styles directory will be minified and bundled into a "/Styles/css" link at the top of each post or BlogEngine.NET page, assuming "Enable optimization" is checked in BlogEngine.NET's Advanced Settings.
Managing File Order
You'll want to control the order of files in the bundle, especially for CSS. There are two main ways to do this.
- Adjust the filenames in your directory so that they get loaded in the order you choose.
- Use
AddFile()
for each file (in order) instead of AddDirectory()
for the whole directory
The BlogEngine.NET developers use the first option, adding a two digit integer to the beginning of the filename. Now you have complete control over the order of your files in a bundle.
Original Order
- modern-responsive.css
- modern.css
- OrchardHouse.css
- site.css
New Order
- 01modern.css
- 02modern-responsive.css
- 03site.css
- 04OrchardHouse.css
What About Running Unoptimized/Unbundled?
If you're not bundling, what you want is exactly the same order with the CSS and Javascript files as you have in the bundles. If "Enable optimization" in unchecked, BlogEngine.NET reads in the files using the regular DirectoryInfo().GetFiles()
method, which reads in the files in alphabetical order. Then the list is used to make a link tag for each file:
public static void AddStyle(System.Web.UI.Page page, string lnk)
{
// global styles on top, before theme specific styles
if(lnk.Contains("Global.css") || lnk.Contains("Styles/css"))
page.Header.Controls.AddAt(0, new LiteralControl(
string.Format("\n", lnk)));
else
page.Header.Controls.Add(new LiteralControl(
string.Format("\n", lnk)));
}
AddStyle()
puts Global.css at the top of the page header (when unoptimized), and it does the same for the style bundle (when optimized, since all the style bundles begin with "Styles/css"). When you are unoptimized, you are adding the list of files to the header, but you'll either get them in reverse order (if they match the if
, they are added at the 0 index of the page header, reversing the order) or you'll get them at the bottom of the page header (if they match the else
. The theme style will be in the middle of the header, above the dynamically loaded styles, so your theme is generally quite boogered.
My solution was to modify Helpers in BlogEngine.Core, using Linq to parse the integer I put at the beginning of each filename:
public static void AddStyle(System.Web.UI.Page page, string lnk)
{
// global styles on top, before theme specific styles
int index = GetIndexFromFilename(lnk);
if(lnk.Contains("Global.css") || lnk.Contains("Styles/css") || index > 0)
page.Header.Controls.AddAt(index, new LiteralControl(
string.Format("\n", lnk)));
else
page.Header.Controls.Add(new LiteralControl(
string.Format("\n", lnk)));
}
///
/// Get an index number for ordering the file from the filename
///
/// Added by DTM 2/6/2012
///
///
/// 0 if no number at beginning of filename,
/// otherwise returns the number
///
private static int GetIndexFromFilename(string lnk)
{
int beginIndex = lnk.LastIndexOf("/");
string filename;
if (beginIndex > 0)
filename = lnk.Substring(beginIndex + 1);
else
filename = lnk;
char[] charArray = filename.Trim().TakeWhile(c => char.IsDigit(c)).ToArray();
if (charArray.Length > 0)
return Int32.Parse(new string(charArray));
else
return 0;
}
Now BlogEngine.NET will put CSS and javascript files in the order I choose, whether bundled or not.