<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2513338198128732893</id><updated>2011-11-27T23:18:38.022Z</updated><category term='vss'/><category term='VS 2010'/><category term='Sourcesafe'/><title type='text'>Blogging Dot Net (and jQuery too!)</title><subtitle type='html'>Solutions to interesting problems.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>29</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-4623189707541308564</id><published>2010-07-13T18:53:00.001+01:00</published><updated>2010-07-13T18:53:43.513+01:00</updated><title type='text'>Simple OOXML For OOXML SDK v 2.0 released</title><content type='html'>&lt;p&gt;&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0])_"&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The goal of the Simple OOXML project has always been to create documents and spreadsheets using minimum resources, including a server environment. The library provides commonly used functionality whilst hides away the details of creating open xml documents and without a large performance overhead. Documents created with this library and the Open Office SDK can be viewed using Microsoft Excel/Microsoft Word or OpenOffice as well as any third party that supports the format. &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h3&gt;Getting Started&lt;/h3&gt;  &lt;p&gt;Simple OOXML adds the &lt;em&gt;DocumentFormat.OpenXml.Extensions&lt;/em&gt; namespace to version 2.0 of the Open Office SDK. It allows developers to create spreadsheets and documents either from scratch or using predefined templates. All functionality is represented by static functions for high performance tasks, or higher level wrapper functions can provide simpler code expressions with some minor performance loss.&amp;#160; &lt;/p&gt;  &lt;p&gt;The following classes are provided:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;em&gt;SpreadsheetReader&lt;/em&gt; - manipulation of templates, retrieval of document parts, row and column reference functionality &lt;/li&gt;    &lt;li&gt;&lt;em&gt;SpreadsheetWriter&lt;/em&gt; - writing of document parts and creation of document level attributes. Add or remove spreadsheets. &lt;/li&gt;    &lt;li&gt;SpreadsheetStyle - encapsulates font, border and fill handing in a spreadsheet. &lt;/li&gt;    &lt;li&gt;&lt;em&gt;WorsheetReader&lt;/em&gt; - retrieves cell and style information from a worksheet &lt;/li&gt;    &lt;li&gt;&lt;em&gt;WorksheetWriter&lt;/em&gt; - allows the pasting or insertion of data and style - using simple value types or &lt;em&gt;DataTables&lt;/em&gt; - at a cell or range reference. &lt;/li&gt;    &lt;li&gt;&lt;em&gt;DocumentReader&lt;/em&gt; - retrieval of document templates. &lt;/li&gt;    &lt;li&gt;&lt;em&gt;DocumentWriter&lt;/em&gt; - pastes and saves text and text lists using predefined bookmarks. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Download the source files to view the source code, examples as well as a unit testing library which is a useful reference to all the features of the library. Users of the unsupported &lt;em&gt;ExcelPackage&lt;/em&gt; library could consider using this library instead. &lt;/p&gt;  &lt;p&gt;Simple OOXML is licensed under the LGPL. Requires .Net Framework 3.5 or later and can be downloaded from &lt;a href="http://simpleooxml.codeplex.com" target="_blank"&gt;CodePlex&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-4623189707541308564?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/4623189707541308564/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=4623189707541308564' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/4623189707541308564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/4623189707541308564'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/07/simple-ooxml-for-ooxml-sdk-v-20.html' title='Simple OOXML For OOXML SDK v 2.0 released'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-630220382204923261</id><published>2010-05-19T09:46:00.003+01:00</published><updated>2010-05-19T09:49:48.159+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vss'/><category scheme='http://www.blogger.com/atom/ns#' term='VS 2010'/><category scheme='http://www.blogger.com/atom/ns#' term='Sourcesafe'/><title type='text'>VS2010 The mappings for the solution could not be found</title><content type='html'>&lt;p&gt;When trying to open a project with VS 2010 that used Visual SourceSafe, you may receive the following error:&lt;/p&gt;
&lt;p&gt;&lt;span style="font-style:italic;"&gt;The mappings for the solution could not be found&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To fix this problem, go to Tools -&gt; Options -&gt; Source Control -&gt; Plug-in Selection and change to the Visual Sourcesafe plugin from the TFS plugin.&lt;/p&gt;
&lt;p&gt;Another reason to switch to svn ...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-630220382204923261?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/630220382204923261/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=630220382204923261' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/630220382204923261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/630220382204923261'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/05/vs2010-mappings-for-solution-could-not.html' title='VS2010 The mappings for the solution could not be found'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-8447685655366913250</id><published>2010-04-19T18:33:00.001+01:00</published><updated>2010-04-19T18:39:39.142+01:00</updated><title type='text'>Coming to a browser near you – HTML 5</title><content type='html'>&lt;p&gt;Found this great web based slide show detailing the changes and additions that make up HTML 5.0&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_mKDkS8jszEg/S8yVWNWMtJI/AAAAAAAAAD4/-2vSOWif3fs/s1600-h/progress%5B2%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="progress" border="0" alt="progress" src="http://lh6.ggpht.com/_mKDkS8jszEg/S8yVWjnHV2I/AAAAAAAAAD8/Kr4mpg2ps0w/progress_thumb.jpg?imgmax=800" width="244" height="214" /&gt;&lt;/a&gt; &lt;a href="http://lh4.ggpht.com/_mKDkS8jszEg/S8yUzypwZ9I/AAAAAAAAADw/M6x8sCX3ZDk/s1600-h/webworkers%5B5%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="webworkers" border="0" alt="webworkers" src="http://lh3.ggpht.com/_mKDkS8jszEg/S8yU0h9sgnI/AAAAAAAAAD0/KlH4FfxzbRg/webworkers_thumb%5B1%5D.jpg?imgmax=800" width="244" height="214" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://apirocks.com/html5/html5.html#slide1"&gt;http://apirocks.com/html5/html5.html#slide1&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;(Warning – not IE friendly)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-8447685655366913250?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/8447685655366913250/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=8447685655366913250' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8447685655366913250'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8447685655366913250'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/04/coming-to-browser-near-you-html-5.html' title='Coming to a browser near you – HTML 5'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_mKDkS8jszEg/S8yVWjnHV2I/AAAAAAAAAD8/Kr4mpg2ps0w/s72-c/progress_thumb.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-7884950985055212478</id><published>2010-04-19T18:30:00.001+01:00</published><updated>2010-04-19T18:30:39.127+01:00</updated><title type='text'>Great comparison of web browser performance</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Jason Gube at sixrevisions.com has posted a great &lt;a href="http://sixrevisions.com/infographics/performance-comparison-of-major-web-browsers/" target="_blank"&gt;article comparing browser performance&lt;/a&gt;. It pretty much echos day to day performance I’m seeing and backs up my decision to switch to Chrome.&lt;/p&gt;  &lt;img src="http://images.sixrevisions.com/2009/10/15-03_performance_comparison_of_web_browsers_large.jpg" /&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-7884950985055212478?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/7884950985055212478/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=7884950985055212478' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/7884950985055212478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/7884950985055212478'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/04/great-comparison-of-web-browser.html' title='Great comparison of web browser performance'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-3856944432671192679</id><published>2010-04-09T22:59:00.001+01:00</published><updated>2010-04-09T22:59:24.062+01:00</updated><title type='text'>Hitting the thousand mark on stack overflow</title><content type='html'>&lt;p&gt;The last few weeks have seen me actively participating in the community of programmer and pundits known as stackoverflow.com.&lt;/p&gt;  &lt;p&gt;I set myself a target of reaching 1000 rep in two weeks and Im really glad to say I managed to do it in 11 days, thanks in part to a particularly feisty conversation about team participation vs individualism and how it relates to building and testing software.&lt;/p&gt;  &lt;p&gt;My observations are that once a popular user (read user with high reputation or awards) replies to a question, most other users will jump on that band wagon. In practice, choosing other responses that are voted up even only once or twice may be more prudent. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-3856944432671192679?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/3856944432671192679/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=3856944432671192679' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/3856944432671192679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/3856944432671192679'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/04/hitting-thousand-mark-on-stack-overflow.html' title='Hitting the thousand mark on stack overflow'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-2220275348637784753</id><published>2010-02-24T21:43:00.001Z</published><updated>2010-02-24T21:43:07.716Z</updated><title type='text'>Javascript Localization using .Net Resource files and jQuery</title><content type='html'>As a web developer I'm shifting more and more code to the client, and a few weeks ago an interesting situation came up on a multi-lingual project. ie wanted to include translations for validation messages in jQuery code, but stay with the great .Net resource file model.  &lt;div&gt;&lt;/div&gt;  &lt;div&gt;What I came up with was a pattern using JQuery AJAX and JSON to download the resource file to the browser. The translations are available using the same object notation available on the server eg    &lt;div&gt;     &lt;pre style="width: 900px" class="code"&gt;// -- Localisation --
var localResources;
var globalResources;

//Sample JSON for javascript resource values eg {TrackDetail:{HideHelp:&amp;quot;Hide Help&amp;quot;, ShowHelp:&amp;quot;Show Help&amp;quot;}}
//Usage e.g: alert(localResources.TrackDetail.HideHelp);

//Load Localisation values into variables so that they can be available on the client
//Note that these values are loaded asynchronously, the code in the function will not run until the call has completed.
$.getJSON('Localisation.ashx?files=TrackDetail.aspx.resx&amp;amp;local=true', function(data) { localResources = data});
$.getJSON('Localisation.ashx?files=Errors.resx,Strings.resx', function(data) { globalResources = data});&lt;/pre&gt;
  &lt;/div&gt;

  &lt;div&gt;The code on the server reads the local or global resource file and streams the contents back as a JSON formatted object. Note that I opted to roll my own JSON serialization code instead of using one of the built-in serializers.&lt;/div&gt;

  &lt;div&gt;
    &lt;pre style="width: 900px" class="code"&gt;
Imports System.Web
Imports System.Web.Services
Imports System.Xml
Imports System.Resources
Imports System.Reflection

