Home | Blog | Screencasts | Projects
# Saturday, June 06, 2009

A lot of functionality can be added to CRM with JavaScript, if your like me and do the bulk of your normal web development JavaScript with a toolkit like JQuery, you will quickly miss the power and ease of doing things without it. The good news is that it is pretty easy to load JQuery into the page:

 

image

 

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '/ISV/jquery.js';
script.onreadystatechange = function()
{
if(this.readyState == "complete" || this.readyState == "loaded")
   setupPage();
};
 
var head = document.getElementsByTagName('head')[0];
 
head.appendChild(script);

 

Now you can use the setupPage() function with all your familiar JQuery goodness.

Saturday, June 06, 2009 9:12:47 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | CRM | JQuery
# Sunday, April 19, 2009

A little while ago, I was working with a customer who wanted to have the ability to write some annotations on a graph that was presented in a SharePoint site. The idea was that the graph was quite complex and it would help the end user understand it more if it was annotated by a domain expert.

So I put together a little proof of concept web part that makes use of all that JQuery goodness. I figured that it didn’t matter if you were annotating complex graphs, or tagging someone’s face like the way flickr and facebook does, the concept was the same:

 

image

 

image

 

image

 

So like I said the code is proof of concept, not production ready, but I thought I’d post it here anyway, if only to inspire ideas.

I’ve stored the data in a list with the schema of

 

Title:  default setting (single line of text)

JSON: single line of text

 

Or:

image

 

The data will look like this once some annotations have been added:

 

image

 

So I’ve just stored the JSON serialised object as is. The web part will iterate over each item on that list and pump that JSON into some JavaScript that is written to the page, so when the page load’s it will insatiate a JavaScript array of annotations and then render them. Which looks something like:

 

notes = [{ "x1": "11", "y1": "28", "height": "91", "width": "73", "note": "This is bill", "imgID": "0"}, { "x1": "86", "y1": "54", "height": "81", "width": "95", "note": "This is Kate", "imgID": "0"}];

 

You might ask why I have the imgID property, this is used if more than one image on the page is annotated.

 

