﻿<?xml version="1.0" encoding="utf-8"?>
<feed version="1.0" xmlns:blogChannel="http://backend.userland.com/blogChannelModule" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://www.w3.org/2005/Atom">
  <id>http://nothingisalotofsomething.com/</id>
  <title>Nothing Is a Lot of Something</title>
  <updated>2012-11-28T13:38:40+00:00</updated>
  <link href="http://nothingisalotofsomething.com/" />
  <link rel="self" href="http://nothingisalotofsomething.com/" />
  <link rel="alternate" href="http://feeds.feedburner.com/NothingIsALotOfSomething" />
  <subtitle>Sententia Sidus</subtitle>
  <author>
    <name>Jason</name>
  </author>
  <generator uri="http://dotnetblogengine.net/" version="1.0.0.0">BlogEngine.Net Syndication Generator</generator>
  <blogChannel:blogRoll>http://nothingisalotofsomething.com/opml.axd</blogChannel:blogRoll>
  <blogChannel:blink>http://www.dotnetblogengine.net/syndication.axd</blogChannel:blink>
  <dc:creator>Jason</dc:creator>
  <dc:description>Sententia Sidus</dc:description>
  <dc:language>en-US</dc:language>
  <dc:title>Nothing Is a Lot of Something</dc:title>
  <geo:lat>42.305600</geo:lat>
  <geo:long>-122.871600</geo:long>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2012/11/28/Credit-Where-Its-Due.aspx</id>
    <title>Credit Where It's Due</title>
    <updated>2012-11-28T13:37:00+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=7471670c-466b-4b83-ad3f-986fc18032d3" />
    <link href="http://nothingisalotofsomething.com/post/2012/11/28/Credit-Where-Its-Due.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">Thanks for the domain name, Lindsay. Now shut up about it already.
</summary>
    <published>2012-11-28T13:37:00+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2012/11/28/Credit-Where-Its-Due.aspx#comment" />
    <dc:publisher>jaecen</dc:publisher>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=7471670c-466b-4b83-ad3f-986fc18032d3</pingback:target>
    <slash:comments>0</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=7471670c-466b-4b83-ad3f-986fc18032d3</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2012/11/28/Credit-Where-Its-Due.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=7471670c-466b-4b83-ad3f-986fc18032d3</wfw:commentRss>
  </entry>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2012/06/02/Experiences-With-ZeroMQ.aspx</id>
    <title>Experiences With ZeroMQ</title>
    <updated>2012-06-02T12:00:00+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=c0783ae8-219c-476a-841d-c0191a62e519" />
    <link href="http://nothingisalotofsomething.com/post/2012/06/02/Experiences-With-ZeroMQ.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">&lt;p&gt;
Several months ago I came across the very intriguing &lt;a href="http://zeromq.org"&gt;ZeroMQ project&lt;/a&gt;. I enjoy writing scalable services and 0MQ offered a set of paradigms and a library intended to ease a lot of the complexity of scalability. After reading the excellent &lt;a href="http://zguide.zeromq.org/page:all"&gt;guide&lt;/a&gt;, I wrote up a couple trivial experiements and demonstrated them to the team, highlighting the performance and simplicity of implementation. The team was duly impressed (by the library, not my experiments), and it didn&amp;rsquo;t take much effort to keep 0MQ in everyone&amp;rsquo;s mind until we had an opportunity to try it out in a real project.
&lt;/p&gt;
&lt;p&gt;
In late May we got the go-ahead to re-implement our product data feeding service using 0MQ. The service was originally implemented as a highly multithreaded (100&amp;rsquo;s of concurrent threads) single-process Windows service running our own proprietary XML messaging platform over MSMQ. That messaging platform was not designed for the high data volumes we found ourselves handling, and over time we encountered several significant shortcomings that we hoped would be addressed by 0MQ and the paradigms brought with it. I&amp;rsquo;ve listed out those shortcomings along with how we&amp;rsquo;ve found 0MQ addresses them:
&lt;/p&gt;
&lt;h2&gt;Concurrent State&lt;/h2&gt;
&lt;p&gt;
Our original implementation brought along a few legacy concerns that required a significant amount of state sharing between threads. While this allowed our code to take a more traditional object-oriented shape, tracking and safely sharing this state introduced tons of noise into the code as well as proving to be an endless wellspring of bugs and unanticipated complications.
&lt;/p&gt;
&lt;p&gt;
0MQ&amp;rsquo;s solution for concurrent state is simple: there is none. With 0MQ, the philosophy is to use multiple single-threaded instances to achieve concurrency rather than one multithreaded instance. 0MQ then ensures that work is divided among your many instances and handed to each instance on a single thread. We were all in with this philosophy and wrote our instances in a completely state-free (and, hence, functional) style. While this style is a significant change from OOP, the resulting code simplicity more than makes up for it. In my opinion, this is the real silver bullet of 0MQ: achieving full concurrency without ever having to think about it in your code. For the small amount of state we had to share between processes, we wrote an extremely simple in-memory key/value store using 0MQ. Again, since 0MQ ensured we only received messages on a single thread, we didn&amp;rsquo;t have to clutter it with any locks or similar constructs.
&lt;/p&gt;
&lt;h2&gt;Implementation Complexity&lt;/h2&gt;
&lt;p&gt;
Having the entire application hosted in a single process with many threads meant that there was a fair amount of boilerplate in any new code. Additionally, using MSMQ required that we maintained our own envelope, configuration and other data along with the message payload in a single large XML document. These two facts together required hundreds of lines of boilerplate and an understanding of the very complex hosting system in order to implement most basic functionality in any new modules.
&lt;/p&gt;
&lt;p&gt;
When we reimplemented with 0MQ, reducing this complexity was a high priority feature. We packaged each step of our workflow (which we call a worker) as a DLL, then had a single worker host that we would run multiple instance of, loading each worker DLL. Since each worker host is independent, there&amp;rsquo;s no code required to integrate into an existing system.
&lt;/p&gt;
&lt;p&gt;
0MQ also has the concept of message parts: each message can be sent as a series of chunks, and the result can be loaded as such. This allows us to partition the message into different independent areas (e.g. configuration, routing, etc.) and use the most efficient encoding means for each message part, rather than converting everything to XML as in the original implementation. Additionally, we were able to easily expose these parts as native objects rather than XPath expression, resulting in significantly cleaner code.
&lt;/p&gt;
&lt;p&gt;
These changes, along with the elimination of the multithreaded state code mentioned above, gave us an average of an 80% reduction in line count in the affected areas, with most modules moving for 300-400 LOC to 60-80 LOC.
&lt;/p&gt;
&lt;h2&gt;Messaging Performance&lt;/h2&gt;
&lt;p&gt;
MSMQ tended to get in our way as much as it helped, and this was most obvious with regards to messaging performance. Every MSMQ message is persisted to disk, which was not desirable in our service.
&lt;/p&gt;
&lt;p&gt;
0MQ has no built-in persistence and in some naive benchmarks provided a higher throughput than MSMQ (unfortunately I&amp;rsquo;ve lost the numbers). While we didn&amp;rsquo;t have an opportunity to switch out MSMQ for 0MQ on the original implementation for an apples-to-apples comparison, our new implementation is currently running 30% faster on the same data, much of which we attribute to no needing to interface with the disk for every message.
&lt;/p&gt;
&lt;h2&gt;Scalability&lt;/h2&gt;
&lt;p&gt;
The only way to scale our single-process service was to run one instance per server and partition the input set modulo the number of instances. Unfortunately, the amount of work generated by each item in the input set was unknown ahead of time, so this partition was very inefficient and ultimately ineffective without a large number of high-performance servers.
&lt;/p&gt;
&lt;p&gt;
0MQ&amp;rsquo;s many-instance model works across cores on a single machine as well as across multiple machines. We&amp;rsquo;ve written each stage in the workflow to be an independent process, so we can vary the number of processes to match the relative amount of CPU time spent on each one. For example if worker 2 needed twice as much CPU time as worker 1, we can fire up one instance of worker 1 and two instances of worker 2 to provide a constant throughput rate. Since these workers can be on many machines, the relative computing power of each server is irrelevant. Whether we fire up one worker instance each on two small servers or two worker instances on one larger server, the system will gain capacity equally.
&lt;/p&gt;
&lt;p&gt;
Additionally, 0MQ automatically allows instances to freely connect and disconnect, allowing us to scale resources on demand. If we find that we&amp;rsquo;re maxing out our current capacity, we can simply bring a new machine online and the instances will join the existing workflow. Once the spike is over, we shutdown the machine and everything continues on as normal.
&lt;/p&gt;
&lt;h2&gt;Resource Consumption&lt;/h2&gt;
&lt;p&gt;
An instance of the original implementation would often consume all available CPU and RAM when under load, and could eat up dozens of gigabytes of disk space. Each thread would consume a certain amount of resources, and the threads would come and go rapidly. Our only control was to throttle the number of messages handled concurrently by each stage of the workflow, which either resulted in underutilization of the server for small items or resource over-consumption (and sometimes crashes) for large input items.
&lt;/p&gt;
&lt;p&gt;
0MQ uses a single thread per instance, and our resource consumption per instance is stable. We can see our resource consumption stay constant whether under load or idle, and can adjust the number of instances to most efficiently utilize the given resources.
&lt;/p&gt;
&lt;h2&gt;Error Recovery&lt;/h2&gt;
&lt;p&gt;
An unfortunate side effect of shared state is having that state become invalid when an error occurs. We did our best to mitigate this issue, but new exceptions would appear and cascade through the entire system, bringing the service (and sometimes the server) to its knees.
&lt;/p&gt;
&lt;p&gt;
Under 0MQ, each worker process is independent and state free. If a worker crashes, we simply replace it with another instance and continue on.
&lt;/p&gt;
&lt;p&gt;
Working with 0MQ has been a huge pleasure, bringing massive improvements in stability, understandability, and code quality. I also highly recommend anyone working on concurrent applications of any sort, whether or not you plan to use 0MQ, read the &lt;a href="http://zguide.zeromq.org/page:all"&gt;0MQ Guide&lt;/a&gt; to learn how to mitigate most of the current headaches in multithreaded applications. I know for a fact that multithreading code is a path I will never voluntarily tread again.
&lt;/p&gt;
</summary>
    <published>2012-06-02T12:00:00+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2012/06/02/Experiences-With-ZeroMQ.aspx#comment" />
    <category term="0MQ" />
    <dc:publisher>jaecen</dc:publisher>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=c0783ae8-219c-476a-841d-c0191a62e519</pingback:target>
    <slash:comments>1</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=c0783ae8-219c-476a-841d-c0191a62e519</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2012/06/02/Experiences-With-ZeroMQ.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=c0783ae8-219c-476a-841d-c0191a62e519</wfw:commentRss>
  </entry>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2012/05/07/HelloWeb.aspx</id>
    <title>Hello*Web</title>
    <updated>2012-05-07T12:37:00+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=240fb889-6b18-4a41-9c64-09be608f9bee" />
    <link href="http://nothingisalotofsomething.com/post/2012/05/07/HelloWeb.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">&lt;p&gt;