Public Class Localisation
  Implements System.Web.IHttpHandler

  Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
    Dim files As String = context.Request.QueryString(&amp;quot;files&amp;quot;)
    Dim local As String = context.Request.QueryString(&amp;quot;local&amp;quot;)
    Dim isLocal As Boolean
    Dim folder As String = &amp;quot;App_GlobalResources&amp;quot;
  
    context.Response.ContentType = &amp;quot;text/javascript&amp;quot;

    'Write out file as object
    context.Response.Write(&amp;quot;{&amp;quot;)

    'Determine if local resource file
    If local IsNot Nothing Then
      isLocal = CBool(local)
      If isLocal Then folder = &amp;quot;App_LocalResources&amp;quot;
    End If
    If files Is Nothing OrElse files.Length = 0 Then Throw New ArgumentException(&amp;quot;Parameter 'files' was not provided in querystring.&amp;quot;)
  
    Dim flag As Boolean = False
    For Each file As String In files.Split(&amp;quot;,&amp;quot;)
      If flag Then context.Response.Write(&amp;quot;,&amp;quot;)

      Dim className As String = file.Split(&amp;quot;.&amp;quot;)(0)
    
      'Write the class (name of the without any extensions) as the object
      context.Response.Write(className)
      context.Response.Write(&amp;quot;:{&amp;quot;)

      'Open the resx xml file
      Dim filePath As String = context.Server.MapPath(&amp;quot;~\&amp;quot; &amp;amp; folder &amp;amp; &amp;quot;\&amp;quot; &amp;amp; file)
      Dim document As New XmlDocument()
      Dim flag2 As Boolean = False
      document.Load(filePath)

      Dim nodes As XmlNodeList = document.SelectNodes(&amp;quot;//data&amp;quot;)

      For Each node As XmlNode In nodes

        'Write out the comma seperator
        If flag2 Then context.Response.Write(&amp;quot;,&amp;quot;)

        Dim attr As XmlAttribute = node.Attributes(&amp;quot;name&amp;quot;)
        Dim resourceKey As String = attr.Value
        context.Response.Write(resourceKey)
        context.Response.Write(&amp;quot;:&amp;quot;&amp;quot;&amp;quot;)

        'Write either the local or global value
        If isLocal Then
        context.Response.Write(HttpContext.GetLocalResourceObject(String.Format(&amp;quot;~/{0}&amp;quot;,    file.Replace(&amp;quot;.resx&amp;quot;, &amp;quot;&amp;quot;)), resourceKey)) 'Has to be full path to the .aspx page
        Else
          context.Response.Write(HttpContext.GetGlobalResourceObject(className, resourceKey))
        End If
        context.Response.Write(&amp;quot;&amp;quot;&amp;quot;&amp;quot;)

        'Flag that we need a comma seperator
        flag2 = True
      Next

      context.Response.Write(&amp;quot;}&amp;quot;)
      flag = True
    Next
  
    'End file
    context.Response.Write(&amp;quot;}&amp;quot;)
  End Sub

  Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
    Get
      Return True
    End Get
  End Property
End Class&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-2220275348637784753?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/2220275348637784753/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=2220275348637784753' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2220275348637784753'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2220275348637784753'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/02/javascript-localization-using-net.html' title='Javascript Localization using .Net Resource files and jQuery'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-5493985634645905505</id><published>2010-01-30T10:41:00.001Z</published><updated>2010-01-30T10:58:11.044Z</updated><title type='text'>jQuery, Google Closure and packing or minifying for performance?</title><content type='html'>&lt;p&gt;One thing I love about writing jQuery plug-ins is that every line of code counts towards performance. Its like real programming again. There are lots of difference ways in which you can improve performance but one must have is reducing the size of the JavaScript that comes down the wire from the server. &lt;/p&gt;  &lt;p&gt;There are two options to use when reducing JavaScript file size – packing and minifying. Packing uses a compression algorithm such as gzip to physical compress the file to it’s smallest possible size, whilst a decent minifyer removes whitespace, renames variables and internal functions and removes unreachable code.&lt;/p&gt;  &lt;p&gt;A while ago I came across &lt;a title="JavaScript Library Loading Speed" href="http://ejohn.org/blog/library-loading-speed/" target="_blank"&gt;this post by John Resig&lt;/a&gt; pointing out that although packing reduces the file size, it takes longer to ultimately execute, due to the repeated unpacking process every time the script is used.&lt;/p&gt;  &lt;p&gt;After reading this I started using the &lt;a title="Yahoo YUI Compressor" href="http://developer.yahoo.com/yui/compressor/" target="_blank"&gt;Yahoo YUI Compressor&lt;/a&gt; to minify all of my plug-ins, and it worked well, typically reducing file sizes by 70% or more.&lt;/p&gt;  &lt;p&gt;Looking through the jQuery 1.4 release notes this week I noticed that the jQuery team have switched to &lt;a title="Google Closure Compiler" href="http://code.google.com/closure/compiler/" target="_blank"&gt;Google Closure Compiler&lt;/a&gt;. This really is a great tool, for example it reduced my &lt;a title="Exclusive Checkbox plug-in" href="http://bloggingdotnet.blogspot.com/2009/03/exclusive-checkbox-jquery-plug-in.html"&gt;Exclusive Checkbox&lt;/a&gt; plug-in to only &lt;strike&gt;191&lt;/strike&gt; 211 bytes and the &lt;a title="MapReduce plug-in" href="http://bloggingdotnet.blogspot.com/2010/01/writing-map-reduce-jquery-plug-in.html"&gt;MapReduce plug-in&lt;/a&gt; to under 1k in size! You need to be careful when using the compiler with the advanced option (due to the possible renaming of public methods and variables), but this doesn't apply to plug-ins because of the use of a closure – perhaps this is the reason for the name of the compiler.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&lt;span style="background-color: #007dc5; color: #fff"&gt;&amp;nbsp;Update&amp;nbsp;&lt;/span&gt;&lt;/em&gt;&amp;#160; I had to abandon the use of the Advanced option for the Exclusive Checkbox plug-in, increasing the size from 191 to 211 bytes ...&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;a title="Online YUI compressor" href="http://refresh-sf.com/yui/" target="_blank"&gt;Online YUI compressor&lt;/a&gt;     &lt;br /&gt;&lt;a title="Google Closure compiler service" href="http://closure-compiler.appspot.com/home" target="_blank"&gt;Online Google Closure compiler service&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-5493985634645905505?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/5493985634645905505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=5493985634645905505' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/5493985634645905505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/5493985634645905505'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/01/jquery-google-closure-and-packing-or.html' title='jQuery, Google Closure and packing or minifying for performance?'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-8136186161230235925</id><published>2010-01-28T22:57:00.001Z</published><updated>2010-01-28T23:01:39.779Z</updated><title type='text'>Simple OOXML Updated</title><content type='html'>&lt;p&gt;Simple OOXML is a helper library that I wrote for the Open XML Format SDK 2.0 to make the creation of Open Office XML documents easier. You can modify or create any .docx or .xlsx document without Microsoft Word or Microsoft Excel or any other software (ideal for server environments).&lt;/p&gt;  &lt;p&gt;Version 2.1.3678 has been released over at &lt;a href="http://simpleooxml.codeplex.com/"&gt;Codeplex&lt;/a&gt; which works with the December CTP of the SDK.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-8136186161230235925?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/8136186161230235925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=8136186161230235925' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8136186161230235925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8136186161230235925'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/01/simple-ooxml-updated.html' title='Simple OOXML Updated'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-2986579088164115039</id><published>2010-01-28T22:39:00.001Z</published><updated>2010-01-30T11:09:35.602Z</updated><title type='text'>Batch conversion of docx files to pdf</title><content type='html'>&lt;p&gt;I recently worked on a project where we had over 50 .docx files to convert to .pdf. Now Office 2007 can do this for you, but only one file at a time. Luckily I was able to piece together some code (.net 3.5 csharp) which automated this process. Select a folder and all .doc and .docx files in the folder and any subfolder will be converted to pdf and saved with a .pdf extension.&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="pdfbatch" border="0" alt="pdfbatch" src="http://www.opencomponents.com/images/pdfbatch.png" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;You still need Office 2007 or later to do this – download the code here:&lt;/p&gt;  &lt;p&gt;&lt;a title="PDF Batch Converter" href="http://www.opencomponents.com/downloads/PDF Batch Converter.zip"&gt;http://www.opencomponents.com/downloads/PDF Batch Converter.zip&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Disclaimer: Use at own risk. No kittens were harmed during the making of this software.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-2986579088164115039?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/2986579088164115039/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=2986579088164115039' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2986579088164115039'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2986579088164115039'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/01/batch-conversion-of-docx-files-to-pdf.html' title='Batch conversion of docx files to pdf'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-3167548288522143541</id><published>2010-01-25T23:35:00.001Z</published><updated>2010-01-25T23:35:42.509Z</updated><title type='text'>MapReduce plug-in Release Candidate now available</title><content type='html'>&lt;p&gt;The MapReduce plug-in for jQuery has been uploaded to the following url:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://plugins.jquery.com/project/MapReduce"&gt;http://plugins.jquery.com/project/MapReduce&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The plug-in implements a a type of map/reduce algorithm that executes synchronously or asynchronously in the browse whilst minimising loss in performance. Supports long running operations without script timeout messages.&lt;/p&gt;  &lt;p&gt;Add map and reduce functions using the $.map and $.reduce methods and execute the functions using the $.execute method. Nested map and reduce functions are supported.&lt;/p&gt;  &lt;p&gt;The download contains two samples of finding the prime numbers in a sequence of numbers, one using a plain javascript function and one using map reduce to calculate this asynchronously without causing script timeout errors in the browser.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Options&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;The following options can be used when calling the &lt;i&gt;$.execute&lt;/i&gt; method:&lt;/p&gt;  &lt;p&gt;&lt;i&gt;&lt;strong&gt;async&lt;/strong&gt;&lt;/i&gt;: true or false - determines whether the $.execute method runs asynchronously (default false)    &lt;br /&gt;&lt;i&gt;&lt;strong&gt;race&lt;/strong&gt;&lt;/i&gt;: no of functions to call per iteration when &lt;i&gt;async&lt;/i&gt; is true (default 100)    &lt;br /&gt;&lt;i&gt;&lt;strong&gt;interval&lt;/strong&gt;&lt;/i&gt;: interval in ms between iterations when &lt;i&gt;async&lt;/i&gt; is true (default 1ms)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-3167548288522143541?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/3167548288522143541/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=3167548288522143541' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/3167548288522143541'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/3167548288522143541'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/01/mapreduce-plug-in-release-candidate-now.html' title='MapReduce plug-in Release Candidate now available'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-7423064026644415599</id><published>2010-01-23T13:58:00.001Z</published><updated>2010-01-30T11:01:39.110Z</updated><title type='text'>Introduction to Map Reduce using jQuery</title><content type='html'>&lt;p&gt;(Solved: How to avoid the &amp;quot;A script on this page is causing Internet Explorer to run slowly.&amp;quot; message) &lt;/p&gt;  &lt;p&gt;When &lt;a title="spellayt spell checking plug-in" href="http://bloggingdotnet.blogspot.com/2009/03/creating-spell-as-you-type-jquery-plug.html"&gt;writing the spellayt spell checking plug-in&lt;/a&gt;, I came across an interesting problem with long running JavaScript code – the browser would timeout and display a message box to the user with the text &amp;quot;A script on this page is causing Internet Explorer to run slowly.&amp;quot; Even worse, the dialog gives the user the option to cancel the script, potentially breaking your page.&lt;/p&gt;  &lt;p&gt;The solution was to use a timer to simulate a background thread, but the solution wasn’t generic and felt far from elegant. This week, with the &lt;a href="http://arstechnica.com/open-source/news/2010/01/googles-mapreduce-patent-what-does-it-mean-for-hadoop.ars"&gt;approval of Google’s Distributed MapReduce patent&lt;/a&gt;, I realised that a simpler non-distributed map reduce algorithm would enable the running of code both asynchronously and over long periods of time in JavaScript without complex state management and stack issues.&lt;/p&gt;  &lt;p&gt;Map reduce works by taking key/value pairs and running them through a function to create intermediate key/value pairs until all the data has been processed (map). The data is then filtered (reduced) to obtain a result. Joel Spolsky (Joel on Software) has a &lt;a href="http://www.joelonsoftware.com/items/2006/08/01.html"&gt;nice article&lt;/a&gt; that explains the fundamentals behind map reduce. &lt;/p&gt;  &lt;h4&gt;Sieve of Eratosthenes&lt;/h4&gt;  &lt;p&gt;The Sieve of Eratosthenes is an algorithm for finding prime numbers, &lt;a href="http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes"&gt;this Wikipedia article&lt;/a&gt; has a&amp;#160; visual explanation of how it works. Its a great example of something that is processor intensive but can be broken up into smaller steps.&lt;/p&gt;  &lt;p&gt;A standard JavaScript implementation (using Euler’s optimisation) would look something like this:&lt;/p&gt;  &lt;pre class="code"&gt;function eratosthenesSieve(upperBound) {

   var upperBoundSquareRoot = Math.floor(Math.sqrt(upperBound), 0);
   var isComposite = [];

   for (var m = 2; m &amp;lt;= upperBoundSquareRoot; m++) {

      if (!isComposite[m]) {

         _primes.push(m);

         for(var k = m * m; k &amp;lt;= upperBound; k += m) {
            isComposite[k] = true;
         }
      }
   }

   for(m = upperBoundSquareRoot; m &amp;lt;= upperBound; m++) {
      if (!isComposite[m]) _primes.push(m);
   }
}&lt;/pre&gt;

