
There's been a lot of buzz lately about Microsoft's decision to adopt the open jQuery javascript library into it's Visual Studio development platform and it's getting .Net web developers very excited, including the Blackbaud NetCommunity development and Interactive design teams. In this article I'm going to show you how you can begin using jQuery with NetCommunity today to add some zing to your web site.
"Microsoft is looking to make jQuery part of their official development platform. Their JavaScript offering today includes the ASP.NET Ajax Framework and they’re looking to expand it with the use of jQuery. This means that jQuery will be distributed with Visual Studio (which will include jQuery intellisense, snippets, examples, and documentation).
[Microsoft isn't] looking to make any modifications to jQuery (both in the form of code or licensing) - they simply wish to promote its use as-is. They’ve recognized its position as the most popular JavaScript library and wish to see its growth and popularity continue to flourish."
Excerpt from
http://jquery.com/blog/2008/09/28/jquery-microsoft-nokia/ The announcement came in a blog post by Scott Guthrie, a vice president in Microsoft's developer division, who described the library's attraction:
"A big part of the appeal of jQuery is that it allows you to elegantly (and efficiently) find and manipulate HTML elements with minimum lines of code. jQuery supports this via a nice "selector" API that allows developers to query for HTML elements, and then apply "commands" to them. One of the characteristics of jQuery commands is that they can be "chained" together - so that the result of one command can feed into another. jQuery also includes a built-in set of animation APIs that can be used as commands. The combination allows you to do some really cool things with only a few keystrokes."
The jQueryScript Custom Part
The download for this article is a cool custom part that makes adding jQuery scripting to any NetCommunity page a snap. When placed on a page or template the part automatically attaches links to the jQuery and jQuery UI script libraries to the NetCommunity page. In addition, the part editor provides a textarea for creating and editing the jQuery script you want spit onto the page. It also provides a slick way to attach jQuery themes (CSS and images that you can create using the
jQuery ThemeRoller) to your page, so that you have complete control over the style of the jQuery UI elements you chose to use, such as popup dialogs, tabs, and accordians.
What would I do with jQuery?
This is the fun part.
jQuery has a very elegant syntax for locating elements on a web page and allowing you to manipulate them in countless ways. You can add new elements, remove, move, hide, show, and restyle existing ones. You can also add interesting effects, like sliding or fading in and out. jQuery also provides a very clean model for hooking into the events of any page element, such as the click, hover, resize, and focus events.
That's great Mike, can we see an example?
Example: A Better Weblog Display
One practical use I found for it was to reskin the NetCommunity Weblog part. The Labs website uses weblog parts throughout as a means to store, display, and RSS feed most of its content. We have a weblog part for each major area of the site, for now that's NetCommunity, Infinty, and Other Stuff. The weblogs are used primarily as a table of contents for each of the articles on the site (which are actually Article Custom Parts - we'll cover those in another post). Ideally, the weblog stories are short abstracts of the full article, but even so the weblog part can get very long on a page very quickly as it renders the content of every story one after the other. It is very difficult to tell where one story ends and another begins.
There are two approaches to solving this basic problem with the built-in weblog part.
The first solution you can see on the
Labs home page, where we've built a new custom part that consumes each feed and renders just the titles as links to the actual articles. This is cool in itself and we'll share that code and blog about that another day, but it doesn't use jQuery so that brings us to the second approach.
With the second approach, using a little jQuery script we can completely manipulate the default layout and behavior of the weblog part to render something totally different. Check out these two very different version of the same page:
See how the first version renders all of the blog stories in a continuous page as long as your leg?
Now, see how the second version reduces the blog content down to just the story titles and as you click on each one it opens the story in-place in a pretty window, automatically closing the previous open story, providing a close button, and injecting a read more link into the bottom of the reading pane.
That's cool.
Once you have the jQueryScript custom part installed you can use techiniques like this all over your site to change the default look, content, and behavior of any parts and pages using simple jQuery, without having to build specific custom parts. So let's take a look at how its done.
Using the jQueryScript Custom Part
To have your script execute when the page loads you start with something like this:
$(document).ready(function(){
// your script here
});
This function will execute on each page load and is where you hook in your script to monkey with the page.
At the heart of jQuery is the idea of selecting something on the page and messing with it. You can select an element by its tagname, its DOM id, its class name, its location, and several other ways. To create the blog script on Labs, I used Firefox and Firebug to inspect the classnames that are slapped on the various elements of the weblog part. I then selected the various elements of the blog output and rearranged a few things, and inserted others.
Here is my entire script (the download has a text version of it for your consumption)
var lastOpenStory=null;
var lastOpenStoryContainer=null;
$(document).ready(function(){
ToggleNews();
});
function ToggleNews (){
$(".NewsChannelItemDesc").hide();
$(".NewsChannelItemDate").hide();
$(".NewsChannelItemTitle").prepend("<img src='document.doc?id=193' style='margin-right:5px;' />");
$(".ChannelStoryEditButtonWAI").parent().parent().toggle();
$(".NewsChannelItemPending").removeClass("taLeft").css("text-align","right");
$(".NewsChannelItem").css("margin-top","3px").attr("open", "0");
$(".NewsChannelItem").click(function(e){
if ($(this).attr("open") == "0") {
e.preventDefault();
}
CloseStory();
//mark clicked story as open
$(this).attr("open", "1");
lastOpenStoryContainer = $(this);
lastOpenStory = $(this).find(".NewsChannelItemDesc");
//show story
$(lastOpenStory).toggle();
// hilite the title row
$(this).find(".NewsChannelItemTitle").parent().css("background-color", "#e5f6d1");
// style the story
$(this).css("border", "1pt solid #e5f6d1").css("padding", "8px");
//add close button
$(this).find(".NewsChannelItemPending").append("<img src='document.doc?id=192' id='btnStoryClose' />");
$("#btnStoryClose").click(function(e){CloseStory();});
//show edit/delete buttons
$(this).find(".ChannelStoryEditButtonWAI").parent().parent().toggle();
//add more link
var href= $(this).find(".NewsChannelItemTitle").find("a").attr("href");
if (href){
$(this).append("<tbody id='divStoryMoreLink'><tr><td colspan='2' style='text-align:left;'><a id='anchorStoryMoreLink' href='about:blank'>Read the full story</a></td></tr></tbody>")
$("#anchorStoryMoreLink").attr("href", href);
$("#divStoryMoreLink").find("td").css("border-bottom","3px solid #e5f6d1");
}
});
function CloseStory(){
if (lastOpenStory){
//close last story and clean it up
$(lastOpenStory).toggle();
$(lastOpenStoryContainer).attr("open", "0");
$(lastOpenStoryContainer).css("border", "0").css("padding", "0px");
$(lastOpenStoryContainer).find(".NewsChannelItemTitle").parent().css("background-color", "#fff");
$(lastOpenStoryContainer).find(".NewsChannelItemPending").find("#btnStoryClose").remove();
$(lastOpenStoryContainer).find(".ChannelStoryEditButtonWAI").parent().parent().toggle();
$("#divStoryMoreLink").remove();
}
lastOpenStory=null;
lastOpenStoryContainer=null;
}
//add mouse over effects to stories
$(".NewsChannelItem").hover(
function(){
$(this).css("cursor", "pointer");},
function(){
$(this).css("cursor", "default");
}
);
}
As the page loads I'm hiding every story's content by selecting and hiding every element with the NewsChannelItemDesc classname:
$(".NewsChannelItemDesc").hide();
since this is the class assigned to each story's content. I then assign a click handler to each story which will toggle the display of the story content. This sample also shows some other cool things you can do, like adding addtional elements to the page. See how each story title gets a little orange document image placed in front of it:
$(".NewsChannelItemTitle").prepend("<img src='document.doc?id=193' style='margin-right:5px;' />");
and maybe this is a nice seque into how the part also helps you
theme your jQuery content, in this case the little document image you see is from a jQuery theme I built with the
jQuery Themeroller.
Themes and the NetCommunity Document API
Since jQuery is very much about document partying it makes sense that it has a rich set of methods that allow you to go nuts with CSS. jQuery also has a very nice set of UI components such as Tabs, Accordians, and Dialogs, all of which can be themed using predefined CSS classes. Typical with these types of UI elements is the need to reference images in the CSS. If you play with the ThemeRoller site you'll see that it lets you build your own themes then generates images for you with the colors you chose and creates a CSS style sheet that uses the images. These images are typcially used for styling the jQuery UI controls for button backgrounds, resize handles, toggle glyphs, and so on.
When you've built your theme, it then lets you download it as a zip file. The zip contains the CSS file and all the associated images. So how can we easily use that on our NetCommunity site? Why the Document Library API of course!
The doument libarary APIs expose access to the document storage and retrieval engine within NetCommunity, the same model used by the Document Parts. Documents of any kind may be stored in NetCommunity and retrieved by a URL (e.g.
http://labs.blackbaud.com/netcommunity/document.doc?id=14) or in code, using the API. The API supports adding, replacing, deleting, listing, and retrieving documents in a given library.
You can have multiple libraries on your site, and libraries are created by creating a Document Part. Each Document Part has it's own library, and each libary has its own role-based security rules - who can upload, access, etc.
The Document Part is the default presentation of a libary on your site, and it is rather basic and visually inflexible, but through the API you can get creative and present document libaries and even use documents in new and more interesting ways.
Documents are stored in the database and their access and retrieval is very well optimized, they employ caching on the web server to save trips to the database and they play very nicely in the browser caching model to save on uneeded downloads, just as the image library does. Documents can be of practically any mime type, meaning they can be just about any kind of file, such as Microsoft Offie docs, CSS files, jScript files, PDF, XML files, image files, video, or just plain text files, etc. The NetCommunity document model, is essentially a virtual file system.
So How Does this Help Our jQuery Story?
To add support for using jQuery themes we need the ability to store and reference the theme's CSS file and the images it references. One approach would be to copy and paste the theme CSS into a standard NetCommunity stylesheet and upload the images into the image library. We would then need to modify the CSS so that the image url() references it contains would point to the uploaded image library files:
background: url(images/272626_40x100_textures_03_highlight_soft_20.png)
would need to become something like:
background: url(view.image?id=272)
Needless to say it would take a while to do this with the 50 or so images that exist in a jQuery theme. So here's a more convenient approach that the jQuery Custom part takes by using a document library to hold the CSS file and related images:
- Upload the theme zip file created on jQuery ThemeRoller site
- Crack the zip file using the Zip routines in ICSharpCode.SharpZipLib which ships with NetCommunity
- Add each image file into the Document Library of the users chosing
- Update the CSS file to use image Urls that reference the document.doc?id=x Urls for each added image
- Add the modified CSS file to the selected Document Libary
In the jQuery Custom Part's display control, we attach the CSS file to the page using:
Dim Url As String = Me.ResolveUrl(Me.API.Navigation.GetDocumentURL(id))
Me.API.Pages.CurrentPage.AddStylesheet(Url)
For the user, who in this case is the jQuery author/site designer, they just need to create a Document Part to hold the CSS file and images, and have at it. The custom part supports adding as many themes/images as you like, and chosing which one to use at display time. So one library could hold all your themes and each unique instance of the jQuery part on different pages could reference its theme of choice.
And of course once you've uploaded theme images into a document library your free to use them yourself in other content, which is what I did in my sample jQuery script above, to add the little orange document image

(which I'm also using here!).
You could make the case that if the image library had similar methods in the API for add, edit, delete it would be a better place to keep the images for consistency sake. Maybe one day it will, but for now document libraries work very well and under the hood are implemented identically to the image library so there is no performance penalty. You could also make the case that its nice to keep the images and CSS together and not overload the image gallery. I need a good rationalization every day.
Embedded Resources
So the jQuery part allows you to create page and template level jQuery script that executes on page load, tap into jQuery themes, and monkey at will with the page containing the part and all of the parts on it. But what it you wanted to use jQuery in your custom part's editor control? Our model so far has been to put our themes into a document library, but I do that step in the editor, so I have chicken-egg problem if I want to theme my editor.
For this I use ASP.Net embedded resources. I used
ThemeRoller to build the theme I want to use in my editor control, then added all the generated images to my project, marking each as an embedded resource. I then took the CSS file from themeRoller and modified all of the image url references to use WebResource urls to get the images as embedded resources, so:
background: url(images/272626_40x100_textures_03_highlight_soft_20.png)
now becomes:
background: url(<%=WebResource("272626_40x100_textures_03_highlight_soft_20.png")%>)
I'm not going to get into the details of using embedded resources, since there is plenty of help on that subject available online.
Since converting 50 plus image references in to WebResource links is as tedious as adding them to the image library, I created a WinForm utility ThemeRollerEmbedder available in the solution in the download that will take a ThemeRoller zip file, crack out the images, and create a new CSS file with the WebResource urls. It also creates the <assembly: webresource > attribute needed for each image in a seperate file you can add to your project. Run the utility on your themeRoller file, put the images, css file, and assembly attribute file in your project and be sure to mark the css and image files as embedded resources and your good to go.
Take a look at the code-behind for how the embedded resources for the CSS, and jQuery script files are used. Pretty cool way to deploy dependant files in your custom parts.
That's if for now. Have fun with jQuery, I think maybe now I'll use it to build a nicer Document Part display.