While I spend most of my development time in the C# world, I love the opportunity to learn new languages. I&amp;rsquo;ve created innumerable &amp;ldquo;Hello World&amp;rdquo;&amp;rsquo;s in a variety of languages, but I rarely spend enough time with them to get a grasp of their paradigms and idioms. Usually I just can&amp;rsquo;t think of a problem sufficiently interesting to hold my attention long enough to learn a language and then implement the program.
&lt;/p&gt;
&lt;p&gt;
I&amp;rsquo;ve decided that is no longer an excuse.
&lt;/p&gt;
&lt;p&gt;
I&amp;rsquo;ve embarked on my own little polyglot challenge: I&amp;rsquo;m going to implement a simple web service in a number of languages with the goal of exploring the idioms and features of each language. Over time, I hope to explore even further by expanding both the number of languages and the features of the web service.
&lt;/p&gt;
&lt;p&gt;
Starting with a new language on bare metal is not a good way to learn the idioms, though. Bit pushing and stream writing looks pretty much the same in most any language. Instead, I will build on existing web frameworks so that I can focus on the higher-level aspects of the language and see how the language is used by those frameworks.
&lt;/p&gt;
&lt;p&gt;
For the first round, I&amp;#39;ve decided to use the following languages and frameworks:
C# / ASP.NET Web API
F# / ASP.NET Web API
Node / Express
Erlang / Webmachine
Clojure / Noir
Scala / Unfiltered
Ruby / Sinatra
Python / Pyramid
&lt;/p&gt;
&lt;p&gt;
This list contains all of the languages I&amp;rsquo;m most interested in trying out, plus they all have the bonus of being hostable on &lt;a href="http://heroku.com"&gt;Heroku&lt;/a&gt; or &lt;a href="http://appharbor.com"&gt;AppHarbor&lt;/a&gt;. The framework choices are based on what I determined to be the most popular service-oriented option for each language. The service-oriented part is important, as I&amp;rsquo;m not interested in creating any UI and I&amp;rsquo;d rather avoid having to come up to speed on a very large framework just to use a tiny subset of it.
&lt;/p&gt;
&lt;p&gt;
The web service I&amp;rsquo;ll be writing is designed to be simple while demonstrating a few different processes:
Connecting to a web service multiple times and receiving JSON data
Combining the data
Filtering the data
Mapping it to a simpler structure
Returning the structure as JSON
&lt;/p&gt;
&lt;p&gt;
The service I&amp;rsquo;ll be connecting to is a little Node app I wrote called Hunter that scrapes the official Magic: The Gathering card database &lt;a href="http://gatherer.wizards.com/"&gt;Gatherer&lt;/a&gt; and returns the card data as JSON. 
&lt;/p&gt;
&lt;p&gt;
The implemented service will be expected to expose a REST method using the following format: /subtype/x, where &amp;ldquo;x&amp;rdquo; is the card subtype to filter for. It will connect to Hunter, request all cards from the three sets in the Innistrad block (requiring three calls), filter for only the cards with the given subtype, and return a JSON array of the card names and colors.
&lt;/p&gt;
&lt;p&gt;
Again, the idea is to explore the idioms and paradigms of each language, so while such a simple service could easily be implemented as a single class/module/line/whatever in each language, I&amp;rsquo;ll be treating my implementation as if it were something significantly more complex.
&lt;/p&gt;
&lt;p&gt;
I&amp;rsquo;ll be posting the code for each implementation on &lt;a href="http://github.com/Jaecen"&gt;Github&lt;/a&gt; and welcome any feedback.
&lt;/p&gt;
&lt;p&gt;
My hope is that over the coming months I&amp;rsquo;ll be able to see some new ideas and glean a basic level of proficiency on a number of new platforms.
&lt;/p&gt;
</summary>
    <published>2012-05-07T12:37:00+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2012/05/07/HelloWeb.aspx#comment" />
    <category term="Hello*Web" />
    <dc:publisher>jaecen</dc:publisher>
    <dc:description>Intro to my polyglot programming series.</dc:description>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=240fb889-6b18-4a41-9c64-09be608f9bee</pingback:target>
    <slash:comments>0</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=240fb889-6b18-4a41-9c64-09be608f9bee</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2012/05/07/HelloWeb.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=240fb889-6b18-4a41-9c64-09be608f9bee</wfw:commentRss>
  </entry>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2010/02/26/Manual-and-Undefined-ASPNET-Events.aspx</id>
    <title>Manual and Undefined ASP.NET Events</title>
    <updated>2010-02-27T00:22:20+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=87d5be24-e096-471a-8d8d-ac36d2447661" />
    <link href="http://nothingisalotofsomething.com/post/2010/02/26/Manual-and-Undefined-ASPNET-Events.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">&lt;p&gt;ASP.NET provides some very handy magic to map the .NET event model to the web. Normally, we're perfectly happy to let it handle all the details for us. There are those cases, though, where we need to pop the hood and get our hands dirty. I'll outline how ASP.NET events work behind the scenes, then show the two different ways we can work with them outside of the normal event handling patterns.&lt;/p&gt;  &lt;h2&gt;How ASP.NET Events Work&lt;/h2&gt;  &lt;p&gt;When you create a control on a page with an event, ASP.NET adds a javascript event handler to the HTML element for that control. The javascript takes a form similar to this:&lt;/p&gt;  &lt;pre class="brush: js;"&gt;__doPostBack(control, arguments);&lt;/pre&gt;

