<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>JavaScript</title>
        <link>http://www.ridgway.co.za/category/22.aspx</link>
        <description>Posts related to JavaScript development techniques and tools.</description>
        <language>en-ZA</language>
        <copyright>Eden Ridgway</copyright>
        <managingEditor>eden@ridgway.co.za</managingEditor>
        <generator>Subtext Version 1.9.5.176</generator>
        <item>
            <title>Setting up an Optimised Deployment Version of your AJAX ASP.Net Application</title>
            <link>http://ridgway.co.za/archive/2008/01/28/setting-up-an-optimised-deployment-version-of-your-ajax-asp.net.aspx</link>
            <description>&lt;p&gt;With rich AJAX web applications one needs to look at optimising resources such as your CSS, JavaScript and image files. Yahoo has collected together a great &lt;a href="http://developer.yahoo.com/performance/rules.html"&gt;collection of best practices&lt;/a&gt; that have been put into their &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; Firefox extension. These should be taken as a guideline and not gospel. In general however the core principals are as follows:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;strong&gt;Keep server roundtrips to a minimum&lt;/strong&gt;. The techniques for the various resource types are as follows:
    &lt;ul&gt;
        &lt;li&gt;&lt;em&gt;JavaScript&lt;/em&gt; - combine them into a single file if possible. One has to make certain they are combined in the correct order in the file. &lt;/li&gt;
        &lt;li&gt;&lt;em&gt;CSS&lt;/em&gt; - again merge them as far as possible. &lt;/li&gt;
        &lt;li&gt;&lt;em&gt;Images&lt;/em&gt; - Use &lt;a href="http://www.alistapart.com/articles/sprites"&gt;CSS sprites&lt;/a&gt; where one combines several images into one and uses the background image offset to display the one you are after. To create the sprites you can always use this online sprite generator. &lt;/li&gt;
    &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Reduce the size of each request&lt;/strong&gt;. This is done in two ways:
    &lt;ul&gt;
        &lt;li&gt;&lt;em&gt;GZip/Deflate&lt;/em&gt; compress text resources by configuring IIS and/or using an ASP.Net HTTP Module. &lt;/li&gt;
        &lt;li&gt;&lt;em&gt;Reduce file sizes&lt;/em&gt;. The tools I use for each type are:
        &lt;ul&gt;
            &lt;li&gt;JavaScript - &lt;a href="http://www.raboof.com/projects/Jazmin/"&gt;Jazmin&lt;/a&gt; provides pretty good minification by removing whitespace. If you want better minification then you should take a look at the Dojo &lt;a href="http://dojotoolkit.org/docs/shrinksafe"&gt;ShrinkSafe&lt;/a&gt; compressor which will shorten the name of local variables and functions to reduce the file size even more. The downside to this is it makes troubleshooting a production issue more difficult. &lt;/li&gt;
            &lt;li&gt;CSS - &lt;a href="http://csstidy.sourceforge.net/index.php"&gt;CSSTidy&lt;/a&gt; not only reduces whitespace but optimises the CSS expressions as well. &lt;/li&gt;
            &lt;li&gt;Images - As PNGs are my image format of choice I use &lt;a href="http://brh.numbera.com/software/pnggauntlet/"&gt;PNGGauntlet&lt;/a&gt; to reduce the image file size. &lt;/li&gt;
        &lt;/ul&gt;
        &lt;/li&gt;
    &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Ensure resources are cached&lt;/strong&gt;. If you know that the files are going to change infrequently then all you have to do is set IIS to enable content expiration for a folder/file to expire after a set number of days, say 10. This will ensure that the client browser caches the resources and does not re-download them. However depending on the frequency of your deployments and your server architecture you most likely want to ensure that your etags are setup correctly. These are used to indicate whether or not the cached browser resources have changed. When loading a page the browser will send a request to the server, with the etag, for each resource and if it has not changed then the server will only return a 304 status code. Note that this advice is contrary to Yahoo where they tell you to remove etags for a web farm scenario. What you should rather do is ensure that the etag change number is the same for each server in the web farm. This ensures that the etag generated for the same resource on a different server is the same (how to do this varies depending on your version of IIS - read this &lt;a href="http://www.lowtek.com/blog/2006/09/save-web-server-bandwidth-by-fixing.html"&gt;post&lt;/a&gt; for more IIS6 information). If you are using a single web server however the whole this is a non-issue for you. &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Okay so we know we want to combine and minify our CSS and JavaScript files when we deploy to our test, staging and production environments, but we don't want to develop and debug our applications with monolithic files with no whitespace and cryptic names. The problem is that our pages contain references to these files and we are merge them them together for deployment our references will now be incorrect. Well this is quite easily solved using compiler directives, like so:&lt;/p&gt;
&lt;div class="code"&gt;&lt;span style="background-color: rgb(255, 255, 153);"&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;%&lt;/font&gt;&lt;font color="#ff0000"&gt; #if DEBUG %&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;      &lt;br /&gt;
    &lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#ff0000"&gt; type&lt;/font&gt;&lt;font color="#0000ff"&gt;="text/javascript"&lt;/font&gt;&lt;font color="#ff0000"&gt; src&lt;/font&gt;&lt;font color="#0000ff"&gt;='&amp;lt;%= ResolveUrl("~/JavaScript/File1.js") %&amp;gt;'&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;/&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;      &lt;br /&gt;
    &lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#ff0000"&gt; type&lt;/font&gt;&lt;font color="#0000ff"&gt;="text/javascript"&lt;/font&gt;&lt;font color="#ff0000"&gt; src&lt;/font&gt;&lt;font color="#0000ff"&gt;='&amp;lt;%= ResolveUrl("~/JavaScript/File2.js") %&amp;gt;'&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;/&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;      &lt;br /&gt;
    &lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#ff0000"&gt; type&lt;/font&gt;&lt;font color="#0000ff"&gt;="text/javascript"&lt;/font&gt;&lt;font color="#ff0000"&gt; src&lt;/font&gt;&lt;font color="#0000ff"&gt;='&amp;lt;%= ResolveUrl("~/JavaScript/File3.js") %&amp;gt;'&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;/&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;      &lt;br /&gt;
&lt;/font&gt;&lt;span style="background-color: rgb(255, 255, 153);"&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;%&lt;/font&gt;&lt;font color="#ff0000"&gt; #else %&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;      &lt;br /&gt;
    &lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#ff0000"&gt; type&lt;/font&gt;&lt;font color="#0000ff"&gt;="text/javascript"&lt;/font&gt;&lt;font color="#ff0000"&gt; src&lt;/font&gt;&lt;font color="#0000ff"&gt;='&amp;lt;%= ResolveUrl("~/JavaScript/Section.minified.js") %&amp;gt;'&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;/&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;      &lt;br /&gt;
&lt;/font&gt;&lt;span style="background-color: rgb(255, 255, 153);"&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;%&lt;/font&gt;&lt;font color="#ff0000"&gt; #endif %&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&lt;/font&gt; &lt;/div&gt;
&lt;p&gt;For CSS files you can either use the directives around alternative &amp;lt;link&amp;gt; elements or around @import statements, like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;style&lt;/font&gt;&lt;font color="#ff0000"&gt; type&lt;/font&gt;&lt;font color="#0000ff"&gt;="text/css"&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;font color="#800000"&gt;      &lt;br /&gt;
&lt;/font&gt;&lt;font color="#0000ff"&gt;&lt;span style="background-color: rgb(255, 255, 153);"&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;%&lt;/font&gt;&lt;font color="#ff0000"&gt; #if DEBUG %&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font color="#800000"&gt;      &lt;br /&gt;
    &lt;/font&gt;&lt;font color="#0000ff"&gt;@&lt;/font&gt;&lt;font color="#800000"&gt;import '&amp;lt;%&lt;/font&gt;&lt;font color="#0000ff"&gt;=&lt;/font&gt;&lt;font color="#800000"&gt; ResolveUrl&lt;/font&gt;&lt;font color="#0000ff"&gt;(&lt;/font&gt;&lt;font color="#800000"&gt;"~&lt;/font&gt;&lt;font color="#0000ff"&gt;/&lt;/font&gt;&lt;font color="#800000"&gt;Css&lt;/font&gt;&lt;font color="#0000ff"&gt;/&lt;/font&gt;&lt;font color="#800000"&gt;File1&lt;/font&gt;&lt;font color="#0000ff"&gt;.&lt;/font&gt;&lt;font color="#800000"&gt;css"&lt;/font&gt;&lt;font color="#0000ff"&gt;)&lt;/font&gt;&lt;font color="#800000"&gt; %&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#800000"&gt;';      &lt;br /&gt;
    &lt;/font&gt;&lt;font color="#0000ff"&gt;@&lt;/font&gt;&lt;font color="#800000"&gt;import '&amp;lt;%&lt;/font&gt;&lt;font color="#0000ff"&gt;=&lt;/font&gt;&lt;font color="#800000"&gt; ResolveUrl&lt;/font&gt;&lt;font color="#0000ff"&gt;(&lt;/font&gt;&lt;font color="#800000"&gt;"~&lt;/font&gt;&lt;font color="#0000ff"&gt;/&lt;/font&gt;&lt;font color="#800000"&gt;Css&lt;/font&gt;&lt;font color="#0000ff"&gt;/&lt;/font&gt;&lt;font color="#800000"&gt;File2&lt;/font&gt;&lt;font color="#0000ff"&gt;.&lt;/font&gt;&lt;font color="#800000"&gt;css"&lt;/font&gt;&lt;font color="#0000ff"&gt;)&lt;/font&gt;&lt;font color="#800000"&gt; %&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#800000"&gt;';      &lt;br /&gt;
&lt;/font&gt;&lt;font color="#0000ff"&gt;&lt;span style="background-color: rgb(255, 255, 153);"&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;%&lt;/font&gt;&lt;font color="#ff0000"&gt; #else %&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font color="#800000"&gt;      &lt;br /&gt;
    &lt;/font&gt;&lt;font color="#0000ff"&gt;@&lt;/font&gt;&lt;font color="#800000"&gt;import '&amp;lt;%&lt;/font&gt;&lt;font color="#0000ff"&gt;=&lt;/font&gt;&lt;font color="#800000"&gt; ResolveUrl&lt;/font&gt;&lt;font color="#0000ff"&gt;(&lt;/font&gt;&lt;font color="#800000"&gt;"~&lt;/font&gt;&lt;font color="#0000ff"&gt;/&lt;/font&gt;&lt;font color="#800000"&gt;Css&lt;/font&gt;&lt;font color="#0000ff"&gt;/&lt;/font&gt;&lt;font color="#800000"&gt;CombinedStyles&lt;/font&gt;&lt;font color="#0000ff"&gt;.&lt;/font&gt;&lt;font color="#800000"&gt;minified&lt;/font&gt;&lt;font color="#0000ff"&gt;.&lt;/font&gt;&lt;font color="#800000"&gt;css"&lt;/font&gt;&lt;font color="#0000ff"&gt;)&lt;/font&gt;&lt;font color="#800000"&gt; %&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#800000"&gt;';      &lt;br /&gt;
&lt;/font&gt;&lt;font color="#0000ff"&gt;&lt;span style="background-color: rgb(255, 255, 153);"&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;%&lt;/font&gt;&lt;font color="#ff0000"&gt; #endif %&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&lt;/font&gt;       &lt;br /&gt;
&amp;lt;/&lt;/font&gt;&lt;font color="#800000"&gt;style&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt; &lt;/div&gt;
&lt;p&gt;There is a catch to this however, since the aspx pages are compiled separately, setting your application to compile as a release build will not cause the references to change. What you need to do is change the compilation setting in the application web.config file, which should be set to debug="false" in the production environment:&lt;/p&gt;
&lt;div class="code"&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;compilation&lt;/font&gt;&lt;font color="#ff0000"&gt; debug&lt;/font&gt;&lt;font color="#0000ff"&gt;="false"&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;      &lt;br /&gt;
  ...       &lt;br /&gt;
