<?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; jQuery</title>
	<atom:link href="http://bililite.com/blog/category/jquery/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>jQuery UI 1.8</title>
		<link>http://bililite.com/blog/2010/03/25/jquery-ui-1-8/</link>
		<comments>http://bililite.com/blog/2010/03/25/jquery-ui-1-8/#comments</comments>
		<pubDate>Thu, 25 Mar 2010 20:58:58 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1187</guid>
		<description><![CDATA[Well, they released jQuery UI 1.8 two days ago, and now everything is broken. I understand why the changes were made, but it sure is inconvenient. Plugins and posts updated so far: timepickr widget tutorial extending widgets flexcal googlesearch and ajaxpopup textpopup and hebrewkeyboard And on top of that, chili stopped working with jQuery 1.4 [...]]]></description>
			<content:encoded><![CDATA[<p>Well, they released jQuery UI 1.8 two days ago, and now everything is broken. I understand why the changes were made, but it sure is inconvenient. Plugins and posts updated so far:</p>
<ul>
<li><a href="/blog/2009/07/09/updating-timepickr">timepickr</a></li>
<li><a href="/blog/understanding-jquery-ui-widgets-a-tutorial/">widget tutorial</a></li>
<li><a href="/blog/extending-jquery-ui-widgets/">extending widgets</a></li>
<li><a href="/blog/2009/04/03/new-jquery-widget-flexcal/">flexcal</a></li>
<li><a href="/blog/2009/03/25/new-widgets-googlesearch-and-ajaxpopup/">googlesearch and ajaxpopup</a></li>
<li><a href="/blog/2009/01/02/new-ui-widgets-textpopup-and-hebrewkeyboard/">textpopup and hebrewkeyboard</a></li>
</ul>
<p>And on top of that, <a href="http://noteslog.com/chili/">chili</a> stopped working with <a href="http://jquery14.com/day-01/jquery-14">jQuery 1.4</a> and I haven't fixed it yet. <a href="http://noteslog.com/contact/">Andrea Ercolino</a> was nice enough to send me an updated version, but I haven't gotten around to installing it yet.</p>
<p>Update: got chili to work!</p>
<p>So much for using my spare time to <a href="/blog/2010/03/14/learning-scheme/">learn Scheme</a>!</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2010/03/25/jquery-ui-1-8/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Updating timepickr</title>
		<link>http://bililite.com/blog/2009/07/09/updating-timepickr/</link>
		<comments>http://bililite.com/blog/2009/07/09/updating-timepickr/#comments</comments>
		<pubDate>Fri, 10 Jul 2009 01:07:32 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=1009</guid>
		<description><![CDATA[Updated for jQuery UI 1.8 3/25/2010 I like Maxime Haineault's timepickr. But I found it was written for jQuery 1.2, and didn't trigger events correctly with the current jQuery. It also had more dependencies than I wanted, to the extent that the minified version is 49k large, and had some CSS quirks in Internet Explorer [...]]]></description>
			<content:encoded><![CDATA[<h4>Updated for jQuery UI 1.8 3/25/2010</h4>
<script type="text/javascript"
  src="http://jqueryui.com/themeroller/themeswitchertool/">
</script>
<script src="/inc/jquery.timepickr.js"></script><script src="/inc/jquery.hoverIntent.js"></script>
<p><a href="/blog/2009/06/29/dont-reinvent-the-wheel/#more-986">I like</a> Maxime Haineault's <a href="http://haineault.com/media/jquery/ui-timepickr/page/">timepickr</a>. But I found it was written for jQuery 1.2, and didn't trigger events correctly with the current jQuery. It also had more dependencies than I wanted, to the extent that the minified version is 49k large, and had some CSS quirks in Internet Explorer that I couldn't figure out without dissecting the whole thing. So I dissected it and rebuilt it smaller, updated for jQuery 1.3. I didn't put back a lot of the options that I didn't need, so it's not exactly an improvement. But it works for me.</p>
<input /><script>$('input:last').timepickr()</script>
<p>Download <a href="/inc/jquery.timepickr.js">the code</a>.</p>
<p>I also added the ability to use <a href="http://cherne.net/brian/resources/jquery.hoverIntent.html">hoverIntent</a> so you can roll the mouse over the intervening buttons without triggering them:</p>
<input /><script>$('input:last').timepickr({hoverIntent: true})</script>
<span id="more-1009"></span>
<h3>Examples</h3>
<pre><code class="language-javascript demo">
$('.timepickr-example:eq(0)').timepickr();
</code></pre>
<pre><code class="language-html demo">
&lt;input class="timepickr-example" /&gt;
</code></pre>
<pre><code class="language-javascript demo">
$('.timepickr-example:eq(1)').timepickr({
  convention: 24,
  format: '{h}{m} hours',
  hoverIntent: true
});
</code></pre>
<pre><code class="language-html demo">
&lt;input class="timepickr-example" /&gt;
</code></pre>
<pre><code class="language-javascript demo">
$('.timepickr-example:eq(2)').timepickr({
  trigger: 'nothing',
  handle: $('#clock')
});
</code></pre>
<pre><code class="language-html demo">
&lt;input class="timepickr-example" /&gt; &lt;img id="clock" src="/images/clock.png" /&gt;
</code></pre>
<pre><code class="language-javascript demo">
$('.timepickr-example:eq(3)').timepickr({
  select: function(){
    alert('Time is '+this.value);
  }
});
</code></pre>
<pre><code class="language-html demo">
&lt;input class="timepickr-example" /&gt;
</code></pre>
<h2>Options</h2>
<pre><code><dl>
<dt>top {Number}</dt>
<dd>Pixel offset of the top left button. Default: 6.</dd>
<dt>left {Number}</dt>
<dd>Pixel offset of the top left button. Default: 0.</dd>
<dt>animSpeed {Number|slow|normal|fast}</dt>
<dd>Speed to animate showing/hiding the buttons. Default: 0.</dd>
<dt>trigger {String}</dt>
<dd>Event to trigger showing the buttons. Default: 'click'.</dd>
<dt>convention {12|24}</dt>
<dd>Whether to use a 12-hour or 24-hour clock. Default:  12.</dd>
<dt>format {String}</dt>
<dd>String that determines how to display the time. Uses simple formatting: '{h}' is replaced by the hour;
'{m}' is replaced by the minutes; '{suffix}' is replaced by the suffix (used in the 12-hour clock); '{prefix}' is
replaced by the prefix (used in the 24-hour clock). Default: '{h}:{m} {suffix}'</dd>
<dt>handle {false|String|jQuery object}</dt>
<dd>Other element that will trigger showing the buttons when clicked. If handle is not false, then
$(handle).click() shows the buttons. Default: false.</dd>
<dt>prefix {Array(String)}</dt>
<dd>Strings to use as labels for the 24-hour clock. Displaying all 24 hours is too long to be usable, so we
split them into two halves. Default: ['00-11', '12-23'].</dd>
<dt>suffix {Array(String)}</dt>
<dd>Strings to use as AM/PM labels for the 12-hour clock. Default: ['am', 'pm'].</dd>
<dt>rangeMin {Array(String)}</dt>
<dd>Values to use for the minutes list. Default: ['00', '15', '30', '45'].</dd>
<dt>resetOnBlur {Boolean}</dt>
<dd>True to reset the element to its original value if nothing is clicked; false to keep the value generated
by the mouseover even if not clicked. Default: true.</dd>
<dt>val {false|String}</dt>
<dd>If not false, value to assign to the element when the buttons are shown. Default: false.</dd>
<dt>hoverIntent {Boolean}</dt>
<dd>True to use the hoverIntent plugin if available. Note that timepickr does not provide any way to
adjust the hoverIntent paramenters. Default: false.</dd>
<dt>select {undefined|function}</dt>
<dd>Callback function, called when the time buttons are clicked. Also available as an event
'timepickrselect'</dd>
</dl></code></pre>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/07/09/updating-timepickr/feed/</wfw:commentRss>
		<slash:comments>49</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>Keyboard accesibility in flexcal</title>
		<link>http://bililite.com/blog/2009/05/27/keyboard-accesibility-in-flexcal/</link>
		<comments>http://bililite.com/blog/2009/05/27/keyboard-accesibility-in-flexcal/#comments</comments>
		<pubDate>Thu, 28 May 2009 03:34:34 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=813</guid>
		<description><![CDATA[I added keyboard accessibility to flexcal, based on the AOL style guide. Tabbing into the trigger for a textpopup makes it pop. The focus remains on the input element and requires another tab to put the focus on the calendar (a bit awkward to require two tabs but I didn't want to have the input [...]]]></description>
			<content:encoded><![CDATA[<p>I added keyboard accessibility to <a href="/blog/2009/04/03/new-jquery-widget-flexcal/"><code>flexcal</code></a>, based on the <a href="http://dev.aol.com/dhtml_style_guide#datepicker">AOL style guide</a>. Tabbing into the trigger for a <a href="/blog/2009/01/02/new-ui-widgets-textpopup-and-hebrewkeyboard/">textpopup</a> makes it pop. The focus remains on the input element and requires another tab to put the focus on the calendar (a bit awkward to require two tabs but I didn't want to have the input element lose focus every time it gets it),  and the escape key (or tabbing out) hides it.</p>
<p>Unfortunately, control-page up/down if used by Firefox to change its tabs and I can't override that, so I use alt-page up/down to skip years. Alt-left/right arrow changes calendars.</p>
<p>Unfortunately, the tabbing in and out doesn't work in Chrome; after tabbing out when the focus returns to the input element it won't leave. I'm not sure what the problem is. And it uses tabindex=0 which isn't supported by Safari at all. I think I will leave it as is and wait for the browsers to catch up to me.</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/05/27/keyboard-accesibility-in-flexcal/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Getting flexcal to Work With the Theme Switcher</title>
		<link>http://bililite.com/blog/2009/05/18/getting-flexcal-to-work-with-the-theme-switcher/</link>
		<comments>http://bililite.com/blog/2009/05/18/getting-flexcal-to-work-with-the-theme-switcher/#comments</comments>
		<pubDate>Mon, 18 May 2009 18:35:18 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=705</guid>
		<description><![CDATA[Just checking. No reason it shouldn't work with the jQuery UI themeswitcher. The slot machine animation has a white background, which looks off with some themes. The text color for the dates unfortunately is (by the CSS specificity rules) overridden by the blog's text color, so dark themes are near unreadable. $.ui.flexcal.subclass('ui.inlineflexcal', { _init: function() [...]]]></description>
			<content:encoded><![CDATA[<p>Just checking. No reason it shouldn't work with the jQuery UI <a href="http://jqueryui.com/docs/Theming/ThemeSwitcher">themeswitcher</a>. The slot machine animation has a white background, which looks off with some themes. The text color for the dates unfortunately is (by the CSS specificity rules) overridden by the <em>blog's</em> text color, so dark themes are near unreadable.</p>
<script type="text/javascript"
  src="http://jqueryui.com/themeroller/themeswitchertool/">
</script>
<script src="/inc/jquery.ui.subclass.js"></script>
<script src="/inc/jquery.textpopup.js"></script>
<script src="/inc/jquery.flexcal.js"></script>
<script type="text/javascript" src="/inc/jquery.cycle.all.min.js"></script>
<script>
$.ui.flexcal.subclass('ui.inlineflexcal', {
  _init: function() {this.show()},
  _attach: function(){
    $('<div><div style="clear:both"/></div>').insertAfter(this.element).prepend(this.box().css({position: 'relative', 'float': 'left', top: 0}));
  },
  commit: function(d){
	this.d = d;
	this.box().find('td a').removeClass('ui-state-active');
	this.box().find('td a[rel='+$.ui.flexcal.date2string(d)+']').addClass('ui-state-active');
	this.element.val(this.format(d));
	this._trigger('commit', 0, d);
  }
});

// largely copied from the $.fn.cycle code 
function cycle(opts) { 
    var curr = opts.elements.eq(opts.currSlide), next = opts.elements.eq(1-opts.currSlide), fwd = !(opts.rev ^ opts.l10n.isRTL); 
    opts.lastSlide = opts.currSlide; // shuffle needs to know this 
    opts.slideCount = 2; 
    opts.cssAfter = { left: 0, top: 0 , opacity: 1}; 
    opts.cssBefore = opts.animIn = opts.animOut = {}; 
    opts.speedIn = opts.speedIn || opts.speed, 
    opts.speedOut = opts.speedOut || opts.speed, 
    opts.easeIn = opts.easeIn || opts.easing, 
    opts.easeOut = opts.easeOut || opts.easing; 
    $.fn.cycleW = opts.$cont.width(); $.fn.cycleH = opts.$cont.height(); // hack to fool the cycle transitions 
    $.fn.cycle.transitions[opts.fx](opts.$cont, opts.elements, opts); // set up the transition. fx must be a valid transition name 
    $.each(opts.before, function(i,o) { 
        o.apply(next, [curr, next, opts, fwd]); 
    }); 
  // stage the after callacks 
    var after = function() { 
        $.each(opts.after, function(i,o) { 
            o.apply(next, [curr, next, opts, fwd]); 
        }); 
    }; 
 
    if (opts.fxFn){ // fx function provided? 
        opts.fxFn(curr, next, opts, after, fwd); 
    }else{ 
        next.css(opts.cssBefore); 
        var fn = function() { 
            next.animate(opts.animIn, opts.speedIn, opts.easeIn, after) 
        }; 
        curr.animate(opts.animOut, opts.speedOut, opts.easeOut, function() { 
            curr.css(opts.cssAfter); 
            if (!opts.sync) fn(); 
        }); 
        if (opts.sync) fn(); 
    } 
}; 

$(document).ready(function(){

$('#inlineslots').inlineflexcal({
	transition: function(o){ 
		var pane = o.elements.eq(1-o.currSlide), origTable = pane.find('table');
		o.elements.eq(o.currSlide).css({zIndex: 1}).animate({top: -o.$cont.height()}, 'normal', 'easeInBack');
		pane.css({top: o.$cont.height(), zIndex: 2}).show().animate({top: 0}, 'normal', 'easeInBack', function(){
			// make two identical tables without headers and position them correctly (lined up with the bottom)
			var spinners = tableSpinners (origTable, o.gif);
			var tables = origTable.clone().add(origTable.clone()).appendTo(pane).
				find('caption').remove().end().
				css({bottom: 0 , marginBottom: 0, zIndex: 101}).hide();
			function dropOne(){
				if (spinners.length == 0){
					tables.remove();
					return;
				}
				var spinner = spinners.eq(Math.floor(Math.random()*spinners.length));
				var h = spinner.height();
				var left = spinner.offset().left - origTable.offset().left;
				var right = left + spinner.width();
				spinners = spinners.not(spinner.remove());
				origTable.animate({foo: 0}, { // the {foo:0} seems necessary because we need to animate some property, even if it isn't real
					duration: o.duration,
					easing: 'easeOutElastic',
					step: function(now, fx){
						if (fx.state == 0){
							tables.show();
							fx.start = now = 0; 
							fx.end = 3*h; 
						}
						tables.eq(0).css(offsetColumn(right, left, h, now));
						tables.eq(1).css(offsetColumn(right, left, h, now-h));
					},
					complete: dropOne
				});
			}
			setTimeout (dropOne, o.duration);
		})
	}, 
	position: 'tl',
	speed: 0,
	hideOnOutsideClick: false,
	transitionOptions: {
		duration: 1500,
		gif: '/images/ani-bw.gif'
	}
});

function tableSpinners(t, gif){
	var topRow = t.find('tr:first td, tr:first th'), botRow = t.find('tr:last td'), ret = [];
	for(var i = 0; i < topRow.length; ++i){
		var topCell = topRow.eq(i), botCell = botRow.eq(i);
		ret.push($('<img>').appendTo('body').css(topCell.offset()).
			css({
				paddingTop: '2px',
				paddingBottom: '2px',
				position: 'absolute', 
				width: botCell.outerWidth(true), 
				// hack to avoid Firefox table height bug
				height: t.find('tbody').height()+t.find('thead').height(),
				zIndex: 101
			}).
			attr('src', gif)[0]);
	}
	return $(ret);
}

// create a css object with bottom = b and clip set to rect(0 r h l) pixels, with
// the top and bottom of that rect offset by b so the clip rect is fixed on the screen
// and offsetting b if it is out of the range -h..h
function offsetColumn (r, l, h, b){
	b = b%(2*h)-h; // move b into range
	return {bottom: b, clip: ['rect(',b,'px ',r,'px ',b+h,'px ',l,'px)'].join('')};
}

var opts = {
	transition: cycle,
	transitionOptions: {fx: 'fade', sync: true, speed: 600},
	position: 'tl',
	hideOnOutsideClick: false,
	speed: 0,
	calendars: ['en', 'en'],
	calendarNames: ['First', 'Second']
};

$('<label> Sync: <input type="checkbox" checked="checked"></label>').insertBefore('#flexcalcycle').bind('change', function(){
	opts.transitionOptions.sync = !!$(this).find('input').attr('checked');
	$('#flexcalcycle').inlineflexcal('destroy').inlineflexcal(opts);	
});

var easingSelect = $('<select>;').insertBefore('#flexcalcycle').bind('change', function(){
	opts.transitionOptions.easing = $(this).val();
	$('#flexcalcycle').inlineflexcal('destroy').inlineflexcal(opts);	
});
for (var e in $.easing){
	easingSelect.append($('<option>').text(e));
};
var fxSelect = $('<select>').insertBefore('#flexcalcycle').bind('change', function(){
	opts.transitionOptions.fx = $(this).val();
	$('#flexcalcycle').inlineflexcal('destroy').inlineflexcal(opts);	
});
for (var t in $.fn.cycle.transitions){
	fxSelect.append($('<option>').text(t));
};

$('#flexcalcycle').inlineflexcal(opts);
$('#switcher').themeswitcher({
  onSelect: function() { $('#flexcalcycle, #inlineslots').inlineflexcal('hide').inlineflexcal('show')}
});

});

</script>
<div id="switcher"></div>
<h4>Slot Machine Transition</h4>
<div id="inlineslots"></div>
<h4>Cycle Transitions</h4>
<div id="flexcalcycle"></div>
]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/05/18/getting-flexcal-to-work-with-the-theme-switcher/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>flexcal Slot Machine</title>
		<link>http://bililite.com/blog/2009/05/07/flexcal-slot-machine/</link>
		<comments>http://bililite.com/blog/2009/05/07/flexcal-slot-machine/#comments</comments>
		<pubDate>Thu, 07 May 2009 15:35:53 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=631</guid>
		<description><![CDATA[Modified 2009-05-19 to use a simpler technique to make it inline. OK, one more flexcal transition, inspired by Stefan Petre's jQuery slot machine. I should have been balancing my checkbook, but my kids thought this was cooler. $('#slots').flexcal({ transition: function(o){ var pane = o.elements.eq(1-o.currSlide), origTable = pane.find('table'); o.elements.eq(o.currSlide).css({zIndex: 1}).animate({top: -o.$cont.height()}, 'normal', 'easeInBack'); pane.css({top: o.$cont.height(), zIndex: [...]]]></description>
			<content:encoded><![CDATA[<p>Modified 2009-05-19 to use a simpler technique to make it inline.</p>
<p>OK, one more <a href="/blog/2009/04/03/new-jquery-widget-flexcal/"><code>flexcal</code></a> transition, inspired by Stefan Petre's <a href="http://www.eyecon.ro/slotmachine/">jQuery slot machine</a>. I should have been balancing my checkbook, but my kids thought this was cooler.</p>
<script src="/inc/jquery.ui.subclass.js"></script>
<script src="/inc/jquery.textpopup.js"></script>
<script src="/inc/jquery.flexcal.js"></script>
<div style="width: 0" id="slots"></div>
<pre style="height: 20em"><code class="language-javascript demo">
$('#slots').flexcal({
	transition: function(o){ 
		var pane = o.elements.eq(1-o.currSlide), origTable = pane.find('table');
		o.elements.eq(o.currSlide).css({zIndex: 1}).animate({top: -o.$cont.height()}, 'normal', 'easeInBack');
		pane.css({top: o.$cont.height(), zIndex: 2}).show().animate({top: 0}, 'normal', 'easeInBack', function(){
			// make two identical tables without headers and position them correctly (lined up with the bottom)
			var spinners = tableSpinners (origTable, o.gif);
			var tables = origTable.clone().add(origTable.clone()).appendTo(pane).
				find('caption').remove().end().
				css({bottom: 0 , marginBottom: 0, zIndex: 101}).hide();
			function dropOne(){
				if (spinners.length == 0){
					tables.remove();
					return;
				}
				var spinner = spinners.eq(Math.floor(Math.random()*spinners.length));
				var h = spinner.height();
				var left = spinner.offset().left - origTable.offset().left;
				var right = left + spinner.width();
				spinners = spinners.not(spinner.remove());
				origTable.animate({foo: 0}, { // the {foo:0} seems necessary because we need to animate some property, even if it isn't real
					duration: o.duration,
					easing: 'easeOutElastic',
					step: function(now, fx){
						if (fx.state == 0){
							tables.show();
							fx.start = now = 0; 
							fx.end = 3*h; 
						}
						tables.eq(0).css(offsetColumn(right, left, h, now));
						tables.eq(1).css(offsetColumn(right, left, h, now-h));
					},
					complete: dropOne
				});
			}
			setTimeout (dropOne, o.duration);
		})
	}, 
	position: 'tl',
	speed: 0,
	hideOnOutsideClick: false,
	transitionOptions: {
		duration: 1500,
		gif: '/images/ani-bw.gif'
	}
});
$('#slots').flexcal('around', 'commit', function(d){
	this.d = d;
	this.box().find('td a').removeClass('ui-state-active');
	this.box().find('td a[rel='+$.ui.flexcal.date2string(d)+']').addClass('ui-state-active');
	this.element.val(this.format(d));
	this._trigger('commit', 0, d);
}).flexcal('box').css({position: 'static', 'float': 'left'}).parent().css({position: 'static'}).after('&lt;div style="clear: both"/&gt;');
$('#slots').flexcal('show');

function tableSpinners(t, gif){
	var topRow = t.find('tr:first td, tr:first th'), botRow = t.find('tr:last td'), ret = [];
	for(var i = 0; i &lt; topRow.length; ++i){
		var topCell = topRow.eq(i), botCell = botRow.eq(i);
		ret.push($('&lt;img&gt;').appendTo('body').css(topCell.offset()).
			css({
				paddingTop: '2px',
				paddingBottom: '2px',
				position: 'absolute', 
				width: botCell.outerWidth(true), 
				// hack to avoid Firefox table height bug
				height: t.find('tbody').height()+t.find('thead').height(),
				zIndex: 101
			}).
			attr('src', gif)[0]);
	}
	return $(ret);
}

// create a css object with bottom = b and clip set to rect(0 r h l) pixels, with
// the top and bottom of that rect offset by b so the clip rect is fixed on the screen
// and offsetting b if it is out of the range -h..h
function offsetColumn (r, l, h, b){
	b = b%(2*h)-h; // move b into range
	return {bottom: b, clip: ['rect(',b,'px ',r,'px ',b+h,'px ',l,'px)'].join('')};
}
</code></pre>
<p>The code also shows how to make the datepicker "inline".</p>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/05/07/flexcal-slot-machine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>flexcal and cycle</title>
		<link>http://bililite.com/blog/2009/05/05/flexcal-and-cycle/</link>
		<comments>http://bililite.com/blog/2009/05/05/flexcal-and-cycle/#comments</comments>
		<pubDate>Wed, 06 May 2009 04:36:21 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=609</guid>
		<description><![CDATA[Updated for jQuery UI 1.8 and cycle 2.81, but it doesn't work so well anymore The alert reader will notice that the parameters for the transition animation for my flexcal widget are the same as that used by Mike Alsup's cycle plugin. My hope was that I could use { transition: $.fn.cycle.next } and be [...]]]></description>
			<content:encoded><![CDATA[<h4>Updated for jQuery UI 1.8 and cycle 2.81, but it doesn't work so well anymore </h4>
<script src="/inc/jquery.ui.subclass.js"></script>
<script src="/inc/jquery.textpopup.js"></script>
<script src="/inc/jquery.flexcal.js"></script>
<script type="text/javascript" src="/inc/jquery.cycle.all.min.js"></script>
<p>The alert reader will notice that the parameters for the transition animation for my <a href="/blog/2009/04/03/new-jquery-widget-flexcal/"><code>flexcal</code> widget</a> are the same as that used by Mike Alsup's <a href="http://malsup.com/jquery/cycle/">cycle plugin</a>. My hope was that I could use <code class="language-javascript">{ transition: $.fn.cycle.next }</code> and be able to use all those cool effects. It turned out to be not so simple; I couldn't jump into the middle of his code and have things work. But I could create a function that set things up correctly and called his transition code:</p>
<span id="more-609"></span>
<pre style="height: 20em"><code class="language-javascript demo">
// largely copied from the $.fn.cycle code
function cycle(opts) {
	var curr = opts.elements.eq(opts.currSlide), next = opts.elements.eq(1-opts.currSlide), fwd = !(opts.rev ^ opts.l10n.isRTL);
	opts.lastSlide = opts.currSlide; // shuffle needs to know this
	opts.slideCount = 2;
	opts.cssAfter = { left: 0, top: 0 , opacity: 1};
	opts.cssBefore = opts.animIn = opts.animOut = {};
	opts.speedIn = opts.speedIn || opts.speed,
	opts.speedOut = opts.speedOut || opts.speed,
	opts.easeIn = opts.easeIn || opts.easing,
	opts.easeOut = opts.easeOut || opts.easing;
	$.fn.cycleW = opts.$cont.width(); $.fn.cycleH = opts.$cont.height(); // hack to fool the cycle transitions
	$.fn.cycle.transitions[opts.fx](opts.$cont, opts.elements, opts); // set up the transition. fx must be a valid transition name
	$.each(opts.before, function(i,o) {
		o.apply(next, [curr, next, opts, fwd]);
	});
  // stage the after callacks
	var after = function() {
		$.each(opts.after, function(i,o) {
			o.apply(next, [curr, next, opts, fwd]);
		});
	};

	if (opts.fxFn){ // fx function provided?
		opts.fxFn(curr, next, opts, after, fwd);
	}else{
		next.css(opts.cssBefore);
		var fn = function() {
			next.animate(opts.animIn, opts.speedIn, opts.easeIn, after)
		};
		curr.animate(opts.animOut, opts.speedOut, opts.easeOut, function() {
			curr.css(opts.cssAfter);
			if (!opts.sync) fn();
		});
		if (opts.sync) fn();
	}
};
</code></pre>
<p>Now I can use any of the transitions and easing functions (with some attention paid to the <a href="http://malsup.com/jquery/cycle/options.html">options</a>, since we don't start with the correct defaults):</p>
<pre><code class="language-html demo">
	&lt;span&gt;Cycle transitions and easing: &lt;/span&gt;&lt;input id="flexcalcycle"/&gt;
</code></pre>
<pre><code class="language-javascript demo">
var opts = {
	position: 'bl',
	transition: cycle,
	transitionOptions: {fx: 'fade', sync: true, speed: 1000}
};
$('#flexcalcycle').flexcal(opts);

var easingSelect = $('&lt;select&gt;').insertAfter('#flexcalcycle').bind('change', function(){
	opts.transitionOptions.easing = $(this).val();
	$('#flexcalcycle').flexcal('destroy').flexcal(opts).flexcal('show');	
});
for (var e in $.easing){
	easingSelect.append($('&lt;option&gt;').text(e));
};
var fxSelect = $('&lt;select&gt;').insertAfter('#flexcalcycle').bind('change', function(){
	opts.transitionOptions.fx = $(this).val();
	$('#flexcalcycle').flexcal('destroy').flexcal(opts).flexcal('show');	
});
for (var t in $.fn.cycle.transitions){
	fxSelect.append($('&lt;option&gt;').text(t));
};
</code></pre>]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/05/05/flexcal-and-cycle/feed/</wfw:commentRss>
		<slash:comments>0</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>New jQuery Widget: flexcal</title>
		<link>http://bililite.com/blog/2009/04/03/new-jquery-widget-flexcal/</link>
		<comments>http://bililite.com/blog/2009/04/03/new-jquery-widget-flexcal/#comments</comments>
		<pubDate>Fri, 03 Apr 2009 08:19:02 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=524</guid>
		<description><![CDATA[Updated for jQuery 1.8 Just what the world needs&#8212;another date picker Download the code. The current incarnation of the jQuery UI datepicker works fine, but I needed something that could handle the Jewish calendar. This is not the same as localizing datepicker to use Hebrew;that just translates the month and day names but uses the [...]]]></description>
			<content:encoded><![CDATA[<h4>Updated for jQuery 1.8</h4>
<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.scrollintoview.js"></script>
<h4>Just what the world needs&mdash;another date picker</h4>
<p><a href="/inc/jquery.flexcal.js">Download the code</a>.</p>
<span id="more-524"></span>
<p>The current incarnation of the <a href="http://jqueryui.com/demos/datepicker/">jQuery UI datepicker</a> works fine, but I needed something that could handle the <a href="http://en.wikipedia.org/wiki/Jewish_calendar">Jewish calendar</a>. This is not the same as localizing <code>datepicker</code> to use Hebrew;that just translates the month and day names but uses the same <a href="http://en.wikipedia.org/wiki/Gregorian_calendar">Gregorian calendar</a> as everyone uses. I needed a calendar that could switch between multiple, completely different calendar systems.</p>
<p>The algorithms for converting dates from Jewish to Gregorian are readily available <a href="http://sourceforge.net/projects/hebcal/">open-source</a>, so that was easy. <a href="/blog/2009/01/02/new-ui-widgets-textpopup-and-hebrewkeyboard/"><code>textpopup</code></a> is exactly what I need to create the pop-up box for the calendar, and I had already created my own date picker for my old <a href="http://bililite.com">bililite site</a> (don't look at it too closely; it's ugly and I used an old version of Prototype. I was still learning! It works, though). So the pieces were all there.</p>
<p>And so was born <code>flexcal</code>. In its simplest form, it looks like <code>datepicker</code>:</p>
<pre><code class="language-html demo">&lt;span&gt;Using &lt;code&gt;datepicker&lt;/code&gt;: &lt;/span&gt;&lt;input id=&quot;date1&quot;/&gt;
&lt;br/&gt;
&lt;span&gt;Using &lt;code&gt;flexcal&lt;/code&gt;: &lt;/span&gt;&lt;input id=&quot;date2&quot;/&gt;</code></pre>
<pre><code class="language-javascript demo">$('#date1').datepicker();$('#date2').flexcal();</code></pre>
<p>But it can show multiple calendars and allows localization to different calendar systems:</p>
<pre><code class="language-html demo">&lt;span&gt;Jewish/civil calendar: &lt;/span&gt;&lt;input id=&quot;date3&quot;/&gt;</code></pre>
<pre><code class="language-javascript demo">$('#date3').flexcal({
  position: 'bl',
  calendars: ['en', 'jewish', 'he-jewish'],
  'class': 'multicalendar'
});</code></pre>
<p>And it allows animated transitions (which I think is in the works for datepicker as well):</p>
<pre><code class="language-html demo">&lt;span&gt;Fading transition: &lt;/span&gt;&lt;input id=&quot;date3-1&quot;/&gt;</code></pre>
<pre><code class="language-javascript demo">$('#date3-1').flexcal({
  position: 'bl',
  transition: function(o){
    o.elements.eq(o.currSlide).fadeOut(o.duration);
    o.elements.eq(1-o.currSlide).fadeIn(o.duration);
  },
  transitionOptions: {duration: 'slow'}	
});</code></pre>
<h4>Options</h4>
<p>It's a subclass of <a href="/blog/2009/01/02/new-ui-widgets-textpopup-and-hebrewkeyboard/"><code>textpopup</code></a>, so all the options from <a href="/blog/2009/01/02/new-ui-widgets-textpopup-and-hebrewkeyboard/"><code>textpopup</code></a> and <a href="/blog/2009/03/25/new-widgets-googlesearch-and-ajaxpopup/"><code>ajaxpopup</code></a> are available. The <code>url</code> defaults to <a href="/inc/flexcal">/inc/flexcal.html</a>, which contains:</p>
<div id="htmldisplay" style="border: 1px solid black; overflow: scroll; height: 250px;">
<pre><code></code></pre></div>
<script>$.get('/inc/flexcal.html', function(data){
  $('#htmldisplay code').html(data.replace(/</g,'&lt;').replace(/>/g,'&gt;')).chili({codeLanguage: function() {return 'html';}});
});
</script>
<p>Note that all the required CSS is included in the html file, and that it uses the <a href="http://jqueryui.com/themeroller/">themeable jQuery classes</a>. Setting the <code>'class'</code> option to <code>multicalendar</code> makes the date rectangles longer, giving more room for the tab bar.</p>
<p>The <code>flexcal</code> options themselves are:</p>
<div class="prelike"><code><dl>
<dt>calendars {Array(String || Object)}</dt>
<dd>Array of calendars to display. Each string is a key into the $.ui.flexcal.l10n object.
Default is ['en'], which means that the default is one calendar, using the localization of
$.ui.flexcal.l10n.en. If the item is an object then it is the localization object itself</dd>
<dt>calendarNames {Array(String)}</dt>
<dd>Names to display on the tabs for the calendars. If empty, uses the default name
from the localization object</dd>
<dt>transitionOptions</dt>
<dd>Options to pass to the transition function, below</dd>
<dt>transition {Function}</dt>
<dd>Function to handle the transition from one calendar or month to another.
Signature is function(options), where options is copied from transitionOptions,
augmented by the following fields:
  <dl>
  <dt>options.$cont</dt>
  <dd>jQuery object that is the container for the calendars</dd>
  <dt>options.elements</dt>
  <dd>jQuery object that has two elements, each an absolutely-positioned &lt;div&gt;
one on top of the other, that are the calendars to be transitioned</dd>
  <dt>options.currSlide</dt>
  <dd>The index of the current calendar. Thus, options.elements.eq(options.currSlide)
is the currently-showing calendar and options.elements.eq(1-options.currSlide)
is the currently hidden calendar that needs to be shown</dd>
  <dt>options.rev</dt>
  <dd>Boolean to indicate that the animation should show a "reverse" transition,
going to a previous month.</dd>
  <dt>options.l10n</dt>
  <dd>Localization object for the calendar to be displayed
(options.elements.eq(1-options.currSlide)). The transition function can
use options.l10n.isRTL to determine if "next month" should be animated
right-to-left or left-to-right</dd>
  </dl>
The default is function(o){
		o.elements.eq(o.currSlide).hide();
		o.elements.eq(1-o.currSlide).show();
	}</dd>
<dt>hidetabs {true|false|'conditional'}</dt>
<dd>True to hide the tab bar, false to show it and 'conditional' to
hide it if there is only one calendar to display</dd>
<dt>l10n {Object (see below)}</dt>
<dd>Localization object to use if one of the fields in the calendar array item is undefined</dd>
</dl></code></div>
<p>The localization (<a href="http://en.wiktionary.org/wiki/L10n">l10n</a> for short) object is the key to the whole thing. $.flexcal.l10n contains objects, each of which is a localization object with the following fields (named to match the corresponding fields in $.datepicker.regional):</p>
<div class="prelike"><code><dl>
<dt>name {String}</dt><dd>Default display name, if not overridden by calendarNames</dd>
<dt>calendar {Function}</dt><dd>Calendar generating function, defaults to Gregorian calendar.
Takes a Date object, d,  and returns an object with the following fields:
<dl>
  <dt>first {Date}</dt><dd>First date of the month containing d</dd>
  <dt>last {Date}</dt><dd>Last date of the month containing d</dd>
  <dt>prev {Date}</dt><dd>Date one month before d</dd>
  <dt>next {Date}</dt><dd>Date one month after d</dd>
  <dt>prevYear {Date}</dt><dd>Date one year before d</dd>
  <dt>nextYear {Date}</dt><dd>Date one year after d</dd>
  <dt>y</dt><dd>Number of the year of d</dd>
  <dt>m</dt><dd>0-indexed number of the month of d</dd>
  <dt>dow</dt><dd>0-indexed number of the day of the week of first</dd>
</dl>
</dd>
<dt>monthNames {Array(String)}</dt><dd>Names of the months</dd>
<dt>dayNamesMin {Array(String)}</dt><dd>Names of the days of the week</dd>
<dt>isRTL {Boolean}</dt><dd>True if the calendar should display right-to-left</dd>
<dt>prevText {String}</dt><dd>Wording on the "previous month" button. The CSS for
jQuery UI replaces this with an icon but still uses the text for the title. By the jQuery UI
guidelines, it should not include an arrow or chevron</dd>
<dt>nextText {String}</dt><dd>Wording on the "next month" button</dd>
<dt>years {Function}</dt><dd>Function to convert a year number to the displayed string.
Default is function(n) {return n}</dd>
<dt>dates{Function}</dt><dd>Function to convert a date number to the displayed string.
Default is function(n) {return n}</dd>
</dl></code></div>
<pre><code class="language-javascript">
// default l10n calendar
$.ui.flexcal.prototype.options = {
	name: 'flexcal',
	calendar: function(d){
		var m = d.getMonth(), y = d.getFullYear(), date = d.getDate(), first = new Date (y, m, 1);
		var prev = new Date (y, m-1, date), next = new Date (y, m+1, date);
		if (prev.getDate() != date) prev = new Date (y, m, 0); // adjust for too-short months
		if (next.getDate() != date) next = new Date (y, m+2, 0);
		return {
			first: first,
			last: new Date (y, m+1, 0),
			prev: prev,
			next: next,
			m: m,
			y: y,
			dow: first.getDay()
		};
	},
	monthNames: ['January','February','March','April','May','June',
		'July','August','September','October','November','December'],
	dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'],
	isRTL: false,
	prevText: 'Previous',
	nextText: 'Next',
	years: function(n) {return n},
	days: function(n) {return n}
};
</code></pre>
<p>The plugin comes with three localizations defined (the ones I wanted): <code>$.ui.flexcal.l10n.en</code> (English-language civil calendar), <code>$.ui.flexcal.l10n.jewish</code> (Jewish  calendar with English names) and <code>$.ui.flexcal.l10n['jewish-he']</code> (Jewish  calendar with Hebrew names), and two calendar-generating functions:  <code>$.ui.flexcal.calendars.gregorian</code> and <code>$.ui.flexcal.calendars.jewish</code>.</p>
<p>The options object that is passed to the transition function was designed to allow drop-in use of Mike Alsup's excellent <a href="http://www.malsup.com/jquery/cycle/">cycle plugin</a>, with <code class="language-javascript">$(selector).flexcal({transition: $.fn.cycle.next})</code>, though I haven't tested this yet.</p>
<h4>Examples</h4>
<p>French/English calendar. We grab the French localization from the <code>datepicker</code> svn.</p>
<pre><code class="language-html demo">&lt;input id=&quot;date4&quot;/&gt;</code></pre>
<pre><code class="language-javascript demo">$.getScript('http://dev.jqueryui.com/export/2435/trunk/ui/i18n/ui.datepicker-fr.js', function(){
  $.datepicker.setDefaults($.datepicker.regional['']);
  $.ui.flexcal.l10n.fr = $.datepicker.regional.fr;
  $('#date4').flexcal({
    position: 'rt',
    calendars: ['fr', 'en'],
    calendarNames: ['Fran&#231;ais', 'Anglais']
  });
});</code></pre>
<p>The <a href="http://en.wikipedia.org/wiki/French_Revolutionary_Calendar">French Revolutionary calendar</a>. This is just to show off how flexible the widget is; 10-day weeks and 5-day months are no problem. View source to see the calendar algorithms.</p>
<script>
$.ui.flexcal.l10n.jacobin = {
	name: 'Jacobin',
	calendar: function(d){
		var j = (function (d){
			// algorithm from Romme's rule; valid 1992-2091. See http://en.wikipedia.org/wiki/French_Revolutionary_Calendar#Converting_from_the_Gregorian_Calendar
			// look at  http://www.fourmilab.ch/documents/calendar/ for more accurate algorithms
			var y = d.getFullYear();
			var jNY = new Date(y, 8, 22); // Jacobin New Year
			if (jNY > d) jNY = new Date(y-1, 8, 22);
			var jD = (d-jNY)/(24*60*60*1000); // day of the Jacobin year, 0-indexed
			return {y: jNY.getFullYear()-1791, m: Math.floor(jD/30), d: Math.floor(jD % 30)+1};
		})(d);
		// number of Sans-culottide days this year (5 for regular year, 6 for leap year. Our algorithm is only accurate for 1992-2091, so this is simple
		function sc() {return d.getFullYear() % 4 == 0 ? 6 : 5}
		function addDay(d, n){
			if (n === undefined) n = 1;
			return new Date(d.getFullYear(), d.getMonth(), d.getDate()+n);
		}
		var prev = addDay(d, -30);
		if (j.m == 0) if (j.d > sc()) prev = addDay(prev, 31-sc()-j.d);
		var next = addDay(d, 30);
		if (j.m == 11) if (j.d > sc()) next = addDay(next, -j.d+sc());
		var first = addDay(d, -j.d+1)
		return {
			first: first,
			last: addDay(first, j.m == 12 ? sc()-1 : 29),
			prev: prev,
			next: next,
			m: j.m, 
			y: j.y,
			dow: 0
		};
	},
	monthNames: ["Vend&#233;miaire", "Brumaire", "Frimaire", "Niv&#244;se",
		"Pluvi&#244;se", "Vent&#244;se", "Germinal", "Flor&#233;al", "Prairial",
		"Messidor", "Thermidor", "Fructidor", "Sans-culottide"],
	dayNamesMin: ['1<sup>i</sup>','2<sup>i</sup>','3<sup>i</sup>','4<sup>i</sup>','5<sup>i</sup>',
		'6<sup>i</sup>','7<sup>i</sup>','8<sup>i</sup>','9<sup>i</sup>','10<sup>i</sup>'],
	years: archaicNumbers([ 
		[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'] 
	])
};
</script>
<pre><code class="language-html demo">&lt;input id=&quot;date5&quot;/&gt;</code></pre>
<pre><code class="language-javascript demo">$('#date5').flexcal({
  position: 'rt',
  calendars: ['en', 'jacobin']
});
</code></pre>
<p><code>flexcal</code> with fancier transitions and using my <code>scrollIntoView</code> plugin (put the input box at the bottom of the window to see the scrolling effect)</p>
<pre><code class="language-html demo">&lt;input id=&quot;date6&quot;/&gt;</code></pre>
<pre><code class="language-javascript demo">$('#date6').flexcal({
	position: 'bl',
	calendars: ['en', 'jewish'],
	transition: function(o){
		var dir = o.rev ^ o.l10n.isRTL;
		var first = o.elements.eq(o.currSlide), second = o.elements.eq(1-o.currSlide);
		var h = o.$cont.height(), w = o.$cont.width();
		first.css({zIndex: 1});
		second.css({zIndex: 0}).show();
		first.animate({foo: 0}, { // the {foo:0} seems necessary because we need to animate some property, even if it isn't real
			duration: o.speed,
			step: function(now, fx){
				if (fx.state == 0){
					fx.start = now = dir ? 0 : w;
					fx.end = dir ? w : 0;
				}
				if (dir){
					first.css('clip', 'rect(0px '+w+'px '+h+'px '+now+'px)');
					second.css('clip', 'rect(0px '+now+'px '+h+'px 0px)');
				}else{
					first.css('clip', 'rect(0px '+now+'px '+h+'px 0px)');
					second.css('clip', 'rect(0px '+w+'px '+h+'px '+now+'px)');
				}
			},
			complete: function() {
				// clip is so inconsistently implemented. This way works in FF3, Opera 9, Safari 3,  IE7
				first.hide().css('clip', 'rect(auto)');
				second.css('clip', 'rect(auto)');
			}
		});
	},
	transitionOptions: {speed: 'slow'},
	shown: function() {$(this).flexcal('box').scrollIntoView()}
});
</code></pre>
<p>Using <a href="http://docs.jquery.com/UI/Draggable"><code>draggable</code></a>. The option <code>cancel:  '.ui-state-default'</code> makes sure that clickable elements aren't used as drag handles. Some <code>position</code> values require you have to do some CSS adjusting to keep the size from changing (<code>draggable</code> sets left and top; <code>textpopup</code> may set right and bottom). A real draggable calendar probably should have the cursor change on hover also.</p>
<pre><code class="language-html demo">&lt;input id=&quot;date7&quot;/&gt;</code></pre>
<pre><code class="language-html demo">&lt;input id=&quot;date8&quot;/&gt;</code></pre>
<pre><code class="language-javascript demo">$('#date7').flexcal({
  position: 'rt',
  calendars: ['en', 'fr'],
  calendarNames: ['', 'French']
}).flexcal('box').draggable({cancel: '.ui-state-default', cursor: 'move'});

$('#date8').flexcal({
  position: 'lt',
  calendars: ['en', 'fr'],
  calendarNames: ['', 'French']
}).flexcal('box').draggable({
  cancel: '.ui-state-default',
  cursor: 'move',
  start: function(){
    $(this).css({right: 'auto', bottom: 'auto'});
  }
});
</code></pre>
<h4>Differences from <code>datepicker</code></h4>
<p>I intentionally gave this widget fewer options than <code>datepicker</code>; I just included what I thought I would need. Since it uses my <a href="/blog/extending-jquery-ui-widgets/">subclass-able widget framework</a>, it can easily be extended to be more capable. One thing that is still definitely lacking is keyboard accessibility; I don't know anything about that and have to start experimenting. To be added in some later version, for sure.</p>
<h4>The power of <a href="/blog/extending-jquery-ui-widgets/">Extending Widgets</a></h4>
<p>Some examples of extending <code>flexcal</code> to be more <code>datepicker</code>-like.</p>
<h5>Date formatting</h5>
<p>The format for the date that is inserted into the text box is very simple; mm/dd/yyyy. You can subclass <code>flexcal</code> to use <code>datepicker</code>'s formatting (as with any subclassing, you should look at the <a href="/inc/jquery.flexcal.js">source code</a> to figure out what the code is doing):</p>
<pre><code class="language-html demo">&lt;input id=&quot;date9&quot;/&gt;</code></pre>
<pre><code class="language-html demo">&lt;input id=&quot;date10&quot;/&gt;</code></pre>
<pre><code class="language-javascript demo">
$.ui.flexcal.subclass('ui.fancyflexcal', {
  format: function(d){
    return $.datepicker.formatDate (this.options.dateFormat, d, $.datepicker.regional['']);
  },
  options: {
    dateFormat: $.datepicker.W3C
  }
});

$('#date9').fancyflexcal();
$('#date10').fancyflexcal({dateFormat: 'D, M d, yy'});
</code></pre>
<h5>Filtering dates</h5>
<p>Filtering dates can be added similarly, or, even better, with aspect-oriented programming:</p>
<pre><code class="language-html demo">&lt;input id=&quot;date11&quot;/&gt;</code></pre>
<pre><code class="language-javascript demo">
// allow weekdays only
$('#date11').flexcal().flexcal('after', '_adjustHTML', function (cal){
  cal.find('a.commit').filter(function(){
    var dow = (new Date(this.rel)).getDay();
    return (dow == 0 || dow == 6);
  }).removeClass('commit')['ui-unclickable']().addClass('ui-state-disabled');
});
</code></pre>
<h5>Drop-down menus</h5>
<p>Creating drop-down menus is a bit more complicated, because we can't assume that all the month names in the <code>monthNames</code> array are present in every year, and the definition of the localization calendar routine does not provide with us with a way to get the alternate calendar date for a given <code>Date</code>. The following routines help:</p>
<div style="border: 1px solid black; overflow: scroll; height: 250px"><pre><code class="language-javascript demo">
function option(d, l10n, cal, isMonth, selected){
  return [
    '&lt;option',
    selected ? ' selected="selected"' : '',
    ' value="', $.ui.flexcal.date2string(d), '"&gt;',
    isMonth ? l10n.monthNames[cal.m] : l10n.years(cal.y), 
    '&lt;/option&gt;'
  ].join('');
}
window.monthSelect = function(currentdate, l10n){
  var f = l10n.calendar;
  var currentcal = f(currentdate), ret = [option(currentdate, l10n, currentcal , true, true)], d = currentdate;
  for (var cal = currentcal; d = cal.prev, cal = f(d), cal.y == currentcal.y; ){
    ret.unshift(option(d, l10n, cal, true, false));
  }
  for (cal = currentcal; d = cal.next, cal = f(d), cal.y == currentcal.y; ){
    ret.push(option(d, l10n, cal, true, false));
  }
  return $('&lt;select&gt;').html(ret.join(''));
};
window.yearSelect = function(currentdate, l10n, n){
  var f = l10n.calendar;
  var currentcal = f(currentdate), ret = [option(currentdate, l10n, currentcal , false, true)], d = currentdate;
  for (var i = 0, cal = currentcal; d = cal.prevYear, cal = f(d), i < n; ++i){
    ret.unshift(option(d, l10n, cal, false, false));
  }
  for (var i = 0, cal = currentcal; d = cal.nextYear, cal = f(d), i < n; ++i){
    ret.push(option(d, l10n, cal, false, false));
  }
  return $('&lt;select&gt;').html(ret.join(''));
};
</code></pre></div>
<pre><code class="language-html demo">&lt;input id=&quot;date12&quot;/&gt;</code></pre>
<pre><code class="language-javascript demo">
$('#date12').flexcal({'class': 'multicalendar', calendars: ['en','he-jewish']}).flexcal('after', '_adjustHTML', function (cal){
  cal.find('.ui-datepicker-month').html(monthSelect(this.d, this.o.l10n));
  cal.find('.ui-datepicker-year').html(yearSelect(this.d, this.o.l10n, 5));
  var self = this;
  cal.find('select').bind('change', function(){self.setDate($(this).val())});
});
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/04/03/new-jquery-widget-flexcal/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>New Widgets: googleSearch and ajaxpopup</title>
		<link>http://bililite.com/blog/2009/03/25/new-widgets-googlesearch-and-ajaxpopup/</link>
		<comments>http://bililite.com/blog/2009/03/25/new-widgets-googlesearch-and-ajaxpopup/#comments</comments>
		<pubDate>Wed, 25 Mar 2009 20:54:58 +0000</pubDate>
		<dc:creator>Danny</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://bililite.com/blog/?p=426</guid>
		<description><![CDATA[I made a jQuery UI widget (a subclass of textpopup) that hijaxes a Google search form to show the results in a popup box rather than on a new page. Google AJAX search returns only the top four results, so the popup box includes a link to the full search page. Download the code. Download [...]]]></description>
			<content:encoded><![CDATA[<p>I made a <a href="http://ui.jquery.com/">jQuery UI</a>
 widget (a <a href="/blog/extending-jquery-ui-widgets/">subclass</a>
 of <a href="/blog/2009/01/02/new-ui-widgets-textpopup-and-hebrewkeyboard/"><code>textpopup</code></a>) that hijaxes a Google search form to show the results in a popup box rather than on a new page. <a href="http://bililite.com/blog/2009/01/02/new-ui-widgets-textpopup-and-hebrewkeyboard/">Google AJAX search</a> returns only the top four results, so the popup box includes a link to the full search page.</p>
<p><a href="/inc/jquery.googlesearch.js">Download the code</a>.</p>
<p>Download the <a href="/inc/jquery.textpopup.js"><code>textpopup</code> code</a> and the <a href="/inc/jquery.ui.subclass.js">widget subclassing code</a>.</p>
<span id="more-426"></span>
<h4><code>$.ui.googleSearch</code></h4>
<script src="/inc/jquery.ui.subclass.js"></script>
<script src="/inc/jquery.textpopup.js"></script>
<script src="/inc/jquery.googlesearch.js"></script>
<p>So if you have a simple Google search for your site:</p>
<pre><code class="language-html demo">
&lt;form method="get" action="http://www.google.com/search" &gt;
	&lt;input name="q" type="text"/&gt;
	&lt;input type="submit" value="Search with Google"/&gt;
	&lt;input name="sitesearch" value="http://bililite.com/blog" type="hidden"/&gt;
&lt;/form&gt;</code></pre>
<p>You can use the <a href="http://code.google.com/apis/ajaxsearch/">Google AJAX Search API</a> to make it AJAX-y:</p>
<pre><code class="language-html">
&lt;!-- Make sure to include the Google code and use your own Google API key here --&gt;
&lt;script src=&quot;http://www.xgoogle.com/jsapi?key=ABQIAAAA-9ImZ-drhltmN0pO3seqMBRffDClpIMT8zovbMirRiSUKbarGBR2yyqzhOxfMfoD2YyJmOkXgk65qw&quot;&gt;&lt;/script&gt;
</code></pre>
<pre><code class="language-html demo">
&lt;form method="get" action="http://www.google.com/search" &gt;
	&lt;input name="q" type="text" id="searchexample" /&gt;
	&lt;input type="submit" value="Search with Google"/&gt;
	&lt;input name="sitesearch" value="http://bililite.com/blog" type="hidden"/&gt;
&lt;/form&gt;

&lt;!-- and make sure to style the result (otherwise it's transparent and impossible to read)--&gt;
&lt;style&gt;
.searchresults {
  background: white;
  padding: 5px;
  border: 2px outset black;
  width: 500px;
}
&lt;/style&gt;
</code></pre>
<script>google.load("search","1")</script>
<pre><code class="language-javascript">
// load the search code
google.load("search","1");
</code></pre>
<pre><code class="language-javascript demo">
// create the search widget
$('#searchexample').googleSearch();
</code></pre>
<p>It has one option of its own, <code>contents</code>, which is shown in the results while the search is running. It can be anything accepted by <a href="http://docs.jquery.com/Attributes/html#val"><code>$.fn.html</code></a>. The default is <code>$([])</code>.</p>
<p>It can use all the options from <a href="/inc/jquery.textpopup.js"><code>textpopup</code></a> (it defaults the <code>trigger</code> option to <code>null</code> since you only want to see the search results when the form is submitted), plus the <code>url</code> option from <code>ajaxpopup</code> below; the default is <code>'/inc/search.html'</code> which in this demo contains the following:</p>
<pre><code class="language-html">
&lt;div class=&quot;searchresults&quot;&gt;
	&lt;h3&gt;Search Results&lt;/h3&gt;
	&lt;div class=&quot;resultsdiv&quot;&gt;&lt;/div&gt;
	&lt;a class=&quot;moreresults&quot;&gt;More Results &amp;raquo;&lt;/a&gt;
&lt;/div&gt;
</code></pre>
<p>The file pointed to by <code>url</code> must have a <code class="language-javascript">&lt;div class=&quot;resultsdiv&quot;&gt;</code> for the results to be put and a <code class="language-javascript">&lt;a class=&quot;moreresults&quot;&gt;</code> for the
link to the full Google search.</p>
<h4><code>$.ui.ajaxpopup</code></h4>
<p>Both the <code>googleSearch</code> above and the <a href="/blog/2009/01/02/new-ui-widgets-textpopup-and-hebrewkeyboard/"><code>hebrewKeyboard</code> widget</a> use the same strategy for building the popup box: instead of creating a string on the fly (like <pre><code class="language-javascript">$('&lt;div class=&quot;searchresults&quot;&gt;&lt;h3&gt;Search Results&lt;/h3&gt;&lt;div class=&quot;resultsdiv&quot;&gt;&lt;/div&gt;&lt;a class=&quot;moreresults&quot;&gt;More Results &amp;raquo;&lt;/a&gt;&lt;/div&gt;').appendTo(box);</code></pre>), they pull the skeleton of the HTML from an external URL via AJAX and modify that as needed. I think it's much easier to debug and customize.</p>
<p>I abstracted that into its own widget, <code>ajaxpopup</code> that has one option, <code>url</code> that is the URL of the HTML to be loaded into the popup box. The default value is undefined.</p>
<p>The retrieved HTML is cached, so the AJAX is only called once. Any <code class="language-html">&lt;style&gt;</code> elements are pulled out and appended to the <code class="language-html">&lt;head&gt;</code> and the remainder is put in the popup box.</p>
<p>It includes a hack to use <a href="http://en.wikipedia.org/wiki/Data_URI_scheme"><code>data:</code> URL's</a>, even in Internet Explorer, so you can include the HTML inline.</p>
<p>For example, if the file <a href="/blog/blogfiles/ajaxpopupexample.html">/blog/blogfiles/ajaxpopupexample.html</a> is:</p>
<iframe src="/blog/blogfiles/ajaxpopupexample.html" style="border: 1px solid black"></iframe>
<p>Then you can do:</p>
<pre><code class="language-html demo">&lt;input id="ajaxpopup-demo-1"/&gt;</code></pre>
<pre><code class="language-javascript demo">$('#ajaxpopup-demo-1').ajaxpopup({url: "/blog/blogfiles/ajaxpopupexample.html"});</code></pre>
<p>And you can do:</p>
<pre><code class="language-html demo">&lt;input id="ajaxpopup-demo-2"/&gt;</code></pre>
<pre><code class="language-javascript demo">$('#ajaxpopup-demo-2').ajaxpopup({url: 'data:,&lt;span style=&quot;background:white&quot;&gt;Hello, world&lt;/span&gt;'});</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://bililite.com/blog/2009/03/25/new-widgets-googlesearch-and-ajaxpopup/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