&lt;p&gt;The “control” parameter is the client ID of the control invoking the event. The “arguments” parameter defines the event. It usually just contains the name of the event, e.g. “click” or “selectionChanged”, though sometimes it carries additional contextual data.&lt;/p&gt;

&lt;p&gt;When __doPostBack() is called, it sets two hidden form fields: __EVENTTARGET is set to the “control” parameter and __EVENTARGS is set to the “arguments” parameter. It then POSTs the containing &amp;lt;form&amp;gt; (which is why all ASP.NET pages must have a single &amp;lt;form&amp;gt; with runat=”server” on them).&lt;/p&gt;

&lt;p&gt;Once the ASP.NET page receives the POST from with the form data, it begins the normal page lifecycle. After it’s loaded, it inspects the __EVENTTARGET value to figure out which control to pass the __EVENTARGS to.&lt;/p&gt;

&lt;p&gt;Any control capable of responding to postback events must implement the IPostBackEventHandler interface. The interface contains a method, LoadPostData(eventArgument), which the page will call, passing __EVENTARGS as the eventArgument. From there, the control parses the event argument and performs further processing, which usually consists of firing the associated .NET events.&lt;/p&gt;

&lt;h2&gt;Generating and Handling ASP.NET Events Without Using .NET Events&lt;/h2&gt;

&lt;p&gt;It’s possible for you to create and respond to ASP.NET events without using .NET events and handlers. You can generate the events from anywhere, but handling them in such a manner requires that you create a class derived from Control that implements IPostBackEventHandler:&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class MyControl : Control, IPostBackEventHandler
{
    public void RaisePostBackEvent(string eventArgument)
    {
    }
}&lt;/pre&gt;

&lt;p&gt;Since eventArgument is a string, you can pass whatever arbitrary value you wish. The typical format in ASP.NET is “eventName:extraData”, but there’s no requirement that you follow that format. You can then act on that value in any matter you wish, including firing your own .NET events.&lt;/p&gt;

&lt;p&gt;Note that if your class inherits from WebControl (including UserControl), you won’t be able to handle events in this fashion, as WebControl implements IPostBackEventHandler.LoadPostData() as a private method.&lt;/p&gt;

&lt;p&gt;To generate an event, you simply need to call __doPostBack() in javascript, passing in a control’s client ID and the appropriate event arguments. ASP.NET also provides a method to generate javascript postback and callback code on the server side: Page.ClientScript.GetPostBackEventReference() and Page.ClientScript.GetCallbackEventReference() respectively. Placing the return value from these methods in the onclick event of an HTML control will allow you to fire your own arbitrary ASP.NET events.&lt;/p&gt;

&lt;h2&gt;Detecting ASP.NET Outside of the Event Handler&lt;/h2&gt;

&lt;p&gt;Sometimes you may need to determine what event is being processed outside the event handler, such as in the PageLoad method. Since __doPostBack() creates an event simply by setting form fields, you can determe what control and event by inspecting Request.Form[&amp;quot;__EVENTTARGET&amp;quot;] and Request.Form[&amp;quot;__EVENTARGS&amp;quot;] respectively.&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    if(IsPostBack
        &amp;amp;&amp;amp; Request.Form[&amp;quot;__EVENTTARGET&amp;quot;] == myControl.ClientID
        &amp;amp;&amp;amp; Request.Form[&amp;quot;__EVENTARGS&amp;quot;].StartsWith(&amp;quot;click&amp;quot;))
    {
        // We know we’re going to end up processing the click event on myControl.
    }
}&lt;/pre&gt;