&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;/&lt;/font&gt;&lt;font color="#800000"&gt;compilation&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt; &lt;/div&gt;
&lt;p&gt;Now on to the merging and minification. This could be accomplished using MsBuild or a batch file, but a batch file is simpler, so I'll discuss that. On one of the build steps for the web site(s) invoke the batch file which then ensures that the optimised files are always up to date. So you want to add something like this to your pre/post build compilation step in the project property window:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://ridgway.co.za/images/ridgway_co_za/WindowsLiveWriter/SettingupanOptimisedDeplo.NetApplication_E3AF/image_4.png"&gt;&lt;img width="504" height="196" border="0" src="http://ridgway.co.za/images/ridgway_co_za/WindowsLiveWriter/SettingupanOptimisedDeplo.NetApplication_E3AF/image_thumb_1.png" alt="image" style="border-width: 0px;" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;I setup the "Build Scripts" folder to contain the optimisation programs as well as text files with lists of files to merge, for example:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://ridgway.co.za/images/ridgway_co_za/WindowsLiveWriter/SettingupanOptimisedDeplo.NetApplication_E3AF/image_12.png"&gt;&lt;img width="164" height="105" border="0" src="http://ridgway.co.za/images/ridgway_co_za/WindowsLiveWriter/SettingupanOptimisedDeplo.NetApplication_E3AF/image_thumb_5.png" alt="image" style="border-width: 0px;" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;You'll notice that I group the JavaScript files on the site by Section of the site and not by page. The granularity of file grouping really depends on the application, but by section or function is a pretty good starting point. Each line of the file will contain a file name, like this:&lt;/p&gt;
&lt;p&gt; SubDir\File1.js&lt;br /&gt;
SubDir\File2.js &lt;/p&gt;
&lt;p&gt;I don't need to specify the full relative path of the files, in respect to the build folder because when I develop web sites all JavaScript files are placed within a JavaScript folder off the root of the web site.&lt;/p&gt;
&lt;p&gt;In my simplified scenario the "Optimise Web Resources.bat" batch file contains the following:&lt;/p&gt;
&lt;div class="code"&gt;
&lt;pre style="background: rgb(255, 255, 255) none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);"&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;set&lt;/span&gt; OLDDIR&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;%CD%&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(105, 105, 105);"&gt;:: Change to the current directory&lt;/span&gt;&lt;span style="font-family: mon;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;cd&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;/&lt;/span&gt;d &lt;span style="color: rgb(0, 140, 0);"&gt;%0&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;\&lt;/span&gt;.. &lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(105, 105, 105);"&gt;::Ensure there are no old temporary files lying around&lt;/span&gt; &lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;del&lt;/span&gt; *.js&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;del&lt;/span&gt; *.css&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(105, 105, 105);"&gt;::Create a single file for the JavaScript in the admin section&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;for&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;/&lt;/span&gt;F &lt;span style="color: rgb(0, 0, 230);"&gt;"tokens=*"&lt;/span&gt; %%f &lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;in&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;AdminJsFiles.txt&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;do&lt;/span&gt; &lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;call&lt;/span&gt; &lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;type&lt;/span&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;"..\Web Sites\Site\JavaScript\%%f"&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;&amp;gt;&lt;/span&gt; AdminCombined.js&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;type&lt;/span&gt; AdminCombined.js &lt;span style="color: rgb(128, 128, 48);"&gt;|&lt;/span&gt; jazmin &lt;span style="color: rgb(128, 128, 48);"&gt;&amp;gt;&lt;/span&gt; AdminCombined.minified.js&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;copy&lt;/span&gt; AdminCombined.minified.js &lt;span style="color: rgb(0, 0, 230);"&gt;"..\Web Sites\Site\JavaScript\AdminCombined.minified.js"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(105, 105, 105);"&gt;::Combine the Stylesheets into a single file&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;for&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;/&lt;/span&gt;F &lt;span style="color: rgb(0, 0, 230);"&gt;"tokens=*"&lt;/span&gt; %%f &lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;in&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;(&lt;/span&gt;CssFilesToMerge.txt&lt;span style="color: rgb(128, 128, 48);"&gt;)&lt;/span&gt; &lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;do&lt;/span&gt; &lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;call&lt;/span&gt; &lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;type&lt;/span&gt; &lt;span style="color: rgb(0, 0, 230);"&gt;"..\Web Sites\Site\CSS\%%f"&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;&amp;gt;&lt;/span&gt; CombinedStyles.css&lt;br /&gt;csstidy.exe CombinedStyles.css &lt;span style="color: rgb(128, 128, 48);"&gt;-&lt;/span&gt;&lt;span style="color: rgb(128, 128, 48);"&gt;-&lt;/span&gt;template&lt;span style="color: rgb(128, 128, 48);"&gt;=&lt;/span&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;high&lt;/span&gt; CombinedStyles.minified.css&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;copy&lt;/span&gt; CombinedStyles.minified.css &lt;span style="color: rgb(0, 0, 230);"&gt;"..\Web Sites\Site\CSS\CombinedStyles.minified.css"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(105, 105, 105);"&gt;::Remove the temporary files&lt;/span&gt;&lt;span style="font-family: mon;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;del&lt;/span&gt; *.js&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;del&lt;/span&gt; *.css&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(105, 105, 105);"&gt;::Restore the old "current directory"&lt;/span&gt; &lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(128, 0, 0);"&gt;chdir&lt;/span&gt; &lt;span style="color: rgb(128, 128, 48);"&gt;/&lt;/span&gt;d %OLDDIR%&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The one part of the batch file that confuses most people is the FOR /F "token=*" statement. That reads in a line at a time from the file specified in brackets and puts it in the %%f variable. The contents of each file is then appended into a single file using a "type SourceFile &amp;gt;&amp;gt; DestinationFile" statement. In the case of the JavaScript files they are then piped through the Jazmin compressor/minifier, the output of which is redirected in an output file, i.e. AdminCombined.minified.js. With the CSS files, the command line syntax is a little different, but the outcome is the same.&lt;/p&gt;
&lt;p&gt;Hopefully the rest of the batch file is pretty self evident and there is enough information here to get you started on implementing the approach yourself. If there are areas that need more explanation please leave a comment on the blog post.&lt;/p&gt;&lt;img src="http://ridgway.co.za/aggbug/193.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eden Ridgway</dc:creator>
            <guid>http://ridgway.co.za/archive/2008/01/28/setting-up-an-optimised-deployment-version-of-your-ajax-asp.net.aspx</guid>
            <pubDate>Mon, 28 Jan 2008 04:02:57 GMT</pubDate>
            <comments>http://ridgway.co.za/archive/2008/01/28/setting-up-an-optimised-deployment-version-of-your-ajax-asp.net.aspx#feedback</comments>
            <wfw:commentRss>http://ridgway.co.za/comments/commentRss/193.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Simple JavaScript Undo Manager for DTOs</title>
            <link>http://ridgway.co.za/archive/2007/11/07/simple-javascript-undo-manager-for-dtos.aspx</link>
            <description>When looking at implementing undo functionality, two common patterns that are used are the &lt;a href="http://www.dofactory.com/Patterns/PatternMemento.aspx"&gt;Memento&lt;/a&gt; or &lt;a href="http://www.dofactory.com/Patterns/PatternCommand.aspx"&gt;Command&lt;/a&gt; design patterns. For an example of using the Memento pattern on JavaScript objects take a look at Lawrence Carvalho's &lt;a href="http://www.nodetraveller.com/blog/javascript/actasundo/"&gt;ActAsUndoable post&lt;/a&gt;. This approach however did not suite the needs of the application I'm currently working on because the changes I'm making are on &lt;a href="http://martinfowler.com/eaaCatalog/dataTransferObject.html"&gt;Data Transfer Objects&lt;/a&gt; (DTOs) that have been serialized from the server using ASP.Net web services. As such I don't want to add any functionality to them and I don't really care about breaking encapsulation because all object values are public. I take these client side objects and "databind" (logically speaking) them to various UI elements, such as tree nodes and form fields. What I want to be able to offer however is the ability to cancel out changes without going back to the server to fetch the original data. I also want any undone changes to apply to the original objects and therefore simply cloning the objects by serializing and deserializing them using ASP.Net JSON serialization methods is not an option (it would make the object to UI element association management a pain). If however you don't care about preserving object references then this is a perfect solution for you, and can be done like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="blue"&gt;function &lt;/font&gt;&lt;font color="black"&gt;cloneObjectGraph(targetObject)&lt;br /&gt;
{&lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;jsonSerializedData &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;Sys.Serialization.JavaScriptSerializer.serialize(targetObject)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    return &lt;/font&gt;&lt;font color="black"&gt;Sys.Serialization.JavaScriptSerializer.deserialize(jsonSerializedData)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;}&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
So what I have created is a very simple pair of objects, UndoManager and UndoItem, that are ideally suited to taking a snapshot of state on simple JavaScript object graph, where all the values you care about are "public", and then being able to rollback the object graph to the snapshot point while preserving the original object references. If you want functionality such as the ability store state of more complex objects that have getters/setters and "private" variables (as is present with the &lt;a href="http://yuiblog.com/blog/2007/06/12/module-pattern/"&gt;Module pattern&lt;/a&gt; and others) then don't use these objects, rather take a look at the ActAsUndoable memento approach. Also if you want to implement an "action by action" undo/redo interface like word then rather look at using the command pattern.&lt;br /&gt;
&lt;br /&gt;
So getting on to UndoManager and UndoItem objects. As you can see from the diagram below, they are quite straightforward. The UndoManager exists to allow you to simply have a stack of changes that you can undo on a last in last out basis. The UndoItem object handles the storing of a single undo operation on an object graph.&lt;br /&gt;
&lt;div style="text-align: center;"&gt;&lt;a href="http://www.ridgway.co.za/images/ridgway_co_za/UndoManagerClassDiagram(1).png"&gt;&lt;img border="0" src="/images/ridgway_co_za/UndoManagerClassDiagram(1).png" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;
The way UndoItem takes a snapshot of an objects state is to "iterate" over all the object attributes using a for..in loop and copy any simple type to the _undoState variable. Any complex object it encounters, such as an object, date or array, it creates another UndoItem for that and adds it to _undoState in the attributes place. Hence it builds up a hierarchy of Undo Items it can call on when undo is called. When undoing the changes it uses the _objectReference it stored when constructing the UndoItem to copy the values back from the _undoState on to the original object.&lt;br /&gt;
&lt;br /&gt;
This gives one a really simple and unobtrusive way of capturing and restoring state. Say you have a person object that you want to save to save the state for, make changes and then undo them, you use an UndoItem to do it in the following way:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;person &lt;/font&gt;&lt;font color="blue"&gt;= &lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;{ &lt;br /&gt;
    IsActive: &lt;/font&gt;&lt;font color="blue"&gt;true&lt;/font&gt;&lt;font color="black"&gt;,&lt;br /&gt;
    Team: { Name: &lt;/font&gt;&lt;font color="#808080"&gt;'Admin' &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
}&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
var &lt;/font&gt;&lt;font color="black"&gt;undoItem &lt;/font&gt;&lt;font color="blue"&gt;= new &lt;/font&gt;&lt;font color="black"&gt;UndoItem(person)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;person.IsActive &lt;/font&gt;&lt;font color="blue"&gt;= false;&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;person.Team.Name &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="#808080"&gt;'General User'&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;undoItem.undo()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
If you want to manage a stack of changes then you use the UndoManager like so:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;team &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;{ Name: &lt;/font&gt;&lt;font color="#808080"&gt;'Admin' &lt;/font&gt;&lt;font color="black"&gt;}&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
var &lt;/font&gt;&lt;font color="black"&gt;person &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;{ Name: &lt;/font&gt;&lt;font color="#808080"&gt;'Joe' &lt;/font&gt;&lt;font color="black"&gt;}&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
var &lt;/font&gt;&lt;font color="black"&gt;undoManager &lt;/font&gt;&lt;font color="blue"&gt;= new &lt;/font&gt;&lt;font color="black"&gt;UndoManager()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;undoManager.saveUndoPoint(team)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;undoManager.saveUndoPoint(person)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;team.Name &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="#808080"&gt;'General User'&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;person.Name &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="#808080"&gt;'Bob'&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="darkgreen"&gt;//Will undo the person name change&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;undoManager.undo()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="darkgreen"&gt;//Will undo the team name change&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;undoManager.undo()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
If you are keen to use the objects  then you can &lt;a target="_blank" href="http://www.ridgway.co.za/demos/UndoManager.zip"&gt;download it here&lt;/a&gt; (the code is not minified). I have also created a set of Scriptaculous unit tests that you can &lt;a target="_blank" href="http://www.ridgway.co.za/demos/UndoManager/UndoManagerTests.htm"&gt;run here&lt;/a&gt;.&lt;img src="http://ridgway.co.za/aggbug/189.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eden Ridgway</dc:creator>
            <guid>http://ridgway.co.za/archive/2007/11/07/simple-javascript-undo-manager-for-dtos.aspx</guid>
            <pubDate>Wed, 07 Nov 2007 06:47:10 GMT</pubDate>
            <comments>http://ridgway.co.za/archive/2007/11/07/simple-javascript-undo-manager-for-dtos.aspx#feedback</comments>
            <wfw:commentRss>http://ridgway.co.za/comments/commentRss/189.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Using Synchronous ASP.Net AJAX Web Service Calls and Scriptaculous to Test your JavaScript</title>
            <link>http://ridgway.co.za/archive/2007/10/30/using-synchronous-asp.net-ajax-web-service-calls-and-scriptaculous-to.aspx</link>
            <description>As our web applications become more JavaScript heavy it is becomes increasingly important that we have tests for this code. In the past I have managed to get away with it but in retrospect it was a risky approach to take. So in my latest project I started assessing the various JavaScript unit testing frameworks and decided &lt;a href="http://wiki.script.aculo.us/scriptaculous/show/UnitTesting" target="_blank"&gt;Scriptaculous&lt;/a&gt; was the way to go. I excluded &lt;a href="http://www.jsunit.net/" target="_blank"&gt;JsUnit&lt;/a&gt; as an option because it is not object oriented and didn't give me enough information about which tests had passed. The Scriptaculous unit testing framework also has some useful features like the ability to wait before completing the test and benchmarking of calls.&lt;br /&gt;
&lt;br /&gt;
The one big problem you have when testing an AJAX application however is that the out-of-band calls are made asynchronously. This means that if you are testing a method that does a load and want to ensure that certain values are set or calls made once the data has loaded you have to have a bit of a problem. The Scriptaculous framework's solution to this  is the ability to wait and execute a function that may then contain your asserts. Here is a basic example of what the test would look like:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="blue"&gt;new &lt;/font&gt;&lt;font color="black"&gt;Test.Unit.Runner(&lt;br /&gt;
{&lt;br /&gt;
    testShouldLoadDataOnInit: &lt;/font&gt;&lt;font color="blue"&gt;function&lt;/font&gt;&lt;font color="black"&gt;() &lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;manager &lt;/font&gt;&lt;font color="blue"&gt;= new &lt;/font&gt;&lt;font color="black"&gt;TestManager()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//This makes a web service call&lt;/font&gt;&lt;br /&gt;
&lt;font color="blue"&gt;         &lt;/font&gt;&lt;font color="black"&gt;manager.onInit()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
        &lt;br /&gt;
        this&lt;/font&gt;&lt;font color="black"&gt;.wait(&lt;/font&gt;&lt;font color="maroon"&gt;500&lt;/font&gt;&lt;font color="black"&gt;, &lt;/font&gt;&lt;font color="blue"&gt;function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
                       {&lt;br /&gt;
                          &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.assertNotEqual(manager._TestData, &lt;/font&gt;&lt;font color="blue"&gt;null&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
                       &lt;/font&gt;&lt;font color="black"&gt;})&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;},&lt;br /&gt;
})&lt;/font&gt;&lt;font color="blue"&gt;;&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
I found that this led to inconsistent test results because for various reasons the webservice request callbacks would take a varying amount of time. Of course you could try to cover yourself by making the wait time larger but then your tests take too long to run. Both of these problems will discourage others from using and extending your tests.&lt;br /&gt;
&lt;br /&gt;
Having hand coded XMLHTTP calls in the days before the approach was known as AJAX I knew that it was possible to make the calls synchronously, so all I needed to do was get the generated ASP.Net AJAX webservice  proxies to use a new synchronous  executor. I came across &lt;a href="http://geekswithblogs.net/rashid/archive/2007/07/04/SJAX-Call.aspx" target="_blank"&gt;Amit's SJAX post&lt;/a&gt; that demonstrated how to created a synchronous executor for a Sys.Net.WebRequest object which was very useful. I discovered that one could change the executor for generated webservice proxy calls by changing the default executor type for the Sys.Net.WebRequestManager, like so:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="black"&gt;Sys.Net.WebRequestManager.set_defaultExecutorType(&lt;/font&gt;&lt;font color="#808080"&gt;"Example.Executor"&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
So I took Amit's example, changed it somewhat and got it to work work as a synchronous request handler (using the debug version of the ASP.Net AJAX framework JavaScript as a guide). This resulted in a handler that looked like this (note I have removed code and replaced them with comments for brevity) [&lt;a href="http://www.ridgway.co.za/demos/XMLHttpSyncExecutor.zip"&gt;download full file here&lt;/a&gt;]:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="black"&gt;Type.registerNamespace(&lt;/font&gt;&lt;font color="#808080"&gt;'Sjax'&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;Sjax.XMLHttpSyncExecutor &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
{&lt;br /&gt;
    Sjax.XMLHttpSyncExecutor.initializeBase(&lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
    this&lt;/font&gt;&lt;font color="black"&gt;._started &lt;/font&gt;&lt;font color="blue"&gt;= false;&lt;br /&gt;
    this&lt;/font&gt;&lt;font color="black"&gt;._responseAvailable &lt;/font&gt;&lt;font color="blue"&gt;= false;&lt;br /&gt;
    this&lt;/font&gt;&lt;font color="black"&gt;._onReceiveHandler &lt;/font&gt;&lt;font color="blue"&gt;= null;&lt;br /&gt;
    this&lt;/font&gt;&lt;font color="black"&gt;._xmlHttpRequest &lt;/font&gt;&lt;font color="blue"&gt;= null;&lt;br /&gt;
    &lt;br /&gt;
    this&lt;/font&gt;&lt;font color="black"&gt;.get_aborted &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//Parameter validation code removed here...&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;return false;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
&lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.get_responseAvailable &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//Parameter validation code removed here...&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;return this&lt;/font&gt;&lt;font color="black"&gt;._responseAvailable&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
&lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.get_responseData &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//Parameter validation code removed here...&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;return this&lt;/font&gt;&lt;font color="black"&gt;._xmlHttpRequest.responseText&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
&lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.get_started &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//Parameter validation code removed here...&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;return this&lt;/font&gt;&lt;font color="black"&gt;._started&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
&lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.get_statusCode &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//Parameter validation code removed here...&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;return this&lt;/font&gt;&lt;font color="black"&gt;._xmlHttpRequest.status&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
&lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.get_statusText &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//Parameter validation code removed here...&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;return this&lt;/font&gt;&lt;font color="black"&gt;._xmlHttpRequest.statusText&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
&lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.get_xml &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//Code removed&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
&lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.executeRequest &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//Parameter validation code removed here...&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;webRequest &lt;/font&gt;&lt;font color="blue"&gt;= this&lt;/font&gt;&lt;font color="black"&gt;.get_webRequest()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
        if &lt;/font&gt;&lt;font color="black"&gt;(webRequest &lt;/font&gt;&lt;font color="blue"&gt;=== null&lt;/font&gt;&lt;font color="black"&gt;)&lt;br /&gt;
        {&lt;br /&gt;
            &lt;/font&gt;&lt;font color="blue"&gt;throw &lt;/font&gt;&lt;font color="black"&gt;Error.invalidOperation(Sys.Res.nullWebRequest)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
        &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;body &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;webRequest.get_body()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
        var &lt;/font&gt;&lt;font color="black"&gt;headers &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;webRequest.get_headers()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
        var &lt;/font&gt;&lt;font color="black"&gt;verb &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;webRequest.get_httpVerb()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
        var &lt;/font&gt;&lt;font color="black"&gt;xmlHttpRequest &lt;/font&gt;&lt;font color="blue"&gt;= new &lt;/font&gt;&lt;font color="black"&gt;XMLHttpRequest()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
        this&lt;/font&gt;&lt;font color="black"&gt;._onReceiveHandler &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;Function.createCallback(&lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;._onReadyStateChange, { sender:&lt;/font&gt;&lt;font color="blue"&gt;this &lt;/font&gt;&lt;font color="black"&gt;})&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
        this&lt;/font&gt;&lt;font color="black"&gt;._started &lt;/font&gt;&lt;font color="blue"&gt;= true;&lt;br /&gt;
        &lt;/font&gt;&lt;font color="black"&gt;xmlHttpRequest.onreadystatechange &lt;/font&gt;&lt;font color="blue"&gt;= this&lt;/font&gt;&lt;font color="black"&gt;._onReceiveHandler&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
        &lt;/font&gt;&lt;font color="black"&gt;xmlHttpRequest.open(verb, webRequest.getResolvedUrl(), &lt;/font&gt;&lt;font color="blue"&gt;false&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;; &lt;/font&gt;&lt;font color="darkgreen"&gt;// False to call Synchronously&lt;br /&gt;
&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;if &lt;/font&gt;&lt;font color="black"&gt;(headers)&lt;br /&gt;
        {&lt;br /&gt;
            &lt;/font&gt;&lt;font color="blue"&gt;for &lt;/font&gt;&lt;font color="black"&gt;(&lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;header &lt;/font&gt;&lt;font color="blue"&gt;in &lt;/font&gt;&lt;font color="black"&gt;headers)&lt;br /&gt;
            {&lt;br /&gt;
                &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;val &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;headers[header]&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
                if &lt;/font&gt;&lt;font color="black"&gt;(&lt;/font&gt;&lt;font color="blue"&gt;typeof&lt;/font&gt;&lt;font color="black"&gt;(val) !&lt;/font&gt;&lt;font color="blue"&gt;== &lt;/font&gt;&lt;font color="#808080"&gt;"function"&lt;/font&gt;&lt;font color="black"&gt;)&lt;br /&gt;
                {&lt;br /&gt;
                    xmlHttpRequest.setRequestHeader(header, val)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
                &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;if &lt;/font&gt;&lt;font color="black"&gt;(verb.toLowerCase() &lt;/font&gt;&lt;font color="blue"&gt;=== &lt;/font&gt;&lt;font color="#808080"&gt;"post"&lt;/font&gt;&lt;font color="black"&gt;)&lt;br /&gt;
        {&lt;br /&gt;
            &lt;/font&gt;&lt;font color="blue"&gt;if &lt;/font&gt;&lt;font color="black"&gt;((headers &lt;/font&gt;&lt;font color="blue"&gt;=== null&lt;/font&gt;&lt;font color="black"&gt;) || !headers[&lt;/font&gt;&lt;font color="#808080"&gt;'Content-Type'&lt;/font&gt;&lt;font color="black"&gt;])&lt;br /&gt;
            {&lt;br /&gt;
                xmlHttpRequest.setRequestHeader(&lt;/font&gt;&lt;font color="#808080"&gt;'Content-Type'&lt;/font&gt;&lt;font color="black"&gt;, &lt;/font&gt;&lt;font color="#808080"&gt;'application/x-www-form-urlencoded'&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
            &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
&lt;br /&gt;
            &lt;/font&gt;&lt;font color="blue"&gt;if &lt;/font&gt;&lt;font color="black"&gt;(!body)&lt;br /&gt;
            {&lt;br /&gt;
                body &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="#808080"&gt;''&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
            &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;._started &lt;/font&gt;&lt;font color="blue"&gt;= true;&lt;br /&gt;
        this&lt;/font&gt;&lt;font color="black"&gt;._xmlHttpRequest &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;xmlHttpRequest&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
        &lt;/font&gt;&lt;font color="black"&gt;xmlHttpRequest.send(body)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
&lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.getAllResponseHeaders &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;()&lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//Parameter validation code removed here...&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;return this&lt;/font&gt;&lt;font color="black"&gt;._xmlHttpRequest.getAllResponseHeaders()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
    &lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.getResponseHeader &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;(header)&lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="darkgreen"&gt;//Parameter validation code removed here...&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;return this&lt;/font&gt;&lt;font color="black"&gt;._xmlHttpRequest.getResponseHeader(header)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
    &lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;._onReadyStateChange &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;(e)&lt;br /&gt;
    {  &lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;executor &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;e.sender&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
            &lt;br /&gt;
        if &lt;/font&gt;&lt;font color="black"&gt;(executor._xmlHttpRequest.readyState &lt;/font&gt;&lt;font color="blue"&gt;=== &lt;/font&gt;&lt;font color="maroon"&gt;4&lt;/font&gt;&lt;font color="black"&gt;)&lt;br /&gt;
        {&lt;br /&gt;
            &lt;/font&gt;&lt;font color="darkgreen"&gt;//Validation code removed here...&lt;br /&gt;
&lt;br /&gt;
            &lt;/font&gt;&lt;font color="black"&gt;executor._responseAvailable &lt;/font&gt;&lt;font color="blue"&gt;= true;&lt;br /&gt;
&lt;br /&gt;
            &lt;/font&gt;&lt;font color="black"&gt;executor._xmlHttpRequest.onreadystatechange &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;Function.emptyMethod&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
            &lt;/font&gt;&lt;font color="black"&gt;executor._onReceiveHandler &lt;/font&gt;&lt;font color="blue"&gt;= null;&lt;br /&gt;
&lt;br /&gt;
            &lt;/font&gt;&lt;font color="black"&gt;executor._started &lt;/font&gt;&lt;font color="blue"&gt;= false;&lt;br /&gt;
            &lt;br /&gt;
            var &lt;/font&gt;&lt;font color="black"&gt;webRequest &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;executor.get_webRequest()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
            &lt;/font&gt;&lt;font color="black"&gt;webRequest.completed(Sys.EventArgs.Empty)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
            &lt;br /&gt;
            &lt;/font&gt;&lt;font color="darkgreen"&gt;//Once the completed callback handler has processed the data it needs from the XML HTTP request we can clean up&lt;br /&gt;
            &lt;/font&gt;&lt;font color="black"&gt;executor._xmlHttpRequest &lt;/font&gt;&lt;font color="blue"&gt;= null;&lt;br /&gt;
        &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Sjax.XMLHttpSyncExecutor.registerClass(&lt;/font&gt;&lt;font color="#808080"&gt;'Sjax.XMLHttpSyncExecutor'&lt;/font&gt;&lt;font color="black"&gt;, Sys.Net.WebRequestExecutor)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
So now we can create tests that test objects that make synchronous webservice calls by changing the default executor in the setup of the unit test like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="blue"&gt;new &lt;/font&gt;&lt;font color="black"&gt;Test.Unit.Runner(&lt;br /&gt;
{&lt;br /&gt;
    setup: &lt;/font&gt;&lt;font color="blue"&gt;function&lt;/font&gt;&lt;font color="black"&gt;() &lt;br /&gt;
    { &lt;br /&gt;
        Sys.Net.WebRequestManager.set_defaultExecutorType(&lt;/font&gt;&lt;font color="#808080"&gt;"Sjax.XMLHttpSyncExecutor"&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;},&lt;br /&gt;
&lt;br /&gt;
    testShouldLoadDataOnInit: &lt;/font&gt;&lt;font color="blue"&gt;function&lt;/font&gt;&lt;font color="black"&gt;() &lt;br /&gt;
    {&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;manager &lt;/font&gt;&lt;font color="blue"&gt;= new &lt;/font&gt;&lt;font color="black"&gt;TestManager()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
        &lt;/font&gt;&lt;font color="black"&gt;manager.onInit()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
        &lt;br /&gt;
        this&lt;/font&gt;&lt;font color="black"&gt;.assertNotEqual(manager._TestData, &lt;/font&gt;&lt;font color="blue"&gt;null&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;},&lt;br /&gt;
})&lt;/font&gt;&lt;font color="blue"&gt;;&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
I think you'll agree that with the extensibility they have provided in the ASP.Net AJAX framework, what we have now is quite a neat way of ensuring that our tests run consistently and as fast as they can. My thanks go out to Microsoft and the Scriptaculous team! :)&lt;img src="http://ridgway.co.za/aggbug/188.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eden Ridgway</dc:creator>
            <guid>http://ridgway.co.za/archive/2007/10/30/using-synchronous-asp.net-ajax-web-service-calls-and-scriptaculous-to.aspx</guid>
            <pubDate>Tue, 30 Oct 2007 06:17:07 GMT</pubDate>
            <comments>http://ridgway.co.za/archive/2007/10/30/using-synchronous-asp.net-ajax-web-service-calls-and-scriptaculous-to.aspx#feedback</comments>
            <wfw:commentRss>http://ridgway.co.za/comments/commentRss/188.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Using ASP.Net AJAX WebServices (ScriptServices) in ExtJS</title>
            <link>http://ridgway.co.za/archive/2007/10/09/using-asp.net-ajax-webservices-scriptservices-in-extjs.aspx</link>
            <description>I've only just started investigating &lt;a href="http://extjs.com/"&gt;ExtJS&lt;/a&gt; and what it can possibly provide over and above the ASP.Net AJAX framework. The first thing I wanted to do was use the ASP.Net AJAX JavaScript webservice proxies instead of the expected REST style services. Unfortunately I ran into several problems and ended up using a slightly different approach to what I found on the &lt;a href="http://extjs.com/forum" target="_blank"&gt;ExtJS forums&lt;/a&gt;. Let me quickly outline the problem. In ExtJS they use a store wrapper for their data which is assigned to a grid panel. This store object may also know how to fetch external data that it needs via a proxy, which is defined like so:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;ds &lt;/font&gt;&lt;font color="blue"&gt;= new &lt;/font&gt;&lt;font color="black"&gt;Ext.data.Store({&lt;br /&gt;
    proxy: &lt;/font&gt;&lt;font color="blue"&gt;new &lt;/font&gt;&lt;font color="black"&gt;Ext.data.HttpProxy(&lt;br /&gt;
         {&lt;br /&gt;
          url: &lt;/font&gt;&lt;font color="#808080"&gt;'/WebServices/OrderService.asmx/GetOrders'&lt;/font&gt;&lt;font color="black"&gt;,&lt;br /&gt;
          params: { userId: getUserId(), day: &lt;/font&gt;&lt;font color="blue"&gt;new Date&lt;/font&gt;&lt;font color="black"&gt;() }&lt;br /&gt;
         }),&lt;br /&gt;
    reader: &lt;/font&gt;&lt;font color="blue"&gt;new &lt;/font&gt;&lt;font color="black"&gt;Ext.data.JsonReader(&lt;br /&gt;
        {&lt;br /&gt;
           id: &lt;/font&gt;&lt;font color="#808080"&gt;'WorkDoneId'&lt;br /&gt;
        &lt;/font&gt;&lt;font color="black"&gt;},&lt;br /&gt;
    ...&lt;br /&gt;
})&lt;/font&gt;&lt;font color="blue"&gt;;&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
Unfortunately, try as I might to get this to call my web service (and I tried quite a few different approaches), I would get errors returned by the service. It may have been that unlike all the examples I had seen on the web, like &lt;a target="_blank" href="http://daniellarson.spaces.live.com/blog/cns!D3543C5837291E93!966.entry"&gt;Daniel Larson's&lt;/a&gt; my service calls actually needed to send parameters through to the service. One was to add additional properties to the ScriptMethod attribute on the web service like so:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="black"&gt;[ScriptMethod(ResponseFormat &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;ResponseFormat.Json, UseHttpGet &lt;/font&gt;&lt;font color="blue"&gt;= true&lt;/font&gt;&lt;font color="black"&gt;, XmlSerializeString &lt;/font&gt;&lt;font color="blue"&gt;= false&lt;/font&gt;&lt;font color="black"&gt;)]&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
I also tried using the ASPProxy class found on &lt;a href="http://extjs.com/forum/showthread.php?t=8587&amp;amp;highlight=asp+webservice+ajax"&gt;this forum post&lt;/a&gt;. This also didn't work properly and there were a few minor irritations with the code. It all seemed pretty silly since with the ScriptManager service reference I had a client side proxy that I knew would work properly. So I created the AspWebServiceProxy object that would allow you to simply pass in a reference to the client side web service proxy object and method and use that:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="black"&gt;AspWebServiceProxy &lt;/font&gt;&lt;font color="blue"&gt;= function &lt;/font&gt;&lt;font color="black"&gt;(conn)&lt;br /&gt;
           {&lt;br /&gt;
              AspWebServiceProxy.superclass.constructor.call(&lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
              &lt;/font&gt;&lt;font color="black"&gt;Ext.apply(&lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;, conn)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
           &lt;/font&gt;&lt;font color="black"&gt;}&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="black"&gt;Ext.extend(AspWebServiceProxy, Ext.data.DataProxy, &lt;br /&gt;
{&lt;br /&gt;
     load : &lt;/font&gt;&lt;font color="blue"&gt;function &lt;/font&gt;&lt;font color="black"&gt;(params, reader, callback, scope, arg)&lt;br /&gt;
            {&lt;br /&gt;
               &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;userContext &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;{&lt;br /&gt;
                                    callback: callback, &lt;br /&gt;
                                    reader: reader, &lt;br /&gt;
                                    arg: arg, &lt;br /&gt;
                                    scope: scope&lt;br /&gt;
                                 }&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
               &lt;br /&gt;
               var &lt;/font&gt;&lt;font color="black"&gt;proxyWrapper &lt;/font&gt;&lt;font color="blue"&gt;= this;&lt;br /&gt;
               &lt;br /&gt;
               &lt;/font&gt;&lt;font color="darkgreen"&gt;//Handles the response we get back from the web service call&lt;br /&gt;
               &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;webServiceCallback &lt;/font&gt;&lt;font color="blue"&gt;= function&lt;/font&gt;&lt;font color="black"&gt;(response) &lt;br /&gt;
                                        { &lt;br /&gt;
                                            proxyWrapper.loadResponse(response, userContext)&lt;/font&gt;&lt;font color="blue"&gt;; &lt;br /&gt;
                                        &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
               &lt;br /&gt;
               &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;serviceParams &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;[]&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
               &lt;br /&gt;
               &lt;/font&gt;&lt;font color="darkgreen"&gt;//Convert the params into an array of values so that they can be used in the call (note assumes that the properties on the object are in the correct order)&lt;br /&gt;
               &lt;/font&gt;&lt;font color="blue"&gt;for &lt;/font&gt;&lt;font color="black"&gt;(&lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;property &lt;/font&gt;&lt;font color="blue"&gt;in &lt;/font&gt;&lt;font color="black"&gt;params)&lt;br /&gt;
               {&lt;br /&gt;
                  serviceParams.push(&lt;/font&gt;p&lt;font color="black"&gt;arams[property])&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
               &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
               &lt;br /&gt;
               &lt;/font&gt;&lt;font color="darkgreen"&gt;//Add the webservice callback handlers&lt;br /&gt;
               &lt;/font&gt;&lt;font color="black"&gt;serviceParams.push(webServiceCallback)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
               &lt;/font&gt;&lt;font color="black"&gt;serviceParams.push(&lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.handleErrorResponse)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
               &lt;br /&gt;
               &lt;/font&gt;&lt;font color="darkgreen"&gt;//Make the actual ASP.Net web service call&lt;br /&gt;
               &lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.webServiceProxyMethod.apply(&lt;/font&gt;&lt;font color="blue"&gt;this&lt;/font&gt;&lt;font color="black"&gt;.webServiceProxy, serviceParams)&lt;/font&gt;&lt;font color="blue"&gt;; &lt;br /&gt;
            &lt;/font&gt;&lt;font color="black"&gt;},&lt;br /&gt;
            &lt;br /&gt;
     handleErrorResponse : &lt;/font&gt;&lt;font color="blue"&gt;function&lt;/font&gt;&lt;font color="black"&gt;(response, userContext, &lt;/font&gt;&lt;font color="black"&gt;methodName&lt;/font&gt;&lt;font color="black"&gt;)&lt;br /&gt;
                           {&lt;br /&gt;
                              &lt;/font&gt;&lt;font color="blue"&gt;alert&lt;/font&gt;&lt;font color="black"&gt;(&lt;/font&gt;&lt;font color="#808080"&gt;"Error while calling web service method:&lt;/font&gt;&lt;font color="#808080"&gt;" + &lt;/font&gt;&lt;font color="black"&gt;methodName +&lt;/font&gt;&lt;font color="#808080"&gt; "\n" &lt;/font&gt;&lt;font color="black"&gt;+ &lt;/font&gt;&lt;font color="black"&gt;response&lt;/font&gt;&lt;font color="black"&gt;.get_message())&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
                           &lt;/font&gt;&lt;font color="black"&gt;},&lt;br /&gt;
 &lt;br /&gt;
     loadResponse : &lt;/font&gt;&lt;font color="blue"&gt;function &lt;/font&gt;&lt;font color="black"&gt;(response, userContext, methodName)&lt;br /&gt;
                    {&lt;br /&gt;
                        &lt;/font&gt;&lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;result &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;userContext.reader.readRecords(response)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
                        &lt;/font&gt;&lt;font color="black"&gt;userContext.callback.call(userContext.scope, result, userContext.arg, &lt;/font&gt;&lt;font color="blue"&gt;true&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
                    &lt;/font&gt;&lt;font color="black"&gt;}&lt;br /&gt;
        &lt;br /&gt;
})&lt;/font&gt;&lt;font color="blue"&gt;;&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
So if you had an ASP.Net web service script reference on your page, like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt; &lt;font color="blue"&gt;&amp;lt;&lt;/font&gt;&lt;font color="maroon"&gt;asp:ScriptManager&lt;/font&gt;&lt;font color="red"&gt; ID&lt;/font&gt;&lt;font color="blue"&gt;="PageScriptManager"&lt;/font&gt;&lt;font color="red"&gt; runat&lt;/font&gt;&lt;font color="blue"&gt;="server"&amp;gt;&lt;/font&gt;&lt;font color="black"&gt;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="blue"&gt;&amp;lt;&lt;/font&gt;&lt;font color="maroon"&gt;Services&lt;/font&gt;&lt;font color="blue"&gt;&amp;gt;&lt;/font&gt;&lt;font color="black"&gt;&lt;br /&gt;
        &lt;/font&gt;&lt;font color="blue"&gt;&amp;lt;&lt;/font&gt;&lt;font color="maroon"&gt;asp:ServiceReference&lt;/font&gt;&lt;font color="red"&gt; Path&lt;/font&gt;&lt;font color="blue"&gt;="/WebServices/OrderService.asmx"&lt;/font&gt;&lt;font color="red"&gt; InlineScript&lt;/font&gt;&lt;font color="blue"&gt;="false"&lt;/font&gt;&lt;font color="red"&gt; &lt;/font&gt;&lt;font color="blue"&gt;/&amp;gt;&lt;br /&gt;
    &amp;lt;/&lt;/font&gt;&lt;font color="maroon"&gt;Services&lt;/font&gt;&lt;font color="blue"&gt;&amp;gt;&lt;/font&gt;&lt;font color="black"&gt;&lt;br /&gt;
&lt;/font&gt;&lt;font color="blue"&gt;&amp;lt;/&lt;/font&gt;&lt;font color="maroon"&gt;asp:ScriptManager&lt;/font&gt;&lt;font color="blue"&gt;&amp;gt;&lt;/font&gt;&lt;font color="black"&gt;&lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
to use the proxy class one would change the code at the start of the article to look like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;  &lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;ds &lt;/font&gt;&lt;font color="blue"&gt;= new &lt;/font&gt;&lt;font color="black"&gt;Ext.data.Store({&lt;br /&gt;
    proxy: &lt;/font&gt;&lt;font color="blue"&gt;new &lt;/font&gt;&lt;font color="black"&gt;AspWebServiceProxy(&lt;br /&gt;
         {&lt;br /&gt;
          webServiceProxy: Example.OrderService,&lt;br /&gt;
          webServiceProxyMethod: Example.OrderService.GetOrders,&lt;br /&gt;
          params: {userId: getUserId(), day: &lt;/font&gt;&lt;font color="blue"&gt;new Date&lt;/font&gt;&lt;font color="black"&gt;()}&lt;br /&gt;
         }),&lt;br /&gt;
    reader: &lt;/font&gt;&lt;font color="blue"&gt;new &lt;/font&gt;&lt;font color="black"&gt;Ext.data.JsonReader(&lt;br /&gt;
        {&lt;br /&gt;
           id: &lt;/font&gt;&lt;font color="#808080"&gt;'WorkDoneId'&lt;br /&gt;
        &lt;/font&gt;&lt;font color="black"&gt;},&lt;br /&gt;
    ...&lt;br /&gt;
})&lt;/font&gt;&lt;font color="blue"&gt;; &lt;/font&gt; 	&lt;/div&gt;
&lt;br /&gt;
I'm sure that I'll be tweaking the AspWebServiceProxy object over time as I discover its short comings, but at least it is a decent start and is hopefully useful to others as well. One of my concerns are the manner in which the attributes on the extraParams object must be specified in the same order as the web service method parameters. I did it this way to keep it consistent with all the other examples I had seen where they used an object as opposed to an array. I may change this, but I like the fact that with the current approach the parameter value pairs are obvious.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="color: rgb(255, 0, 0);"&gt;Update - 2 Nov 2007: Changed the service options of extraParams to params. So now one can use dataStore.load({params: {userId: 4, day: new Date()} }) to load data after the creation of the data source.&lt;/span&gt;&lt;img src="http://ridgway.co.za/aggbug/186.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eden Ridgway</dc:creator>
            <guid>http://ridgway.co.za/archive/2007/10/09/using-asp.net-ajax-webservices-scriptservices-in-extjs.aspx</guid>
            <pubDate>Tue, 09 Oct 2007 05:19:34 GMT</pubDate>
            <comments>http://ridgway.co.za/archive/2007/10/09/using-asp.net-ajax-webservices-scriptservices-in-extjs.aspx#feedback</comments>
            <slash:comments>20</slash:comments>
            <wfw:commentRss>http://ridgway.co.za/comments/commentRss/186.aspx</wfw:commentRss>
        </item>
        <item>
            <title>Dojo Based CruiseControl Statistics Graphs</title>
            <link>http://ridgway.co.za/archive/2007/04/22/dojo-based-cruisecontrol-statistics-graphs.aspx</link>
            <description>I have been making a lot of changes to the CruiseControl Statistics replacement I released a while ago. Most significantly I changed it to use the Dojo charting library and divided the information into tabs. It now looks like this:&lt;br /&gt;
&lt;br /&gt;
&lt;table width="100%" align="center"&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td valign="top" align="center"&gt;&lt;a target="_blank" href="http://www.flickr.com/photos/47913250@N00/480980818/"&gt;&lt;img width="500" height="251" border="0" alt="CCNetDojoStatisticsBuildReportGraph" src="http://farm1.static.flickr.com/172/480980818_7b8dcb95bb.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
            &lt;td valign="top" align="center"&gt;&lt;a title="Photo Sharing" target="_blank" href="http://www.flickr.com/photos/47913250@N00/474503297/"&gt;&lt;img width="205" height="240" border="0" alt="DojoHistoricBuildReportGraphs" src="http://farm1.static.flickr.com/219/474503297_e5ffd14a9e_m.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td colspan="2"&gt;&lt;br /&gt;
            &lt;div style="text-align: center;"&gt;&lt;a target="_blank" href="http://www.flickr.com/photos/47913250@N00/480973085/"&gt;&lt;img width="500" height="172" border="0" alt="CCNetStatisticsBuildTables" src="http://farm1.static.flickr.com/191/480973085_471b68da5b.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold; text-decoration: underline;"&gt;Reasons for the Changes&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The reasons for these changes were as follows:&lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;The display was becoming cluttered with a lot of information that could be nicely separated into tabs. &lt;/li&gt;
    &lt;li&gt;I can now defer the rendering of the tables and historic graphs until the point where the user actually wants to view them. This should dramatically reduce the load time on projects that have a lot of statistics. &lt;/li&gt;
    &lt;li&gt;The dojo charting library is significantly more flexible than PlotKit and will allow for more freedom in future versions. I also expect that others may want graphs that use more than one plotter, like a combination bar and line chart. &lt;/li&gt;
    &lt;li&gt;I will be able to tweak the charting library to do things like rotate the x-axis tick labels so that more of them can be placed on the axis. &lt;/li&gt;
&lt;/ol&gt;
&lt;span style="font-weight: bold; text-decoration: underline;"&gt;Purpose of the Graphs&lt;/span&gt;&lt;br style="text-decoration: underline;" /&gt;
&lt;br /&gt;
In my previous posts I forgot to explain why I wanted to do all of this in the first place. While having good looking graphs instead of an ugly table is great, what I really wanted to get the following from this, is the following:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;Determine whether or not the development teams were following our development process and making certain their code compiles before checking in. A red flag for me is a project that is frequently broken. I chat to the team and determine the root cause and rectify it. &lt;/li&gt;
    &lt;li&gt;Being able to easily spot projects that are taking long or progressively longer to build and see how I can ensure that it stays at acceptable levels so that the team continue to use CruiseControl effectively. &lt;/li&gt;
    &lt;li&gt;Determine if the teams are fixing their FxCop violations and writing and using unit tests on their projects.&lt;br /&gt;
    &lt;/li&gt;
&lt;/ul&gt;
Other future benefits I would like to get from this are:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;The addition of line counting statistics so I can see how much projects are being worked on and get a sense of code churn. Of course line counting is fraught with problems, but at least it's something. &lt;/li&gt;
    &lt;li&gt;Track project complexity and ensure that we keep it under control. &lt;/li&gt;
&lt;/ul&gt;
&lt;span style="font-weight: bold; text-decoration: underline;"&gt;Installation&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The &lt;a target="_blank" href="http://www.ridgway.co.za/archive/2007/03/23/180.aspx#InstallationInstructions"&gt;installation instructions&lt;/a&gt; are the same as the previous version.&lt;br /&gt;
&lt;br /&gt;
&lt;table&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;Works well in the following browsers: &lt;/td&gt;
            &lt;td&gt;&lt;img style="width: 32px; height: 32px;" alt="Internet Explorer" src="http://www.ridgway.co.za/Images/ridgway_co_za/InternetExplorerIcon.jpg" /&gt; &lt;img style="width: 32px; height: 32px;" alt="FireFox" src="http://www.ridgway.co.za/Images/ridgway_co_za/FireFoxIcon.jpg" /&gt; &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Has some rendering quirks in: &lt;/td&gt;
            &lt;td style="text-align: center;"&gt;&lt;img width="32" height="32" alt="Opera" src="http://www.ridgway.co.za/Images/ridgway_co_za/OperaIcon.jpg" /&gt; &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td style="vertical-align: middle;"&gt;Have not tested:&lt;/td&gt;
            &lt;td style="vertical-align: middle; text-align: center;"&gt;&lt;img style="width: 32px; height: 32px;" alt="WebKit/Safari" src="http://www.ridgway.co.za/Images/ridgway_co_za/WebKitIcon.jpg" /&gt;&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
&lt;hr style="width: 100%; height: 2px;" /&gt;
&lt;span style="font-weight: bold; text-decoration: underline;"&gt;Downloads / Updates&lt;/span&gt;&lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&lt;span style="text-decoration: line-through;"&gt;&lt;/span&gt;Version 2.3 - &lt;/span&gt;&lt;span&gt;27 April 2007] - Changes:&lt;/span&gt;
    &lt;ol&gt;
        &lt;li&gt;Significant restructuring of the graph configuration logic (now lives in a separate file).&lt;span&gt;&lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;&lt;span&gt;Removed the timeline logic of the graphs.&lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;&lt;span&gt;Added &lt;span style="font-weight: bold; color: rgb(0, 0, 128);"&gt;coverage graphs&lt;/span&gt; (assumes that the coverage information is stored in the Coverage element). &lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;&lt;span&gt;Added tooltips to the graphs (there are problems in IE).&lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;&lt;span&gt;Graph rendering is now queued with a "Loading..." message in the graph display area. This has helped improve the user experience on the History tab which can be quite slow when displaying a lot of data.&lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;&lt;span&gt;Miscellaneous other changes in tick generation etc.&lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;&lt;span&gt;Table cells of greater than 25 characters now wrap. The exception to this is when you have a word without any spaces that exceeds this and is obviously not wrapped. This sometimes happens in the "Build Error Message" column where the path to a file may be very long. I'm considering implementing a solution that splits these up based on a best guess separator (underscore, full stop, or slash).&lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;&lt;span&gt;The successful/failed builds are color coded in the detailed table now.&lt;/span&gt; &lt;/li&gt;
    &lt;/ol&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&lt;span style="text-decoration: line-through;"&gt;&lt;/span&gt;Version 2.4 - &lt;/span&gt;&lt;span&gt;28 April 2007] - Changes:&lt;/span&gt;
    &lt;ol&gt;
        &lt;li&gt;&lt;span&gt;Fixed a problem that was caused when some statistics had coverage values and others didn't.&lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;&lt;span&gt;Dramatically improved the performance of the summary logic and it now summarises data on demand. The performance problem here was due to the fact I was using the dojo ArrayList method in the distinct method. On a report with over 1.7MB of data this caused Internet Explorer (it doesn't happen in FF) to display the "A script on this page is causing Internet Explorer to run slowly" message. The performance tweaks have removed this problem, but I'm concerned that it may reappear when processing a much larger file. I created a test case with over 8.5MB of data and I didn't receive the message. However the rendering of the tables on such a report is a bit of a joke. I need to look at further improving the performance of the table rendering functions (I don't believe that doing it in the XSLT is going to render the benefit one would expect due to the amount of HTML the client ends up downloading).&lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;&lt;span&gt;Split the detailed and summary tables on to separate tabs and now obviously only render them when the user wants to view the respective tab. However this is still too slow.&lt;/span&gt; &lt;/li&gt;
        &lt;li&gt;&lt;span&gt;Increased the time delay between the rendering of the graphs to 150ms.&lt;/span&gt; &lt;/li&gt;
    &lt;/ol&gt;
    &lt;/li&gt;
    &lt;li&gt;[&lt;a href="http://www.ridgway.co.za/demos/CCNetStatisticsGraphs-2.5.zip"&gt;Version 2.5&lt;/a&gt; - 28 April 2007] - Changes:
    &lt;ol&gt;
        &lt;li&gt;Fixed a bug in the tick mark generation that I introduced while making the changes above. Note that now sometimes the last tick mark on the y-axis does not render. I suspect that this is due to the wonders of JavaScript floating point maths (I still need to investigate this fully), but it is a relatively minor issue so I'm not that phased about it. &lt;/li&gt;
        &lt;li&gt;Added the ability to drill down into the detail statistics from the summary table. &lt;/li&gt;
        &lt;li&gt;Now only averages, counts, etc the successful builds (except the for the failed count of course :) ). &lt;/li&gt;
        &lt;li&gt;Various code refactoring. &lt;/li&gt;
    &lt;/ol&gt;
    &lt;/li&gt;
    &lt;li&gt;[&lt;a href="http://www.ridgway.co.za/demos/CCNetStatisticsGraphs-2.6.zip"&gt;Version 2.6&lt;/a&gt; - 18 May 2007] - Changes:
    &lt;ol&gt;
        &lt;li&gt;Fixed a bug caused by unescaped string sequences in the JavaScript statistics object literal definition (i.e. \u was causing a hexidecimal value expected error in IE). Thank you Mo for reporting this. &lt;/li&gt;
    &lt;/ol&gt;
    &lt;/li&gt;
    &lt;li&gt;[&lt;a href="http://www.ridgway.co.za/images/ridgway_co_za/CCNetStatisticsGraphs-2.7.zip"&gt;Version 2.7&lt;/a&gt; - 22 May 2007] - Changes:
    &lt;ol&gt;
        &lt;li&gt;Have improved the manner in which the graphs are configured thereby making it easier for others to add their own custom settings. The details of how this is setup can be found &lt;a href="http://ridgway.co.za/archive/2007/05/21/adding-custom-graphs-to-the-cruisecontrol.net-statistics-replacement.aspx"&gt;here&lt;/a&gt;. &lt;/li&gt;
        &lt;li&gt;Now can jump to the build from the detailed data table.&lt;/li&gt;
        &lt;li&gt;Added pre-configured complexity and sloc graphs. &lt;/li&gt;
    &lt;/ol&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;hr style="width: 100%; height: 2px;" /&gt;
&lt;span style="text-decoration: underline;"&gt;&lt;span style="font-weight: bold;"&gt;Concerns&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;The Dojo getBorderBox method that the tab control uses is really slow. In some test cases it takes over 5 seconds in total. &lt;/li&gt;
    &lt;li&gt;When switching out of the statistics page Internet Explorer (not FF) struggles and it can take a few seconds just to go back to the listing page. I'm wondering if this is caused by the Dojo framework in some way or by the graphs etc. &lt;/li&gt;
    &lt;li&gt;Internet Explorer is significantly slower than FireFox and Opera. This is inline with the graph rendering performance results at &lt;font face="Arial"&gt;&lt;a href="http://www.ajaxperformance.com/?p=58"&gt;http://www.ajaxperformance.com/?p=58&lt;/a&gt;.&lt;/font&gt;&lt;/li&gt;
    &lt;li&gt;&lt;font face="Arial"&gt;The detailed table rendering performance for projects with lots of builds. A possible solution to this is to introduce paging.&lt;br /&gt;
    &lt;/font&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;span style="font-weight: bold; text-decoration: underline;"&gt;&lt;/span&gt;&lt;hr style="width: 100%; height: 2px;" /&gt;
&lt;span style="font-weight: bold; text-decoration: underline;"&gt;To Do&lt;/span&gt;&lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;Use a frameworks such as &lt;a target="_blank" href="http://www.jsunit.net/"&gt;JsUnit&lt;/a&gt; and &lt;a target="_blank" href="http://jsmock.sourceforge.net/"&gt;JSMock&lt;/a&gt; (maybe even &lt;a href="http://j3unit.sourceforge.net/"&gt;J3Unit&lt;/a&gt;) to properly test the logic. The &lt;a target="_blank" href="http://www.softwareverify.com/javascript/coverage/feature.html"&gt;JavaScript Coverage Validator &lt;/a&gt;may also be of use. &lt;/li&gt;
    &lt;li&gt;Cut down the size of the dojo toolkit distributed with the graphs as it takes quite some time to deploy and there is a lot unnecessary files in the deployment package at the moment.. Basically I need to create a &lt;a target="_blank" href="http://dojotoolkit.org/node/19"&gt;modified build of the Dojo Toolkit&lt;/a&gt; that only includes the pieces that I am using.&lt;/li&gt;
    &lt;li&gt;Find a solution to the single word greater than 25 characters problem in the details table which causes the columns to be very wide. &lt;/li&gt;
    &lt;li&gt;Find a solution to the Internet Explorer tick tooltip problem. At the moment only the ticks for the last series rendered display tooltips. I believe this is because of the way the Graphing library nest each series inside of a div and layer them on top of each other, thereby preventing the data points on the underlying series to receive the events required to display the ticks. &lt;/li&gt;
    &lt;li&gt;Possibly make the ticks a little more informative (requires modifications to the Dojo library). I could use the Dojo tooltip widget to display detailed build information or I could place the information to the right of the graph. &lt;/li&gt;
    &lt;li&gt;Implement filtering in the historic build page that allows graphs to be filtered by date range. I'm a bit concerned about the performance impact of having one master filter that then causes all the graphs to be redrawn.&lt;/li&gt;
&lt;/ol&gt;&lt;img src="http://ridgway.co.za/aggbug/183.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eden Ridgway</dc:creator>
            <guid>http://ridgway.co.za/archive/2007/04/22/dojo-based-cruisecontrol-statistics-graphs.aspx</guid>
            <pubDate>Sun, 22 Apr 2007 07:00:28 GMT</pubDate>
            <comments>http://ridgway.co.za/archive/2007/04/22/dojo-based-cruisecontrol-statistics-graphs.aspx#feedback</comments>
            <slash:comments>23</slash:comments>
            <wfw:commentRss>http://ridgway.co.za/comments/commentRss/183.aspx</wfw:commentRss>
        </item>
        <item>
            <title>A Simple Dojo Charting Example</title>
            <link>http://ridgway.co.za/archive/2007/04/13/A-Simple-Dojo-Charting-Example.aspx</link>
            <description>&lt;p&gt;When evaluating the &lt;a href="http://dojotoolkit.org/" target="_blank"&gt;Dojo Charting library&lt;/a&gt; as an option for my CruiseControl statistics page replacement it was very evident that the Dojo team hadn't gotten around to providing documentation for this donated library.  If you are unfamiliar with what the features the library offers you should take a look at this &lt;a href="http://ajaxian.com/archives/dojo-charting-engine-released" target="_blank"&gt;Ajaxian post&lt;/a&gt;.  There is also a &lt;a href="http://archive.dojotoolkit.org/nightly/charting/tests/charting/test_engine.html"&gt;test page&lt;/a&gt; for the library which I could have sworn was down a day or two ago.&lt;/p&gt;
&lt;p&gt;In the interest of possibly helping others save some time getting into the chrting library I wanted to post a basic introductory example.  Please be aware that I do not know this library in depth, nor have I worked with the Dojo libraries extensively.&lt;/p&gt;
&lt;p&gt;What is great about the Dojo charting implementation is that it is very versatile.  This however comes at the cost of a slightly more involved structure which makes it somewhat more tedious to use than &lt;a href="http://www.liquidx.net/plotkit/" target="_blank"&gt;PlotKit&lt;/a&gt;.  The composition of the various graphing objects is illustrated in the diagram below.  As you can see from the relationships in the diagram they have allowed for features such as the ability to render multiple different graphs, using different plotters, in one plot area.  They also have different implementations of the Axis, PlotArea and Plotter objects for VML and SVG rendering.&lt;/p&gt;
&lt;p align="center"&gt;&lt;a href="http://www.flickr.com/photos/47913250@N00/457296586/" target="_blank" title="Photo Sharing"&gt;&lt;img width="461" height="391" border="0" src="http://farm1.static.flickr.com/216/457296586_b45eb89c0d_o.gif" alt="Dojo Charting Diargam" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;The Example&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So to create a graph one would start off by including a reference to the dojo library and the required namespaces:&lt;/p&gt;
&lt;div class="code"&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#ff0000"&gt; type&lt;/font&gt;&lt;font color="#0000ff"&gt;="text/javascript"&lt;/font&gt;&lt;font color="#ff0000"&gt; src&lt;/font&gt;&lt;font color="#0000ff"&gt;="dojo/dojo.js"&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;/&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#ff0000"&gt; type&lt;/font&gt;&lt;font color="#0000ff"&gt;="text/javascript"&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="#006400"&gt;//Include the required dojo libraries/namespaces&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;dojo.require(&lt;/font&gt;&lt;font color="#808080"&gt;"dojo.collections.Store"&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;dojo.require(&lt;/font&gt;&lt;font color="#808080"&gt;"dojo.charting.Chart"&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;dojo.require(&lt;/font&gt;&lt;font color="#808080"&gt;'dojo.json'&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/&lt;/font&gt;&lt;font color="#800000"&gt;script&lt;/font&gt;&lt;font color="#0000ff"&gt;&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;&lt;/font&gt; &lt;/div&gt;
&lt;p&gt;You then define or retrieve the graph data and store it in the collection store.  This then will be fed into your graph as a series, defined like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;font color="#0000ff"&gt;var &lt;/font&gt;&lt;font color="#000000"&gt;exampleData &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;[&lt;br /&gt;
    { time: &lt;/font&gt;&lt;font color="#800000"&gt;10&lt;/font&gt;&lt;font color="#000000"&gt;, count: &lt;/font&gt;&lt;font color="#800000"&gt;7382 &lt;/font&gt;&lt;font color="#000000"&gt;},&lt;br /&gt;
    { time: &lt;/font&gt;&lt;font color="#800000"&gt;20&lt;/font&gt;&lt;font color="#000000"&gt;, count: &lt;/font&gt;&lt;font color="#800000"&gt;1852 &lt;/font&gt;&lt;font color="#000000"&gt;},&lt;br /&gt;
    { time: &lt;/font&gt;&lt;font color="#800000"&gt;35&lt;/font&gt;&lt;font color="#000000"&gt;, count: &lt;/font&gt;&lt;font color="#800000"&gt;2397 &lt;/font&gt;&lt;font color="#000000"&gt;},&lt;br /&gt;
    { time: &lt;/font&gt;&lt;font color="#800000"&gt;50&lt;/font&gt;&lt;font color="#000000"&gt;, count: &lt;/font&gt;&lt;font color="#800000"&gt;1442 &lt;/font&gt;&lt;font color="#000000"&gt;},&lt;br /&gt;
    { time: &lt;/font&gt;&lt;font color="#800000"&gt;55&lt;/font&gt;&lt;font color="#000000"&gt;, count: &lt;/font&gt;&lt;font color="#800000"&gt;1854 &lt;/font&gt;&lt;font color="#000000"&gt;}&lt;br /&gt;
]&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;br /&gt;
var &lt;/font&gt;&lt;font color="#000000"&gt;store &lt;/font&gt;&lt;font color="#0000ff"&gt;= new &lt;/font&gt;&lt;font color="#000000"&gt;dojo.collections.Store()&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;store.setData(exampleData)&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;br /&gt;
var &lt;/font&gt;&lt;font color="#000000"&gt;timeSeries &lt;/font&gt;&lt;font color="#0000ff"&gt;= new &lt;/font&gt;&lt;font color="#000000"&gt;dojo.charting.Series({&lt;br /&gt;
                                dataSource: store,&lt;br /&gt;
                                bindings: { x: &lt;/font&gt;&lt;font color="#808080"&gt;"time"&lt;/font&gt;&lt;font color="#000000"&gt;, y: &lt;/font&gt;&lt;font color="#808080"&gt;"count" &lt;/font&gt;&lt;font color="#000000"&gt;},&lt;br /&gt;
                                label: &lt;/font&gt;&lt;font color="#808080"&gt;"Example Series"&lt;br /&gt;
                            &lt;/font&gt;&lt;font color="#000000"&gt;})&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;/font&gt; &lt;/div&gt;
&lt;p&gt;Next come the axis definitions where you can specify the data display range, data source and the tick labels:&lt;/p&gt;
&lt;div class="code"&gt;&lt;font color="#006400"&gt;//Define the x-axis&lt;br /&gt;
&lt;/font&gt;&lt;font color="#0000ff"&gt;var &lt;/font&gt;&lt;font color="#000000"&gt;xAxis &lt;/font&gt;&lt;font color="#0000ff"&gt;= new &lt;/font&gt;&lt;font color="#000000"&gt;dojo.charting.Axis()&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="#006400"&gt;//Set the upper and lower data range values&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;xAxis.range &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;/font&gt;&lt;font color="#000000"&gt;{ lower: exampleData[&lt;/font&gt;&lt;font color="#800000"&gt;0&lt;/font&gt;&lt;font color="#000000"&gt;].time, upper: exampleData[exampleData.length-&lt;/font&gt;&lt;font color="#800000"&gt;1&lt;/font&gt;&lt;font color="#000000"&gt;].time }&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;xAxis.origin &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;/font&gt;&lt;font color="#808080"&gt;"max"&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;xAxis.showTicks &lt;/font&gt;&lt;font color="#0000ff"&gt;= true;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;xAxis.label &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;/font&gt;&lt;font color="#808080"&gt;"Example chart"&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="#006400"&gt;//Setup the x tick marks on the chart&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;xAxis.labels &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;/font&gt;&lt;font color="#000000"&gt;[ &lt;br /&gt;
                    { label: &lt;/font&gt;&lt;font color="#808080"&gt;'First'&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;20 &lt;/font&gt;&lt;font color="#000000"&gt;}, &lt;br /&gt;
                    { label: &lt;/font&gt;&lt;font color="#808080"&gt;'Second'&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;25 &lt;/font&gt;&lt;font color="#000000"&gt;}, &lt;br /&gt;
                    { label: &lt;/font&gt;&lt;font color="#808080"&gt;'Third'&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;35 &lt;/font&gt;&lt;font color="#000000"&gt;}, &lt;br /&gt;
                    { label: &lt;/font&gt;&lt;font color="#808080"&gt;'Fourth'&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;50 &lt;/font&gt;&lt;font color="#000000"&gt;}, &lt;br /&gt;
                    { label: &lt;/font&gt;&lt;font color="#808080"&gt;'Fifth'&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;55 &lt;/font&gt;&lt;font color="#000000"&gt;}&lt;br /&gt;
               ]&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
               &lt;br /&gt;
&lt;/font&gt;&lt;font color="#006400"&gt;//Define the y-axis&lt;br /&gt;
&lt;/font&gt;&lt;font color="#0000ff"&gt;var &lt;/font&gt;&lt;font color="#000000"&gt;yAxis &lt;/font&gt;&lt;font color="#0000ff"&gt;= new &lt;/font&gt;&lt;font color="#000000"&gt;dojo.charting.Axis()&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;yAxis.range &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;/font&gt;&lt;font color="#000000"&gt;{ lower: &lt;/font&gt;&lt;font color="#800000"&gt;0&lt;/font&gt;&lt;font color="#000000"&gt;, upper: &lt;/font&gt;&lt;font color="#800000"&gt;5000 &lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;yAxis.showLines &lt;/font&gt;&lt;font color="#0000ff"&gt;= true;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;yAxis.showTicks &lt;/font&gt;&lt;font color="#0000ff"&gt;= true;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;yAxis.label &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;/font&gt;&lt;font color="#808080"&gt;"Time Taken"&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
   &lt;br /&gt;
&lt;/font&gt;&lt;font color="#006400"&gt;//Setup the y tick marks on the chart&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;yAxis.labels &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;/font&gt;&lt;font color="#000000"&gt;[ &lt;br /&gt;
                  { label: &lt;/font&gt;&lt;font color="#808080"&gt;"0s"&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;0 &lt;/font&gt;&lt;font color="#000000"&gt;},&lt;br /&gt;
                  { label: &lt;/font&gt;&lt;font color="#808080"&gt;"1s"&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;1000 &lt;/font&gt;&lt;font color="#000000"&gt;}, &lt;br /&gt;
                  { label: &lt;/font&gt;&lt;font color="#808080"&gt;"2s"&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;2000 &lt;/font&gt;&lt;font color="#000000"&gt;}, &lt;br /&gt;
                  { label: &lt;/font&gt;&lt;font color="#808080"&gt;"3s"&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;3000 &lt;/font&gt;&lt;font color="#000000"&gt;}, &lt;br /&gt;
                  { label: &lt;/font&gt;&lt;font color="#808080"&gt;"4s"&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;4000 &lt;/font&gt;&lt;font color="#000000"&gt;}, &lt;br /&gt;
                  { label: &lt;/font&gt;&lt;font color="#808080"&gt;"5s"&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#0000ff"&gt;value&lt;/font&gt;&lt;font color="#000000"&gt;: &lt;/font&gt;&lt;font color="#800000"&gt;5000 &lt;/font&gt;&lt;font color="#000000"&gt;} &lt;br /&gt;
               ]&lt;/font&gt;&lt;font color="#0000ff"&gt;;    &lt;/font&gt; &lt;/div&gt;
&lt;p&gt;You then define how the data will be plotted by defining a Plot and assigning the series with a plotter to render it: &lt;/p&gt;
&lt;div class="code"&gt;&lt;font color="#0000ff"&gt;var &lt;/font&gt;&lt;font color="#000000"&gt;chartPlot &lt;/font&gt;&lt;font color="#0000ff"&gt;= new &lt;/font&gt;&lt;font color="#000000"&gt;dojo.charting.Plot(xAxis, yAxis)&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;chartPlot.addSeries({ &lt;br /&gt;
                data: timeSeries, &lt;br /&gt;
                plotter: dojo.charting.Plotters.CurvedArea &lt;br /&gt;
              })&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;/font&gt; &lt;/div&gt;
&lt;p&gt;This then needs to be rendered into a specific area, so one defines the PlotArea and adds the Plot to it: &lt;/p&gt;
&lt;div class="code"&gt;&lt;font color="#0000ff"&gt;var &lt;/font&gt;&lt;font color="#000000"&gt;chartPlotArea &lt;/font&gt;&lt;font color="#0000ff"&gt;= new &lt;/font&gt;&lt;font color="#000000"&gt;dojo.charting.PlotArea()&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;chartPlotArea.size &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;/font&gt;&lt;font color="#000000"&gt;{ width: &lt;/font&gt;&lt;font color="#800000"&gt;380&lt;/font&gt;&lt;font color="#000000"&gt;, height: &lt;/font&gt;&lt;font color="#800000"&gt;170 &lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;chartPlotArea.padding &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;/font&gt;&lt;font color="#000000"&gt;{ top: &lt;/font&gt;&lt;font color="#800000"&gt;20&lt;/font&gt;&lt;font color="#000000"&gt;, right: &lt;/font&gt;&lt;font color="#800000"&gt;20&lt;/font&gt;&lt;font color="#000000"&gt;, bottom: &lt;/font&gt;&lt;font color="#800000"&gt;30&lt;/font&gt;&lt;font color="#000000"&gt;, left: &lt;/font&gt;&lt;font color="#800000"&gt;50 &lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="#006400"&gt;//Add the plot to the area &lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;chartPlotArea.plots.push(chartPlot)&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;/font&gt; &lt;/div&gt;
&lt;p&gt;Finally one needs to create the chart and add the PlotArea to it.  The chart also needs to have a container element, which one assigns to the chart.node.&lt;/p&gt;
&lt;div class="code"&gt;&lt;font color="#0000ff"&gt;var &lt;/font&gt;&lt;font color="#000000"&gt;chart &lt;/font&gt;&lt;font color="#0000ff"&gt;= new &lt;/font&gt;&lt;font color="#000000"&gt;dojo.charting.Chart(&lt;/font&gt;&lt;font color="#0000ff"&gt;null&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#808080"&gt;"Example chart"&lt;/font&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;font color="#808080"&gt;"This is the example chart description"&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="#006400"&gt;//Add the plot area at an offset of 10 pixels from the top left&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;chart.addPlotArea({ x: &lt;/font&gt;&lt;font color="#800000"&gt;10&lt;/font&gt;&lt;font color="#000000"&gt;, y: &lt;/font&gt;&lt;font color="#800000"&gt;10&lt;/font&gt;&lt;font color="#000000"&gt;, plotArea: chartPlotArea })&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
&lt;br /&gt;
&lt;/font&gt;&lt;font color="#006400"&gt;//Setup the chart to be added to the DOM on load&lt;br /&gt;
&lt;/font&gt;&lt;font color="#000000"&gt;dojo.addOnLoad(&lt;/font&gt;&lt;font color="#0000ff"&gt;function&lt;/font&gt;&lt;font color="#000000"&gt;()&lt;br /&gt;
               {&lt;br /&gt;
                   chart.node &lt;/font&gt;&lt;font color="#0000ff"&gt;= &lt;/font&gt;&lt;font color="#000000"&gt;dojo.byId(&lt;/font&gt;&lt;font color="#808080"&gt;"GraphContainerArea"&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
                   &lt;/font&gt;&lt;font color="#000000"&gt;chart.render()&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;br /&gt;
               &lt;/font&gt;&lt;font color="#000000"&gt;})&lt;/font&gt;&lt;font color="#0000ff"&gt;;&lt;/font&gt; &lt;/div&gt;
&lt;p&gt;And voila, you have a graph that looks like this:&lt;/p&gt;
&lt;p align="center"&gt;&lt;a href="http://www.flickr.com/photos/47913250@N00/457296570/" target="_blank" title="Photo Sharing"&gt;&lt;img width="409" height="199" border="0" src="http://farm1.static.flickr.com/175/457296570_3c9e315e32_o.jpg" alt="Dojo Graph Example" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To see the graph in action, take a look at this &lt;a href="http://www.ridgway.co.za/demos/DojoChartExample/DemoDojoGraph.html" target="_blank"&gt;demo page&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://ridgway.co.za/aggbug/182.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eden Ridgway</dc:creator>
            <guid>http://ridgway.co.za/archive/2007/04/13/A-Simple-Dojo-Charting-Example.aspx</guid>
            <pubDate>Fri, 13 Apr 2007 05:14:08 GMT</pubDate>
            <comments>http://ridgway.co.za/archive/2007/04/13/A-Simple-Dojo-Charting-Example.aspx#feedback</comments>
            <slash:comments>9</slash:comments>
            <wfw:commentRss>http://ridgway.co.za/comments/commentRss/182.aspx</wfw:commentRss>
        </item>
        <item>
            <title>CruiseControl.Net Statistics Graphs using PlotKit</title>
            <link>http://ridgway.co.za/archive/2007/03/27/181.aspx</link>
            <description>[&lt;span style="font-weight: bold;"&gt;Update&lt;/span&gt;: This does not apply to the Dojo Toolkit vesion of the Statistics Plugin.  For information on how to customize that version go to this &lt;a href="http://ridgway.co.za/archive/2007/05/21/adding-custom-graphs-to-the-cruisecontrol.net-statistics-replacement.aspx"&gt;page&lt;/a&gt;.] &lt;br /&gt;
&lt;br /&gt;
As promised in my previous post, &lt;a href="http://ridgway.co.za/archive/2007/03/23/180.aspx"&gt;CruiseControl Statistics Graphs&lt;/a&gt;, here is a high level description of the implementation  that allowed me to display graphs in CruiseControl.Net.  I've also included a quick guide on how to extend the report to include your own custom statistics that you may have published as part of your build.&lt;br /&gt;
&lt;br /&gt;
After I had decided to create a graphical view of the CruiseControl statistics I assessed various options that were available.  My immediate thoughts were to use XSLT to create either SVG or VML graphs.  The problem is that I wanted something that was quick and easy to implement and quite frankly the effort required to do all of this using xslt was just too great.  My attentions then turned to doing the majority of the processing in JavaScript.  The Dojo Charting library was the first solution that sprung to mind, but on further investigation is appeared to be undergoing some restructuring and the documentation is almost non-existent.  It was then that I came across &lt;a href="http://www.liquidx.net/plotkit/"&gt;PlotKit&lt;/a&gt;, a cross browser charting framework built on top of MochiKit.  It was simple and had documentation so it was a clear winner.&lt;br /&gt;
&lt;br /&gt;
So the Statistics Graph solution ended up being structured as follows:&lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;StatisticsGraphs.xsl - creates JSON representation of the detailed stats data and the general page structure&lt;/li&gt;
    &lt;li&gt;StatisticsGraphs.js - contains the logic to summarise the detailed stats data and to create the plotkit graphs and tables&lt;/li&gt;
    &lt;li&gt;MochiKit_lite_packed.js, PlotKit_Packed.js - packed versions of the libraries that provide the graphing functionality (from PlotKit)&lt;/li&gt;
    &lt;li&gt;excanvas.js - provides canvas emulation for Internet Explorer (from PlotKit)&lt;/li&gt;
&lt;/ol&gt;
If one wanted to create their own custom summary graph of new statistics on the page (for more info on including custom statistics see &lt;a href="http://www.kiwidude.com/blog/2006/10/ccstatistics-for-cruisecontrolnet-11.html"&gt;Grant Drake's blog post&lt;/a&gt;), you would open up StatisticsGraphs.js and do the following:&lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;Edit the generateDailySummaries function and add your summarised statistic to daySummary&lt;br /&gt;
    &lt;br /&gt;
    &lt;div class="code"&gt; &lt;font color="blue"&gt;var &lt;/font&gt;&lt;font color="black"&gt;daySummary &lt;/font&gt;&lt;font color="blue"&gt;= &lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;{&lt;br /&gt;
    day : day,&lt;br /&gt;
    successfulBuildCount : count(currentStatistics, &lt;/font&gt;&lt;font color="#808080"&gt;"Status"&lt;/font&gt;&lt;font color="black"&gt;, &lt;/font&gt;&lt;font color="blue"&gt;function&lt;/font&gt;&lt;font color="black"&gt;(item) { &lt;/font&gt;&lt;font color="blue"&gt;return &lt;/font&gt;&lt;font color="black"&gt;(item[&lt;/font&gt;&lt;font color="#808080"&gt;"Status"&lt;/font&gt;&lt;font color="black"&gt;] &lt;/font&gt;&lt;font color="blue"&gt;== &lt;/font&gt;&lt;font color="#808080"&gt;"Success"&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;; &lt;/font&gt;&lt;font color="black"&gt;}),&lt;br /&gt;
    failedBuildCount : count(currentStatistics, &lt;/font&gt;&lt;font color="#808080"&gt;"Status"&lt;/font&gt;&lt;font color="black"&gt;, &lt;/font&gt;&lt;font color="blue"&gt;function&lt;/font&gt;&lt;font color="black"&gt;(item) { &lt;/font&gt;&lt;font color="blue"&gt;return &lt;/font&gt;&lt;font color="black"&gt;(item[&lt;/font&gt;&lt;font color="#808080"&gt;"Status"&lt;/font&gt;&lt;font color="black"&gt;] &lt;/font&gt;&lt;font color="blue"&gt;== &lt;/font&gt;&lt;font color="#808080"&gt;"Failure"&lt;/font&gt;&lt;font color="black"&gt;)&lt;/font&gt;&lt;font color="blue"&gt;; &lt;/font&gt;&lt;font color="black"&gt;}),&lt;br /&gt;
    lastBuildLabel : getLastValue(currentStatistics, &lt;/font&gt;&lt;font color="#808080"&gt;"BuildLabel"&lt;/font&gt;&lt;font color="black"&gt;),&lt;br /&gt;
    testCount : testCount,&lt;br /&gt;
    testsPassed : testCount - testFailures - testsIgnored,&lt;br /&gt;
    testFailures : testFailures,&lt;br /&gt;
    testsIgnored : testsIgnored,&lt;br /&gt;
    fxCopWarnings : average(currentStatistics, &lt;/font&gt;&lt;font color="#808080"&gt;"FxCop Warnings"&lt;/font&gt;&lt;font color="black"&gt;),&lt;br /&gt;
    fxCopErrors : average(currentStatistics, &lt;/font&gt;&lt;font color="#808080"&gt;"FxCop Errors"&lt;/font&gt;&lt;font color="black"&gt;),&lt;br /&gt;
    averageBuildDuration: average(currentStatistics, &lt;/font&gt;&lt;font color="#808080"&gt;"Duration"&lt;/font&gt;&lt;font color="black"&gt;),&lt;br /&gt;
    minBuildDuration: min(currentStatistics, &lt;/font&gt;&lt;font color="#808080"&gt;"Duration"&lt;/font&gt;&lt;font color="black"&gt;),&lt;br /&gt;
    maxBuildDuration: max(currentStatistics, &lt;/font&gt;&lt;font color="#808080"&gt;"Duration"&lt;/font&gt;&lt;font color="black"&gt;),&lt;br /&gt;
    &lt;br /&gt;
    &lt;/font&gt;&lt;font color="darkgreen"&gt;//Insert new summarised statistics here&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;averageCodeCoverage : average(currentStatistics, &lt;/font&gt;&lt;font color="#808080"&gt;"CodeCoverage"&lt;/font&gt;&lt;font color="black"&gt;)&lt;br /&gt;
    }&lt;/font&gt;&lt;font color="blue"&gt;;&lt;/font&gt; 	&lt;/div&gt;
    &lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt; Create a new function to create the graph.  Note that the createGraph function creates the title, graph and legend for you. eg.&lt;br /&gt;
    &lt;br /&gt;
    &lt;div class="code"&gt;&lt;font color="blue"&gt;function &lt;/font&gt;&lt;font color="black"&gt;createCodeCoverageGraph()&lt;br /&gt;
    {&lt;br /&gt;
    createGraph({&lt;br /&gt;
    graphName : &lt;/font&gt;&lt;font color="#808080"&gt;"Average Daily Code Coverage"&lt;/font&gt;&lt;font color="black"&gt;,&lt;br /&gt;
    series : [&lt;br /&gt;
    { &lt;br /&gt;
    name : &lt;/font&gt;&lt;font color="#808080"&gt;"Average Code Coverage"&lt;/font&gt;&lt;font color="black"&gt;, &lt;br /&gt;
    attributeName: &lt;/font&gt;&lt;font color="#808080"&gt;"averageCodeCoverage"&lt;/font&gt;&lt;font color="black"&gt;, &lt;br /&gt;
    color : Color.greenColor() &lt;br /&gt;
    }&lt;br /&gt;
    ]&lt;br /&gt;
    })&lt;br /&gt;
    } &lt;/font&gt; 	&lt;/div&gt;
    &lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt; Then call your new function in the setupGraphs&lt;br /&gt;
    &lt;br /&gt;
    &lt;div class="code"&gt; &lt;font color="blue"&gt;function &lt;/font&gt;&lt;font color="black"&gt;setupGraphs()&lt;br /&gt;
    {&lt;br /&gt;
    createBuildReportGraph()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;createBuildDurationGraph()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;createTestGraph()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;createFxCopGraph()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;br /&gt;
    &lt;/font&gt;&lt;font color="darkgreen"&gt;//Insert calls to functions that create new graphs here&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;createCodeCoverageGraph()&lt;/font&gt;&lt;font color="blue"&gt;;&lt;br /&gt;
    &lt;/font&gt;&lt;font color="black"&gt;}&lt;/font&gt;&lt;/div&gt;
    &lt;/li&gt;
&lt;/ol&gt;
So if you are keen to give it a go take a look at my &lt;a href="http://ridgway.co.za/archive/2007/03/27/180.aspx"&gt;previous post&lt;/a&gt; for the download.&lt;img src="http://ridgway.co.za/aggbug/181.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eden Ridgway</dc:creator>
            <guid>http://ridgway.co.za/archive/2007/03/27/181.aspx</guid>
            <pubDate>Tue, 27 Mar 2007 07:23:42 GMT</pubDate>
            <comments>http://ridgway.co.za/archive/2007/03/27/181.aspx#feedback</comments>
            <wfw:commentRss>http://ridgway.co.za/comments/commentRss/181.aspx</wfw:commentRss>
        </item>
        <item>
            <title>CruiseControl Statistics Graphs</title>
            <link>http://ridgway.co.za/archive/2007/03/23/180.aspx</link>
            <description>&lt;p&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;[&lt;/span&gt;&lt;span style="font-weight: bold; color: rgb(255, 0, 0);"&gt;Update&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;: &lt;/span&gt;Please note that this approach has been replaced by  a Dojo based solution available from &lt;a href="http://www.ridgway.co.za/archive/2007/04/22/Dojo-Based-CruiseControl-Statistics-Graphs.aspx"&gt;http://www.ridgway.co.za/archive/2007/04/22/Dojo-Based-CruiseControl-Statistics-Graphs.aspx&lt;/a&gt;] &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt; Lets face it, the new CruiseControl Statistics report is damn ugly and is not as useful as it could be.  So I decided to remedy this by replacing the standard statistics xslt transform with one that generates both graphs and tables.  The results are graphs in CruiseControl that look something like the images below.  So what my new CruiseControl Statistics report gives you at the moment is as follows:&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;span style="font-weight: bold;"&gt;Build Report Graph&lt;/span&gt; - the number of failed/successful builds per day and the last build of each day&lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;span style="font-weight: bold;"&gt;Build Duration Graph&lt;/span&gt; - the average and maximum build duration per day&lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;span style="font-weight: bold;"&gt;Test Summary Graph&lt;/span&gt; - the average number of tests that passed, failed or were ignored&lt;/li&gt;
    &lt;li&gt;&lt;span style="font-weight: bold;"&gt;FxCop Summary Graph&lt;/span&gt; - the average number of warnings and errors per day&lt;/li&gt;
    &lt;li&gt;&lt;span style="font-weight: bold;"&gt;Build Summary Statistics Table&lt;/span&gt; - a table containing the data that was used to generate the daily stats&lt;/li&gt;
    &lt;li&gt;&lt;span style="font-weight: bold;"&gt;Build Detailed Statistics Table&lt;/span&gt; - contains the original detailed statistics data as would have been displayed in the old statistics report&lt;/li&gt;
&lt;/ul&gt;
&lt;table width="100%"&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td valign="top" align="center"&gt;    &lt;a target="_blank" href="http://www.flickr.com/photos/47913250@N00/453488193/"&gt;&lt;img width="187" height="240" border="0" alt="BuildReportGraphs" src="http://farm1.static.flickr.com/247/453488193_0d4daf9344_m.jpg" /&gt;&lt;/a&gt; &lt;/td&gt;
            &lt;td valign="top" align="center"&gt; &lt;a href="http://www.flickr.com/photos/47913250@N00/431068505/" title="Photo Sharing" target="_blank"&gt;&lt;img width="240" height="99" border="0" src="http://farm1.static.flickr.com/152/431068505_4e1aec6aaf_m.jpg" alt="Build Statics Tables" /&gt;&lt;/a&gt;    &lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
&lt;a name="InstallationInstructions"&gt;&lt;/a&gt;&lt;span style="font-weight: bold; text-decoration: underline;"&gt;Installation Instructions&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
I'll get on to the actual implementation in &lt;a href="http://www.ridgway.co.za/archive/2007/03/23/181.aspx"&gt;another post&lt;/a&gt;, but for now here is how to get it working on your CruiseControl.Net build server:&lt;a href="http://ridgway.co.za/archive/2007/03/23/181.aspx"&gt; &lt;/a&gt;
&lt;ol&gt; &lt;a href="http://ridgway.co.za/archive/2007/03/23/181.aspx"&gt;    &lt;/a&gt;
    &lt;li&gt;Download the latest stable release: &lt;a href="http://www.ridgway.co.za/Demos/CCNetStatisticsGraphs-1.3.zip"&gt;CCNetStatisticsGraphs-1.3.zip&lt;/a&gt;    .  Updates are also present at the end of this post if you feel adventurous.  &lt;span style="font-weight: bold; font-style: italic; color: rgb(255, 0, 0);"&gt;[Note: I have a new version available that uses the Dojo library]&lt;/span&gt;&lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;Unzip the webdashboard folder over the current CruiseControl webdashboard folder (usually found at C:\Program Files\CruiseControl.NET)&lt;/li&gt;
    &lt;li&gt;Open the dashboard.config file in the webdashboard folder and change the the projectStatisticsPlugin setting to point to the new StatisticsGraphs.xsl stylesheet, like this:&lt;br /&gt;
    &lt;br /&gt;
    &lt;div class="code"&gt; &lt;font color="blue"&gt;&amp;lt;&lt;/font&gt;&lt;font color="maroon"&gt;projectStatisticsPlugin&lt;/font&gt;&lt;font color="red"&gt; xslFileName&lt;/font&gt;&lt;font color="blue"&gt;="xsl\StatisticsGraphs.xsl"&lt;/font&gt;&lt;font color="red"&gt; &lt;/font&gt;&lt;font color="blue"&gt;/&amp;gt;&lt;/font&gt; 	&lt;/div&gt;
    &lt;/li&gt;
&lt;/ol&gt;
If it everything is in order you should see the following type of report when viewing your project statics:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="text-align: center;"&gt;&lt;a title="Photo Sharing" target="_blank" href="http://www.flickr.com/photos/47913250@N00/431084927/"&gt;&lt;img width="240" height="124" border="0" alt="Build Report in CruiseControl" src="http://farm1.static.flickr.com/149/431084927_273e0aad54_m.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;
Future improvements to this will largely depend on the interest generated by this post.  If enough people find it useful I'll add new features.  Of course if you extend it, please let me know about it.  Some potential enhancements are:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;The ability to filter the report data&lt;/li&gt;
    &lt;li&gt;Additional reports such as code coverage and complexity reports (based on whether or not the stats are present of course)&lt;/li&gt;
    &lt;li&gt;Static table headers and sorting&lt;/li&gt;
&lt;/ul&gt;
&lt;span&gt;&lt;/span&gt;&lt;hr style="width: 100%; height: 2px;" /&gt;
&lt;br /&gt;
&lt;span&gt;&lt;span style="font-weight: bold; text-decoration: underline; color: rgb(0, 0, 0);"&gt;Notes&lt;/span&gt;&lt;br style="color: rgb(0, 0, 0);" /&gt;
&lt;br style="color: rgb(0, 0, 0);" /&gt;
&lt;span style="color: rgb(0, 0, 0);"&gt;Using PlotKit as the graph rendering engine unfortunately also results in the following issues in the graphs:&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;
&lt;ol&gt;
    &lt;li&gt;Graphs where you would only expect integer values, display decimals.  There does not seem to be any easy solution to this apart from modifying PlotKit code or ignoring the auto-scaled y-axis figures and writing one's own routine to determine the values and specify the y-axis tick values. [&lt;a style="font-style: italic;" href="#Updates"&gt;Integer y-axis values were implemented in version 1.4&lt;/a&gt;]     &lt;/li&gt;
    &lt;li&gt;There are problems with the labels being truncated on both the vertical and horizontal axis.  One can alleviate the problem somewhat using different margins but at some point you run into trouble.  The space required really needs to be determined dynamically (by the graphing engine).  [&lt;a style="font-style: italic;" href="#Updates"&gt;Problem mostly removed in version 1.5&lt;/a&gt;]&lt;/li&gt;
    &lt;li&gt;With the FxCop report it is difficult to predict whether or not the errors will be greater than warnings and vice-a-versa and hence you cannot use a filled line graph because it may cover up the one set of values.  What would be nice is if you could have the one set of values peek through the other using alpha blending.  Canvas does provide the ability to do this to some degree through the globalCompositeOperation property on the canvas, however this does not work in the IECanvas emulation layer.&lt;/li&gt;
&lt;/ol&gt;
Given the problems mentioned above I may very well switch to using another graphing approach or roll a modified version of Plotkit out.&lt;br /&gt;
&lt;br /&gt;
Some of the suggested enhancements I have received are:&lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;span style="font-weight: bold;"&gt;Removal of outliers&lt;/span&gt; - Severe spikes completely mess up the scaling of the graphs.  This one requires some thought because obviously it could also have the affect of making people believe that the graphs are incorrect when in fact the outliers have been removed.&lt;/li&gt;
    &lt;li&gt;&lt;span style="font-weight: bold;"&gt;Horizontal scaling&lt;/span&gt; - Add in zero values to the graphs for missing days from the start of recorded builds. [&lt;a style="font-style: italic;" href="#Updates"&gt;Implemented in version 1.4&lt;/a&gt;]&lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;span style="font-weight: bold;"&gt;Removal of graphs if there no values&lt;/span&gt; - This one I had considered before and since someone else has asked for it, I'm going to implement it it.  It's most obvious when you are not using FxCop and end up with a horrible empty grey block in your report. [&lt;a style="font-style: italic;" href="#Updates"&gt;Implemented in version 1.4&lt;/a&gt;]&lt;/li&gt;
&lt;/ol&gt;
&lt;hr style="width: 100%; height: 2px;" /&gt;
&lt;a name="Updates"&gt;&lt;/a&gt;&lt;strong&gt;&lt;u&gt;Updates&lt;/u&gt;&lt;/strong&gt;&lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;span&gt; [Version 1.2 - 8 April 2007] - Fixed an issue caused by apostrophes in the generated statistics (new version 1.2 released) (reported by Grant Drake).&lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&lt;a href="http://www.ridgway.co.za/Demos/CCNetStatisticsGraphs-1.3.zip"&gt;Version 1.3&lt;/a&gt; - &lt;/span&gt;&lt;span&gt;8 April 2007] - Fixed an issue in the duration calculation where the duration had an int + string value which of course resulted in a string concatenation :) &lt;/span&gt;&lt;span&gt;(reported by Grant Drake).&lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;&lt;span&gt;[&lt;a href="http://www.ridgway.co.za/Demos/CCNetStatisticsGraphs-1.4.zip"&gt;Version 1.4&lt;/a&gt; - 9 April 2007] - Implemented the following features:&lt;/span&gt;
    &lt;ol&gt;
        &lt;li&gt;&lt;span&gt;Graphs are not displayed when there are no values for them&lt;/span&gt;&lt;/li&gt;
        &lt;li&gt;&lt;span&gt;The x-series values are now a timeline not just all the builds next to each other.  So if there was a build on the 1st of a month and the next build was only on the 4th, zero values are filled in for the 2nd and 3rd of the month.  This has a down side though because at the moment it is displaying weekends for which most projects will have no activity and if a project is worked on sporadically you have large gaps in the graph.  I do believe however there is value in seeing the activity over time in this way and maybe it it worth having some sort of filtering option that allows one to remove weekends and/or days for which there are no statistics.&lt;br /&gt;
        &lt;/span&gt;&lt;/li&gt;
        &lt;li&gt;&lt;span&gt;The y-axis no longer displays values (I manually determined the y-ticks and added them to the graph).&lt;br /&gt;
        &lt;/span&gt;&lt;/li&gt;
    &lt;/ol&gt;
    Only download this release if you are curious about the new functionality.  I will be writing a series of WatiN tests to ensure that everything is working as expected as the mini-project evolves.  My biggest concern for this version is with locales specifically in relation to dates.  To fill in missing days I've used JavaScript date functions and there is an assumption that the browser's locale is the same as that of the server.  Unfortunately this is due to the fact that the CruiseControl month in the statistics xml file is a short name.  Furthermore I cannot use the StartTime value because it is possible that the format of that information is not consistent: one of my example statistics files has StartTime values in both yyyy/MM/dd and dd/MM/yyyy format.&lt;br /&gt;
    &lt;em style="color: rgb(255, 0, 0);"&gt;I found out on the 22 April 2007 that this version suffers from a daylight savings time bug that affected projects that started when daylight savings was in effect.  I have fixed this release.&lt;/em&gt;&lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;span&gt;[&lt;a href="http://www.ridgway.co.za/Demos/CCNetStatisticsGraphs-1.5.zip"&gt;Version 1.5&lt;/a&gt; - 10 April 2007] - Modified the PlotKit canvas to allow rendering of HTML labels (i.e. it won't escape your tags) and now draws the graph border and tick marks in the same colour as the axis label colour (I may remove this modification and simply create a new custom canvas).  This has allowed me to reduce the font size of the x-axis labels and put the date on a separate line to the build label.  Unfortunately it has also meant that I'm now distributing the uncompressed version of the PlotKit library (I may use a JavaScript compressor on them later).&lt;/span&gt; &lt;em style="color: rgb(255, 0, 0);"&gt;I found out on the 22 April 2007 that this version suffers from a daylight savings time bug that affected projects that started when daylight savings was in effect.  I have fixed this release.&lt;/em&gt;&lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;&lt;span&gt;[Version 2.1] - This is available from &lt;a href="http://www.ridgway.co.za/archive/2007/04/22/Dojo-Based-CruiseControl-Statistics-Graphs.aspx"&gt;this post&lt;/a&gt;.&lt;br /&gt;
    &lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;img src="http://ridgway.co.za/aggbug/180.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Eden Ridgway</dc:creator>
            <guid>http://ridgway.co.za/archive/2007/03/23/180.aspx</guid>
            <pubDate>Fri, 23 Mar 2007 05:04:17 GMT</pubDate>
            <comments>http://ridgway.co.za/archive/2007/03/23/180.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://ridgway.co.za/comments/commentRss/180.aspx</wfw:commentRss>
        </item>
    </channel>
</rss>