<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Neil Ang (web-development)</title>
    <link>http://neilang.com</link>
    <description>Neil Ang Feed</description>
    <language>en-us</language>
    <generator>Symphony (build 1701)</generator>
    <item>
      <title>Redirect to iPhone site with htaccess</title>
      <link>http://neilang.com/entries/redirect-to-iphone-site-with-htaccess/</link>
      <pubDate>Sat, 06 Feb 2010 01:49:00 GMT</pubDate>
      <guid>http://neilang.com/entries/redirect-to-iphone-site-with-htaccess/</guid>
      <description>&lt;p&gt;This is my implementation of how to redirect to an iPhone optimised website using Apache rewrite rules and cookies. Although this redirect can be achieved easily through server-side scripting, websites that implement heavy page caching need to redirect at the request level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The interaction:&lt;/strong&gt; If the user is using an iPhone to view the site, I wanted to automatically redirect them to the iphone site, however I also wanted to allow the user to switch between the two versions of the website freely - and to remember their preference for which version of the site they wanted to view when they returned. I also didn't want to embed absolute URLs into links or append parameters to the end of urls.&lt;/p&gt;

&lt;p&gt;So these are the rules I came up with to redirect an iphone visiting &lt;kbd&gt;www.example.com&lt;/kbd&gt; to &lt;kbd&gt;i.example.com&lt;/kbd&gt;. On each page of the site I include a link to switch between the two sites. Example. If the user is viewing &lt;kbd&gt;i.example.com/about.html&lt;/kbd&gt; there would be a server relative link to "/noiphone/about.html" and on the normal site there would be a link to "/iphone/about.html" etc.&lt;/p&gt;


&lt;code&gt;&lt;em&gt;# Always redirect to the iphone site (unless there is a cookie)&lt;/em&gt; &lt;br /&gt; RewriteCond %{HTTP_USER_AGENT} iPhone &lt;br /&gt; RewriteCond %{HTTP_HOST} !^i.example.com &lt;br /&gt; RewriteCond %{HTTP_COOKIE} !^.*no_iphone=yes.*$ [NC] &lt;br /&gt; RewriteRule ^/(.*)$ http://i.example.com/$1 [R,L] &lt;br /&gt; &lt;br /&gt; &lt;em&gt;# User requested normal site (sets cookie)&lt;/em&gt; &lt;br /&gt; RewriteCond %{REQUEST_URI} ^/noiphone/.*$ &lt;br /&gt; RewriteRule ^/noiphone/(.*)$ http://www.example.com/$1 [R,L,CO=no_iphone:yes:.example.com:10080] &lt;br /&gt; &lt;br /&gt; &lt;em&gt;# User requested iphone site (removes cookie)&lt;/em&gt; &lt;br /&gt; RewriteCond %{REQUEST_URI} ^/iphone/.*$ &lt;br /&gt; RewriteRule ^/iphone/(.*)$ http://i.example.com/$1 [R,L,CO=no_iphone:0:.example.com:-1]&lt;/code&gt;

&lt;p&gt;You may notice that the cookie expire time to remember their preference is set to 10080 minutes (1 week), you may want to adjust this if you don't want their preference to expire.&lt;/p&gt;
					     &lt;p&gt;&lt;a title="What are you, chicken?!" href="http://neilang.com/entries/redirect-to-iphone-site-with-htaccess/#comment"&gt;Please post a comment&lt;/a&gt;&lt;/p&gt;
					   </description>
    </item>
    <item>
      <title>How to create a spell checking web spider</title>
      <link>http://neilang.com/entries/how-to-create-a-spell-checking-web-spider/</link>
      <pubDate>Sat, 24 Oct 2009 10:18:00 GMT</pubDate>
      <guid>http://neilang.com/entries/how-to-create-a-spell-checking-web-spider/</guid>
      <description>&lt;p&gt;Recently I found out my employer was paying a third-party company to regularly check the spelling on their website and send them a monthly report. So I thought I would write a simple spider that could to do the same thing and save them money.&lt;/p&gt;

&lt;h3&gt;The setup&lt;/h3&gt;
&lt;p&gt;My solution uses ruby (plus some gems) and the awesome &lt;a href="http://aspell.net/"&gt;Aspell&lt;/a&gt; (an open source spell checker).&lt;/p&gt;

&lt;p&gt;First of all you will need to download and install &lt;a href="http://aspell.net/"&gt;Aspell&lt;/a&gt;, and an Aspell dictionary for the language you want to use. In this example I will be using the English dictionary.&lt;/p&gt;


&lt;p&gt;To install Aspell on OS X, download and unpack the latest version, and then using terminal:&lt;/p&gt;

&lt;p&gt;
  &lt;code&gt;cd path/to/aspell &lt;br /&gt; ./configure &lt;br /&gt; make &lt;br /&gt; sudo make install&lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;To install a dictionary, repeat the steps above with a downloaded dictionary.&lt;/p&gt;

&lt;p&gt;You can verify that Aspell installed correctly and that the dictionaries are loaded by typing these commands in terminal: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;aspell -v&lt;br /&gt;aspell dicts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next you will need to install &lt;em&gt;raspell&lt;/em&gt;, which is a gem used to interact with Aspell. You will also need another gem called &lt;em&gt;spider&lt;/em&gt;, which will take care of the web crawling work for us. And finally, &lt;em&gt;hpricot&lt;/em&gt; or &lt;em&gt;nokogiri&lt;/em&gt; to handle the HTML.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo gem install raspell &lt;br /&gt;sudo gem install spider&lt;br /&gt;sudo gem install hpricot&lt;/code&gt;&lt;/p&gt;