To have a play around with the web part, you can download the WSP here, you just need to deploy the solution in the normal manner (stsadm –o addsolution –filename <path to httpcode.imageannotate.wsp>

 

Next step is to activate the Image Annotation web part feature on the site:

 

image

 

Add the web part as normal from the web part menu:

 

image

 

Once the web part is on the page, you need to set List setting to the SharePoint list that will store the JSON data (this list must conform to the schema above):

 

image

 

 

The client ID is the most technical part, this is a selector that gets used directly by JQuery, so any valid JQuery selector will work. A couple of examples:

This will find the default image on the homepage of a standard collabration portal:

img[src*='/PublishingImages/newsarticleimage.jpg']

 

All the images produced by a Dundas chart:

img[src*='SharePointChartAxd.axd']

 

Hopefully you get the idea.

 

I haven’t added any way to delete the annotation from the image itself, rather you need to go to the custom list and delete the item from there. The web part is smart enough to not allow users who can’t add items to the list. Credit should also go to odyniec.net for the imgAreaSelect JQuery plugin which I’ve made use of.

The web part injects lots of JavaScript into the page, so the code might look a little messy because of this, but the basic premise is pretty straight forward.

 

The source code can be found here. It includes a HttpHandler (ImageAnnotate.ashx) for handling all the ajax calls from the client side script. Remember this is just some sample code, use it at your own risk.

Sunday, April 19, 2009 6:49:54 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | JQuery | Sharepoint
# Sunday, March 08, 2009

I thought it might be fun to play around with the people search of MOSS. I wanted to have an auto complete like experience, where you could start typing someone’s name and a list of suggestions are displayed below:

 

image

 

I started by including JQuery and the Autocomplete JQuery plugin into a style library.

 

Next I created a Handler called PeopleSearch.ashx, I placed this in the _layouts directory (or 12 hive \TEMPLATE\LAYOUTS )

The code for this handler is simple:

 

   1: <%@ WebHandler Language="C#" Class="GenericHandler1" %>
   2: <%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   3: <%@ Assembly Name="Microsoft.Office.Server.Search, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   4: <%@ Assembly Name="Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   5:  
   6: using System;
   7: using System.Web;
   8: using System.Data;
   9: using Microsoft.SharePoint;
  10: using Microsoft.Office.Server.Search.Query;
  11:  
  12: public class GenericHandler1 : IHttpHandler {
  13:     
  14:     public void ProcessRequest (HttpContext context) {
  15:         context.Response.ContentType = "text/plain";
  16:     
  17:         string prefixText = context.Request["q"]; 
  18:         using (SPSite siteCollection = SPContext.Current.Site)
  19:         {
  20:             // create a new FullTextSqlQuery class
  21:             FullTextSqlQuery query = new FullTextSqlQuery(siteCollection);
  22:             query.QueryText = string.Format("SELECT Title FROM SCOPE() WHERE FREETEXT(defaultproperties, '{0}*') AND \"Scope\"='People' ", prefixText);
  23:             query.ResultTypes = ResultType.RelevantResults;
  24:             query.RowLimit = 10;
  25:  
  26:             // execute the query
  27:             ResultTableCollection queryResults = query.Execute();
  28:             ResultTable queryResultsTable = queryResults[ResultType.RelevantResults];
  29:                         
  30:     
  31:             while(queryResultsTable.Read()){
  32:                 context.Response.Write(queryResultsTable.GetString(0) +  Environment.NewLine);              
  33:             }    
  34:         }      
  35:         
  36:     }
  37:  
  38:     public bool IsReusable {
  39:         get {
  40:             return false;
  41:         }
  42:     }
  43:  
  44: }

It simply executes a full text query on the People Search Scope with the current text as the argument. Download the handler here.

 

Next I added a content Editor web part to the PeopleSearch.aspx page and included the following JavaScript (in the HTML source view):

 

   1: <script type="text/javascript" language="javascript" src="http://server/Style%20Library/jquery-1.2.6.min.js"/>
   1:  
   2: <script type="text/javascript" language="javascript" src="http://server/Style%20Library/jquery.autocomplete.min.js"/>
   3:  
   4: <script type="text/javascript">
   5:  
   6:     $(document).ready(function() {
   7:  
   8:         $("input[id*='_InputKeywords']").autocomplete("/_layouts/PeopleSearch.ashx");
   9:         $("input[id*='_InputKeywords']").attr("autocomplete","off");
  10:     
  11:     });
  12:  
</script>

 

The first thing I do is add a new attribute to prevent IE (or Firefox) from showing the previous search items. The next thing is to add the call the autocomplete plugin on the search textbox (which is found with because we know the id always ends with _InputKeywords).

 

I also added some styles from the autocomplete css to the css of the site.

 

It’s really that simple to get look ahead searching on people in MOSS. In fact this same approach could work on all the search pages.

Sunday, March 08, 2009 11:07:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [3] - Trackback
code | JQuery | MOSS
# Saturday, December 20, 2008

I’ve written a web part that can help you insert JavaScript into a SharePoint page. Currently there is nothing stopping you using a content editor web part, but it has a few limitations. First is the fact that the JavaScript doesn’t stand out, people may think that the content inside the editor is blank, when in fact it contains JavaScript.

By having a dedicated web part for JavaScript it becomes clearer that JavaScript lives on the page, also we can add a few features that make working with JavaScript a little easier.

 

image

I’ve made the chrome state set to None by default, so you won’t see the web part at all during normal render time (only design time).

 

The properties:

 

image

 

Page load JavaScript: This can be any JavaScript that you want to run when JQuery loads, that is any code you want to live inside of:

$(document).ready(function(){});

Something cool to try out (from EndUserSharePoint) try adding: $('#LeftNavigationAreaCell').toggle();  This will remove the left hand navigation.

 

Page level JavaScript: This is JavaScript that you just want to live on the page, it could be globally scoped variables or some functions that you have defined.

 

Script Includes: Each new line can be the URL to a JavaScript file to be included in the page, this is particularly useful for including JQuery plugins.

 

Use Google Libraries: Just a little novelty, it will use the Google Ajax API’s to load JQuery instead of the embedded JQuery resource.

 

You can have multiple web parts on the same page, the best bit about this is that all the code will be output into one place, so if you have one web part with some page load JavaScript that has say: alert(‘load’);  and anther that includes the left nav cell hide from above, the result in the page would be:

$(document).ready(function(){alert’load’); $('#LeftNavigationAreaCell').toggle();  });

 