&lt;p&gt;The &lt;em&gt;isComposite&lt;/em&gt; variable contains an array of boolean indicating whether is element is prime (false) or not (true). The &lt;em&gt;_primes&lt;/em&gt; array also contains the prime numbers. 

  &lt;br /&gt;&lt;/p&gt;

&lt;h4&gt;Redefining the problem as a set of Map / Reduce functions&lt;/h4&gt;

&lt;p&gt;The great thing about map/reduce is that once the problem is defined as a set of functions, it doesn’t matter how the map/reduce is implemented, you should always get the same results. Usually, each for loop will result in a matching &lt;em&gt;$.map&lt;/em&gt; or &lt;em&gt;$.reduce&lt;/em&gt; function. Reorganising the &lt;em&gt;eratosthenesSieve&lt;/em&gt; function to use map reduce requires the following steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;Map&lt;/em&gt;&lt;/strong&gt; an array containing the initial values – in this case the index of the array is the number we are testing, and a boolean will indicate whether it is a prime or composite number. &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;&lt;em&gt;Map&lt;/em&gt;&lt;/strong&gt; each prime up to the square root of the upper bound. For each &lt;strong&gt;&lt;em&gt;Map &lt;/em&gt;&lt;/strong&gt;that is a prime, strike out it’s square multiplied by the remaining numbers until the upper bound is reached. &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;&lt;em&gt;Reduce&lt;/em&gt;&lt;/strong&gt; the array by totalling the number of primes (false values) left. &lt;/li&gt;

  &lt;li&gt;Execute the functions to return the result. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code now looks like this:&lt;/p&gt;

&lt;pre class="code"&gt;function eratosthenesSieve2(upperBound) {

   //Create an array of booleans representing prime = false
   var composites = new Array(upperBound);

   //Set all to prime (false) to start
   $.map(composites, function(index, value) {
      return false;
   });

   //Calculate the sqr root of the upper bound
   var upperBoundSquareRoot = Math.floor(Math.sqrt(upperBound), 0);

   //For each item, calculate the non primes
   $.map(composites, function(index, value) {

      if (index &amp;lt; 2 || index &amp;gt; upperBoundSquareRoot) return value;

      //If the array item is a prime then map all non primes
      if (value == false) {

         var k = index * index;

         $.map(composites, function(index2, value2) {

            if (index2 == k) {

               k += index;
               return true;
            }
            return value2;
         });
      }
      return value;
   });
                
   //Count only the primes
   $.reduce(composites, 0, function(index, value, result) {

      if (index &amp;gt;= 2 &amp;amp;&amp;amp; value == false) result++;
      return result;
   });

   return $.execute();
}&lt;/pre&gt;

&lt;p&gt;The first &lt;em&gt;$.map&lt;/em&gt; function initialises the &lt;em&gt;composites&lt;/em&gt; array. The second and third map &lt;em&gt;$.map &lt;/em&gt;strikes out multiples of primes. The &lt;em&gt;$.reduce&lt;/em&gt; function counts the remaining primes by accumulating the &lt;em&gt;result&lt;/em&gt; variable. Note the extra work we have to do to simulate the functionality of the for loop and when we haven’t performed any processing we return the same value that was passed into the function.&lt;/p&gt;

&lt;p&gt;Finally, we &lt;em&gt;$.execute&lt;/em&gt; the functions. If you attach a debugger to the script, you’ll notice the functions only start executing once this method is called.&lt;/p&gt;