&lt;h3&gt;The script&lt;/h3&gt;
&lt;p&gt;With that done, you can use the spider gem to crawl all the html pages on a website, then use hpricot to extract the relevant words from the page and check each one with raspell. E.g.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#!/usr/bin/env ruby &lt;br /&gt; &lt;br /&gt; require 'rubygems' &lt;br /&gt; require 'spider' &lt;br /&gt; require 'raspell' &lt;br /&gt; require 'hpricot' &lt;br /&gt; &lt;br /&gt; domain = 'http://www.example.com/' &lt;br /&gt; &lt;br /&gt; speller = Aspell.new('en_GB') &lt;br /&gt; &lt;br /&gt; Spider.start_at(domain) do |s| &lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;s.add_url_check do |a_url| &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a_url.match("^#{domain}") &lt;br /&gt; &amp;nbsp;&amp;nbsp;end &lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;s.on :success do |a_url, resp, prior_url| &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unless resp['content-type'].match('text/html') &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;puts "Skipping #{a_url}" &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;next &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;puts "On page #{a_url}" &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document = Hpricot(resp.body) &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.search('head').remove &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.search('script').remove &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.search('link').remove &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.search('meta').remove &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.search('style').remove &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;words = document.inner_text.gsub(/\s+/, ' ').strip.split(/\s/) &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;speller.list_misspelled(words).each do |mistake| &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;puts " * Found mistake \"#{mistake}\" perhaps you meant \"#{speller.suggest(mistake).first}\"" &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end &lt;br /&gt; &amp;nbsp;&amp;nbsp;end &lt;br /&gt; &lt;br /&gt; end &lt;br /&gt; &lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A few things to note about this script:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I have set the spider to use the "en_GB" dictionary (as this is what we use in Australia), but you can set it to any dictionary you installed (e.g. "en_US").&lt;/li&gt;
  &lt;li&gt;If you wanted to use nokogiri instead of hpricot, simply replace the Hpricot declaration with &lt;kbd&gt;document = Nokogiri(resp.body)&lt;/kbd&gt; and &lt;kbd&gt;require&lt;/kbd&gt; it at the start of the script.&lt;/li&gt;
  &lt;li&gt;Hpricot is used to strip out typically non-visible sections of the page, so that we only spellcheck the displayed words.&lt;/li&gt;
  &lt;li&gt;The spider won't search outside of the set domain.&lt;/li&gt;
  &lt;li&gt;The spider is also capable of performing a link check as it crawls.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see this is only a very basic implementation, but demonstrates how easily you can create your own spelling spider!&lt;/p&gt;
					     &lt;p&gt;&lt;a title="What are you, chicken?!" href="http://neilang.com/entries/how-to-create-a-spell-checking-web-spider/#comment"&gt;Please post a comment&lt;/a&gt;&lt;/p&gt;
					   </description>
    </item>
    <item>
      <title>Validate an ISSN using Perl or JavaScript</title>
      <link>http://neilang.com/entries/validate-an-issn-using-perl-or-javascript/</link>
      <pubDate>Fri, 12 Jun 2009 07:57:00 GMT</pubDate>
      <guid>http://neilang.com/entries/validate-an-issn-using-perl-or-javascript/</guid>
      <description>&lt;p&gt;Last week I wrote about &lt;a href="/entries/how-to-check-if-an-isbn-is-valid-in-perl-or-javasc/"&gt;validating an ISBN&lt;/a&gt;, so this week I thought I would post the code to validate an ISSN.&lt;/p&gt;

&lt;p&gt;There are only a few minor differences between validating an ISSN and ISBN.&lt;/p&gt;

&lt;h3&gt;Validate ISSN in Perl&lt;/h3&gt;