After you deploy the solution, be sure to activate the feature under ‘site features’:

 

image

You can download the solution package from here.

Saturday, December 20, 2008 2:08:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
code | JQuery | Sharepoint
# Saturday, November 29, 2008

Have you seen the AJAX libraries API from google? They can be found here: http://code.google.com/apis/ajaxlibs/

What are they?

Basically its a content distribution network (CDN) for the most popular JavaScript libraries such as JQuery. It provides the officially released libraries in a gzipped form, served from a global CDN, so that your end users hit a server near them.

 

I feel like I’ve been living under a rock to have just discovered this service. It looks pretty easy to implement.

Saturday, November 29, 2008 2:10:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
JQuery

I came across an interesting scenario the other day where I needed to select some html elements with JQuery that had a class name like:

   1: <input type="radio" class="star star_id_45 star_group_5" />

I wanted to be able to select the elements that contained the class ‘star_id_45’, which as you can see is in the middle of the class string.

 

The answer was the following code:

 

   1: $("input[class*='star_id_45']")

 

Notice the use of the asterix (*), that was the key to getting the selector to work properly.

Saturday, November 29, 2008 1:56:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
JQuery
# Sunday, November 23, 2008

I ran across an interesting bug with the JQuery thickbox, I pointed it to a URL which was an ASP.NET ashx handler that generates thumbnail images, the result in the browser was this garbled response:

 

image

 

Using Firebug we see that it expects the result to be text/html

image

