Ajax coming to a BlogIt near you soon...

Version 1.5 of BlogIt is now functionally complete. Remaining is some skin enhancements to support Ajax editing, and a lot of testing -- if you want to help test the new version drop me a line.

My goal has been to try and release every 4 weeks. This release is behind that schedule.Why? Partly due to external 'real-life' factors, but also because I got carried away with features. All very worthwhile, but at some point you need to balance time against actually releasing, even when working on a hobby project. So, I'll have a little more focus on the four week timeframe next time around. I'm not trying to be exact here, but a little more focus is a good thing.

BlogIt migration from Subversion to Git

The full history of BlogIt source code is now available on GitHub. You can find and download the current release, all prior releases, and also the most recent 'stable' development release. Here's a quick run down for those not familiar with GitHub:

  • Production and Development Releases: Use the Branch pull-down to select which code base to view. You'll always see the most recent production version when you first visit the page.
  • Past Versions: Each version of BlogIt is identified by a tag. Use the tag pull-down to select a specific version.
  • Download: After selecting a branch/version use cunningly the hidden "Download Source" link near the top right, to download a zip or tarball.

Read more...

Onwards with the Ajaxification of BlogIt

Just a quick update to let you know that the Ajaxification of BlogIt is moving forward pretty well. Most recently added was some full Ajax on comment editing. You can see it in action if you check out one of the test blog entries on the BlogIt test site. Login using password "blogadmin", click "quick edit' next to a comment, and see the wonders ensue!

jqTOC 1.1 Release Notes

It's been a while since I did anything with jqTOC, a jQuery plugin I wrote 3 years ago that generates a table of contents for a page. Time seems to have treated it well. At some point though the ability to show multiple TOCs broke, and that meant the demo didn't work. Not a very good advertisement for potential users.

This latest revision fixes the problem, and so it's now possible to generate more than one TOC on a page. For instance, if you had separate containers within your page, that you want separate table of contents for. The fix involved a minor change to to the CSS, so if you happen to be upgrading from an earlier version, then you need to update your CSS to reference toc_header and toc_content as classes, not IDs.

Speeding up BlogIt by caching files with ExpiresByType

The recent 1.4.0 version of BlogIt adds a bunch of user-interface fancies, and Ajax goodies. The cost of doing this was the addition of jQuery, jQuery-UI, and a couple of jQuery libraries, all weighing in at over 100k. Unfortunately, depending on how your host is configured, this might make BlogIt driven sites load a little slower.

One way to minimize the effect of this is to force your visitors browser to cache javascript, css, and image files, by adding future expiry headers. This won't affect the load speed the first time visitors hit your site, but it will help for subsequent loads by that visitor. If you host uses Apache, and have the mod_expires module loaded, then you can use the ExpiresByType directive to set an expiration date relative to the current date.
Read more...

Redux: Set IFRAME height based on size of remotely loaded content

Shortly after writing a quick guide on how to set IFRAME height based on size of remotely loaded content an error came to light where Chrome and Firefox would either keep growing the iframe with after each load, or would set the iframe too large if subsequent content was shorter than the initial content.

I wasn't able to determine the exact cause of the problem, but I implemented a work around which cured the problem. I've extracted the core code as a template to resolve the problem. Basically we call a function periodically, and try to see if the IFRAME height remains constant. If it does, then assume it's stopped changing, and call the local resizing function with the hopefully correct height.

REMOTE.COM/REMOTE.HTML

var IFRAME={};
function isHeightStatic(h, x){
	IFRAME.resizeHeight[IFRAME.resizeHeight.length] = h;

	// Test to see if h has changed over last x iterations.
	for (var i=IFRAME.resizeHeight.length-1; i >= IFRAME.resizeHeight.length-x; i--)  // don't test last element, as we just added it
		if (i < 0 || IFRAME.resizeHeight[i]!=h) return false;  //value is or may still be changing
	return true;
}
function iframeResizePipe(){
	// noticable when subsequent content is shorter.
	//var height = jQuery(document.body).height();
	var height = document.body.parentNode.scrollHeight;
	var heightStatic = isHeightStatic(height,4);  //same height for 1 second? (4 * 250ms)
	if (IFRAME.resizeHeight.length>48 || heightStatic) //Iterate 50 times at 250ms delay = 12 seconds load time
		clearInterval(IFRAME.resizeTimer);

	if (!heightStatic){
		// Going to 'pipe' the data to the parent through the helpframe..
		var pipe = document.getElementById('helpframe');
		if ( pipe == undefined ) return;
		// Cachebuster a precaution here to stop browser caching interfering
		pipe.src = 'http://local.com/helper.html?height='+height+'&cacheb='+Math.random();
	}
}
jQuery(document.body).ready( function(){
	// Ready event seems to trigger before remote images are loaded. When images don't speficy height,
	// cause page height to be calculated wrongly (from Larger Images).
	// So ping every 250ms to see if the height has increase, and reset the iframe size.
	IFRAME.resizeTimer = setInterval('iframeResizePipe()', 250);
});