&lt;code&gt;sub valid_issn { &lt;br /&gt; &amp;nbsp;&amp;nbsp;my $issn = $_[0]; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $issn =~ s/[^\dX]//gi; &lt;br /&gt; &amp;nbsp;&amp;nbsp;return if length($issn) != 8; &lt;br /&gt; &amp;nbsp;&amp;nbsp;my $sum = 0; &lt;br /&gt; &amp;nbsp;&amp;nbsp;my @chars = split('', $issn); &lt;br /&gt; &amp;nbsp;&amp;nbsp;$chars[7] = 10 if uc($chars[7]) eq 'X'; &lt;br /&gt; &amp;nbsp;&amp;nbsp;for (my $char = 0; $char &amp;lt; @chars; $char++) { &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sum += (8-$char) * $chars[$char]; &lt;br /&gt; &amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;nbsp;&amp;nbsp;return (($sum % 11) == 0); &lt;br /&gt; } &lt;br /&gt; &lt;/code&gt;

&lt;h3&gt;Validate ISSN in JavaScript&lt;/h3&gt;

&lt;code&gt;function isValidISSN (issn) { &lt;br /&gt; &amp;nbsp;&amp;nbsp;issn = issn.replace(/[^\dX]/gi, ''); &lt;br /&gt; &amp;nbsp;&amp;nbsp;if(issn.length != 8){ &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return false; &lt;br /&gt; &amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;nbsp;&amp;nbsp;var chars = issn.split(''); &lt;br /&gt; &amp;nbsp;&amp;nbsp;if(chars[7].toUpperCase() == 'X'){ &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chars[7] = 10; &lt;br /&gt; &amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;nbsp;&amp;nbsp;var sum = 0; &lt;br /&gt; &amp;nbsp;&amp;nbsp;for (var i = 0; i &amp;lt; chars.length; i++) { &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sum += ((8-i) * parseInt(chars[i])); &lt;br /&gt; &amp;nbsp;&amp;nbsp;}; &lt;br /&gt; &amp;nbsp;&amp;nbsp;return ((sum % 11) == 0); &lt;br /&gt; }&lt;/code&gt;
					     &lt;p&gt;&lt;a title="What are you, chicken?!" href="http://neilang.com/entries/validate-an-issn-using-perl-or-javascript/#comment"&gt;Please post a comment&lt;/a&gt;&lt;/p&gt;
					   </description>
    </item>
    <item>
      <title>How to check if an ISBN is valid in Perl or JavaScript</title>
      <link>http://neilang.com/entries/how-to-check-if-an-isbn-is-valid-in-perl-or-javasc/</link>
      <pubDate>Thu, 04 Jun 2009 22:22:00 GMT</pubDate>
      <guid>http://neilang.com/entries/how-to-check-if-an-isbn-is-valid-in-perl-or-javasc/</guid>
      <description>&lt;p&gt;The cool thing about an ISBN is that its last digit is a "check digit", which validates the rest of the number. How does it work? "It is calculated on a modulus 11 with weights 10-2, using X in lieu of 10 where ten would occur as a check digit." &lt;a href="http://www.isbn.org/standards/home/isbn/international/html/usm4.htm"&gt;More info&lt;/a&gt;.&lt;/p&gt;


&lt;h3&gt;Validate an ISBN in Perl&lt;/h3&gt;
&lt;p&gt;Here is a simple subroutine I wrote to check if a number entered is valid: &lt;/p&gt;
&lt;p&gt;
&lt;code&gt;sub valid_isbn { &lt;br /&gt; &amp;nbsp;&amp;nbsp;my $isbn = shift; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $isbn =~ s/[^\dX]//gi; &lt;br /&gt; &amp;nbsp;&amp;nbsp;return if length($isbn) != 10; &lt;br /&gt; &amp;nbsp;&amp;nbsp;my $sum = 0; &lt;br /&gt; &amp;nbsp;&amp;nbsp;my @chars = split('', $isbn); &lt;br /&gt; &amp;nbsp;&amp;nbsp;$chars[9] = 10 if uc($chars[9]) eq 'X'; &lt;br /&gt; &amp;nbsp;&amp;nbsp;for (my $char = 0; $char &amp;lt; @chars; $char++) { &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$sum += (10-$char) * $chars[$char]; &lt;br /&gt; &amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;nbsp;&amp;nbsp;return (($sum % 11) == 0); &lt;br /&gt; }&lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;Code would not be complete without tests!&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;use Test::More tests =&amp;gt; 12; &lt;br /&gt; &lt;br /&gt; is(valid_isbn("ISBN 0843610727"), 1); &lt;br /&gt; is(valid_isbn("99921-58-10-7"), 1); &lt;br /&gt; is(valid_isbn("9971 5 0210 0"), 1); &lt;br /&gt; is(valid_isbn("0-8044-2957-X"), 1); &lt;br /&gt; is(valid_isbn("0943396042"), 1); &lt;br /&gt; is(valid_isbn("0-9752298-0-X"), 1); &lt;br /&gt; &lt;br /&gt; isnt(valid_isbn("ISBN 0843610723"), 1); &lt;br /&gt; isnt(valid_isbn("91921-58-10-7"), 1); &lt;br /&gt; isnt(valid_isbn("9971 5 0214 0"), 1); &lt;br /&gt; isnt(valid_isbn("0-8044-2957-2"), 1); &lt;br /&gt; isnt(valid_isbn("0943394042"), 1); &lt;br /&gt; isnt(valid_isbn("0-9757297-0-X"), 1);&lt;/code&gt;&lt;/p&gt;


&lt;h3&gt;Validate an ISBN in JavaScript&lt;/h3&gt;

&lt;p&gt;And here it is written as a JavaScript function:&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;&amp;lt;script type="text/javascript" charset="utf-8"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;function isValidISBN (isbn) { &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isbn = isbn.replace(/[^\dX]/gi, ''); &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(isbn.length != 10){ &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return false; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var chars = isbn.split(''); &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(chars[9].toUpperCase() == 'X'){ &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;chars[9] = 10; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sum = 0; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (var i = 0; i &amp;lt; chars.length; i++) { &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sum += ((10-i) * parseInt(chars[i])); &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return ((sum % 11) == 0); &lt;br /&gt; &amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;lt;/script&amp;gt;&lt;/code&gt;
&lt;/p&gt;
					     &lt;p&gt;&lt;a title="What are you, chicken?!" href="http://neilang.com/entries/how-to-check-if-an-isbn-is-valid-in-perl-or-javasc/#comment"&gt;Please post a comment&lt;/a&gt;&lt;/p&gt;
					   </description>
    </item>
    <item>
      <title>Computer namer</title>
      <link>http://neilang.com/entries/computer-namer/</link>
      <pubDate>Sun, 22 Feb 2009 11:30:00 GMT</pubDate>
      <guid>http://neilang.com/entries/computer-namer/</guid>
      <description>&lt;p&gt;Last week I was working with rails, and decided to test how much I had learnt to date. I picked a small website idea I had had aeons ago, and wanted to see how quickly and easily I could put it together in rails.&lt;/p&gt;

&lt;p&gt;To my surprise I did better then I thought. In less then an hour I had the site, database, administration screen and mailer setup. I did a bit more work later on, and added ajax, mac style buttons and &lt;a href="http://twitter.com/computernamer"&gt;twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The site - &lt;a href="http://computernamer.com/"&gt;Computer namer&lt;/a&gt; - is a random name generator for naming your computer. It was fun to make and  (hopefully) fun to use.&lt;/p&gt;

&lt;p&gt;Please have a look at it, and let me know what you think. Also, make a suggestion for a computer name if you can think of one.&lt;/p&gt;
					     &lt;p&gt;&lt;a title="What are you, chicken?!" href="http://neilang.com/entries/computer-namer/#comment"&gt;Please post a comment&lt;/a&gt;&lt;/p&gt;
					   </description>
    </item>
    <item>
      <title>Generating a calendar using XSLT</title>
      <link>http://neilang.com/entries/generating-a-calendar-using-xslt/</link>
      <pubDate>Sun, 16 Nov 2008 07:43:00 GMT</pubDate>
      <guid>http://neilang.com/entries/generating-a-calendar-using-xslt/</guid>
      <description>&lt;p&gt;Recently I was tasked to build an interactive calendar in XSLT. I wanted the calendar to always start on a Sunday as well as have clean markup. I couldn't find any good examples on the web while I was building it, so I decided to post the base of my solution.&lt;/p&gt;

&lt;p&gt;
	&lt;img src="http://neilang.com/workspace/upload/calendar.png" alt="Picture of final product" /&gt;
&lt;/p&gt;

&lt;p&gt;Insert the following variables and templates into your stylesheet:&lt;/p&gt;

&lt;code&gt;&amp;lt;xsl:variable name="DisplayDate" select="date:date()"/&amp;gt; &lt;br /&gt; &amp;lt;xsl:variable name="Year" select="date:year($DisplayDate)"/&amp;gt; &lt;br /&gt; &amp;lt;xsl:variable name="Month" select="date:month-in-year($DisplayDate)"/&amp;gt; &lt;br /&gt; &amp;lt;xsl:variable name="MonthName" select="date:month-name($DisplayDate)" /&amp;gt; &lt;br /&gt; &amp;lt;xsl:variable name="NumberOfDaysInMonth"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;xsl:call-template name="DaysInMonth"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:with-param name="month" select="$Month" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:with-param name="year" select="$Year" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;/xsl:call-template&amp;gt; &lt;br /&gt; &amp;lt;/xsl:variable&amp;gt; &lt;br /&gt; &amp;lt;xsl:variable name="FirstDayInWeekForMonth"&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;lt;xsl:choose&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:when test="$Month &amp;amp;lt; 10"&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:value-of select="date:day-in-week(date:date(concat($Year,'-0', $Month, '-01')))" /&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:when&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:otherwise&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:value-of select="date:day-in-week(date:date(concat($Year,'-', $Month, '-01')))" /&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:otherwise&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:choose&amp;gt;&lt;br /&gt;
&amp;lt;/xsl:variable&amp;gt; &lt;br /&gt; &amp;lt;xsl:variable name="WeeksInMonth"&amp;gt;&amp;lt;xsl:value-of select="($NumberOfDaysInMonth + $FirstDayInWeekForMonth - 1) div 7" /&amp;gt;&amp;lt;/xsl:variable&amp;gt; &lt;br /&gt; &lt;br /&gt; &amp;lt;xsl:template name="DaysInMonth"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;xsl:param name="month"&amp;gt;&amp;lt;xsl:value-of select="$Month" /&amp;gt;&amp;lt;/xsl:param&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;xsl:param name="year"&amp;gt;&amp;lt;xsl:value-of select="$Year" /&amp;gt;&amp;lt;/xsl:param&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;xsl:choose&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:when test="$month = 1 or $month = 3 or $month = 5 or $month = 7 or $month = 8 or $month = 10 or $month = 12"&amp;gt;31&amp;lt;/xsl:when&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:when test="$month=2"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:choose&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:when test="$year mod 4 = 0"&amp;gt;29&amp;lt;/xsl:when&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:otherwise&amp;gt;28&amp;lt;/xsl:otherwise&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:choose&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:when&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:otherwise&amp;gt;30&amp;lt;/xsl:otherwise&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;/xsl:choose&amp;gt; &lt;br /&gt; &amp;lt;/xsl:template&amp;gt; &lt;br /&gt; &lt;br /&gt; &amp;lt;xsl:template name="Calendar"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;table summary="Monthly calendar"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;caption&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:value-of select="$MonthName" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:text&amp;gt; &amp;lt;/xsl:text&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:value-of select="$Year" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/caption&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;tr&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;th abbr="Sunday"&amp;gt;Sun&amp;lt;/th&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;th abbr="Monday"&amp;gt;Mon&amp;lt;/th&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;th abbr="Tuesday"&amp;gt;Tue&amp;lt;/th&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;th abbr="Wednesday"&amp;gt;Wed&amp;lt;/th&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;th abbr="Thursday"&amp;gt;Thu&amp;lt;/th&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;th abbr="Friday"&amp;gt;Fri&amp;lt;/th&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;th abbr="Saturday"&amp;gt;Sat&amp;lt;/th&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/tr&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:call-template name="CalendarWeek"/&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;/table&amp;gt; &lt;br /&gt; &amp;lt;/xsl:template&amp;gt; &lt;br /&gt; &lt;br /&gt; &amp;lt;xsl:template name="CalendarWeek"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;xsl:param name="week"&amp;gt;1&amp;lt;/xsl:param&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;xsl:param name="day"&amp;gt;1&amp;lt;/xsl:param&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;tr&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:call-template name="CalendarDay"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:with-param name="day" select="$day" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:call-template&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;/tr&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;xsl:if test="$WeeksInMonth &amp;amp;gt; $week"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:call-template name="CalendarWeek"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:with-param name="week" select="$week + 1" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:with-param name="day" select="$week * 7 - ($FirstDayInWeekForMonth - 2)" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:call-template&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;/xsl:if&amp;gt; &lt;br /&gt; &amp;lt;/xsl:template&amp;gt; &lt;br /&gt; &lt;br /&gt; &amp;lt;xsl:template name="CalendarDay"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;xsl:param name="count"&amp;gt;1&amp;lt;/xsl:param&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;xsl:param name="day" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;xsl:choose&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:when test="($day = 1 and $count != $FirstDayInWeekForMonth) or $day &amp;amp;gt; $NumberOfDaysInMonth"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;td class="empty"&amp;gt;&amp;lt;xsl:text disable-output-escaping="yes"&amp;gt;&amp;amp;amp;nbsp;&amp;lt;/xsl:text&amp;gt;&amp;lt;/td&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:if test="$count &amp;amp;lt; 7"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:call-template name="CalendarDay"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:with-param name="count" select="$count + 1" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:with-param name="day" select="$day" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:call-template&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:if&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:when&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:otherwise&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;td&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:value-of select="$day" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/td&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:if test="$count &amp;amp;lt; 7"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:call-template name="CalendarDay"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:with-param name="count" select="$count + 1" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;xsl:with-param name="day" select="$day + 1" /&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:call-template&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:if&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/xsl:otherwise&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;lt;/xsl:choose&amp;gt; &lt;br /&gt; &amp;lt;/xsl:template&amp;gt;&lt;/code&gt;

&lt;p&gt;For this code to work, you need to be using &lt;a href="http://exslt.org/"&gt;exslt&lt;/a&gt;.&lt;/p&gt;

&lt;code&gt;&amp;lt;xsl:stylesheet version="1.0" &lt;br /&gt; &amp;nbsp;&amp;nbsp;xmlns:xsl="http://www.w3.org/1999/XSL/Transform" &lt;br /&gt; &amp;nbsp;&amp;nbsp;xmlns:exslt="http://exslt.org/common" &lt;br /&gt; &amp;nbsp;&amp;nbsp;xmlns:date="http://exslt.org/dates-and-times"&amp;gt;&lt;/code&gt;

&lt;p&gt;Once you have added the templates you can call the calendar like this: &lt;/p&gt;

&lt;code&gt;&amp;lt;xsl:call-template name="Calendar" /&amp;gt;&lt;/code&gt;

&lt;p&gt;Finally add some CSS to the page so your calendar looks more presentable:&lt;/p&gt;

&lt;code&gt;&amp;lt;style type="text/css" media="screen"&amp;gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;table{ &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:1px solid #CCC; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border-collapse: collapse; &lt;br /&gt; &amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;nbsp;&amp;nbsp;caption{ &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-weight:bold; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font-size:120%; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin-bottom:8px; &lt;br /&gt; &amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;td,th{ &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;text-align:center; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vertical-align:middle; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;width:36px; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;height:34px; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;border:1px solid #CCC; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;padding:0; &lt;br /&gt; &amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;th{ &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;height:inherit; &lt;br /&gt; &amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;.empty{ &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;background-color:#EEE; &lt;br /&gt; &amp;nbsp;&amp;nbsp;} &lt;br /&gt; &amp;lt;/style&amp;gt; &lt;br /&gt; &lt;br /&gt; &lt;/code&gt;

&lt;p&gt;This is only the base of the calendar I built, and you can expand this to make it browse-able. But I will leave that for another post. &lt;/p&gt;
					     &lt;p&gt;&lt;a title="What are you, chicken?!" href="http://neilang.com/entries/generating-a-calendar-using-xslt/#comment"&gt;Please post a comment&lt;/a&gt;&lt;/p&gt;
					   </description>
    </item>
    <item>
      <title>How to setup virtual hosts on Mac OS X 10.5 (Leopard)</title>
      <link>http://neilang.com/entries/how-to-setup-virtual-hosts-on-mac-os-x-105-leopar/</link>
      <pubDate>Sat, 20 Sep 2008 04:29:00 GMT</pubDate>
      <guid>http://neilang.com/entries/how-to-setup-virtual-hosts-on-mac-os-x-105-leopar/</guid>
      <description>&lt;p&gt;Whilst developing a new project it can be convenient to setup a virtual host on your local machine. For example, if I wanted to have a local development/demonstration version of a website I'm developing (e.g. &lt;a href="http://learnhowtojuggle.info"&gt;http://learnhowtojuggle.info&lt;/a&gt;), I could setup the following virtual host: &lt;a href="http://learnhowtojuggle.dev" rel="nofollow"&gt;http://learnhowtojuggle.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;N.B. Only follow this tutorial if you are comfortable with editing system configurations and are aware of the risks. If you have &lt;a href="http://macromates.com/"&gt;TextMate&lt;/a&gt; installed, you can click the file path links in these instructions to quickly open them on your local computer (&lt;a href="http://manual.macromates.com/en/using_textmate_from_terminal.html"&gt;More Information&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;1. Modify the virtual hosts configuration&lt;/h3&gt;

&lt;p&gt;Use terminal to find and modify your virtual hosts configuration file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mate &lt;a href="txmt://open/?url=file:///private/etc/apache2/extra/httpd-vhosts.conf" class="textmate"&gt;/private/etc/apache2/extra/httpd-vhosts.conf&lt;/a&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Append to this bottom of this file an entry for your virtual domain:&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt; &lt;br /&gt; ServerAdmin webmaster@learnhowtojuggle.dev &lt;br /&gt; DocumentRoot "/Users/neil/Sites/learnhowtojuggle" &lt;br /&gt; ServerName learnhowtojuggle.dev &lt;br /&gt; ServerAlias learnhowtojuggle.dev &lt;br /&gt; ErrorLog "/private/var/log/apache2/learnhowtojuggle.dev-error_log" &lt;br /&gt; &amp;lt;/VirtualHost&amp;gt;&lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;Make sure the &lt;kbd&gt;DocumentRoot&lt;/kbd&gt; you enter actually exists. For convenience, I've placed mine in my Site folder. You can now save and close this file.&lt;/p&gt;

&lt;h3&gt;2. Check Apache knows about virtual hosts&lt;/h3&gt;

&lt;p&gt;Next we will need to modify the Apache configuration file to make sure it includes the virtual hosts file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mate &lt;a href="txmt://open/?url=file:///private/etc/apache2/httpd.conf&amp;amp;line=460" class="textmate"&gt;/private/etc/apache2/httpd.conf&lt;/a&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Locate the following line and remove the # sign (also known as an "octothorpe" ) from in front of it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Include /private/etc/apache2/extra/httpd-vhosts.conf&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Save and close this file as well.&lt;/p&gt;

&lt;h3&gt;3. Restart Apache&lt;/h3&gt;

&lt;p&gt;For any changes to take effect on the Apache server, you will need to restart it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo httpd -k restart&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;4. Update hosts file&lt;/h3&gt;

&lt;p&gt;Finally, the easiest way to let our browser know to use our local server when it get a request for our virtual host is to modify the hosts file. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;mate &lt;a href="txmt://open/?url=file:///private/etc/hosts" class="textmate"&gt;/private/etc/hosts&lt;/a&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add the following entry:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;127.0.0.1	learnhowtojuggle.dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After you save and close this file, navigate to &lt;a href="http://learnhowtojuggle.dev" rel="nofollow"&gt;http://learnhowtojuggle.dev&lt;/a&gt; in your browser to see the newly created virtual host on your local machine. &lt;/p&gt;


&lt;h3&gt;Additional Configuration&lt;/h3&gt;

&lt;p&gt;Since I am serving my new virtual host out of my Sites folder, and I want to use my .htacess file for URL rewriting, I had to update the Apache configuration again to allow permission for this.&lt;/p&gt;

&lt;p&gt;The configuration for my Site folder was located at &lt;kbd&gt;/private/etc/apache2/users/neil.conf&lt;/kbd&gt; (yours will be different based on your system profile name). I then set my permissions to be pretty loose since it is only my local machine.&lt;/p&gt;

&lt;p&gt;Example configuration: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;Directory "/Users/neil/Sites/"&amp;gt; &lt;br /&gt; Options Indexes FollowSymLinks MultiViews &lt;br /&gt; AllowOverride All &lt;br /&gt; Order allow,deny &lt;br /&gt; Allow from all &lt;br /&gt; &amp;lt;/Directory&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After you update your directory permissions, you will need to restart apache again for these to changes to take effect.&lt;/p&gt;
					     &lt;p&gt;&lt;a title="What are you, chicken?!" href="http://neilang.com/entries/how-to-setup-virtual-hosts-on-mac-os-x-105-leopar/#comment"&gt;Please post a comment&lt;/a&gt;&lt;/p&gt;
					   </description>
    </item>
    <item>
      <title>Learn How To Juggle</title>
      <link>http://neilang.com/entries/learn-how-to-juggle/</link>
      <pubDate>Sat, 15 Mar 2008 23:11:00 GMT</pubDate>
      <guid>http://neilang.com/entries/learn-how-to-juggle/</guid>
      <description>&lt;p&gt;Last Thursday I was talking with &lt;a href="http://www.deanjrobinson.com/"&gt;Dean&lt;/a&gt; about ideas for new sites. Most of them were silly ideas and would never work, but there was one I felt I could accomplish successfully.&lt;/p&gt;
&lt;p&gt;So over the last couple of days I've been working on a new site that teaches visitors how to juggle. I'm happy to announce it is now live. There are some pages that I'm still writing, but the basics for learning how to juggle are there.&lt;/p&gt;

&lt;p&gt;Please check out my new site: &lt;a href="http://learnhowtojuggle.info/?utm_source=neilang&amp;amp;utm_medium=banner&amp;amp;utm_campaign=Self%2BPromotion"&gt;Learn How To Juggle&lt;/a&gt;, and leave a comment on this post of your thoughts.&lt;/p&gt;
					     &lt;p&gt;&lt;a title="What are you, chicken?!" href="http://neilang.com/entries/learn-how-to-juggle/#comment"&gt;Please post a comment&lt;/a&gt;&lt;/p&gt;
					   </description>
    </item>
    <item>
      <title>A better technique to stop spam?</title>
      <link>http://neilang.com/entries/a-better-technique-to-stop-spam/</link>
      <pubDate>Fri, 01 Feb 2008 21:52:34 GMT</pubDate>
      <guid>http://neilang.com/entries/a-better-technique-to-stop-spam/</guid>
      <description>&lt;p&gt;A couple of weeks ago I wrote an entry on how to &lt;a href="/entries/outsmarting-comment-spam/"&gt;outsmart comment spam&lt;/a&gt; with javascript. 
Although this is a good technique, it has a drawback - code maintenance. 
If there is a change to the mark up of the form, it is more time consuming to alter the javascript than the HTML. &lt;/p&gt;

&lt;p&gt;To overcome this problem and still out smart spam, I've been testing a new method which uses javascript to change the form action. In it's simplest form the javascript can look like this:&lt;/p&gt;

&lt;code&gt;&amp;lt;script type="text/javascript" charset="utf-8"&amp;gt; &lt;br /&gt;&amp;nbsp;function redirectForm(form){ &lt;br /&gt; &amp;nbsp;&amp;nbsp;form.action = ''; &lt;br /&gt; &amp;nbsp;&amp;nbsp;form.submit(); &lt;br /&gt;&amp;nbsp;} &lt;br /&gt; &amp;lt;/script&amp;gt; &lt;br /&gt; &lt;br /&gt; &amp;lt;form action="/no-javascript" onsubmit="redirectForm(this);" method="post" accept-charset="utf-8"&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;p&amp;gt;&amp;lt;input type="text" name="test" /&amp;gt;&amp;lt;/p&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;p&amp;gt;&amp;lt;input type="submit" value="Submit" /&amp;gt;&amp;lt;/p&amp;gt; &lt;br /&gt; &amp;lt;/form&amp;gt;&lt;/code&gt;

&lt;p&gt;&lt;small&gt;N.B. The default action for &lt;a href="http://21degrees.com.au/products/symphony/"&gt;Symphony&lt;/a&gt; forms is to submit to the same page, which is why the action is set to empty string.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;If you use multiple forms that have different actions, you could expand the javascript to handle those as well:&lt;/p&gt;

&lt;code&gt;&amp;lt;script type="text/javascript" charset="utf-8"&amp;gt; &lt;br /&gt;&amp;nbsp;function redirectForm(form, action){ &lt;br /&gt; &amp;nbsp;&amp;nbsp;form.action = action; &lt;br /&gt; &amp;nbsp;&amp;nbsp;form.submit(); &lt;br /&gt; &amp;nbsp;} &lt;br /&gt; &amp;lt;/script&amp;gt; &lt;br /&gt; &lt;br /&gt; &amp;lt;form action="/no-javascript/" onsubmit="redirectForm(this, '');" method="post" accept-charset="utf-8"&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;p&amp;gt;&amp;lt;input type="text" name="test" /&amp;gt;&amp;lt;/p&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;p&amp;gt;&amp;lt;input type="submit" value="Submit" /&amp;gt;&amp;lt;/p&amp;gt; &lt;br /&gt; &amp;lt;/form&amp;gt;&lt;/code&gt;

&lt;p&gt;This is just a start, there's more you can do to make this JS smarter (but I'll let you figure that out). So far this technique has worked great for me. Remember to setup a real page for the &lt;a href="/no-javascript/"&gt;fake submission url&lt;/a&gt;, so that authentic users of your site without JS enabled are alerted that there submission was unsuccessful.&lt;/p&gt;

&lt;p&gt;To get an idea on how much spam you've stopped, check your server logs to see for the number of requests to your &lt;a href="/no-javascript/"&gt;fake submission url&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please leave a comment with your thoughts on this technique vs the &lt;a href="/entries/outsmarting-comment-spam/"&gt;previous one&lt;/a&gt;, or if you know of another solution to stop spam.&lt;/p&gt;
					     &lt;p&gt;&lt;a title="What are you, chicken?!" href="http://neilang.com/entries/a-better-technique-to-stop-spam/#comment"&gt;Please post a comment&lt;/a&gt;&lt;/p&gt;
					   </description>
    </item>
    <item>
      <title>Outsmarting comment spam</title>
      <link>http://neilang.com/entries/outsmarting-comment-spam/</link>
      <pubDate>Sun, 13 Jan 2008 08:27:00 GMT</pubDate>
      <guid>http://neilang.com/entries/outsmarting-comment-spam/</guid>
      <description>&lt;p&gt;It is often hard to find the right balance between stopping comment spam, and encouraging users to leave comments. For example, although captcha's are effective, I don't like asking my users to enter a (sometimes cryptic) code to leave a comment.&lt;/p&gt;
&lt;p&gt;I put a lot of effort into making sure comment spam doesn't make it on my web site. However, late 2007 I started receiving comment spam from a spam-bot who wasn't selling anything, or linking back to their site, it was just nonsense that didn't get caught by my anti-spam methods.&lt;/p&gt;

&lt;p&gt;To counter-act this annoying comment spam, I wrote a very simple piece of JavaScript that inserted the comment form dynamically. This meant that unless the spam-bot could interpret JavaScript, it wouldn't detect the embedded form on the page and attempt to spam it.&lt;/p&gt;

&lt;p&gt;Heres what the JS looks like:&lt;/p&gt;

&lt;code&gt;function insertCommentForm(){ &lt;br /&gt; &amp;nbsp;&amp;nbsp;var parent = document.getElementById('myCommentForm'); &lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;var form = document.createElement('form'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;form.setAttribute('action', ''); &lt;br /&gt; &amp;nbsp;&amp;nbsp;form.setAttribute('method', 'post'); &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;var fieldset = document.createElement('fieldset'); &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;// Input: Name &lt;br /&gt; &amp;nbsp;&amp;nbsp;var nameLabel = document.createElement('label'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;var nameText = document.createTextNode('Name '); &lt;br /&gt; &amp;nbsp;&amp;nbsp;var nameInput = document.createElement('input'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;nameInput.setAttribute('name', 'name'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;nameInput.setAttribute('value', document.getElementById('myComment-name').value); &lt;br /&gt; &amp;nbsp;&amp;nbsp;nameLabel.appendChild(nameText); &lt;br /&gt; &amp;nbsp;&amp;nbsp;nameLabel.appendChild(nameInput); &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;//Input: Email &lt;br /&gt; &amp;nbsp;&amp;nbsp;var emailLabel = document.createElement('label'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;var emailText = document.createTextNode('Email '); &lt;br /&gt; &amp;nbsp;&amp;nbsp;var emailNote = document.createElement('small'); &lt;br /&gt;&amp;nbsp;&amp;nbsp;emailNote.innerHTML = 'Required but never displayed'; &lt;br /&gt; &amp;nbsp;&amp;nbsp;var emailInput = document.createElement('input'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;emailInput.setAttribute('name', 'email'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;emailInput.setAttribute('value', document.getElementById('myComment-email').value); &lt;br /&gt; &amp;nbsp;&amp;nbsp;emailLabel.appendChild(emailText); &lt;br /&gt; &amp;nbsp;&amp;nbsp;emailLabel.appendChild(emailNote); &lt;br /&gt; &amp;nbsp;&amp;nbsp;emailLabel.appendChild(emailInput); &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;//Input: Website&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;var websiteLabel = document.createElement('label'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;var websiteText = document.createTextNode('Website '); &lt;br /&gt; &amp;nbsp;&amp;nbsp;var websiteNote = document.createElement('small'); &lt;br /&gt;&amp;nbsp;&amp;nbsp;websiteNote.innerHTML = 'http://'; &lt;br /&gt; &amp;nbsp;&amp;nbsp;var websiteInput = document.createElement('input'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;websiteInput.setAttribute('name', 'website'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;websiteInput.setAttribute('value', document.getElementById('myComment-url').value); &lt;br /&gt; &amp;nbsp;&amp;nbsp;websiteLabel.appendChild(websiteText); &lt;br /&gt; &amp;nbsp;&amp;nbsp;websiteLabel.appendChild(websiteNote); &lt;br /&gt; &amp;nbsp;&amp;nbsp;websiteLabel.appendChild(websiteInput); &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;//Input: Comment &lt;br /&gt; &amp;nbsp;&amp;nbsp;var commentLabel = document.createElement('label'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;var commentText = document.createTextNode('Comment '); &lt;br /&gt; &amp;nbsp;&amp;nbsp;var commentTextarea = document.createElement('textarea'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;commentTextarea.setAttribute('name', 'comment'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;commentTextarea.setAttribute('rows', '12'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;commentTextarea.setAttribute('cols', '24'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;commentLabel.appendChild(commentText); &lt;br /&gt; &amp;nbsp;&amp;nbsp;commentLabel.appendChild(commentTextarea); &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;//Hidden: Section&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;var sectionHiddenInput = document.createElement('input'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;sectionHiddenInput.setAttribute('type', 'hidden'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;sectionHiddenInput.setAttribute('name', 'section'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;sectionHiddenInput.setAttribute('value', 'entries'); &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;//Hidden: Remember &lt;br /&gt; &amp;nbsp;&amp;nbsp;var rememberHiddenInput = document.createElement('input'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;rememberHiddenInput.setAttribute('type', 'hidden'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;rememberHiddenInput.setAttribute('name', 'remember'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;rememberHiddenInput.setAttribute('value', 'on'); &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;//Hidden: Entry Handle &lt;br /&gt; &amp;nbsp;&amp;nbsp;var rememberHiddenInput = document.createElement('input'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;rememberHiddenInput.setAttribute('type', 'hidden'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;rememberHiddenInput.setAttribute('name', 'entry-handle'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;rememberHiddenInput.setAttribute('value', document.getElementById('myComment-entry').value); &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;//Hidden: Submit &lt;br /&gt; &amp;nbsp;&amp;nbsp;var submitInput = document.createElement('input'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;submitInput.setAttribute('type', 'submit'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;submitInput.setAttribute('id', 'submit'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;submitInput.setAttribute('value', 'Post Comment'); &lt;br /&gt; &amp;nbsp;&amp;nbsp;submitInput.setAttribute('name', 'action[comment]'); &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;fieldset.appendChild(nameLabel); &lt;br /&gt; &amp;nbsp;&amp;nbsp;fieldset.appendChild(emailLabel); &lt;br /&gt; &amp;nbsp;&amp;nbsp;fieldset.appendChild(websiteLabel); &lt;br /&gt; &amp;nbsp;&amp;nbsp;fieldset.appendChild(commentLabel); &lt;br /&gt; &amp;nbsp;&amp;nbsp;fieldset.appendChild(sectionHiddenInput); &lt;br /&gt; &amp;nbsp;&amp;nbsp;fieldset.appendChild(rememberHiddenInput); &lt;br /&gt; &amp;nbsp;&amp;nbsp;fieldset.appendChild(submitInput); &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;form.appendChild(fieldset); &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;document.getElementById('myCommentWarning').style.display='none'; &lt;br /&gt; &amp;nbsp;&amp;nbsp; &lt;br /&gt; &amp;nbsp;&amp;nbsp;parent.appendChild(form); &lt;br /&gt; }&lt;/code&gt;

&lt;p&gt;Since using this JS I haven't received any comment spam. And until spam-bots evolve to read JavaScript, this is a simple and effective technique to reduce time spent dealing with spam.&lt;/p&gt;

&lt;p&gt;Also, if you use &lt;a href="http://21degrees.com.au/products/symphony/"&gt;Symphony&lt;/a&gt;, this JS will work with your comment forms too. &lt;a href="/about"&gt;Contact me&lt;/a&gt; if you would like the updates to the XSL you will need to make this work.&lt;/p&gt;
					     &lt;p&gt;&lt;a title="What are you, chicken?!" href="http://neilang.com/entries/outsmarting-comment-spam/#comment"&gt;Please post a comment&lt;/a&gt;&lt;/p&gt;
					   </description>
    </item>
  </channel>
</rss>