&lt;p&gt;Please note that this method of detecting events goes contrary to the ASP.NET event handling model and can lead to difficulties in understanding and debugging your code. It’s usually best to find a solution that works within the existing framework when handling events.&lt;/p&gt;</summary>
    <published>2010-02-27T00:22:20+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2010/02/26/Manual-and-Undefined-ASPNET-Events.aspx#comment" />
    <category term="ASP.NET" />
    <dc:publisher>jaecen</dc:publisher>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=87d5be24-e096-471a-8d8d-ac36d2447661</pingback:target>
    <slash:comments>0</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=87d5be24-e096-471a-8d8d-ac36d2447661</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2010/02/26/Manual-and-Undefined-ASPNET-Events.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=87d5be24-e096-471a-8d8d-ac36d2447661</wfw:commentRss>
  </entry>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2009/03/19/Custom-Buttons-with-CSS-Sprites.aspx</id>
    <title>Custom Buttons with CSS Sprites</title>
    <updated>2009-03-19T18:52:00+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=034f2936-f091-448c-9dfe-29cd2fdb84e8" />
    <link href="http://nothingisalotofsomething.com/post/2009/03/19/Custom-Buttons-with-CSS-Sprites.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">&lt;p&gt;I published an article on my company&amp;rsquo;s blog about creating custom buttons using CSS sprites. &lt;a href="http://blog.vortx.com/analytics/fast-and-easy-custom-buttons/"&gt;You can view it here&lt;/a&gt;. I didn&amp;rsquo;t have the space to expand on indicating other button states with sprites, but be aware that you can apply the same technique used for the disabled styles to pseudo-classes like :active and :hover.&lt;/p&gt;</summary>
    <published>2009-03-19T18:52:00+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2009/03/19/Custom-Buttons-with-CSS-Sprites.aspx#comment" />
    <category term="CSS" />
    <category term="Vortx LiveWire" />
    <dc:publisher>jaecen</dc:publisher>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=034f2936-f091-448c-9dfe-29cd2fdb84e8</pingback:target>
    <slash:comments>0</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=034f2936-f091-448c-9dfe-29cd2fdb84e8</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2009/03/19/Custom-Buttons-with-CSS-Sprites.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=034f2936-f091-448c-9dfe-29cd2fdb84e8</wfw:commentRss>
  </entry>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2008/07/23/WPF-MVC-FTW!.aspx</id>
    <title>WPF MVC FTW!</title>
    <updated>2008-07-23T23:18:00+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=77c13161-d48b-422a-803d-9b34cb136c53" />
    <link href="http://nothingisalotofsomething.com/post/2008/07/23/WPF-MVC-FTW!.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">&lt;p&gt;The &lt;a href="http://en.wikipedia.org/wiki/Model-view-controller"&gt;Model/View/Controller paradigm&lt;/a&gt; has been making &lt;a href="http://weblogs.asp.net/scottgu/archive/2008/07/14/asp-net-mvc-preview-4-release-part-1.aspx"&gt;big news&lt;/a&gt; in the ASP.NET development community as of late. &lt;a href="http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaDesignPatterns/chapter_5_section_4.html"&gt;Mac programmers&lt;/a&gt; and &lt;a href="http://msdn.microsoft.com/en-us/library/4x1xy43a(VS.80).aspx"&gt;crusty old MFC devs&lt;/a&gt; may roll their eyes, but I think any attempt to draw developers’ attention to higher order design principles is laudable.&lt;/p&gt;  &lt;p&gt;ASP.NET may have required some finagling to get it to work in the MVC pattern, but I’ve found that WPF is perfectly aligned for MVC design. Since you’re allowed to pass POCO’s (Plain Old C# Objects) to UI elements, it’s simple as pie to separate your model from your presentation. I don’t have to worry about instantiating and initializing the right control for a particular data type; I simply define a DataTemplate for the class, and it magically uses the UI in the template wherever it sees an instance of that class.&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; margin: 10px 0px 10px 10px; float: right; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="models" border="0" alt="models" align="right" src="http://nothingisalotofsomething.com/image.axd?picture=WindowsLiveWriter/WPFMVCFTW_14D80/models_3.png" width="199" height="137" /&gt;One of my recent personal projects is for managing Magic: The Gathering card collections. I created a data entry app to allow for quick entry of large collections of MTG cards. Each card comes from a set, so my models were pretty obvious. I created a library project, and added a &lt;strong&gt;Card&lt;/strong&gt; and a &lt;strong&gt;Set&lt;/strong&gt; class, as well as some support classes, to the Models namespace. As per the MVC pattern, these models are exclusively for storing data and data-related logic. They contain no UI functionality whatsoever.&lt;/p&gt;  &lt;p align="left"&gt;Now that I had my data, it was time for the UI. I decided to display the list of sets in a combo box, but I had a couple special needs. First, the cards don’t say which set they come from; instead, they show the set’s symbol, a small graphic unique to each set. I needed to display the symbols in the combo box so that the user could choose the correct set, even if they didn’t know the name. Second each set has a standard three letter abbreviation. I wanted the user to be able to type the abbreviation into the combo box to quickly select the desired set, without requiring moving the hands from keyboard to mouse.&lt;/p&gt;  &lt;p&gt;I set out by creating a Views folder in my Interface project. For lack of imagination, I named the views after their respective models, though there can be many views for the same model and I may find myself having to rename them later. Each view inherits from &lt;strong&gt;ViewBase &lt;/strong&gt;(itself inheriting from &lt;strong&gt;UserControl&lt;/strong&gt;), which defines a &lt;strong&gt;Model&lt;/strong&gt; dependency property and events for the &lt;strong&gt;Model&lt;/strong&gt; property changing.&lt;/p&gt;  &lt;p&gt;The set view went through several iterations to get the right data displayed in a compact space. Initially it was displaying everything from the release date to the number of cards in the set, but eventually I ended up with a very reasonable set of controls:&lt;/p&gt;  &lt;pre class="brush: xml;"&gt;&amp;lt;Grid Margin=&amp;quot;2&amp;quot; VerticalAlignment=&amp;quot;Center&amp;quot; DataContext=&amp;quot;{ Binding }&amp;quot;&amp;gt;
    &amp;lt;Grid.ColumnDefinitions&amp;gt;
        &amp;lt;ColumnDefinition MaxWidth=&amp;quot;33&amp;quot; /&amp;gt;
        &amp;lt;ColumnDefinition MaxWidth=&amp;quot;8&amp;quot; /&amp;gt;
        &amp;lt;ColumnDefinition Width=&amp;quot;*&amp;quot; /&amp;gt;
        &amp;lt;ColumnDefinition MaxWidth=&amp;quot;25&amp;quot; /&amp;gt;
    &amp;lt;/Grid.ColumnDefinitions&amp;gt;
    &amp;lt;Grid.RowDefinitions&amp;gt;
        &amp;lt;RowDefinition /&amp;gt;
    &amp;lt;/Grid.RowDefinitions&amp;gt;
    &amp;lt;TextBlock x:Name=&amp;quot;lblAbbreviation&amp;quot; Grid.Column=&amp;quot;0&amp;quot; Grid.Row=&amp;quot;0&amp;quot; HorizontalAlignment=&amp;quot;Left&amp;quot; VerticalAlignment=&amp;quot;Center&amp;quot; Text=&amp;quot;{ Binding Path=Abbreviation }&amp;quot; /&amp;gt;
    &amp;lt;TextBlock Grid.Column=&amp;quot;1&amp;quot; Grid.Row=&amp;quot;0&amp;quot; HorizontalAlignment=&amp;quot;Left&amp;quot; Text=&amp;quot;-&amp;quot; /&amp;gt;
    &amp;lt;TextBlock x:Name=&amp;quot;lblName&amp;quot; Grid.Column=&amp;quot;2&amp;quot; Grid.Row=&amp;quot;0&amp;quot; HorizontalAlignment=&amp;quot;Left&amp;quot; VerticalAlignment=&amp;quot;Center&amp;quot; FontWeight=&amp;quot;Bold&amp;quot; Text=&amp;quot;{ Binding Path=Name }&amp;quot; /&amp;gt;
    &amp;lt;Image x:Name=&amp;quot;imgSymbol&amp;quot; Grid.Column=&amp;quot;3&amp;quot; Grid.Row=&amp;quot;0&amp;quot; Stretch=&amp;quot;None&amp;quot; HorizontalAlignment=&amp;quot;Right&amp;quot; Source=&amp;quot;{ Binding Path=Symbol }&amp;quot; /&amp;gt;
&amp;lt;/Grid&amp;gt;&lt;/pre&gt;

&lt;p&gt;Now for the magic of XAML and DataTemplate. I added a DataTemplate element to the resources for my main window:&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;Window.Resources&amp;gt;
    &amp;lt;DataTemplate DataType=&amp;quot;{x:Type Models:Set}&amp;quot;&amp;gt;
        &amp;lt;Views:Set Model=&amp;quot;{ Binding }&amp;quot; /&amp;gt;
    &amp;lt;/DataTemplate&amp;gt;
&amp;lt;/Window.Resources&amp;gt;&lt;/pre&gt;

&lt;p&gt;Now whenever my window sees a &lt;strong&gt;Models.Set&lt;/strong&gt; object, it will substitute it with a &lt;strong&gt;Views.Set&lt;/strong&gt; control, passing in the &lt;strong&gt;Models.Set &lt;/strong&gt;as the &lt;strong&gt;Model&lt;/strong&gt; property.&lt;/p&gt;

&lt;p&gt;Next, I created my combo box with the formatting I needed:&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;ComboBox   x:Name=&amp;quot;cboSet&amp;quot;
            IsEditable=&amp;quot;False&amp;quot;
            IsReadOnly=&amp;quot;True&amp;quot;
            TextSearch.TextPath=&amp;quot;Abbreviation&amp;quot;
            HorizontalContentAlignment=&amp;quot;Stretch&amp;quot;
            SelectionChanged=&amp;quot;cboSet_SelectionChanged&amp;quot; /&amp;gt;&lt;/pre&gt;

&lt;p&gt;Pretty simple stuff. The &lt;strong&gt;IsEditable&lt;/strong&gt; and &lt;strong&gt;IsReadonly&lt;/strong&gt; properties allow the user to type in the box to search the existing values (as opposed to adding new ones). The &lt;strong&gt;TextSearch.TextPath&lt;/strong&gt; tells the combo box what property of the items to compare the user’s typing to. Setting it to “Abbreviation” means when the user types, it will be compared against the three-letter set abbreviation. Remember, I’m going to assign my POCO &lt;strong&gt;Models.Set&lt;/strong&gt; class as the combo box items, not the &lt;strong&gt;Views.Set&lt;/strong&gt;. WPF will take care of displaying my &lt;strong&gt;Models.Set&lt;/strong&gt;’s as &lt;strong&gt;Views.Set&lt;/strong&gt;’s. That means any properties I’m talking about will be on the model, not the view (the way it should be!)&lt;/p&gt;

&lt;p&gt;Finally, it’s time for the big test.&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;cboSet.ItemsSource = Sets;&lt;/pre&gt;

&lt;p&gt;Compile and run…&lt;/p&gt;

&lt;p&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="combobox" border="0" alt="combobox" src="http://nothingisalotofsomething.com/image.axd?picture=WindowsLiveWriter/WPFMVCFTW_14D80/combobox_7.png" width="517" height="436" /&gt; &lt;/p&gt;

&lt;p&gt;Bingo!&lt;/p&gt;

&lt;p&gt;I applied this same process to the &lt;strong&gt;Card&lt;/strong&gt; view, as well as an internal class call &lt;strong&gt;HistoryItem&lt;/strong&gt;. In the end, I got an app that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://nothingisalotofsomething.com/image.axd?picture=WindowsLiveWriter/WPFMVCFTW_14D80/mtgct_2.png"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="MTG Collection Tracker" border="0" alt="MTG Collection Tracker" src="http://nothingisalotofsomething.com/image.axd?picture=WindowsLiveWriter/WPFMVCFTW_14D80/mtgct_thumb.png" width="588" height="440" /&gt;&lt;/a&gt;&lt;/p&gt;</summary>
    <published>2008-07-23T23:18:00+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2008/07/23/WPF-MVC-FTW!.aspx#comment" />
    <category term="Design Patterns" />
    <category term="WPF" />
    <dc:publisher>jaecen</dc:publisher>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=77c13161-d48b-422a-803d-9b34cb136c53</pingback:target>
    <slash:comments>0</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=77c13161-d48b-422a-803d-9b34cb136c53</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2008/07/23/WPF-MVC-FTW!.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=77c13161-d48b-422a-803d-9b34cb136c53</wfw:commentRss>
  </entry>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2008/06/18/Proportional-Image-Resizing-With-XSLT.aspx</id>
    <title>Proportional Image Resizing With XSLT</title>
    <updated>2008-06-18T23:27:46+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=744dab94-a010-41dd-b991-da413a8b329f" />
    <link href="http://nothingisalotofsomething.com/post/2008/06/18/Proportional-Image-Resizing-With-XSLT.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:27483ca1-fa89-4fdd-a9fb-ce19eb7a5d70" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/xml" rel="tag"&gt;xml&lt;/a&gt;,&lt;a href="http://technorati.com/tags/xslt" rel="tag"&gt;xslt&lt;/a&gt;,&lt;a href="http://technorati.com/tags/images" rel="tag"&gt;images&lt;/a&gt;&lt;/div&gt;  &lt;p&gt;Let's say you have a bunch of different sized images that you want to constrain to some maximum dimension. You don't want to enlarge images smaller than you constraining dimension. If your limit was 150px, you might try the following:&lt;/p&gt;  &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;   &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;     &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;img&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;src&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:value-of&lt;/span&gt; &lt;span style="color: #ff0000"&gt;select&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;media/thumbnail/url&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:if&lt;/span&gt; &lt;span style="color: #ff0000"&gt;test&lt;/span&gt;=&amp;quot;&lt;span style="color: #ff0000"&gt;media&lt;/span&gt;/&lt;span style="color: #ff0000"&gt;thumbnail&lt;/span&gt;/&lt;span style="color: #ff0000"&gt;width&lt;/span&gt; &lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt; 150 or media/thumbnail/height &lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt; 150 &amp;quot;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;style&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;            width: 150px; height: 150px;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:if&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;img&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The problem with manually specifying the width and height in the style is that if the original image is not square (and most aren’t) the image will be distorted in one dimension. In order to prevent this distortion, you will need to size both dimensions by the same coefficient.&lt;/p&gt;

&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;
  &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;
    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;img&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;src&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:value-of&lt;/span&gt; &lt;span style="color: #ff0000"&gt;select&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;media/thumbnail/url&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:choose&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:when&lt;/span&gt; &lt;span style="color: #ff0000"&gt;test&lt;/span&gt;=&amp;quot;&lt;span style="color: #ff0000"&gt;media&lt;/span&gt;/&lt;span style="color: #ff0000"&gt;thumbnail&lt;/span&gt;/&lt;span style="color: #ff0000"&gt;width&lt;/span&gt; &lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt; 150 and media/thumbnail/width &lt;span style="color: #ff0000"&gt;&amp;amp;gt;&lt;/span&gt;= media/thumbnail/height&amp;quot;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;            &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;width&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;                &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:value-of&lt;/span&gt; &lt;span style="color: #ff0000"&gt;select&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;(150 div media/thumbnail/width) * media/thumbnail/width&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;px&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;            &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;            &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;height&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;                &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:value-of&lt;/span&gt; &lt;span style="color: #ff0000"&gt;select&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;(150 div media/thumbnail/width) * media/thumbnail/height&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;px&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;            &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:when&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:when&lt;/span&gt; &lt;span style="color: #ff0000"&gt;test&lt;/span&gt;=&amp;quot;&lt;span style="color: #ff0000"&gt;media&lt;/span&gt;/&lt;span style="color: #ff0000"&gt;thumbnail&lt;/span&gt;/&lt;span style="color: #ff0000"&gt;height&lt;/span&gt; &lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt; 150 and media/thumbnail/width &lt;span style="color: #ff0000"&gt;&amp;amp;lt;&lt;/span&gt; media/thumbnail/height&amp;quot;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;            &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;width&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;                &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:value-of&lt;/span&gt; &lt;span style="color: #ff0000"&gt;select&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;(150 div media/thumbnail/height) * media/thumbnail/width&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;            &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;            &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;height&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;                &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:value-of&lt;/span&gt; &lt;span style="color: #ff0000"&gt;select&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;(150 div media/thumbnail/height) * media/thumbnail/height&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;            &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:attribute&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:when&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:choose&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;img&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;In this version, we use an xsl:choose to determine if the height or width is the larger dimension. If the larger dimension is greater than our maximum (again, 150px in this example), then we calculate the percentage that 150px is of that dimension; this is our coefficient. We then multiply both dimension by the coefficient to get the final size: the largest dimension will be 150px, and the smallest will be size proportionally to 150px or less.&lt;/p&gt;

&lt;p&gt;For those so inclined, here’s the sorta math:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;let m be the desired maximum dimension&lt;/p&gt;

  &lt;p&gt;let w and h be the width and height of the image respectively&lt;/p&gt;

  &lt;p&gt;L = maximum(w, h)&lt;/p&gt;

  &lt;p&gt;k = m / L&lt;/p&gt;

  &lt;p&gt;&lt;/p&gt;

  &lt;p&gt;w = w * k&lt;/p&gt;

  &lt;p&gt;h = h * k&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Note that in the example above, we could have simplified it by just applying the percentages directly to the image dimensions instead of taking the extra step of multiplying it by the original dimension. I just liked being able to see that it was exactly 150px in the output.&lt;/em&gt;&lt;/p&gt;</summary>
    <published>2008-06-18T23:27:46+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2008/06/18/Proportional-Image-Resizing-With-XSLT.aspx#comment" />
    <category term="XSLT" />
    <dc:publisher>jaecen</dc:publisher>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=744dab94-a010-41dd-b991-da413a8b329f</pingback:target>
    <slash:comments>2</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=744dab94-a010-41dd-b991-da413a8b329f</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2008/06/18/Proportional-Image-Resizing-With-XSLT.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=744dab94-a010-41dd-b991-da413a8b329f</wfw:commentRss>
  </entry>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2008/06/17/Grouping-with-XSLT.aspx</id>
    <title>Grouping with XSLT</title>
    <updated>2008-06-17T09:43:00+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=3b855a90-f52a-4a50-93d2-8d57cf16ba3a" />
    <link href="http://nothingisalotofsomething.com/post/2008/06/17/Grouping-with-XSLT.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">&lt;p&gt;I've been working on integrating &lt;a href="http://friendfeed.com"&gt;FriendFeed&lt;/a&gt; into this site. For those who are unfamiliar with FriendFeed, it aggregates your activities across the social web and gathers them into a &lt;a href="http://friendfeed.com/jaecen"&gt;single feed&lt;/a&gt;, to which your friends can subscribe. It's a nifty utility with a wide range of supported services, from &lt;a href="http://twitter.com"&gt;Twitter&lt;/a&gt; to &lt;a href="http://www.flickr.com/"&gt;Flickr&lt;/a&gt; to &lt;a href="http://disqus.com/"&gt;Disqus&lt;/a&gt; to &lt;a href="http://digg.com/"&gt;Digg&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;FriendFeed provides a full REST API that can provide results in JSON, XML, RSS, and Atom. Since my implementation doesn't require any client-side interactivity, I decided to use XML, transform it with XSLT on the server, and display the result in the page. The provided XML consists of a &amp;lt;feed&amp;gt; full of &amp;lt;entry&amp;gt;'s. Each entry has details such as the posted date, service (Flicker, Twitter, etc), and title. &lt;/p&gt;  &lt;p&gt;One of my presentation requirements is that the items be grouped by date. I wanted to shw and &amp;quot;post&amp;quot; for each day, with each of the days entries beneath it. I found a &lt;a href="http://www.jenitennison.com/xslt/grouping/muenchian.html"&gt;good method&lt;/a&gt; that provides exactly what I need. &lt;/p&gt;  &lt;p&gt;The crux of the functionality is using XSLT keys. Keys are essentially like database indexes, allowing you to quickly select without repeatedly traversing all of the nodes. You define a key as follows: &lt;/p&gt;  &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;   &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;     &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:key&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;name-of-key&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;match&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;nodes-to-apply&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;use&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;value-to-index&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt; &lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The &lt;font face="courier new,courier"&gt;name&lt;/font&gt; is simply the string you want to use to refer to this key later. The &lt;font face="courier new,courier"&gt;match &lt;/font&gt;is the node you want to apply this key to. The use can be any child element, attribute, or computed value (using a function) defining what you want to be indexed. &lt;/p&gt;

&lt;p&gt;In my case, I wanted to use the &amp;lt;updated&amp;gt; element, which containes the time and date the item was last updated. Since I was going to be grouping on the date and not the time, I used the Microsoft-specific time formatting function to truncate the time and format the date as a number: &lt;/p&gt;

&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;
  &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;
    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:key&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;entries-by-date&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;match&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;/feed/entry&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;use&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;ms:format-date(updated, 'yyyyMMdd')&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt; &lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Now I've defined a key called &amp;quot;entries-by-date&amp;quot; that will quickly pull up all matching &amp;lt;entry&amp;gt; elements for a given date in the format &amp;quot;20080614&amp;quot;. The next step is to iterate through all of the dates and show the corresponding entries. Unfortunately, we can't just iterate through the key we created. Instead, we have to sort the &amp;lt;entry&amp;gt; elements by their formatted &amp;lt;updated&amp;gt; element (which we created the key for), then grab all of the other &amp;lt;entry&amp;gt;'s with the same key. We want to be sure to grab each &amp;lt;entry&amp;gt; only once. Here's a larger snippet showing how to acomplish this: &lt;/p&gt;

&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;
  &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;
    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #008000"&gt;&amp;lt;!-- Root --&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:template&lt;/span&gt; &lt;span style="color: #ff0000"&gt;match&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;/feed&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;span style="color: #008000"&gt;&amp;lt;!-- Group by date --&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:for-each&lt;/span&gt; &lt;span style="color: #ff0000"&gt;select&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;entry[count(. | key('entries-by-date', ms:format-date(updated, 'yyyyMMdd'))[1]) = 1]&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;        &lt;span style="color: #008000"&gt;&amp;lt;!-- Sort by date --&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:sort&lt;/span&gt; &lt;span style="color: #ff0000"&gt;select&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;updated&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;data-type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;number&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;order&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;descending&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    &lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;        &lt;span style="color: #008000"&gt;&amp;lt;!-- Show date groups --&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:call-template&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;dateGrouping&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:for-each&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:template&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The magic happens in the for-each. First, we take the current node and the first node from the key (aka index) with the same calculated date value. We create a set with these two nodes using the &amp;quot;|&amp;quot; operator. If the nodes are different, we'll get a set containing two nodes. If they are the same, there will only be one node in the set. We then check the number of nodes in the set using the count() function. If the count is one, we use it in the for-each; otherwise, we skip it. In the end, this gives us the first entry each date. &lt;/p&gt;

&lt;p&gt;After we have our target date to group by, we want to display each &amp;lt;entry&amp;gt; with the same date. In my stylesheet, I implemented it in the &amp;quot;dateGrouping&amp;quot; template. The actual code is pretty easy: &lt;/p&gt;

&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;
  &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;
    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:template&lt;/span&gt; &lt;span style="color: #ff0000"&gt;name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;dateGrouping&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;span style="color: #008000"&gt;&amp;lt;!-- You could output the date header here --&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:for-each&lt;/span&gt; &lt;span style="color: #ff0000"&gt;select&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;key('entries-by-date', ms:format-date(updated, 'yyyyMMdd'))&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:apply-templates&lt;/span&gt; &lt;span style="color: #ff0000"&gt;select&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;.&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:for-each&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;xsl:template&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;We use the key() function to get every node in the given key with the matching value. We then apply the formatting to each node to get our final result. &lt;/p&gt;</summary>
    <published>2008-06-17T09:43:00+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2008/06/17/Grouping-with-XSLT.aspx#comment" />
    <category term="XSLT" />
    <dc:publisher>jaecen</dc:publisher>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=3b855a90-f52a-4a50-93d2-8d57cf16ba3a</pingback:target>
    <slash:comments>0</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=3b855a90-f52a-4a50-93d2-8d57cf16ba3a</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2008/06/17/Grouping-with-XSLT.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=3b855a90-f52a-4a50-93d2-8d57cf16ba3a</wfw:commentRss>
  </entry>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2008/06/13/Poker-Bot-Competition.aspx</id>
    <title>Poker-Bot Competition</title>
    <updated>2008-06-13T22:06:50+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=050c5e79-daa8-4f2b-8ac7-f9e358fe6b14" />
    <link href="http://nothingisalotofsomething.com/post/2008/06/13/Poker-Bot-Competition.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">&lt;p&gt;People enjoy the idea of creating things to battle with each other, ala &lt;a href="http://www.battlebots.com"&gt;BattleBots&lt;/a&gt;. Here’s an idea that is both fun and potentially lucrative: a bot-only online poker server for real money. You would put money into an account an upload your own custom-written poker bot. Your bot would play until it ran out of money. It would keep its earnings, and you would be allowed to withdraw them. &lt;/p&gt;  &lt;p&gt;The idea is that you would lets these bots run for a long time and play &lt;strong&gt;many&lt;/strong&gt; games (if only machines are playing, games would go incredibly fast). To that end, you would need a bot to be able to choose what buy-in it wanted to play at, so it could adjust up or down depending on its success rate.&lt;/p&gt;  &lt;p&gt;It would be of critical importance to not allow the bots to communicate or know who they’re playing against. If they could coordinate, then could game the system to always win. To that end, each bot would be assigned to a random table, and no identifying information about either the tables (so two bots could know if they’re at the same table) or the other players would be given.&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Since it’s just machines playing, the human factor would be removed. At first glance, that would make it seem like a purely mathematical exercise. However, some strategies would become apparent, and a bot can be coded to detect and play against them. Additionally, a bot could employ random factors to protect against these types of detections. It would make the game very dynamic.&lt;/p&gt;  &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:a17630d4-0fc3-4599-b62d-9698a950eb64" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us Tags: &lt;a href="http://del.icio.us/popular/bots" rel="tag"&gt;bots&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/poker" rel="tag"&gt;poker&lt;/a&gt;&lt;/div&gt;</summary>
    <published>2008-06-13T22:06:50+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2008/06/13/Poker-Bot-Competition.aspx#comment" />
    <category term="Ideas" />
    <dc:publisher>jaecen</dc:publisher>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=050c5e79-daa8-4f2b-8ac7-f9e358fe6b14</pingback:target>
    <slash:comments>0</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=050c5e79-daa8-4f2b-8ac7-f9e358fe6b14</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2008/06/13/Poker-Bot-Competition.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=050c5e79-daa8-4f2b-8ac7-f9e358fe6b14</wfw:commentRss>
  </entry>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2008/06/13/building-a-blog.aspx</id>
    <title>building a blog</title>
    <updated>2008-06-13T20:58:56+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=46d8a6b6-e140-4032-af38-8bd8055d006b" />
    <link href="http://nothingisalotofsomething.com/post/2008/06/13/building-a-blog.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">&lt;p&gt;&lt;img title="bricks" height="240" alt="bricks" src="http://www.nothingisalotofsomething.com/image.axd?picture=WindowsLiveWriter/buildingablog_13517/bricks_3.jpg" width="213" align="right" /&gt;&lt;/p&gt;  &lt;p&gt;My first inclination when planning this site was to roll my own blogging suite; I even convinced the admin to upgrade the server to .NET 3.5 so I could use LINQ. My previous experiences with content management systems left me turned off to the idea of pre-defined layouts and constricting plug-in API’s. I wanted to be able to include every feature to take my fancy at the drop of a hat (and the upload of a .cs file). I wanted to use every AJAX/XHTML/CSS trick in the book as well as new ones that come along. If there’s one thing I knew, it’s that I would much too quickly find the bounds of the likes of &lt;a href="http://www.mamboserver.com/"&gt;Mambo&lt;/a&gt;, &lt;a href="http://drupal.org/"&gt;Drupal&lt;/a&gt;, and &lt;a href="http://www.joomla.org/"&gt;Joomla&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Unfortunately, I can’t design the paint job for a barn. I have the knowledge and tools for great functionality. Great looks? Not so much.&lt;/p&gt;  &lt;p&gt;My next thought was to take an existing open source blogging package and adapt the theming engine and any attractive themes. Luckily for me, the first Google result for “.net open source blog engine” was &lt;a href="http://www.dotnetblogengine.net/"&gt;BlogEngine.NET&lt;/a&gt;. A quick peek through the project showed that there has been a lot of progress since my forays in to open source CMS. I was sold. I dropped any ideas of creating my own software and installed the entire package.&lt;/p&gt;  &lt;p&gt;So far, I couldn’t be happier. Instead of imposing an architecture on you, you are presented with what could be considered a reference application, and you’re free to go to town with whatever ASP.NET techniques you want. Theming ASP.NET master pages. You can use your own server and user controls without any restrictions. And since it’s open source, you can add whatever core functionality you want. In short, it’s what I like to imaging I would have written.&lt;/p&gt;  &lt;p&gt;Back to the core issue of design, BlogEngine.NET comes with a few basic themes, and there is a nice, if a bit anemic, theme gallery on their site (though it was broken when I tried to use it). It turns out, however, that there is a &lt;em&gt;thriving&lt;/em&gt; community of free XHTML/CSS site designs. There is a huge selection available at (and significant overlap between) &lt;a href="http://www.oswd.org/"&gt;oswd.org&lt;/a&gt; and &lt;a href="http://www.opendesigns.org/"&gt;opendesignes.org&lt;/a&gt;. They’re realeased under the Creative Commons Attribution License 2.5, so you’re free to do whatever you want with them (including commercial uses), as long as you give credit to the original authors.&lt;/p&gt;  &lt;p&gt;I also found an interesting site called &lt;a href="http://disqus.com/"&gt;Disqus&lt;/a&gt;, which allows you to implement threaded comments without any local resources. Additionally, it allows users to track comments and reputation across multiple sites. There is a similar service called &lt;a href="http://www.intensedebate.com/"&gt;Intense Debate&lt;/a&gt;, though I went with Disqus since it’s integrated with &lt;a href="http://friendfeed.com"&gt;FriendFeed&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;There are, of course, many other useful services, such as &lt;a href="http://www.feedburner.com/"&gt;FeedBurner&lt;/a&gt; for stats on your RSS, &lt;a href="http://www.google.com/analytics/"&gt;Google Analytics&lt;/a&gt; for stats on your site, and &lt;a href="http://flickr.com/"&gt;Fli&lt;/a&gt;&lt;/p&gt;  &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:feadb00c-3e3a-40f8-a5b0-9ae0a53ed741" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;del.icio.us Tags: &lt;a href="http://del.icio.us/popular/blog" rel="tag"&gt;blog&lt;/a&gt;,&lt;a href="http://del.icio.us/popular/blogengine.net" rel="tag"&gt;blogengine.net&lt;/a&gt;&lt;/div&gt;  &lt;p&gt;&lt;a href="http://flickr.com/"&gt;ckr&lt;/a&gt; for images. I plan to add more to the site as I come across them.&lt;/p&gt;</summary>
    <published>2008-06-13T20:58:56+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2008/06/13/building-a-blog.aspx#comment" />
    <category term="Blog" />
    <dc:publisher>jaecen</dc:publisher>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=46d8a6b6-e140-4032-af38-8bd8055d006b</pingback:target>
    <slash:comments>0</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=46d8a6b6-e140-4032-af38-8bd8055d006b</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2008/06/13/building-a-blog.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=46d8a6b6-e140-4032-af38-8bd8055d006b</wfw:commentRss>
  </entry>
  <entry>
    <id>http://nothingisalotofsomething.com/post/2008/06/11/A-Home-for-My-Words.aspx</id>
    <title>A Home for My Words</title>
    <updated>2008-06-11T13:24:00+00:00</updated>
    <link rel="self" href="http://nothingisalotofsomething.com/post.aspx?id=460e46c4-743b-4398-82e2-5a148d478959" />
    <link href="http://nothingisalotofsomething.com/post/2008/06/11/A-Home-for-My-Words.aspx" />
    <author>
      <name>jaecen</name>
    </author>
    <summary type="html">&lt;p&gt;&lt;img src="http://nothingisalotofsomething.com/image.axd?picture=christen.jpg" alt="" align="right" /&gt;Welcome to &amp;quot;Nothing Is a Lot of Something dot com&amp;quot;, a non-sensical name leading to an eminently sensical destination, assuming your sense is to read about practical software development interspersed with musings on life from the perspective of one, Jason Addington.&lt;/p&gt;&lt;p&gt;If that doesn&amp;#39;t make sense to you, then I&amp;#39;m sorry. To make up for it, I&amp;#39;ll give you this link -- &lt;a href="#" onclick="javascript: history.go(-1)"&gt;link!&lt;/a&gt;  -- to take you back to whatever more interesting place you came from. Have fun. You probably won&amp;#39;t be missed. &lt;/p&gt;&lt;p&gt;Now that all of the boring people are gone, let&amp;#39;s get down to business! I&amp;#39;ve created this blog for three main reasons:&lt;/p&gt;&lt;ol&gt;			&lt;li&gt;To share things I learn regarding software development in order to spread understanding and understand better myself.&lt;/li&gt;		&lt;li&gt;To write down my thoughts so I can hear what others think about them. &lt;/li&gt;		&lt;li&gt;To do something with this domain name that I bought simply because I thought it was funny. &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Sadly, the third reason was the most motivating factor. Still, like oak trees from acorns, great things can come from small ideas! So, on this surreptitiously auspicious day, I give you the smallest of ideas planted in the most fertile of grounds, in the hope that one day we may all enjoy the, uh, shade of our efforts and the, er, acorn flour of our collective cognation?&lt;/p&gt;&lt;p&gt;Meh, I just hope I can remember to water it. &lt;/p&gt;</summary>
    <published>2008-06-11T13:24:00+00:00</published>
    <link rel="related" href="http://nothingisalotofsomething.com/post/2008/06/11/A-Home-for-My-Words.aspx#comment" />
    <category term="Blog" />
    <dc:publisher>jaecen</dc:publisher>
    <pingback:server>http://nothingisalotofsomething.com/pingback.axd</pingback:server>
    <pingback:target>http://nothingisalotofsomething.com/post.aspx?id=460e46c4-743b-4398-82e2-5a148d478959</pingback:target>
    <slash:comments>0</slash:comments>
    <trackback:ping>http://nothingisalotofsomething.com/trackback.axd?id=460e46c4-743b-4398-82e2-5a148d478959</trackback:ping>
    <wfw:comment>http://nothingisalotofsomething.com/post/2008/06/11/A-Home-for-My-Words.aspx#comment</wfw:comment>
    <wfw:commentRss>http://nothingisalotofsomething.com/syndication.axd?post=460e46c4-743b-4398-82e2-5a148d478959</wfw:commentRss>
  </entry>
</feed>