&lt;p&gt;Download the plug-in code from the &lt;a href="http://plugins.jquery.com/project/MapReduce"&gt;Map Reduce plug-in page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next time, I’ll dive into the details of implementing the Map Reduce plug-in and the pitfalls encountered along the way.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-7423064026644415599?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/7423064026644415599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=7423064026644415599' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/7423064026644415599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/7423064026644415599'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/01/writing-map-reduce-jquery-plug-in.html' title='Introduction to Map Reduce using jQuery'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-3349779266347520203</id><published>2010-01-23T08:10:00.001Z</published><updated>2010-01-23T08:16:54.770Z</updated><title type='text'>Object Data Blocks 2.0 Release Candidate</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Object Data Blocks is object persistence style ORM for new database implementations. Define you data layer using classes to define tables and fields and decorate them with attributes to define keys, indexes, constraints etc. A provider model for a specific relational database (currently sql server 2005 or later) takes care of the rest. Any changes to the assembly can be deployed at runtime seamlessly to the database without any loss of data.&lt;/p&gt;  &lt;p&gt;Read more information and view code samples &lt;a title="Object Data Blocks Documentation" href="http://objectdatablocks.codeplex.com/documentation"&gt;here&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Object Data Blocks is released under the GPL 2.0 licence and is hosted at &lt;a title="http://objectdatablocks.codeplex.com" href="http://objectdatablocks.codeplex.com"&gt;http://objectdatablocks.codeplex.com&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-3349779266347520203?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/3349779266347520203/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=3349779266347520203' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/3349779266347520203'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/3349779266347520203'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2010/01/object-data-blocks-20-release-candidate.html' title='Object Data Blocks 2.0 Release Candidate'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-3051646056739070118</id><published>2009-10-06T22:50:00.001+01:00</published><updated>2009-10-06T22:51:00.273+01:00</updated><title type='text'>Simple OOXML 2.1.3565 Released</title><content type='html'>&lt;p&gt;Create .xlsx and .docx documents from templates or from new without Microsoft Excel or Microsoft Word. This release fixes previously reported issues and adds the following functionality&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Works with August 2009 CTP of the Open XML SDK. &lt;/li&gt;    &lt;li&gt;&lt;i&gt;DeleteRow&lt;/i&gt; and &lt;i&gt;DeleteRows&lt;/i&gt; from a Worksheet &lt;/li&gt;    &lt;li&gt;Use FindColumn to retrieve a column from a Worksheet e.g. to set a column width. &lt;/li&gt; &lt;/ul&gt;  &lt;br /&gt;The following issues have been fixed   &lt;ul&gt;   &lt;li&gt;id# 2227 - PasteDataTable results in a corrupt .xlsx file when pasting a DataTable that contains typeof(float) columns with a CultureInfo other than &amp;quot;en-US&amp;quot; &lt;/li&gt;    &lt;li&gt;id# 2291 - PasteDataTable fails with adjacent existing data &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;   &lt;br /&gt;Note: Download the source code to view examples and unit tests with lots of sample code.&lt;/p&gt;  &lt;p&gt;&lt;a title="http://simpleooxml.codeplex.com" href="http://simpleooxml.codeplex.com"&gt;http://simpleooxml.codeplex.com&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-3051646056739070118?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/3051646056739070118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=3051646056739070118' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/3051646056739070118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/3051646056739070118'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/10/simple-ooxml-213565-released.html' title='Simple OOXML 2.1.3565 Released'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-529655993877144513</id><published>2009-10-06T22:47:00.001+01:00</published><updated>2009-10-06T22:48:59.766+01:00</updated><title type='text'>Announcing the release of the jQuery databind plug-in</title><content type='html'>&lt;p&gt;The databind plug-in allows you to automatically bind the contents of an object in JavaScript (usually retrieved using AJAX/JSON). &lt;/p&gt;  &lt;p&gt;Each key in the object is used as an id (.example) selector within the selector provided e.g.&lt;/p&gt;  &lt;pre&gt;var data = { title: 'Lorem ipsum', text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.' };
$(document).binddata(data);&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;&amp;lt;body&amp;gt; 
    &lt;br /&gt;&amp;lt;div&amp;gt; 

    &lt;br /&gt;&amp;lt;p id=&amp;quot;title&amp;quot; style=&amp;quot;font-weight:bold&amp;quot;&amp;gt;&amp;lt;/p&amp;gt; 

    &lt;br /&gt;&amp;lt;p id=&amp;quot;text&amp;quot;&amp;gt;&amp;lt;/p&amp;gt; 

    &lt;br /&gt;&amp;lt;/div&amp;gt; 

    &lt;br /&gt;&amp;lt;/body&amp;gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Provides the output visible at &lt;a href="http://www.opencomponents.net/databind/example1.htm"&gt;http://www.opencomponents.net/databind/example1.htm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More complicated examples include the ability to bind arrays of data and objects to tables and unbind forms into json.&lt;/p&gt;

&lt;p&gt;More examples and code can be downloaded from: &lt;a href="http://www.opencomponents.net/databind/"&gt;http://www.opencomponents.net/databind/&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-529655993877144513?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/529655993877144513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=529655993877144513' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/529655993877144513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/529655993877144513'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/10/announcing-release-of-databind-plug-in.html' title='Announcing the release of the jQuery databind plug-in'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-5008569381717568607</id><published>2009-06-24T21:42:00.004+01:00</published><updated>2009-06-24T22:29:07.034+01:00</updated><title type='text'>Databinding with jQuery</title><content type='html'>&lt;p&gt;I guess the title of this blog should be "Writing a new jQuery plug-in every week" - that's all I seem to be doing right now - but I keep finding functionality that isn't available as a plug-in, or has just been plain poorly implemented.&lt;/p&gt; 

&lt;p&gt;I'm using jQuery UI for a web project right now, and currently it's missing the ability to turn an html table into a grid styled using the UI css. Now there are some good lightweight plug-ins out there, such as &lt;a href="http://tablesorter.com/docs/"&gt;tablesorter&lt;/a&gt; and I didnt want to reinvent the wheel. Nor did I want to use a heavy wieght plugin such as jqGrid that is impressive but tries to do too much, especially when what you're really only after is a styled table.&lt;/p&gt;

&lt;p&gt;To this end, I'm currently workign on the &lt;a href="http://plugins.jquery.com/project/tablegrid"&gt;TableGrid plug-in&lt;/a&gt;, that uses the guidelines set out by the jQuery UI development team on how to style tables, whilst incorporating the work done by other developers to enable scrolling, paging, sorting etc. (&lt;a href="http://www.opencomponents.net/tablegrid/"&gt;Example1&lt;/a&gt;, &lt;a href="http://www.opencomponents.net/tablegrid/page.htm"&gt;Example2&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Which brings me to the subject of this post. I didn't agree with the mechanism used in other grid plug-ins where the data retrieval element is included inside the plug-in, to load data via ajax or some other method. What if I also wanted to load data into say my form?&lt;/p&gt;

&lt;p&gt;Following my approach of a super plug-in above, I realised I would first need a data binding plug-in so that developers could load table data on-the-fly. A quick look confirmed there was nothing out there that could be used.&lt;/p&gt;


&lt;p&gt;&lt;span style="font-weight:bold;"&gt;The databind plug-in&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.opencomponents.net/databind/"&gt;View examples and download.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the databind plug-in to bind a json object to any html element that has text, is an input or is a table/tbody. Each key (property name) is matched to the id of an element within the target of the &lt;span style="font-style:italic;"&gt;databind&lt;/span&gt; function. The inner text can be used, or the value of an input element - the plug-in is smart enough to decide. Pass array or a collection of objects to the plug-in to bind rows of data to a table or table body.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-5008569381717568607?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/5008569381717568607/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=5008569381717568607' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/5008569381717568607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/5008569381717568607'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/06/databinding-with-jquery.html' title='Databinding with jQuery'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-4829387463607070890</id><published>2009-06-10T19:40:00.006+01:00</published><updated>2009-06-10T19:53:59.120+01:00</updated><title type='text'>UpdatePanel plug-in 1.0.0 released.</title><content type='html'>In a &lt;a href="http://bloggingdotnet.blogspot.com/2009/05/introducing-updatepanel-plug-in.html"&gt;previous post&lt;/a&gt;, I introduced a plug-in that makes it simple for developers to integrate jQuery function calls with Microsoft Ajax UpdatePanels.

The final version has been released today to both &lt;a href="http://plugins.jquery.com/project/updatepanelplugin"&gt;http://plugins.jquery.com&lt;/a&gt; and &lt;a href="http://updatepanelplugin.codeplex.com/"&gt;http://www.codeplex.com&lt;/a&gt;. 

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Functions&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;.panelCreated(fn) - called when the UpdatePanel is created on a page&lt;/li&gt;
&lt;li&gt;.panelUpdated(fn) - called when the panel is updated during a asynchronous (ajax) postback.&lt;/li&gt;
&lt;li&gt;.panelReady(fn) - called when the UpdatePanel is first created or updated.&lt;/li&gt;
&lt;li&gt;.beginRequest(fn) -  called before the processing of an asynchronous postback starts and the postback request is sent to the server.&lt;/li&gt;
&lt;li&gt;.initializeRequest(fn) - called during the initialization of the asynchronous postback. Allows the cancellation of the request.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The download includes the script file and a simple example.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-4829387463607070890?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/4829387463607070890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=4829387463607070890' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/4829387463607070890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/4829387463607070890'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/06/updatepanel-plug-in-100-released.html' title='UpdatePanel plug-in 1.0.0 released.'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-8405162918392535384</id><published>2009-05-17T21:30:00.015+01:00</published><updated>2009-05-17T22:07:15.353+01:00</updated><title type='text'>Getting started with the Simple OOXML library.</title><content type='html'>&lt;p&gt;The Simple OOXML library allows you to create Word Processing (.docx) and Spreadsheet (.xlsx) documents quickly and easily, without having to understand the complex nature of the underlying Xml formats. Because the documents are (mostly) pure xml, these documents can be created in environments even where there is no Microsoft Office installation, such as on a web server.
&lt;/p&gt;&lt;p&gt;Whilst the Open Xml Format SDK provides a convenient object wrapper around the xml specification, it is still far from easy to create these documents. Even with a sound understanding of the specification, the Simple OOXML library tool is still useful in providing wrapper functionality to developers, without having a noticeable performance overhead.
&lt;/p&gt;&lt;p&gt;To use the Simple OOXML library, you need to be using .Net Framework 3.5 (or later). You'll also need to download the Open Xml Format SDK v 2.0. The current release is the April 2009 CTP and is available from the Microsoft website here: &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0"&gt;http://www.microsoft.com/downloads/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0&lt;/a&gt;. Obviously this is pre-release code but so far I've found the CTP releases very stable and haven't found any problems or bugs. Finally, you will need to download a copy of the latest Simple OOXML library release, available at CodePlex: &lt;a href="http://simpleooxml.codeplex.com/Release/ProjectReleases.aspx"&gt;http://simpleooxml.codeplex.com/Release/ProjectReleases.aspx.&lt;/a&gt; Since Simple OOXML is open source and distributed under the LGPL licence, you can use and distribute the binary with any application, commercial or otherwise. You can also have a look at the C# code to see how the library operates.&lt;/p&gt;
&lt;p&gt;To demonstrate the capabilities of the Simple OOXML library, I'll show you how a number of ways to work with Spreadsheet documents by creating a new ASP.NET Web Application in Visual Studio 2008. Follow these steps to get started:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;Create a new ASP.NET Web Application.&lt;/li&gt;
&lt;li&gt;Include references to &lt;em&gt;DocumentFormat.OpenXml&lt;/em&gt; from the SDK and the &lt;em&gt;DocumentFormat.OpenXml.Extensions.dll&lt;/em&gt; from the Simple OOXML release.&lt;/li&gt;&lt;li&gt;Finally, add an asp:Button control to the &lt;em&gt;default.aspx&lt;/em&gt; page that was created for you, so that we have somewhere to run our code from.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Make sure that you are using the following namespaces:&lt;/p&gt;
&lt;pre class="code"&gt;
using System.IO;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Extensions;
&lt;/pre&gt;
&lt;br/&gt;
&lt;p&gt;&lt;strong&gt;Creating a new Spreadsheet (.xlsx) document.
&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the click event handler for the button added above, add the following code:&lt;/p&gt;
&lt;pre class="code"&gt;
protected void Button1_Click(object sender, EventArgs e)
{
   MemoryStream stream = SpreadsheetReader.Create();
   SpreadsheetDocument doc = SpreadsheetDocument.Open(stream, true);
   WorksheetPart worksheetPart = SpreadsheetReader.GetWorksheetPartByName(doc, "Sheet1");
   WorksheetWriter writer = new WorksheetWriter(doc, worksheetPart);

   writer.PasteText("B2", "Hello World");

   //Save to the memory stream
   SpreadsheetWriter.Save(doc);
          
   //Write to response stream
   Response.Clear();
   Response.AddHeader("content-disposition", String.Format("attachment;filename={0}", "performance.xlsx"));
   Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

   stream.WriteTo(Response.OutputStream);
   Response.End();
}
&lt;/pre&gt;
&lt;p&gt;The first four lines of code create a stream containing a new document. This is loaded into a standard &lt;em&gt;SpreadsheetDocument&lt;/em&gt; object. A &lt;em&gt;WorksheetPart&lt;/em&gt; is then retrieved using a sheet name. Finally a &lt;em&gt;WorksheetWriter&lt;/em&gt; is created to enable us to write content to the worksheet. Next, the writer is used to paste the text "Hello World" into cell reference "B2". The static &lt;em&gt;Save&lt;/em&gt; method of the &lt;em&gt;SpreadsheetWriter &lt;/em&gt;class is used to update the stream with the document. The final five lines write the document stream to the browser setting the correct header values for a spreadsheet document.&lt;/p&gt;&lt;p&gt;In the next part of this series I'll demonstrate how to add numeric values, dates, shared text and datatables to worksheet documents.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-8405162918392535384?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/8405162918392535384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=8405162918392535384' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8405162918392535384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8405162918392535384'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/05/getting-started-with-simple-ooxml.html' title='Getting started with the Simple OOXML library.'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-2533832826515372336</id><published>2009-05-17T21:12:00.003+01:00</published><updated>2009-06-04T15:26:11.714+01:00</updated><title type='text'>Introducing the UpdatePanel plug-in.</title><content type='html'>&lt;p&gt;A while ago I blogged about Combining jQuery and Ms Ajax UpdatePanels. (&lt;a href="http://bloggingdotnet.blogspot.com/2009/03/combining-jquery-and-updatepanels.html"&gt;http://bloggingdotnet.blogspot.com/2009/03/combining-jquery-and-updatepanels.html&lt;/a&gt;). The main problem is that the UpdatePanel contents (a Div element) are replaced during each post back from the server and this means that jQuery looses all the references to the elements that have now been replaced in the DOM. My initial work around (and commonly found on the internet) is a little clumsy as it involves hooking the Ms Ajax &lt;em&gt;pageLoad&lt;/em&gt; event and placing all your jQuery code in a separate function which is then called from &lt;em&gt;$(document).ready&lt;/em&gt; or &lt;em&gt;pageLoad&lt;/em&gt;. Not pretty and not clever, especially when you have lots of jQuery and you only want to rerun the jQuery for the panel that has actually changed.
&lt;/p&gt;&lt;p&gt;My subsequent workarounds involved looking into the LiveQuery plug-in and looking at Live events which are new to jQuery 1.3, however I wasn't satisfied with the performance, and the code still looked ugly. And when it looks ugly that usually means there is a better way. What I wanted was code that looked and worked like any other jQuery plug-in. I needed to dig more deeply into the Ms Ajax client runtime. What I found was the PageRequestManager which is created once per page by Ms Ajax and fires the pageLoaded event on each post back. Hooking into this event allows us to examine the panels that have been updated or created. Using this information, it was then relatively straightforward to wrap a jQuery-style functional interface around these events.
&lt;/p&gt;&lt;p&gt;The UpdatePanel plug-in has the following three callbacks:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;.panelCreated(fn)&lt;/i&gt; - called when the UpdatePanel is created on a page&lt;/li&gt;&lt;li&gt;&lt;i&gt;.panelUpdated(fn)&lt;/i&gt; - called when the panel is updated during a asynchronous (ajax) postback.&lt;/li&gt;&lt;li&gt;&lt;i&gt;.panelReady(fn)&lt;/i&gt; - called when the UpdatePanel is first created or updated&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;You would generally use the panelReady event like so:&lt;/p&gt;
&lt;pre class="code"&gt;
$(document).ready(function() {
  //Place jQuery code here for elements selected outside the update panel
  $('#UpdatePanel1').panelReady(function() {
    //Place jQuery code here for elements selected inside the update panel
  });
});
&lt;/pre&gt;
&lt;p&gt;Generally, you would only want to use panelReady, which is called when the page is first loaded, or on each postback. A sample project as well as the script is available at &lt;a href="http://updatepanelplugin.codeplex.com/"&gt;http://updatepanelplugin.codeplex.com/&lt;/a&gt; or &lt;a href="http://plugins.jquery.com/project/updatepanelplugin"&gt;http://plugins.jquery.com/project/updatepanelplugin&lt;/a&gt;
   &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-2533832826515372336?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/2533832826515372336/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=2533832826515372336' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2533832826515372336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2533832826515372336'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/05/introducing-updatepanel-plug-in.html' title='Introducing the UpdatePanel plug-in.'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-9068905428224550939</id><published>2009-05-14T21:45:00.002+01:00</published><updated>2009-05-14T21:49:19.880+01:00</updated><title type='text'>Simple OOXML featured on OpenXmlDeveloper</title><content type='html'>Simple OOXML has been picked up by &lt;a href="http://openxmldeveloper.org"&gt;http://openxmldeveloper.org&lt;/a&gt; - Microsoft's website for promoting the Office Open Xml standard - and is mentioned on their home page. Expect to see an article appear on the site over the next few days.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-9068905428224550939?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/9068905428224550939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=9068905428224550939' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/9068905428224550939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/9068905428224550939'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/05/simple-ooxml-featured-on.html' title='Simple OOXML featured on OpenXmlDeveloper'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-8742759988282727727</id><published>2009-03-23T19:38:00.011Z</published><updated>2010-01-30T10:18:54.491Z</updated><title type='text'>Exclusive Checkbox jQuery Plug-in</title><content type='html'>Web interfaces like Hotmail often have a checkbox or radio button next to each item so that you can select that row. I've always disliked the radio button approach as you are limited to one row at a time eg when deleting. However sometimes you'd like to keep the checkboxes but have them behave like radio buttons. I couldn't find a really simple jQuery plug-in so I decided to write one. Original and minified versions at the end of the article.&amp;#160; &lt;p&gt;(&lt;b&gt;Update:&lt;/b&gt; I changed the code slightly to use the official jQuery extend function for the plug-in definition, and to include the usage in the comments.)&lt;/p&gt;  &lt;pre class="code"&gt;/*
* Exclusive Check Plugin v1.0.2
* Copyright (c) James Westgate
*
* @requires jQuery v1.3.2
*
* Dual licensed under the MIT and GPL licenses:
*   http://www.opensource.org/licenses/mit-license.ph
*   http://www.gnu.org/licenses/gpl.html
*
* @usage $('input:checked').exclusiveCheck();
* @usage $('form input:checked').exclusiveCheck();
* @usage $('table tbody input:checked').exclusiveCheck();
*
*/

//Create closure
(function($) {

    //Plugin definition
    $.fn.extend({

        exclusiveCheck: function() {

            var selector = $(this);

            //Loop through each item in the matched set and apply event handlers
            return this.each(function(i) {

                //When the checkbox gets clicked, uncheck other checkboxes
                $(this).click(function(event) {

                    var clicked = this;

                    //Uncheck all except current
                    if (this.checked) {
                        selector.each(function() {
                            if (this != clicked) this.checked = false;
                        });
                    }
                });
            });
        }
    });

// end of closure
})(jQuery);&lt;/pre&gt;

&lt;p&gt;&lt;a href="http://www.opencomponents.com/downloads/ExclusiveCheck plug-in.zip"&gt;Download original and minified version.&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-8742759988282727727?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/8742759988282727727/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=8742759988282727727' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8742759988282727727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8742759988282727727'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/03/exclusive-checkbox-jquery-plug-in.html' title='Exclusive Checkbox jQuery Plug-in'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-8736109565144492705</id><published>2009-03-05T21:56:00.014Z</published><updated>2009-03-26T17:34:59.583Z</updated><title type='text'>Introducing the Simple OOXML library</title><content type='html'>&lt;span xmlns=""&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;The new xml based document formats (.xlsx, .docx, .pptx etc) introduced with Office 2007 finally provided developers with the ability to create documents on a server without having to have either MS Word or Excel installed or to use a 3&lt;/span&gt;&lt;sup&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;rd&lt;/span&gt;&lt;/sup&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; party component.
&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;However producing this Xml is very complicated and it's certainly not a pretty format. The OOXML SDK goes as far as wrapping the Xml elements of the specification into a set of .net classes, but still falls short of the higher-level functionality required to actually create documents. Some open source libraries, such as ExcelPackage, are incomplete or discontinued and I wanted to create a simple, robust fully supported library that fits right into the object model of version 2.0 of the sdk.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:+0;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;span style="color:black;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:+0;"&gt;&lt;span style="font-size:+0;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;The &lt;/span&gt;&lt;a href="http://simpleooxml.codeplex.com/"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;http://simpleooxml.codeplex.com&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; project addresses this issue by providing a layer of abstraction over version 2.0 as a set of simple classes and methods to create new spreadsheet (Excel) and word processing (Word) files with the following benefits:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;No Excel or Word is required on the server.&lt;/li&gt;&lt;li&gt;No in-depth knowledge of the OOXML standard or SDK is required.&lt;/li&gt;&lt;li&gt;New documents can be created or existing templates can be modified.&lt;/li&gt;&lt;li&gt;Ability to stream directly to the browser or between servers&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:+0;"&gt;&lt;span style="font-size:+0;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;High perfor&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:+0;"&gt;&lt;span style="font-size:+0;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;mance&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;The following table outlines the classes used to create and read office open xml documents:&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;div style="TEXT-DECORATION: underline"&gt;&lt;table style="BORDER-COLLAPSE: collapse" border="0"&gt;&lt;colgroup&gt;&lt;col style="WIDTH: 170px"&gt;&lt;col style="WIDTH: 469px"&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: black 0.5pt solid; PADDING-LEFT: 7px; BORDER-LEFT: black 0.5pt solid; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;strong&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;DocumentReader&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: black 0.5pt solid; PADDING-LEFT: 7px; BORDER-LEFT: medium none; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;Static functions used to create new word processing documents.
&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: black 0.5pt solid; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;strong&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;DocumentWriter&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: medium none; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;Static functions used to paste text items into a word processing document using bookmarks and save the document to a stream or file.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: black 0.5pt solid; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;strong&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;SpreadsheetReader&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: medium none; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;Static functions to create new spreadsheet documents and to retrieve worksheet, column and row references. Get style and defined name range parts. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: black 0.5pt solid; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;strong&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;SpreadsheetStyle&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: medium none; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;Create an object that can set style, color and font information in a spreadsheet and retrieve and compare style parts.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: black 0.5pt solid; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;strong&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;SpreadsheetWriter&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: medium none; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;Static functions to create style parts in a spreadsheet, control shared strings and helper functions when working with spreadsheet documents&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: black 0.5pt solid; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;strong&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;WorksheetReader&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: medium none; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;High level and static fucntions to retrieve cell and style information from a worksheet. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: black 0.5pt solid; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;strong&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;WorksheetWriter&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: medium none; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;p&gt;&lt;span style="color:black;"&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;High level and static functions to write text, numeric and datatable based values to a worksheet. Draw borders, insert rows, merge cells and set print areas.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: black 0.5pt solid; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;span class="Apple-style-span"   style="font-family:arial;font-size:small;"&gt;&lt;/span&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: black 0.5pt solid; PADDING-RIGHT: 7px; BORDER-TOP: medium none; PADDING-LEFT: 7px; BORDER-LEFT: medium none; BORDER-BOTTOM: black 0.5pt solid"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p style="TEXT-DECORATION: underline"&gt;&lt;span style="font-size:+0;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;span style="font-size:+0;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;span style="font-size:+0;"&gt;&lt;span style="font-size:+0;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;Simple OOXML requires version 2.0 of the OOXML SDK which can be found here:

&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;div&gt;&lt;span xmlns=""&gt;&lt;span style="font-size:+0;"&gt;&lt;span style="font-size:+0;"&gt;&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;http://www.microsoft.com/downloads/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;span xmlns=""&gt;&lt;span style="font-size:+0;"&gt;&lt;span style="font-size:+0;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;In my next series of posts, I'll show how easy and powerful this library is.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0"&gt; &lt;p&gt;&lt;span style="color:#30332d;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span style="font-size:+0;"&gt;&lt;span style="font-size:+0;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/a&gt;&lt;/span&gt;&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0"&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="FONT-STYLE: italic"&gt;(The DocumentFormat.OpenXml.Extensions.Testing unit test project included in the source on Codeplex contains samples of almost every type of operation supported by the library.)&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-8736109565144492705?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/8736109565144492705/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=8736109565144492705' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8736109565144492705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8736109565144492705'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/03/introducing-simple-ooxml-library.html' title='Introducing the Simple OOXML library'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-7356571555813897975</id><published>2009-03-03T21:37:00.011Z</published><updated>2009-05-14T08:58:25.265+01:00</updated><title type='text'>Creating a Spell as you type jQuery plug-in - Part 3</title><content type='html'>&lt;span xmlns=""&gt;&lt;p&gt;In my previous posts, I described how to create a jQuery plug-in called spellayt (Spell as you Type) that provided spelling correction to Internet Explorer users.&lt;/p&gt;&lt;p&gt;
&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;The timed work queue pattern&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The performance of initial implementations of the spellayt  were disappointing, you would often wait a few seconds whilst the latest words typed in were checked and highlighted. Even worse, you could get the dreaded "A script on this page is causing Internet Explorer to run slowly." message. I soon found I needed a solution usng a multi-threaded approach so that the spell checking could happen in the background to the user typing. A quick check confirmed that JavaScript does not support more than one thread although the search did reveal some interesting options.&lt;/p&gt;&lt;p&gt;Because the word breaking and dictionary checks could potentially take a few seconds each, I decided that only one word could be checked against the dictionary at a time. The trick is to use lots of short functions at regular intervals, so that the UI can continue to process events from the user.&lt;/p&gt;&lt;p&gt;This gave me the idea for implementing the following pattern which I call the Timed Work Queue pattern. In the plug-in, a call to &lt;em&gt;doWordBreak()&lt;/em&gt; starts the ball rolling when the input receives focus:&lt;/p&gt;

&lt;pre class="code"&gt;
//Global values
$.fn.spellayt.global = {
  options: null,         //plug-in options
  wordQueue: new Array() //word breaking queue
};

$(this).focus(function(event) {

  //Start the the word breaking queue
  doWordBreak();
});
&lt;/pre&gt;
&lt;p&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;The &lt;/span&gt;&lt;em&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;doWordBreak()&lt;/span&gt;&lt;/em&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; function is actually really straight forward. It shifts the oldest work item off the queue and executes that item. The function then calls itself again in a predetermined time (50 ms seems to be a good figure)&lt;/span&gt;&lt;/p&gt;
&lt;pre class="code"&gt;
//Pops the next word off the word breaking queue
function doWordBreak() {
  var g = $.fn.spellayt.global;

  //Get an item off the queue
  if (g.wordQueue.length &amp;gt; 0) {
     var work = g.wordQueue.shift();
     work.call(work.data);
  }

  //Process the next item
  g.wordTimer = setTimeout(function() { doWordBreak(); }, g.options.milliseconds);
}
&lt;/pre&gt;
&lt;p&gt;So what does the object look like that we are pushing onto the work queue ? The breaktext function breaks text into sentences and words, and loads words onto the queue one at a time&lt;/p&gt;
&lt;pre class="code"&gt;
//Check the spelling for all words in the input provided
function breakText(input) {
  if (input == null) return;

  //Split text into a 2d array of sentences and words
  var sentences = splitWords(input.value);

  if (sentences == null) return;

  //Add the call to checkWord() to the work queue for each word in each sentence
  for (var i = 0; i &amp;lt; sentences.length; i++) {

    $.fn.spellayt.global.wordQueue.push({ call: function(parm) { checkSentence(parm); }, data: sentences[i] });

  }
};
&lt;/pre&gt;
&lt;p&gt;The object consists of a function pointer &lt;em&gt;call &lt;/em&gt;and data to be passed to the function &lt;em&gt;data&lt;/em&gt;. As long as your function has a single parameter (e.g. a JSON object), you could queue up any combination of functions to execute.&lt;/p&gt;&lt;p&gt;This is really handy as you can push completely different function pointers onto the queue, as well as putting some items ahead of others with a higher priority.&lt;/p&gt;&lt;/span&gt;
&lt;div&gt;&lt;a href="http://bloggingdotnet.blogspot.com/2009/01/creating-spell-as-you-type-jquery.html"&gt;Creating a spell as you type jQuery plugin - part 1&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://bloggingdotnet.blogspot.com/2009/02/creating-spell-as-you-type-jquery.html"&gt;Creating a spell as you type jQuery plugin - part 2&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;Download: &lt;a href="http://plugins.jquery.com/project/spellayt"&gt;http://plugins.jquery.com/project/spellayt&lt;/a&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-7356571555813897975?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/7356571555813897975/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=7356571555813897975' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/7356571555813897975'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/7356571555813897975'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/03/creating-spell-as-you-type-jquery-plug.html' title='Creating a Spell as you type jQuery plug-in - Part 3'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-6225033496493675493</id><published>2009-03-03T18:38:00.003Z</published><updated>2009-04-21T22:30:31.055+01:00</updated><title type='text'>Compress ASP.NET response streams</title><content type='html'>When sending large amounts of data from IIS to the browser, it is sometimes worth compressing certain types of data such as text documents. 
Although compression is not supported by IIS 6.0, most browsers support basic gzip compression and they notify the server of this ability by sending a header in each request.

The following piece of code shows how to use the System.IO.Compression namespace to add a filter to the output stream that compresses the output whilst checking and setting the correct headers. In this example, a .xls document containing in a string is being sent to the client:

&lt;pre class="code" style="width:900px"&gt;
&lt;font size=3&gt;
    'Send response with content type to display as MS Excel
    context.Response.Clear()
    context.Response.Buffer = True

    context.Response.AddHeader("content-disposition", String.Format( "attachment;filename={0}", fileName))
    context.Response.ContentEncoding = Encoding.UTF8

    context.Response.Cache.SetCacheability(HttpCacheability.Private)

    'Compress the output as it may be very large
    'When flushing or closing+ending the stream, the compression filter does not have a chance to write the compression footer
    'Therefore, make sure the compression filter stream is closed before flushing
    AddCompression(context)

    context.Response.ContentType = "application/vnd.ms-excel"

    'Write to response
    context.Response.Write(_reportXmlss)

    'context.Response.Flush() 'Do not flush if using compression
    'context.Response.Close()
    context.Response.End()
&lt;/font&gt;
&lt;/pre&gt;

The &lt;i&gt;AddCompression&lt;/i&gt; method checks the appropriate headers and adds a compression filter stream to the output:

&lt;pre class="code"&gt;
&lt;font size=3&gt;'Add compression to the response stream
    Public Sub AddCompression(ByVal context As HttpContext)

        Dim acceptEncoding As String = context.Request.Headers("Accept-Encoding")
        If acceptEncoding Is Nothing OrElse acceptEncoding.Length = 0 Then Return

        'Convert to lower to check
        acceptEncoding = acceptEncoding.ToLower

        'Gzip or Compress compression
        'Compress compression is quicker and performs better compression so try that first
        If (acceptEncoding.Contains("deflate")) Then

            context.Response.Filter = New DeflateStream(context.Response.Filter, CompressionMode.Compress)
            context.Response.AppendHeader("Content-Encoding", "deflate")

        ElseIf acceptEncoding.Contains("gzip") Then

            context.Response.Filter = New GZipStream(context.Response.Filter, CompressionMode.Compress)
            context.Response.AppendHeader("Content-Encoding", "gzip")

        End If

    End Sub&lt;/font&gt;
&lt;/pre&gt;

To check if compression is being used, I use the awesome &lt;a href="http://www.httpwatch.com/"&gt;HttpWatch&lt;/a&gt;, which shows useful information such as headers, amount of compression and bytes sent/received.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-6225033496493675493?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/6225033496493675493/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=6225033496493675493' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/6225033496493675493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/6225033496493675493'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/03/when-sending-large-amounts-of-data-from.html' title='Compress ASP.NET response streams'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-2513785587691301024</id><published>2009-03-02T21:01:00.004Z</published><updated>2009-03-24T20:11:26.506Z</updated><title type='text'>Combining jQuery and UpdatePanels</title><content type='html'>Some of you may have noticed using Microsoft Ajax and jQuery together works fine until you do a post back in e.g. an UpdatePanel and the jQuery plugins referencing elements contained in the panel stop working. I believe these are the event handlers which are bound to the old DOM elements by jQuery. &lt;div&gt;
&lt;/div&gt;&lt;div&gt;A simple work around is to combine a bit of Microsoft AJAX with jQuery - in this example I rerun all my jQuery that would normally reside in &lt;span class="Apple-style-span" style="  "&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;$(document).ready &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;after each ajax callback instead.&lt;/div&gt;&lt;pre&gt;
//Jquery document ready
$(document).ready(function(){
  RunScript();
})

//This is called after every page load by ajax
//It is used instead of the normal document ready function
function pageLoad(sender, arg) {
  if (arg.get_isPartialLoad()) {
      RunScript();
  };
}

//Main Jquery function
function RunScript() {
 //... normal jQuery code here etc
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-2513785587691301024?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/2513785587691301024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=2513785587691301024' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2513785587691301024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2513785587691301024'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/03/combining-jquery-and-updatepanels.html' title='Combining jQuery and UpdatePanels'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-2988727845523791550</id><published>2009-02-27T10:43:00.006Z</published><updated>2009-03-24T20:10:41.629Z</updated><title type='text'>Spell as you Type 1.0 plug-in released</title><content type='html'>Today I released the Spell as you Type jQuery plug-in. It provides inline (red wavy) spell checking and correction options to Internet Explorer users - functionality that is built into every other browser.&lt;div&gt;&lt;/div&gt;&lt;div&gt;Codeplex: &lt;a href="http://openspell.codeplex.com/"&gt;http://openspell.codeplex.com/&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Jquery: &lt;a href="http://plugins.jquery.com/project/spellayt"&gt;http://plugins.jquery.com/project/spellayt&lt;/a&gt;
&lt;div&gt;
&lt;/div&gt;&lt;div&gt;To use, simply call the spellayt function in jQuery&lt;/div&gt;&lt;pre class="code"&gt;
$(document).ready(function() {
$('#txtTextarea').spellayt();
})
&lt;/pre&gt;
&lt;div&gt;&lt;div&gt;The plug-in works fully on the client side. For more information, view my series on building this plug-in:&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://bloggingdotnet.blogspot.com/2009/01/creating-spell-as-you-type-jquery.html"&gt;Creating a spell as you type jQuery plugin - part 1&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://bloggingdotnet.blogspot.com/2009/02/creating-spell-as-you-type-jquery.html"&gt;Creating a spell as you type jQuery plugin - part 2&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://bloggingdotnet.blogspot.com/2009/03/creating-spell-as-you-type-jquery-plug.html"&gt;Creating a spell as you type jQuery plugin - part 3&lt;/a&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-2988727845523791550?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/2988727845523791550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=2988727845523791550' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2988727845523791550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2988727845523791550'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/02/spell-as-you-type-10-plug-in-released.html' title='Spell as you Type 1.0 plug-in released'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-2564109811995723544</id><published>2009-02-23T22:41:00.005Z</published><updated>2009-02-23T22:52:44.783Z</updated><title type='text'>Referencing a template file from a Visual Studio Unit Test</title><content type='html'>As part of the test suite for the &lt;a href="http://www.codeplex.com/simpleOOXML"&gt;Simple OOXML&lt;/a&gt; project, I need to reference a .docx file in a template folder in code - the problem being that every unit test is run in it's own output folder. The solution is to copy the template files to the output folder and retrieve the path from the test context:&lt;div&gt;
&lt;/div&gt;&lt;div&gt;1. Make sure the &lt;span class="Apple-style-span" style="font-style: italic;"&gt;TestContex&lt;/span&gt;t is set when the test is run by providing a TestContext property (this is now really simple in 3.5)&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    [TestClass()]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    public class SpreadsheetTests&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;        public TestContext TestContext { get; set; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;        ...&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;2. Make sure that the requested file is copied to the output folder by using the&lt;span class="Apple-style-span" style="font-style: italic;"&gt; DeploymentItem &lt;/span&gt;attribute&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    [TestMethod(), DeploymentItem("Templates\\template.xlsx")]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    public void WorksheetCopyTest()&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    {
&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="white-space: pre;"&gt;        &lt;/span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;3. Reference from code using the &lt;span class="Apple-style-span" style="font-style: italic;"&gt;TestContext.TestDeploymentDir&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;MemoryStream stream = SpreadsheetReader.Copy(string.Format("{0}\\template.xlsx", TestContext.TestDeploymentDir));&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'courier new';"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Note that even though we copied the template.xlsx file from the Templates folder, it ended up in the root output folder.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-2564109811995723544?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/2564109811995723544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=2564109811995723544' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2564109811995723544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/2564109811995723544'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/02/referencing-template-file-from-visual.html' title='Referencing a template file from a Visual Studio Unit Test'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-7651243553865069465</id><published>2009-02-19T15:55:00.010Z</published><updated>2009-04-21T22:32:04.498+01:00</updated><title type='text'>Writing an xlsx document to the response stream using ASP.NET and OOXML</title><content type='html'>Have spent a most frustrating day trying to output an Excel document created using OOXML sdk 2.0 in a web service to the web browser.&lt;div&gt;
&lt;/div&gt;&lt;div&gt;There are two elements to solving this problem&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Using a web service to transfer binary data&lt;/li&gt;&lt;li&gt;Opening Excel on the browser using the new xlsx format&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;This is actually pretty straight forward, as long as you use a byte array, the data is base64 encoded in the background automatically. On the client, use the right content type and use BinaryWrite instead of Write which is where I was having the problem:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;pre class="code"&gt;
Public Function PerformanceReportByUser(ByVal startDate As Date, ByVal endDate As Date) As Byte()
'Get the path to the template
Dim path As String = Server.MapPath("Templates/PerformanceByUserTemplate.xlsx")

'Get the stream containing the report package
Dim stream As MemoryStream = PerformanceReport.ExecuteByUser(path, startDate, endDate)
Return stream.ToArray()
End Function
&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;On the client, process the byte array. Note the content type, and use of BinaryWrite.

&lt;pre class="code"&gt;
Dim bytes() As Byte = portalservice.PerformanceReportByUser(CDate(ctlStartDate.Text), CDate(ctlEndDate.Text))

'Send response with content type to display as MS Excel
Response.Clear()
Response.Buffer = True
Response.AddHeader("content-disposition", String.Format("attachment;filename={0}", "performance.xlsx"))

'The following directive causes a open/save/cancel dialog for Excel to be displayed
Response.Cache.SetCacheability(HttpCacheability.Private)
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

'Write to response
Response.BinaryWrite(bytes)

'Response.Flush() 'Do not flush if using compression
'Response.Close()
Response.End()
&lt;/pre&gt;

&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-7651243553865069465?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/7651243553865069465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=7651243553865069465' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/7651243553865069465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/7651243553865069465'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/02/writing-xlsx-document-to-response-strem.html' title='Writing an xlsx document to the response stream using ASP.NET and OOXML'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-8236102440497638944</id><published>2009-02-10T13:36:00.012Z</published><updated>2009-04-21T22:40:40.320+01:00</updated><title type='text'>Creating a Spell as you type jQuery plugin - Part 2</title><content type='html'>In my &lt;a href="http://bloggingdotnet.blogspot.com/2009/01/creating-spell-as-you-type-jquery.html"&gt;previous post&lt;/a&gt;, I outlined solutions to the technical challenges I faced trying to implement a complete client side browser based spell checking solution.&lt;div&gt;
&lt;/div&gt;&lt;div&gt;With the technical challenges sorted, I looked into writing a JQuery plug-in. The &lt;a href="http://docs.jquery.com/Tutorials"&gt;tutorials&lt;/a&gt; on the JQuery website are a good place to start. I also found the &lt;a href="http://www.learningjquery.com/2007/10/a-plugin-development-pattern"&gt;plugin development pattern&lt;/a&gt; from &lt;a href="http://www.malsup.com/jquery/"&gt;Mike Alsup&lt;/a&gt; extremely useful. It helps you set up a closure to keep your methods and variables private as well as setting up defaults and options (passed in as a JSON). I won’t go into these details here as they are covered so well in the articles I’ve referenced here.&lt;strong&gt;&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;strong&gt;Detecting non-IE browsers in your plug-in.&lt;/strong&gt;

The first task once the structure of the plug-in was in place and the options set up was to block non IE clients from using the plug-in, or more simply to exit the main function without executing any additional code. This was made very simple by using the following built-in JQuery code&lt;span class="Apple-style-span"  style=" ;font-family:'courier new';"&gt;&lt;/span&gt;&lt;/div&gt;

&lt;pre class="code"&gt;
//This plugin is only for IE 6.0 + users
if (!.browser.msie) return;
if (.browser.version &lt;&gt; 6) return;
&lt;/pre&gt;

&lt;div&gt;&lt;span class="Apple-style-span"  style=" ;font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style=" font-weight: bold; font-family:verdana;"&gt;Loading the dictionary using JQuery AJAX&lt;span class="Apple-style-span" style="font-weight: normal; "&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;&lt;b&gt;
&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style=" ;font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style=" font-weight: bold; font-family:verdana;"&gt;&lt;span class="Apple-style-span" style="font-weight: normal; "&gt;The next item of code I wanted to tackle was getting the text-based dictionary into a structure the plug-in could understand and use. The $.ajax function provided an asynchronous way of retrieving data from the server. This is useful, because you don’t want the interface to be blocked whilst you potentially download a large file.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style=" ;font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style=" font-weight: bold; font-family:verdana;"&gt;&lt;span class="Apple-style-span" style="font-weight: normal; "&gt;The success function passes the data to the loadDictionary function. When successful, the .fn.spellayt.loaded() function is called if it has been set on the plug-in. This is similar the event model used in c# programming. Note that this is how function pointers are exposed outside of the plug-in. &lt;span class="Apple-style-span"  style=" ;font-family:Georgia;"&gt;&lt;span style="font-family:verdana;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style=" ;font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style=" font-weight: bold; font-family:verdana;"&gt;&lt;span class="Apple-style-span" style="font-weight: normal; "&gt;&lt;span class="Apple-style-span"  style=" ;font-family:Georgia;"&gt;&lt;span style="font-family:verdana;"&gt;The loadDictionary function loads the words in the dictionary into a three dimensional array, consisting of an array for the first letter of the word, the length and the matching words. In this way, the words starting with the same letter and of the same length can be checked very quickly, as they will most likely have the closest &lt;/span&gt;&lt;a href="http://en.wikipedia.org/wiki/Levenshtein_distance"&gt;&lt;span style="font-family:verdana;"&gt;Levenshtein&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:verdana;"&gt; distance.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:verdana;"&gt;
&lt;/span&gt;&lt;/div&gt;

&lt;pre class="code"&gt;
//Load the dictionary from a url
$.ajax({
  type: "GET",
  url: $.fn.spellayt.global.options.url,
  dataType: "text",
  success: function(data) {

    //Load the dictionary into the dictionaryArray.
    loadDictionary(data);
    if ($.fn.spellayt.loaded != null) $.fn.spellayt.loaded();

  },

  error: function(XMLHttpRequest, textStatus, errorThrown) {
    if ($.fn.spellayt.loadError != null) fn.spellayt.loadError(textStatus);
  }
&lt;/pre&gt;


&lt;div&gt;&lt;strong&gt;Highlighting words that have been misspellt&lt;/span&gt;&lt;/strong&gt;&lt;span style="font-family:verdana;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;"&gt;When the input gains focus, a few things need to happen on a regular basis as the user types&lt;/span&gt;
&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:verdana;"&gt;find new words to check&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:verdana;"&gt;check if a word exists in the dictionary&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:verdana;"&gt;Highlight misspellt words&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-family:verdana;"&gt;This is done by using the &lt;em&gt;setTimer&lt;/em&gt; and &lt;em&gt;setInterval&lt;/em&gt; functions – the difference between them is minimal - the one fires a function every x milliseconds, the other calls a function once after the next x seconds.&lt;/span&gt;&lt;/p&gt;&lt;pre class="code"&gt;
$(this).focus(function(event) {

var g = $.fn.spellayt.global;
if (g.ready) {
 g.current = this;

 //Set a timer to break words every 1 second
 g.breakTimer = setInterval(function() {
   if (g.wordQueue.length == 0) breakText(g.current); }, 1000);

 //Set a timer to highlight words every 1/2 second
 g.highlight = setInterval(function() {
   $.fn.spellayt.highlight(g.current); }, 500);

 //Start the the word breaking queue
 doWordBreak()
}
});
&lt;/pre&gt;


&lt;span style="font-family:verdana;"&gt;The sentence and word breaking is done by the use of regular expression parameters – this is incredibly useful – a word breaking regex can be set as a parameter for e.g. a different language.&lt;/span&gt;
&lt;p&gt;&lt;span style="font-family:Verdana;"&gt;In the next part of this series, Ill look into the problems I faced when the processor intensive code for word breaking and checking blocked the UI and how to emulate a multi-threaded environment in the current version of javascript.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:Verdana;"&gt;&lt;/span&gt; &lt;/p&gt;
&lt;div&gt;&lt;a href="http://bloggingdotnet.blogspot.com/2009/01/creating-spell-as-you-type-jquery.html"&gt;Creating a spell as you type jQuery plugin - part 1&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://bloggingdotnet.blogspot.com/2009/03/creating-spell-as-you-type-jquery-plug.html"&gt;Creating a spell as you type jQuery plugin - part 3&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div&gt;Download: &lt;a href="http://plugins.jquery.com/project/spellayt"&gt;http://plugins.jquery.com/project/spellayt&lt;/a&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-8236102440497638944?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/8236102440497638944/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=8236102440497638944' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8236102440497638944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/8236102440497638944'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/02/creating-spell-as-you-type-jquery.html' title='Creating a Spell as you type jQuery plugin - Part 2'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2513338198128732893.post-5155371758488596375</id><published>2009-01-30T10:11:00.014Z</published><updated>2009-04-21T22:37:51.700+01:00</updated><title type='text'>Creating a Spell as you type Jquery plugin - Part 1</title><content type='html'>Recently I've become enamoured with the Jquery library and we are now prefer it over the buggy Microsoft Ajax Control toolkit for things like modal dialogs, autocompletes, as well as hiding and showing divs etc. In short it rocks and I wish I'd found it sooner.&lt;div&gt;
&lt;/div&gt;&lt;div&gt;A client had the need to to implement spell as you type, complete with red underline "squiggles" in their web based CRM product running in IE6. An example of this can be found in most popular word processing packages, eg the screenshot from Microsoft Word 2007, but less so online in web pages etc.

&lt;a href="http://1.bp.blogspot.com/_mKDkS8jszEg/SYLTbszegwI/AAAAAAAAACA/2998tNy4GbI/s1600-h/word1.PNG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5297028584578122498" style="FLOAT: left; MARGIN: 0px 10px 10px 0px; WIDTH: 278px; CURSOR: hand; HEIGHT: 72px" alt="" src="http://1.bp.blogspot.com/_mKDkS8jszEg/SYLTbszegwI/AAAAAAAAACA/2998tNy4GbI/s320/word1.PNG" border="0" /&gt;&lt;/a&gt;




&lt;/div&gt;&lt;div&gt;Annoyingly, this functionality is built into Firefox and Chrome, but not into Internet Explorer - even the latest IE8 beta. A look around the web produced a couple of hopeful options - a third party component as well as toolbar and plugins from Google and Microsoft Live.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;The main problem we had with all of the above solutions was that they tried to overwrite the underlying input with a new element in the DOM. The font and layout settings were never copied correctly and caused the layout of the page to change. Some browser plugins only offered the popup dialog variety of spell checking, and not the inline "squiggles" approach.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;Happy day - a bespoke developement was on the cards. The first thing I had to determine was "Is this actually possible in a browser?" and "Could this be turned into a JQuery plugin?" . At the start, I made a list of the problems that would need to be resolved in order to have a chance of creating a decent the plugin:&lt;strong&gt;&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;br/&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;1. How to measure the location of words in an existing input/textarea?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;
&lt;/b&gt;&lt;/div&gt;&lt;div&gt;I did recall seeing that IE had some support for returning the exact measurement of text, and although this wasn't valid with the W3C standard, this potentially wasn't a problem, since the plugin was only targeted at IE users anyway.

The &lt;a href="http://msdn.microsoft.com/en-us/library/ms535872(VS.85).aspx"&gt;TextRange&lt;/a&gt; object provides a lot of functionality that would have been very hard to reproduce. You can return the absolute position and size of a piece of text using this object e.g.

&lt;span style="font-family:courier new;"&gt;var wholeWordsOnly = 2;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:courier new;"&gt;var range = input.createTextRange();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:Courier New;"&gt;range.findText(term, 0, wholeWordsOnly);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;This will create a range over an input and position it around the first instance of the word, giving you the absolute position of this word (in pixels) on the page.
&lt;strong&gt;&lt;/strong&gt;&lt;strong&gt;
&lt;/strong&gt;&lt;/div&gt;&lt;br/&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;2. Underlining misspellt words with a squiggly line. &lt;/span&gt;&lt;/div&gt;&lt;div&gt;
This turned out to be simpler than I thought. Once we have the dimensions of the word, it is relatively easy to insert a div with a repeating background image over the word by using a bit of css:&lt;/div&gt;&lt;div&gt;
&lt;span style="font-family:courier new;"&gt;div.spellayt {position:absolute; z-index:96;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:courier new;"&gt;background:url(Images/spellayt.gif) repeat-x; margin: 0px; padding: 0px}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;The image itself is only 4 pixels wide and three pixels high, it is positioned 3 pixels from the bottom of the bounding rectangle of the word, using Jquery to append the new div to the document body:&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:courier new;"&gt;//Append the div to the document body&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:courier new;"&gt;$("&amp;lt;div class="'spellayt'"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:courier new;"&gt;id="'divWord'"&amp;gt;&amp;lt;/div&amp;gt;").appendTo(document.body).show();&lt;/span&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;br/&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;3. Where do I get a list of words and how do I find suggestions when there is a misspelling?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;
&lt;/b&gt;&lt;/div&gt;&lt;div&gt;My next task was to find a dictionary of common use words in the English language. A search on the web located some likely candidates with appropriate licenses (GPL etc). The best option turned to be a plain text list of words, including all variations, capitalisations etc.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;Projects like GNU Aspell have a dictionary of over 150 000 entries! This clocks in at 1.6 mb uncompressed and 300k+ compressed which is a major consideration for implementation of a client side spell checking solution.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;Early on I had decided that the spell checking process would have to occur entirely on the browser for the plugin to be useful to anyone else, and to reduce the massive load that would be placed on the server if every word was to be checked. However with client side caching and server based gzip compression headers, I decided this was acceptable and pushed on.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;Next was one of the potentially show stopping problems - how to get a list of suggestions for misspellings? Again this turned out to be a well defined problem with a solution, including source code! The following page by &lt;a href="http://www.merriampark.com/mgresume.htm"&gt;Michael Gilleland&lt;/a&gt; details the &lt;a href="http://www.merriampark.com/ld.htm"&gt;Levenshtein Distance&lt;/a&gt; algorithm used to compare the "closeness" of two words, as a value. This page contains &lt;a href="http://www.mgilleland.com/ld/ldjavascript.htm"&gt;source code&lt;/a&gt; and a nice demo provided by Lukasz Stilger, which I used with some fixes and modifications for performance.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;At the time this now meant that I thought I had all the tools necessary to tackle the problem and I was ready to go ahead and create my first Jquery plugin "Spell as you type".&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;a href="http://bloggingdotnet.blogspot.com/2009/02/creating-spell-as-you-type-jquery.html"&gt;Creating a spell as you type jQuery plugin - part 2&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://bloggingdotnet.blogspot.com/2009/03/creating-spell-as-you-type-jquery-plug.html"&gt;Creating a spell as you type jQuery plugin - part 3&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Download: &lt;a href="http://plugins.jquery.com/project/spellayt"&gt;http://plugins.jquery.com/project/spellayt&lt;/a&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2513338198128732893-5155371758488596375?l=bloggingdotnet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bloggingdotnet.blogspot.com/feeds/5155371758488596375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2513338198128732893&amp;postID=5155371758488596375' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/5155371758488596375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2513338198128732893/posts/default/5155371758488596375'/><link rel='alternate' type='text/html' href='http://bloggingdotnet.blogspot.com/2009/01/creating-spell-as-you-type-jquery.html' title='Creating a Spell as you type Jquery plugin - Part 1'/><author><name>James Westgate</name><uri>http://www.blogger.com/profile/11208670447265243900</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_mKDkS8jszEg/SYLTbszegwI/AAAAAAAAACA/2998tNy4GbI/s72-c/word1.PNG' height='72' width='72'/><thr:total>0</thr:total></entry></feed>