NOTE: Code extracted from working code, based around jQuery, which has been commented out, with the exception of the onload handler which is performed by jQuery above.

Hope this helps a little at least.

BlogIt Release 1.4.0

This is the start of improving the BlogIt user interface, and making the admin workflow smoother. Many of the typical admin functions now use Ajax, which allows an action to take place without having to reload the page. That means rather than clicking to approve a comment, and waiting a few seconds for the page to reload, the approval is 'immediate! So, approving, unapproving, and deleting comments now happens instantaneously. You click the delete comment link, and it will disappear before your very eyes.

Read more...

BlogIt progress, Ajax on the way

Just a quick note to let you know that progress on the next version of Blogit is going well. As promised we've got some Ajax goodies in this release. As an initial dive, many admin functions are now ajaxified:

  • Approving and unapproving comments is near immediate, as is deleting comments -- no waiting for the page to reload. Click and it's done.
  • Blocking commenter IP addresses is another tool in the fight against spam. Add commenter IP addresses to the BlockList with a direct link -- that's ajax as well.
  • Field validations are now done client-side, which means you get to see the error submitting the page. Much nicer.
  • Ever lost some work by clicking Cancel, or Back in error? Now you'll get a message verifying whether you want to loose your hard written work.

All with a nice shiny look. Of course all existing bugs have been quelled, squashed, and removed. Check out the BlogIt Known Issues list for a full account of what's changing.

A little more testing and we should be done. In the meantime take a look over on the BlogIt test blog for a feel of what's coming.

Blix: PmWiki skin conversion

Blix is one of the more popular skins I've ported over from other systems. It's pretty flexible, and gets used as a foundation for some adaptions. That, and it's pretty colorful by default, but can be muted down pretty easily.


Check for a valid date in PHP, redux

Update 31-Jul-2010: Check out the updated version for a slightly more concise function.

This is an updated PHP based date validator, correcting some issues with the original version. An additional parameter has been added specifying the expected format of the date, using PHP date expressions.

function bi_IsDate($d, $f='%d-%m-%Y %H:%M'){
	if (empty($d))  return true;

	#Convert Unix timestamp to a std format (must not include regular expressions)
	if (preg_match('!\d{5,}!',$d))
		$d=strftime($f,$d);

	$re_day='%d|%e';
	$re_month='%m';
	$re_year='%g|%G|%y|%Y';
	$re_sep='[\/\-\.]';
	$re = array(
		'/'.$re_day.'/' => '(0?[1-9]|[12][0-9]|3[01])',
		'/'.$re_month.'/' => '(0?[1-9]|1[012])',
		'/'.$re_year.'/' => '(19\d\d|20\d\d)',
		'/%H|%I|%l/' => '([0-1]?\d|2[0-3])',
		'/%M/' => '([0-5]\d)'
	);

	$re_date = preg_replace(
		array_keys($re), 
		array_values($re), 
		$f);  #convert $f into a regular expression

	#does %d match the regular expression version of $f?
	if (preg_match('!^'.$re_date.'$!',$d,$x)  
		#determine expected date order based on $f and checkdate
		&& ((preg_match(
				'!^('.$re_day.')' .$re_sep .'('.$re_month.')' .$re_sep .'('.$re_year.')!',$f) 
				&& checkdate($x[2], $x[1], $x[3]))
			|| (preg_match(
				'!^('.$re_month.')' .$re_sep .'('.$re_day.')' .$re_sep .'('.$re_year.')!',$f) 
				&& checkdate($x[1], $x[2], $x[3]))
			|| (preg_match(
				'!^('.$re_year.')' .$re_sep .'('.$re_month.')' .$re_sep. '('.$re_day.')!',$f) 
				&& checkdate($x[3], $x[1], $x[2]))
	))  return true;
	return false;
}