Count me amongst the legions of jQuery fans. I might be a bit later to the party than others, but to my defence I really like writing server based code, so I do as much of that as possible. It’s only when faced with the need to ship code that the UI starts getting the attention it deserves.
Adding jQuery into DNN is an obvious choice, although made somewhat problematic with the standard Prototype library inclusion, and the fact that DNN modules are built with ASP User Controls rather than pages. You’ll see plenty of advice on the internet to either use the built-in DNN 5 jQuery inclusion method, or to just add the includes into the page where your jQuery code is. The first is problematic if you’re building for a broad range of DNN versions, the second is problematic if you want to distribute your code without having a long list of post-install steps to get it working. Another issue is that DNN modules can (and will) be copied to the page more than once, so your code has to be careful of including some sort of duplication check, so that you don't end up with 5 references to the jQuery library next time someone gets a little click-happy with the 'Add Module' button.
With that background, I set about developing a small routine which can be used with any DNN Version, which would provide version-independent, one-line inclusion of the jQuery libaries.
Programmatically Including JavaScript and CSS files in ASP.NET
The first thing to solve in this task is a simple inclusion of javascript includes in the header of the page. This is one of those things that you would think is really easy in ASP.NET, but unfortunately it can be a bit of a trick for new players. Most people start looking around the ClientScriptManager object, but in my opinion it's overblown and messy. You'll often see people using the ClientScriptManager to do the duplication check by adding in an empty bit of javascript - a brutal way to save effort. The easiest (and cleanest) way to do this is to create a specific html tag, and then actually add that to the header file, like this:
HtmlGenericControl scriptInclude = (HtmlGenericControl)page.Header.FindControl(id);
if (scriptInclude == null)
{
scriptInclude = new HtmlGenericControl("script");
scriptInclude.Attributes.Add("src", src);
scriptInclude.Attributes.Add("type", "text/javascript");
scriptInclude.ID = id;
page.Header.Controls.Add(scriptInclude);
}
This code looks for a specific html script include tag in the page header, and if not found, creates the tag and adds it in. Couldn't be simpler really - and no messy, empty <script/> tags lying around in your Html.
Determining the DotNetNuke Version
Why determine the DotNetNuke version? Well, because if you're using DNN 5 and better, then DNN has a built in function for including the jQuery libraries. No need to reinvent the wheel, so if this function is around, might as well use it. However, if you're like me and building modules for a massive variety in DNN versions, there's no telling if the DNN 5 method will exist or not. So you need to determine the DotNetNuke version.
You might head for the object browser and start looking for the version method or property. That's where more young players will dash themselves upon the rocky shores of DNN history. You see, somewhere along the line (about 4.9, I believe) the location of the version property actually moved within the object hierarchy. Thus, in order to determine the version, you first have to know the version to know where to look for it. Trying to use this circular logic will only lead to tears and torn-out tufts of hair.
The only avenue left is to bypass all the built-in methods and just go straight for the assembly version. That's always in the right spot, and thankfully, always correct. Here's my version-independent version checking code:
System.Version ver = System.Reflection.Assembly.GetAssembly(typeof(DotNetNuke.Common.Globals))
.GetName().Version;
if (ver != null)
{
major = ver.Major;
minor = ver.Minor;
build = ver.Build;
revision = ver.Revision;
return true;
}
else
{
major = 0; minor = 0; build = 0; revision = 0;
return false;
}
Incidentally, that method is so version-safe it can be used on any asp.net project, not just DNN. Incidentally, just because you've determined the version, you're not out of the woods yet. Because you can't just compile against a method in a later version library and expect it to all work with an earlier version. No, instead we must fall back to the all-purpose tool of the .NET developers toolbox : reflection:
Type jQueryType = Type.GetType("DotNetNuke.Framework.jQuery, DotNetNuke");
if (jQueryType != null)
{
//run the DNN 5.0 specific jQuery registration code
jQueryType.InvokeMember("RequestRegistration",
System.Reflection.BindingFlags.Static, null, jQueryType, null);
}
This code will run the DNN jQuery 'RequestRegistration' method if it's available (ie, installed in a DNN 5 environment), but will still compile against earlier versions of DNN libraries. For more information on this call, see Joe Brinkman's informative post on jQuery and DotNetNuke 5
Including the jQuery Libaries
So, equipped with code to work out what version we're using, and armed with code to insert in a javascript include file, there's nothing left to do but stitch it all together. Just one last thing : Google, in their infinite bandwidth and hey-we'll-get-your-data generosity, provide a hosted version of the jQuery libraries. I've long ago given up on trying to hide anything from the big G, so I'm happy to just mooch off their site and use the hosted version. Thus the example code contains references to the Google libraries. If you're of a more suspicious and distrusting nature, then by all means include the jQuery library with your app and change the code.
Full Code Listing
/// <summary>
/// Includes the jQuery libraries onto the page
/// </summary>
/// <param name="page" />Page object from calling page/control</param>
/// <param name="includejQueryUI" />if true, includes the jQuery UI libraries</param>
/// <param name="uncompressed" />if true, includes the uncompressed libraries</param>
/// <param name="includeNoConflict" />if true, includes the uncompressed libraries</param>
internal static void InjectjQueryLibary(System.Web.UI.Page page, bool includejQueryUI,
bool uncompressed, bool includeNoConflict)
{
int major, minor, build, revision;
bool injectLib = false;
if (SafeDNNVersion(out major, out minor, out revision, out build))
{
switch (major)
{
case 5:
//todo: all versions of 5?
injectLib = false;
break;
default:
injectLib = true;
break;
}
}
else
injectLib = true;
if (injectLib)
{
//no in-built jQuery libraries into the framework, so include the google version
string lib = null;
if (uncompressed)
lib = "http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js";
else
lib = "http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js";
if (page.Header.FindControl("jquery") == null)
{
System.Web.UI.HtmlControls.HtmlGenericControl jQueryLib
= new System.Web.UI.HtmlControls.HtmlGenericControl("script");
jQueryLib.Attributes.Add("src", lib);
jQueryLib.Attributes.Add("type", "text/javascript");
jQueryLib.ID = "jquery";
page.Header.Controls.Add(jQueryLib);
if (includeNoConflict)
{
// use the noConflict (stops use of $) due to the use of prototype
// with a standard DNN distro
System.Web.UI.HtmlControls.HtmlGenericControl noConflictScript =
new System.Web.UI.HtmlControls.HtmlGenericControl("script");
noConflictScript.InnerText = " jQuery.noConflict(); ";
noConflictScript.Attributes.Add("type", "text/javascript");
page.Header.Controls.Add(noConflictScript);
}
}
}
else
{
//call DotNetNuke.Framework.jQuery.RequestRegistration();
Type jQueryType = Type.GetType("DotNetNuke.Framework.jQuery, DotNetNuke");
if (jQueryType != null)
{
//run the DNN 5.0 specific jQuery registration code
jQueryType.InvokeMember("RequestRegistration",
System.Reflection.BindingFlags.Static, null, jQueryType, null);
}
}
//include the UI libraries??
if (includejQueryUI)
{
string lib = null;
if (uncompressed)
lib = "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.js";
else
lib = "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js";
page.ClientScript.RegisterClientScriptInclude("jqueryUI", lib);
}
}
/// <summary>
/// Helper function to insert a given javascript file into the page header
/// </summary>
/// <param name="page" />Current Page object</param>
/// <param name="id" />Unique (for the page) id of the file</param>
/// <param name="src" />Qualified location of the script file</param>
internal static void IncludeScriptFile(Page page, string id, string src)
{
HtmlGenericControl scriptInclude = (HtmlGenericControl)page.Header.FindControl(id);
if (scriptInclude == null)
{
scriptInclude = new HtmlGenericControl("script");
scriptInclude.Attributes.Add("src", src);
scriptInclude.Attributes.Add("type", "text/javascript");
scriptInclude.ID = id;
page.Header.Controls.Add(scriptInclude);
}
}
/// <summary>
/// Helper function to insert a given CSS file into the page header
/// </summary>
/// <param name="page" />Current Page Object</param>
/// <param name="id" />Unique (for the page) id of the file</param>
/// <param name="href" />Qualified location of the CSS file</param>
internal static void IncludeCSSFile(Page page, string id,string href)
{
HtmlLink cssLink = (HtmlLink)page.Header.FindControl(id);
if (cssLink == null)
{
cssLink = new HtmlLink();
cssLink.Href = href;
cssLink.Attributes.Add("rel", "stylesheet");
cssLink.Attributes.Add("type", "text/css");
cssLink.ID = id;
page.Header.Controls.Add(cssLink);
}
}
/// <summary>
/// Returns a version-safe set of version numbers for DNN
/// </summary>
/// <param name="major" />out value of major version (ie, 4,5 etc)</param>
/// <param name="minor" />out value of minor version (ie 1 in 5.1 etc)</param>
/// <param name="revision" />out value of revision (ie 3 in 5.1.3)</param>
/// <param name="build" />out value of build number</param>
/// <remarks>Dnn moved the version number during about the 4.9 version,
/// which to me was a bit frustrating and caused the need for this reflection method call</remarks>
/// <returns>true if successful, false if not</returns>
internal static bool SafeDNNVersion(out int major, out int minor, out int revision, out int build)
{
System.Version ver = System.Reflection.Assembly.GetAssembly(
typeof(DotNetNuke.Common.Globals)).GetName().Version;
if (ver != null)
{
major = ver.Major;
minor = ver.Minor;
build = ver.Build;
revision = ver.Revision;
return true;
}
else
{
major = 0; minor = 0; build = 0; revision = 0;
return false;
}
}
Code Licensing on this snippet
You're free to take the code on this page and use it how you will, commercial or non-commercial, attributed or not. By all means link back and admit where you pinched it from, or you can try and be sneaky and pass it off as your own work to appear productive to your boss. It's not going to worry me. Just don't try and get me to fix your site if something is wrong! Oh, and as always, please don’t ask for a VB version : there are oodles of converters out there.
Using the jQuery Include Code in a DNN Module
You’ll see that I have included a couple of options in the main method. These are:
- includejQueryUI : this is for including the standard jQueryUI library from google. Note that this occurs even if you’re using DNN 5 or better.
- uncompressed: when set to true, the jQuery code will be downloaded in the uncompressed format. If ‘false’, then the jQuery code will be downloaded in ‘minified’ format. Minified decreases the download payload and speeds up page load time, but you wouldn’t try and debug it.
- includeNoConflict : when set to true, the jQuery.noConflict() call will be included. This can be used to stop the ‘$’ namespace clash between the prototype code library in DNN and jQuery. Personally, I just write all my jQuery code using jQuery(‘’) instead of $(‘’)
Here’s how you would use this code. Let’s assume you keep it in a static class called ‘IncludeFunctions’. In the Page_Load event of your DNN Module User Control, this is the code to include:
IncludeFunctions.InjectjQueryLibary(Page, false, false, false);
That’s it! The code will automagically do all the rest for you.
A further recommendation is to use a conditional compile so that your debug code gets the un-minified version of the jQuery library (while ensuring your RELEASE version uses the minified version:
#if (DEBUG)
IncludeFunctions.InjectjQueryLibary(Page, false, true, false);
#else
IncludeFunctions.InjectjQueryLibary(Page, false, false, false);
#endif
Bonus Code : Including CSS Files
If you're an eagle-eyed reader and actually looked through the code before copy/pasting with abandon, you'll notice that there is a method called 'IncludeCSSFile' in the code. This isn't actually used anywhere in the code to include the jQuery library, but generally with any bit of jQuery code you might care to include with your module, there's generally a couple of CSS files along for the ride. This method allows to to use the same 'no duplicate' principle and include these css files in the Html header, right where they are meant to go. Just use the code like this (again, assuming in the Page_Load event of a DNN Module User Control):
IncludeFunctions.IncludeCSSFile(this.Page, "my-css-file", this.ModulePath + "css/my-css-file.css");
The above code will include the 'my-css-file.css' file (located in the /desktopModules/your-module-name/css path) into the page header.
Further, if you're writing jQuery, chances are you're following the design principle of unobtrusive javascript, so aren't going to include a big chunk of messy script code in the middle of your html. Are You? So you can use the include script function in the code in the same way:
IncludeFunctions.IncludeScriptFile(this.Page, "my-js-file", this.ModulePath + "js/my-js-file.js");
This works in the same way and gives a nice, clean way of including the script files into your code
Note that you might have to remove some line breaks in the main code listing to get the code to work.
If you’ve had success, or find a bug, please let me know via the comments.