Looking at the code it is obvious what the problem is:

 

   1: var urlString = /\.jpg$|\.jpeg$|\.png$|\.gif$|\.bmp$/;
   2: var urlType = baseURL.toLowerCase().match(urlString);
   3:  
   4: if (urlType == '.jpg' || urlType == '.jpeg' || urlType == '.png' || urlType == '.gif' || urlType == '.bmp') {//code to show images

 

The code looks at the extension of what it is calling, if it finds any of the common image types, it will send a different request type, so adding the .ashx extension will fix the issue:

 

   1: var urlString = /\.jpg$|\.jpeg$|\.png$|\.ashx$|\.gif$|\.bmp$/;
   2: var urlType = baseURL.toLowerCase().match(urlString);
   3:  
   4: if (urlType == '.jpg' || urlType == '.jpeg' || urlType == '.ashx' || urlType == '.png' || urlType == '.gif' || urlType == '.bmp') {//code to show images

 

I’ve just simply added the .ashx as a file extension that should be treated as an image type, this fixed the issue.

Sunday, November 23, 2008 1:53:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | JQuery
# Saturday, September 27, 2008

I’ve just uploaded the code that I’ve been blogging about recently to CodePlex:

http://www.codeplex.com/JQueryWebParts/

This contains:

This is now the best place to continue development of these tools.

Saturday, September 27, 2008 2:04:00 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
JQuery | Sharepoint
# Monday, September 15, 2008

I found this fantastic post today that I just had to comment on:

 

In a SharePoint entry form the user can select multiple values from a list. This should include an “All” option so in a long list the user can select or de-select all values with one click.

 

The idea is to use JQuery to perform a select 'All' on all the checkboxes in a list.

DannyE posted this JavaScript:

 

$(”.ms-RadioText[title=’All’] :checkbox”).click(function(){
   var otherids = (this.id).substring(0, (this.id).length-2 );
   $(”input[id^=’”+otherids+”‘]”).attr( “checked” , (this.checked)?”checked”:”" );
});

 

It looks for all the items with the ms-RadioText class that also have a Title attribute that equals 'All', it adds a click handler that will then toggle the checked status of the checkbox, its extremely cool, way better than the latest block buster movie ...

 

Anyway, that's all well and good, I'm not adding much value reposting his stuff, so I'd just like to improve on his deployment recommendation.

Instead of adding a script reference to the master page and then inserting a content editor region to paste the JavaScript in, I would suggest using the JQuery Script Manager:

 

So the idea is to use SharePoint Designer to register the control with the JavaScript. Note here to use the ScriptTemplate you'll have to grab my latest script manager control from here.

<cc1:jQueryManager ID="JQueryManager1" runat="server">        
<ScriptTemplate>
$(".ms-RadioText[title='All'] :checkbox").click(function(){
 var otherids = (this.id).substring(0, (this.id).length-2 );
 $("input[id^='"+otherids+"']").attr( "checked" , (this.checked)?"checked":"" );});
</ScriptTemplate>
</cc1:jQueryManager>

This will insert the JQuery Script Manager and declaratively add the JavaScript code to the page. This way you don't need to worry about the script resources or having multiple client side load functions.

How can you not love JQuery after seeing the power of the above code? it's just that awesome!

Monday, September 15, 2008 10:47:30 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | JQuery | Sharepoint | Work
# Sunday, September 14, 2008

The web part that I'm presenting today is more of a building block, it doesn't do much on its own. It makes use of the JQuery Corners Plugin.

 

image

 

To set the corner types you can modify the Corner Type setting:

 

image

 

The above example will cause the following JavaScript to be output:

 

jQuery('cornerDiv').corner('tr 25px');
 

This web part makes use of the JQuery Script Manager that I've posted about previously.

 

As usual the source can be found here.

 

A screencast of the control in action can also be found here.

 

Sunday, September 14, 2008 9:02:49 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | JQuery | Screencast | Sharepoint | Work
# Wednesday, September 10, 2008

I've posted a couple of articles on JQuery and it's use in SharePoint and also provided some sample web parts. Recently I was putting together these web parts into a single project and I ran into a problem. Each web part was adding the JQuery script resources to the page as if it was the only control on the page, for example the ImageCarousel and the Tag Suggestion web parts both had code that needed to be run on the JQuery load event, this problem would also crop up when multiple instances of the same web part were added to the page. What was outputted to the page was something like:

 

$(document).ready(function(){ 
//function call for webpart1
}
$(document).ready(function(){ 
//function call for webpart2
}
$(document).ready(function(){ 
//function call for webpart2 instance 2
}
 

So I started developing a Script Manager control that I could add to the page that would output the JQuery load event just once, but with all the calls for the page inside this event. As with anything programming related these days, I found what I was looking for at http://codeplex.com/JQueryScriptManager/ I've changed a bit of the implementation, but the basic intent has remained the same. Hopefully I'll be able to get these changes updated to codeplex.

 

Looking at the revised code for the ImageCarousel web part:

 

jQueryManager jqueryManager = null;

protected override void OnInit(EventArgs e)
{
    
    jqueryManager = jQueryManager.GetCurrent(Page);
    if (jqueryManager == null)
    {
        jqueryManager = new jQueryManager();
        Page.Controls.Add(jqueryManager);
    }
    Page.ClientScript.RegisterClientScriptInclude("JCarousel", Page.ClientScript.GetWebResourceUrl(this.GetType(), 
        "JQueryWebParts.js.jquery.jcarousel.pack.js"));
    base.OnLoad(e);
}
 
 

Now inside the PageInit we check for the presence of a JQueryManager script control, if we don't find one, we add it to the controls collection. Just like the ASP.NET Ajax ScriptManager control, there can only be one per page.

 

Now to add code that is globally scoped which also resides in the same script block where our OnLoad event lives:

 

jqueryManager.RegisterScript("-- javascript code without <script> blocks");
//example:
jqueryManager.RegisterScript("var someItems = [5,4,3]");
            
 

To get javascript to run when the page is loaded (this will bundle calls into a single load event):

 

jqueryManager.ReadyFunctions.Add(new RegisterStartFunction("jQuery('#carousel').jcarousel();"));

Use the ReadyFunctions collection which takes a RegisterStartFunction object as a parameter.

 

Internally the JQuery script manager control is performing the following:

 

StringBuilder Start = new StringBuilder();

if (Scripts != null)
{
    foreach (RegisterScriptBlock r in Scripts)
        Start.Append(r.ScriptBlock + Environment.NewLine);
}

if (ReadyFunctions != null)
{
    Start.Append("$(document).ready(function(){");

    foreach (RegisterStartFunction r in ReadyFunctions)
        Start.Append(r.FunctionName + Environment.NewLine);

    Start.Append("});\n\n");
}

Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "JQuery", Start.ToString(), true);

 

The source code can be downloaded here. This includes the updated JQuery Script Manager and the ImageCarousel web part that I've previously posted about.

 

I think this approach will provide a nice platform to continue building web parts based on JQuery Plugins, have fun.

Wednesday, September 10, 2008 10:44:47 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | JQuery | Sharepoint | Work
# Thursday, September 04, 2008

Recently I posted about using JQuery in SharePoint web parts in this post I wrapped up the JCarousel plugin. Instead of requiring the JCarousel.js file to be included into the master page, I instead made the web part inject the HTML to cause the JavaScript file to be loaded.

The first step was to include the packed version of jquery.jcarousel.pack.js into my project:

 

image

 

Notice the Embedded Resource setting for the build action.

 

Now if we add the following code to our assembly.cs file:

 

[assembly: System.Web.UI.WebResource("SPImageCarousel.jquery.jcarousel.pack.js", "text/javascript")]

 

The above line now makes our embedded resource available for use in our web part which is done by the following code:

 

protected override void OnInit(EventArgs e)
{
    Page.ClientScript.RegisterClientScriptInclude("JCarousel", Page.ClientScript.GetWebResourceUrl(this.GetType(), "SPImageCarousel.jquery.jcarousel.pack.js"));
    base.OnLoad(e);
}

 

This code will ensure that the script is only added to the page once since the key "JCarousel" is the first parameter, remember that more than one web part can be added to a page at the same time, so we need to consider this as we develop our web parts. OnInit is the perfect event to call RegisterClientScriptInclude it's nice and early in the page lifecycle.

 

So hopefully you'll find this a nice and easy way to include your JavaScript resources, it does have the disadvantage of requiring a recompile to include a later version of the JavaScript file, so your mileage may vary if this is a concern.

 

Better still if your using .NET 3.5 Service Pack 1, you could make use of the CompsiteScript element of the script manager to combine the javascript files so the browser makes fewer calls to the server.

Thursday, September 04, 2008 11:52:17 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | JQuery | Sharepoint | Work
# Tuesday, September 02, 2008

I've mentioned before that I'm partial to JQuery, I consider it to be the Jessica Alba of JavaScript.

With all the work that has been done creating awesome JQuery based UI components, it seems like such a shame that us SharePoint developers aren't making more use of them.

So I present to you my first JQuery based web part, the Image Carousel:

image

Based on JCarousel, this web part presents the contents of a picture library in a carousel that can be scrolled by the navigation buttons.

 

The web part simply imports the jcarousel.js file, the web part then injects a JavaScript array of the images that reside in the selected picture library along with some javascript that initialises the carousel. The code to find the SharePoint picture library list is trivial:

 

SPSite site = SPContext.GetContext(HttpContext.Current).Site;
SPWeb web = site.OpenWeb();

foreach (SPList list in web.Lists)
{
    if (list.BaseTemplate == SPListTemplateType.PictureLibrary)
    {
        //grab the images from this list
        jsArray.Append(ProcessList(list));
    }
}

 

The web part makes use of custom toolpart for the selection of the picture list:

 

image

 

I think this provides a good template and starting point to start putting more of those JQuery plugins to use inside SharePoint

 

A small screencast of the control in action can be found here.

 

The end effect is some exceptional functionality for little development effort, which is really what JQuery is all about.

Some of the things that you need to think about when integrating:

  • Make sure that the injected div tag that will be used at the host has a unique id, remember that more than one webpart can be added to a page, if you hard code the html to have specific ID's, you will run into problems.
  • Make sure the JavaScript gets injected into the right part of the page, also just like the above point, ensure that each injected bits of code have a unique name, so that multiple webparts on the same page don't cause problems.
  • Use the packed version of the component, SharePoint is bloated enough already, use the smallest possible footprint you can.

 

The source code can be found here.

Tuesday, September 02, 2008 1:23:35 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | JQuery | Sharepoint | Work
# Sunday, August 17, 2008

JQuery is my all time favorite JavaScript library, it's so simple to use and incredibly powerful, but if you include all the plugins that are available, I just don't think that you would find a better tool for client side scripting.

My favorite validation plugin is the 'Validation Plugin', let me give you an example of how it is used:

Lets say you have a signup form, you want the user to select a user name, a password with confirmation and an email address, assume all of this is inside a form tag named 'signup'.

All of the HTML input element ids are 'Password', 'ConfirmPassword', 'Email'.

The JavaScript would be:

 

<script type="text/javascript">
$().ready(function() {
$("#signup").validate({
        rules: {            
            UserName: {
                required: true,
                minLength: 2,
                remote: "/Account/CheckUserName"
            },
            Password: {
                required: true,
                minLength: 2
            },
            ConfirmPassword: {
                required: true,
                minLength: 5,
                equalTo: "#Password"
            },
            Email:{
                required: true,
                email: true,
                remote: "/Account/CheckEmail"
            }
        },
        messages: {
            
            UserName: {
                required: "Please enter a username",
                minLength: "Your username must consist of at least 2 characters",
                remote: jQuery.format("{0} is already in use")
            },
            Password: {
                required: "Please enter a password",
                minLength: "Your password must consist of at least 2 characters"
            },
            ConfirmPassword: {
                required: "Please provide a password",
                minLength: "Your password must be at least 2 characters long",
                equalTo: "Please enter the same password as above"
            },
            Email: {
                required: "Please enter your email address",
                minLength: "Please enter a valid email address",
                remote: jQuery.format("{0} is already in use")
            }
        }
    });
});
</script>

 

Now notice how I used the remote property with '/Account/CheckEmail' and '/Account/CheckUserName', these are URL's that will be called by the client side JavaScript, they will pass along the current value of the textbox, so you could write server side code to validate the input, an ASP.NET MVC Controller Method could look like:

 

public JsonResult CheckUserName(string username)
{
    return Json(CheckValidUsername(username));
}

 

Notice the use of a JsonResult, The ASP.NET MVC framework is great for this type of thing, combine both JQuery and the MVC framework and you have a powerful combination, Jeff Atwood has come to the same conclusion and used both of these tools to build stackoverflow.com. 

All of the above code is so clean and easy to read, can you ever remember saying that about JavaScript code?

Sunday, August 17, 2008 3:56:40 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
ASP.NET MVC | code | JQuery | Work
Statistics
Total Posts: 190
This Year: 3
This Month: 0
This Week: 0
Comments: 38