Archive for the ‘jQuery’ Category

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: 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: {at: 'top', my: 'top'},
	speed: 0,
	hideOnOutsideClick: false,
	transitionOptions: {
		duration: 1500,
		gif: '/images/ani-bw.gif'
	},
	// make room for the inline calendar
	shown: function() { $('#slots').height($('#slots').flexcal('box').height()); }
});
$('#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);
});
$('#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 < 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('')};
}

The code also shows how to make the datepicker "inline".

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 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:

Continue reading ‘flexcal and cycle’ »

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 return information about the first element selected, so they cannot be chained, like $(selector).css('color'). 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 getter list to determine which methods should pass their return value from the plugin instead, and only operate on the first element matched.

This is inelegant, and makes my subclassing code less useful, since the getter list cannot be extended easily, only copied or replaced.

This is due to be improved in jQuery 1.7.2 (or at least it's in the latest nightlies): any method that returns this 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.

Thus, in the widget tutorial, we no longer need the line $.ui.green4.getter = "getLevel"; and getLevel remains unchanged, but setLevel needs to be changed to:

    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
    }, 

I think this will be an improvement, even if it means rewriting all my widgets. I would rather have used a return value of "undefined" as the flag for a chainable method, since that would not require rewriting regular methods, and getters that intentionally return undefined should be few and far between. But no one asked me.

Updated 2015-02-25 to version 3.0, with the new subclassing code.

Updated 2015-02-16 to document changes in default URL and need for separate CSS file.

Updated 2015-02-13 to document additional parameters.

Updated 2015-01-28 to link to github.

Just what the world needs—another date picker

Download the code from github. You need jquery.ui.subclass.js, jquery.textpopup.js, /inc/jquery.flexcal.js and flexcal.css.

Continue reading ‘New jQuery Widget: flexcal’ »

Updated 2011-02-23 to allow for destroying the widget correctly and the new Google Custom Search API

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 the textpopup code and the widget subclassing code.

Continue reading ‘New Widgets: googleSearch and ajaxpopup’ »
Finally, jQuery UI 1.6 final is out, renamed 1.7, and it's on google ajax libraries, so it's minimized (45K for the whole thing; .27 sec to download for me, which is nothing, especially if you're loading at the end of your code so the user is busy reading the content of your site). The contributors list doesn't include me ($.widget uses $.metadata); so much for my fame and fortune. This release still has the droppable bug; looks like that's to be fixed in 1.7.1.

I don't like writing function(){$(this).doStuff()} for callbacks and wanted some more elegant way of binding objects to functions. I could always use something like Prototype's bind, but I don't want to modify built-in types (because John Resig says not to) and I generally just want to bind plugins to jQuery objects, so it ought to be a plugin. Inspired by Ariel Flesler's jquery.sugar and John Resig's ultrachaining.

So we have $.later and $.fn.later. $.later() returns a chainable function that is bound at call time to this. $.later().css('color', 'red').append('<span>more</span>') is the same as function() { $(this).css('color', 'red').append('<span>more</span>') }. $(selector, context).later() is similar, but binds to $(selector, context) at call time: $('p').later().css('color', 'red').append('<span>more</span>') is the same as function() { $('p').css('color', 'red').append('<span>more</span>') }

Download the code.

Examples


	$('#example1').click($.later().toggleClass('bold'));
	

	<div id="example1">
		Example 1: 
		<span>Click Me</span>
	</div>
	

	$('#example2').click($('.example:last').later().animate({fontSize: '+=2'}, $('.example:first').later().fadeOut().fadeIn()));
	

	<div id="example2">
		Example 2: 
		<span class="example">Click Me</span> | 
		<span class="example">Watch Me</span>
	</div>
	

One thing that bugged me about my textpopup plugin was that the popup would not necessarily be visible on screen. datepicker moves the widget to a visible spot on screen, which I find very disconcerting. Ariel Flesler's scrollTo was my inspiration, but it scrolls an element so that its top left corner is at the top left of the screen. I want to scroll as little as possible to have the element in its entirety visible. Thus $('#myelement').scrollIntoView().

Download the code.

See the demo.

Continue reading ‘New plugin scrollIntoView’ »

I've updated my widgets tutorials to use jQuery UI 1.6, pulling the rc5 release off the svn site and turning them into pages rather than posts, since they seem to be so popular. See the widget tutorial and the extending widgets page.

Now all I need is for the UI team to officially release 1.6 so I can load it from googleapis rather than the jquery svn!

I keep telling myself I'll learn Haskell but my brain usually fries halfway through the tutorial. I've always thought that javascript is LISP in C's clothing, and jQuery helps undress it a bit. Now it turns out that really jQuery is really forcing it to cross-dress in Haskell. I guess I understood Haskell all along!