<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Hacking at 0300 &#187; Javascript</title>
	<atom:link href="http://bililite.com/blog/category/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://bililite.com/blog</link>
	<description>Thoughts on web design and programming from a very occasional volunteer webmaster</description>
	<lastBuildDate>Fri, 23 Jul 2010 09:00:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Don&#8217;t Reinvent the Wheel</title>
		<link>http://bililite.com/blog/2009/06/29/dont-reinvent-the-wheel/</link>
		<comments>http://bililite.com/blog/2009/06/29/dont-reinvent-the-wheel/#comments</comments>
		<pubDate>Mon, 29 Jun 2009 18:38:13 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=986</guid>
		<description><![CDATA[See the updated version. I needed a date/time picker and spend a couple of hours making a simple one: $('#datetime').flexdate() Obviously, I didn't spend a couple of hours putting this together. I wrote it quickly, then spend the hours getting it to work even remotely correctly in Internet Explorer. Download the code and the HTML [...]]]></description>
			<content:encoded><![CDATA[<p><strong>See the <a href="/blog/2009/07/09/updating-timepickr/">updated version</a></strong>.</p>
<script src="/inc/jquery.ui.subclass.js"></script>
<script src="/inc/jquery.textpopup.js"></script>
<script src="/inc/jquery.flexcal.js"></script>
<script src="/inc/jquery.flexdate.js"></script>
<script src="/inc/jquery.timepickr.js"></script>
<script type="text/javascript"
  src="http://jqueryui.com/themeroller/themeswitchertool/">
</script>
<p>I needed a date/time picker and spend a couple of hours making a simple one:</p>
<div id="themeswitcher" ></div>
<input id="datetime"/>
<script>$('#datetime').flexdate()</script>
<p>Obviously, I didn't spend a couple of hours putting this together. I wrote it quickly, then spend the hours getting it to work even remotely correctly in <a href="http://youngisrael-stl.org/images/webdesign.png">Internet Explorer</a>.</p>
<span id="more-986"></span>
<p>Download the <a href="/inc/jquery.flexdate.js">code</a> and the <a href="/inc/jflexdate.html">HTML template</a>.</p>
<p>But I wasn't fond of the user interface (edit time by typing, then click on date) or the design, so I decided to search for something better and found, via <a href="http://ejohn.org/blog/picking-time/">John Resig</a>, Maxime Haineault's <a href="http://haineault.com/media/jquery/ui-timepickr/page/">timepickr</a>.</p>
<script>
  $('head').append('<link href="/inc/jquery.timepickr.css" rel="stylesheet" type="text/css"/>');
  $(document).ready(function(){
    $('#themeswitcher').themeswitcher();
  });
</script><input id="timepickr" />
<pre><code class="language-javascript demo">
$('#timepickr').timepickr({convention: 12, resetOnBlur: false});
</code></pre>
<p>Which I like a lot more (I'll have to separate the date from the time-picking, but I think that's the right approach anyway). This should teach me that <a href="http://lmgtfy.com/">Google Is My Friend</a>. No need to invent everything from scratch!</p>
]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/06/29/dont-reinvent-the-wheel/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Copying jQuery UI styles and Detecting CSS Loading</title>
		<link>http://bililite.com/blog/2009/06/24/copying-jquery-ui-styles-and-detecting-css-loading/</link>
		<comments>http://bililite.com/blog/2009/06/24/copying-jquery-ui-styles-and-detecting-css-loading/#comments</comments>
		<pubDate>Wed, 24 Jun 2009 13:49:11 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=844</guid>
		<description><![CDATA[So let's say I want to create elements that match a jQuery UI theme (in my case, I was using a &#60;canvas&#62; element and wanted to copy colors). I could try to parse the CSS file directly, but that runs afoul of the same-origin security problem (I use Google's CDN to get the jQuery UI [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
<p>So let's say I want to create elements that match a <a href="http://jqueryui.com/themeroller/">jQuery UI theme</a> (in my case, I was using a <a href="https://developer.mozilla.org/en/Canvas_tutorial">&lt;canvas&gt; element</a> and wanted to copy colors). I could try to <a href="/blog/2009/01/16/jquery-css-parser/">parse the CSS file directly</a>, but that runs afoul of the <a href="http://www.mozilla.org/projects/security/components/same-origin.html">same-origin security</a> problem (I use Google's CDN to get the jQuery UI stylesheets, as in <nobr>http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.0/themes/smoothness/jquery-ui.css</nobr>). I could go through the document stylesheets to find the relevant styles, but there's an easier way; just create an element with the desired classes and query that:</p>
<pre><code class="language-javascript">var background = $('&lt;div class="ui-state-default"&gt;').css('background-color');</code></pre>
<span id="more-844"></span>
<p>But there's a problem if I'm using <a href="http://jqueryui.com/docs/Theming/ThemeSwitcher">Themeswitcher</a>.
<pre><code class="language-html demo">
&lt;div id="result" style="background: #888; height: 2em;"&gt;&lt;/div&gt;
&lt;div id="themeswitcher"&gt;&lt;/div&gt;
</code></pre>
<pre><code class="language-javascript demo">
function showcolor(){
  var background = $('&lt;div id="tester" class="ui-state-default"&gt;').appendTo('body').css('background-color');
  $('#result').text(background).css('color', background);
  $('#tester').remove();
}
$('#themeswitcher').themeswitcher({
  onSelect: showcolor
});
</code></pre>
<p>If you play with this, you notice that there's a race condition going on. Themeswitcher adds a &lt;link rel="stylesheet"&gt; element and the new stylesheet is loaded asynchronously. Meanwhile, the <code>onSelect</code> callback is called and the style of the test element is grabbed. But, depending on whether the new stylesheet is in the cache and how fast your internet connection is, the new stylesheet may not be loaded. So the color from the old stylesheet is returned.</p>
<h3>Doing It Right (and Failing)</h3>
<p>Figuring out what to do is easy. The theme switcher adds stylesheets with class "ui-theme", so the newest stylesheet is always $('link.ui-theme:last'). So all we need to do is tell when that &lt;link&gt; is loaded.</p>
<p><em>That</em> problem is not easy. One solution is to check whether the stylesheet object associated with the link exists, with <code class="language-javascript">$('link.ui-theme:last')[0].sheet.cssRules</code>
 (see <a href="http://development.lombardi.com/?p=434">this discussion</a>). But stylesheet objects are subject to the <a href="http://developer.yahoo.com/yui/stylesheet/">same-origin security rule</a>, so this fails with outside stylesheets.</p>
<p><a href="http://enhance.qd-creative.co.uk/2008/12/06/the-magic-of-onload-revealed/">It is claimed</a> that the onLoad event is fired when stylesheets are loaded, but <a href="http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2009-March/018835.html">doesn't work in Firefox or Webkit</a>, which rules out all the browsers I care about.</p>
<p>Trust me, I tried this for a long while and did a lot of searching. There's no way to tell that a stylesheet has been loaded. I could create a loop that polled for changes in the test &lt;div&gt; but that's ugly, inefficient and assumes that the new style always uses a different color.</p>
<h3>Hoist the Jolly Roger</h3>
<p>If at first you don't succeed, hack it. There's a different element that <em>does</em> let us know when it's been loaded: images. <code class="language-html">&lt;img src="foo" onerror="func()" /&gt;</code> will try to load the file "foo" and, if that fails, will call <code>func</code>. But there's one catch, a bug? security feature? in Webkit: it will not call the onerror function if the file is in the cache already. It doesn't call onload; it just doesn't do anything. So we test first to see if the onerror trick works, and if it doesn't, we add a random parameter to make sure the file reloads. It means that we don't take advantage of caching the CSS file in Webkit browsers, but the code works:</p>
<pre><code class="language-html demo">
  &lt;input type="button" value="Better onSelect" id="betteronselect" /&gt;
</code></pre>

<pre><code class="language-javascript demo">
var noCache = function() {return '?'+Math.random()};
var href = $('link.ui-theme:last').attr('href');
$('&lt;img /&gt;').attr('src', href).error(function() {
  $.noCache = function() {return '' };
  $(this).remove();
}).appendTo('body');
$('#betteronselect').click(function(){
  $('#themeswitcher').empty().themeswitcher({
     onSelect: function(){
        var href = $('link.ui-theme:last').attr('href')+noCache();
        $('&lt;img /&gt;').attr('src', href).error(function() {
          showcolor();
          $(this).remove();
        }).appendTo('body');
    }
  });
});
</code></pre>

<p>Of course, my analysis may be wrong and the reason that this works is that it delays the <code>showcolor</code> long enough to lose the race. I don't think so, but it will take some experimenting to make sure.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/06/24/copying-jquery-ui-styles-and-detecting-css-loading/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Improving jQuery UI widget getters/setters</title>
		<link>http://bililite.com/blog/2009/04/23/improving-jquery-ui-widget-getterssetters/</link>
		<comments>http://bililite.com/blog/2009/04/23/improving-jquery-ui-widget-getterssetters/#comments</comments>
		<pubDate>Fri, 24 Apr 2009 03:12:36 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=594</guid>
		<description><![CDATA[Right now UI widgets need to explicitly declare "getter" functions (methods that return information about the widget rather than manipulate it) with $.ui.widget.getter = 'gettermethod1 gettermethod2'. The reason is that most jQuery plugins are chainable: $(selector).widget('method').css('color','blue') executes method on each element matched, then css('color','blue') on each element. Some plugins and some uses of plugins instead [...]]]></description>
			<content:encoded><![CDATA[<p>Right now UI widgets need to explicitly declare "getter" functions (methods that return information about the widget rather than manipulate it) with <code class="language-javascript">$.ui.widget.getter = 'gettermethod1 gettermethod2'</code>. The reason is that most jQuery plugins are chainable: <code class="language-javascript">$(selector).widget('method').css('color','blue')</code> executes <code>method</code> on each element matched, then <code>css('color','blue')</code> on each element. Some plugins and some uses of plugins instead return information about the first element selected, so they cannot be chained, like <code class="language-javascript">$(selector).css('color')</code>. For normal methods, the widget code creates a plugin that automatically executes the method named on each element matched, then returns the jQuery object to allow chaining. It uses the <code>getter</code> list to determine which methods should pass their return value from the plugin instead, and only operate on the first element matched.</p>
<p>This is inelegant, and makes <a href="/blog/extending-jquery-ui-widgets/">my subclassing code</a> less useful, since the <code>getter</code> list cannot be extended easily, only copied or replaced.</p>
<p>This is due to be improved in jQuery 1.7.2 (or at least it's in the <a href="http://dev.jqueryui.com/browser/trunk/ui/ui.core.js#L230">latest nightlies</a>): any method that returns <code class="language-javascript">this</code> is a "regular" method that, when used as a plugin, should be chainable. Any method that returns anything else is a "getter" that returns that value when used as a plugin.</p>
<p>Thus, in the <a href="http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/">widget tutorial</a>, we no longer need the line <code class="language-javascript">$.ui.green4.getter = "getLevel";</code> and <code class="language-javascript">getLevel</code> remains unchanged, but <code class="language-javascript">setLevel</code> needs to be changed to:
<pre><code class="language-javascript">    setLevel: function (x) {
        var greenlevels = this._getData('greenlevels'); 
        var level = Math.floor(Math.min(greenlevels.length-1, Math.max(0,x))); 
        this._setData('level', level); 
        this.element.css({background: greenlevels[level]}); 
        return this; // note new return value
    }, 
</code></pre>
<p>I think this will be an improvement, even if it means rewriting all my widgets. I would rather have used a return value of "<code>undefined</code>" as the flag for a chainable method, since that would not require rewriting regular methods, and getters that intentionally return <code>undefined</code> should be few and far between. But no one asked me.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/04/23/improving-jquery-ui-widget-getterssetters/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Finding the Size of a Table</title>
		<link>http://bililite.com/blog/2009/03/27/finding-the-size-of-a-table/</link>
		<comments>http://bililite.com/blog/2009/03/27/finding-the-size-of-a-table/#comments</comments>
		<pubDate>Fri, 27 Mar 2009 07:48:27 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=465</guid>
		<description><![CDATA[&#60;table&#62; elements are among the most frustrating to use if you are trying to style them with Javascript, because their sizes are so unpredictable and poorly controlled with CSS. I was trying to set the size of a table (a calendar, if you must know) by setting the size of each &#60;td&#62; and hoped the [...]]]></description>
			<content:encoded><![CDATA[<p><code class="language-html">&lt;table&gt;</code> elements are among the most frustrating to use if you are trying to style them with Javascript, because their sizes are so unpredictable and poorly controlled with CSS. I was trying to set the size of a table (a calendar, if you must know) by setting the size of each <code class="language-html">&lt;td&gt;</code> and hoped the surrounding <code class="language-html">&lt;div&gt;</code> would expand to fit (the way a div containing any other, fixed size, element would) but the table was shrunk to the smallest possible size. Compare:</p>
<pre><code class="language-html demo">
&lt;div style=&quot;width: 150px; overflow: visible; background:#fedcba;border: 5px solid purple&quot;&gt;This is 150px wide&lt;/div&gt;
&lt;div style=&quot;width: 150px; overflow: visible; border: 5px solid purple&quot;&gt;
	&lt;table border="1"&gt;
		&lt;caption&gt;A simple table&lt;/caption&gt;
		&lt;tbody&gt;
			&lt;tr&gt;&lt;td style=&quot;width: 200px&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;width: 200px&quot;&gt;2&lt;/td&gt;&lt;/tr&gt;
		&lt;/tbody&gt;
	&lt;/table&gt;
&lt;/div&gt;
&lt;div style=&quot;width: 150px; overflow: visible; border: 5px solid purple&quot;&gt;
	&lt;div style="width: 400px; background: #fedcba"&gt;a simple div 400px wide&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>Notice how the lower <code class="language-html">&lt;div&gt;</code> has <code>width: 400px</code> and actually <em>is</em> 400px wide; the surrounding <code class="language-html">&lt;div&gt;</code> is 150px wide but has <code>overflow: visible</code> so you can see the bigger <code class="language-html">&lt;div&gt;</code> extending past it. The <code class="language-html">&lt;table&gt;</code>, on the other hand, even though it has two <code class="language-html">&lt;td&gt;</code>s that are supposed to be 200px wide, is shrunk to fit the enclosing <code class="language-html">&lt;div&gt;</code>. Not good.</p>
<p>The only solution I found was to calculate the width that the <code class="language-html">&lt;table&gt;</code> <em>should</em> have had, and set the width of the enclosing <code class="language-html">&lt;div&gt;</code> to that value. But how to find the "natural" width of the table? We have to set the size of all the enclosing elements to something huge so the table will not be shrunk, get the width of the <code class="language-html">&lt;table&gt;</code>, then set everything back. And, to make things harder, the table has to be visible in order to get its width; all the parameters like <code>offsetWidth</code> and <code>clientWidth</code> are set to zero if the table is not visible. And that includes the case where <em>any</em> of the parent elements have <code>display: none</code>, so jQuery's <code>innerWidth</code> hack (set the CSS of the element itself to <code class="languagejavascript">{display: 'block'; visible: 'hidden'}</code>, get the <code>offsetWidth</code>, then switch the CSS back) won't work; it only affects the visibility of the element itself.</p>
<p>Making the <code class="language-html">&lt;table&gt;</code> itself have <code>position: absolute</code> temporarily solves the shrinking problem but not the visibility problem.</p>
<p>The key was to use jQuery's undocumented <code>$.swap</code> function. It is what jQuery itself uses to calculate sizes of hidden elements. It takes an element, a set of CSS properties and a callback, and temporarily sets the element to have those CSS properies, calls the callback, then restores the CSS. Now all I have to do is use that recursively:</p>
<pre><code class="language-javascript demo">
function truewidth (e, parent){
	if (arguments.length == 1) parent = e.parentNode;
	var ret = 0;
	// we assume the table is in the document, so we eventually reach &lt;body&gt;, and we assume the body is visible
	if (!parent || parent.nodeName.toLowerCase() == 'body') return e.offsetWidth;
	$.swap (
		parent,
		{ position: 'absolute', width: '9999px' , display: 'block' },
		function() { ret = truewidth(e, parent.parentNode) }
	);
	return ret;
}
</code></pre>
<p>And I can use it:</p>
<pre><code class="language-html demo">
&lt;div style=&quot;width: 150px; overflow: visible; background:#fedcba;border: 5px solid purple&quot;&gt;This is 150px wide&lt;/div&gt;
&lt;div id="widthexample" style=&quot;width: 150px; overflow: visible; border: 5px solid purple&quot;&gt;
	&lt;table border="1"&gt;
		&lt;caption&gt;A simple table&lt;/caption&gt;
		&lt;tbody&gt;
			&lt;tr&gt;&lt;td style=&quot;width: 200px&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;width: 200px&quot;&gt;2&lt;/td&gt;&lt;/tr&gt;
		&lt;/tbody&gt;
	&lt;/table&gt;
&lt;/div&gt;
&lt;div style=&quot;width: 150px; overflow: visible; border: 5px solid purple&quot;&gt;
	&lt;div style="width: 400px; background: #fedcba"&gt;a simple div 400px wide&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-javascript demo">
$('#widthexample').width(truewidth($('#widthexample table')[0]));
</code></pre>
<h4>Table height</h4>
<p>One more problem with tables: I wanted to use a <code class="language-html">&lt;caption&gt;</code> for the calendar header (the month/year part) and wanted it to be animated when it changed, so I needed to know the height of the table so I could set the size of the containing element. Easy, no? Just <code class="language-javascript">$('table').height()</code>. Unfortunately, I discovered a heretofore undocumented bug in Firefox: it does not include the caption in the height. IE, Opera, Safari and Chrome all do.</p>
<p>The easiest way I've found to correct for this without browser sniffing is to remove the caption, calculate the height, then put the caption back:</p>
<pre><code class="language-javascript demo">
function trueheight(e){
	for (var child = e.firstChild; child; child = child.nextSibling){
		if (child.nodeName.toLowerCase() == 'caption'){
			e.removeChild(child);
			var h = e.offsetHeight;
			e.insertBefore(child, e.firstChild);
			return h + child.offsetHeight;
		}
	}
	return e.offsetHeight;
}
</code></pre>
<p>Note that if the table is hidden, you may need to use the <code>$.swap</code> technique above. Note also that it uses <code>offsetHeight</code>, which does not include the margin. I can't offhand imagine using margins on a caption, but if you do, you will have to add it back (doing <code>$(child).outerHeight(true)</code> is reasonable).</p>
<p>Demonstrating:</p>
<pre><code class="language-html demo">
&lt;div style="height: 5em"&gt;
	&lt;div class="heightexample" style="border: 1px solid purple"&gt;
		&lt;table border="1" style="position: absolute"&gt;
			&lt;caption&gt;A simple table&lt;/caption&gt;
			&lt;tbody&gt;
				&lt;tr&gt;
					&lt;td&gt;the surrounding div does not&lt;/td&gt;
					&lt;td&gt;correct for caption size&lt;/td&gt;
				&lt;/tr&gt;
			&lt;/tbody&gt;
		&lt;/table&gt;
	&lt;/div&gt;
&lt;/div&gt;
&lt;div style="height: 5em"&gt;
	&lt;div class="heightexample" style="border: 1px solid purple"&gt;
		&lt;table border="1" style="position: absolute"&gt;
			&lt;caption&gt;A simple table&lt;/caption&gt;
			&lt;tbody&gt;
				&lt;tr&gt;
					&lt;td&gt;the surrounding div corrects&lt;/td&gt;
					&lt;td&gt;for caption size&lt;/td&gt;
				&lt;/tr&gt;
			&lt;/tbody&gt;
		&lt;/table&gt;
	&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-javascript demo">
$('.heightexample:eq(0)').height($('.heightexample:eq(0) table').height());
$('.heightexample:eq(1)').height(trueheight($('.heightexample:eq(1) table')[0]));
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/03/27/finding-the-size-of-a-table/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Roman Numerals in Javascript</title>
		<link>http://bililite.com/blog/2009/03/09/roman-numerals-in-javascript/</link>
		<comments>http://bililite.com/blog/2009/03/09/roman-numerals-in-javascript/#comments</comments>
		<pubDate>Mon, 09 Mar 2009 18:48:01 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=412</guid>
		<description><![CDATA[I was looking at some code that included converting arabic to roman numerals, and it was all things like this: function Level(i, v, x) { this.i = i; this.v = v; this.x = x } levels = new Array(); levels[0] = new Level('I', 'V', 'X'); levels[1] = new Level('X', 'L', 'C'); levels[2] = new Level('C', [...]]]></description>
			<content:encoded><![CDATA[<p>I was looking at some code that included converting arabic to roman numerals, and it was all things like <a href="http://www.novaroma.org/via_romana/numbers.html">this</a>:</p>
<span id="more-412"></span>
<pre><code class="language-javascript">
function Level(i, v, x)
        {
        this.i = i;
        this.v = v;
        this.x = x
        }

levels = new Array();

levels[0] = new Level('I', 'V', 'X');
levels[1] = new Level('X', 'L', 'C');
levels[2] = new Level('C', 'D', 'M');

function calcDigit(d, l)
        {
        if (l > 2)
                {
                str = '';
                for (var m = 1; m <= d * Math.pow(10, l - 3) ; m++)
                        str += 'M';
                return str
                }
                        
        else
                if (d == 1)
                        return levels[l].i
                else
                        if (d == 2)
                                return levels[l].i + levels[l].i
                        else
                                if (d == 3)
                                        return levels[l].i + levels[l].i + levels[l].i
                                else
                                        if (d == 4)
                                                return levels[l].i + levels[l].v
                                        else
                                                if (d == 5)
                                                        return levels[l].v
                                                else
                                                        if (d == 6)
                                                                return levels[l].v + levels[l].i
                                                        else
                                                                if (d == 7)
                                                                        return levels[l].v + levels[l].i + levels[l].i
                                                                else
                                                                        if (d == <img src='http://bililite.com/blog/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> 
                                                                                return levels[l].v + levels[l].i + levels[l].i + levels[l].i
                                                                        else
                                                                                if (d == 9)
                                                                                        return levels[l].i + levels[l].x
                                                                                else
                                                                                        return ''
// And so on
</code></pre>
<p>It all reminded me of fourth-grade BASIC. I should have been taking care of patients, but the challenge of writing a better converter kept me busy for a few minutes. Here's what I came up with:</p>
<pre><code class="language-javascript demo">
var romanNumerals = [
	[1000, 'M'],
	[900, 'CM'],
	[500, 'D'],
	[400, 'CD'],
	[100, 'C'],
	[90, 'XC'],
	[50, 'L'],
	[40, 'XL'],
	[10, 'X'],
	[9, 'IX'],
	[5, 'V'],
	[4, 'IV'],
	[1, 'I']
];
arabic2roman = function(n){
	var r = '';
	for (var i = 0; i < romanNumerals.length; ++i){
		for (var x = romanNumerals[i]; n >= x[0]; n -= x[0]) r += x[1];
	}
	return r;
}
roman2arabic = function(r){
	var n = 0;
	for (var i = 0; i < romanNumerals.length; ++i){
		for (var x = romanNumerals[i], l = x[1].length; r.substr(0,l) == x[1]; r = r.substr(l)) n += x[0];
	}
	return n;
}
</code></pre>
<form><input id="source"/><input type="submit" value="Convert"/> <span id="result"></span></form>
<script>
$('#source').parent().submit(function(e){
  e.preventDefault();
  var text = $('#source').val();
  if (/\d/.test(text)) $('#result').text(arabic2roman(text));
  else $('#result').text(roman2arabic(text.toUpperCase()));
});
</script>
<p>Or, if you are willing to assume that your browser will always <a href="http://ejohn.org/blog/javascript-in-chrome/">enumerate objects in order</a>, you can make it a bit smaller:</p>
<pre><code class="language-javascript">
var romanNumerals = { // assumes objects are enumerated in order
	M: 1000,
	CM: 900,
	D: 500,
	CD: 400,
	C: 100,
	XC: 90,
	L: 50,
	XL: 40,
	X: 10,
	IX: 9,
	V: 5,
	IV: 4,
	I: 1
};
arabic2roman = function(n){
	var r = '';
	for (var x in romanNumerals){
		for (var i = romanNumerals[x]; n >= i; n -= i) r += x;
	}
	return r;
}
roman2arabic = function(r){
	var n = 0, i = 0;
	for (var x in romanNumerals){
		for (var l = x.length; r.substr(i,l) == x; i+=l) n += romanNumerals[x];
	}
	return n;
}
</code></pre>
<p>Anyone out there with something smaller?</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/03/09/roman-numerals-in-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
