function Loader( opts ) {
	this.trigger		= function( name, context ) {
		if ( 'undefined' == typeof this.callbacks[ name ] ) {
			return false;
		}
		if ( 'array' != typeof context ) {
			context	= [ context ];
		}
		this.callbacks[ name ].apply( this, context );
	}
	this.onImageLoaded	= function( jImg ) {
		this.n_loaded++;
	}
	this.onImageError	= function( jImg ) {
		for( var i=0; i<this.items.length; i++ ) {
			if ( this.items[ i ].getSrc() == jImg.attr( 'src' ) ) {
				this.items.splice( i, 1 );
				break;
			}
		}
	}
	this.add			= function( image_src ) {
		this.items.push(
			new ImagePreloader( image_src, this )
		);
	}
	this.start			= function() {
		this.trigger( Loader.Events.Start );
		for( var i=0; i<this.items.length; i++ ) {
			this.items[ i ].start();
		}
		var ref		= this;
		this.tmo	= setInterval( function() {
			if ( ref.n_loaded == ref.items.length ) {
				clearInterval( ref.tmo );
				ref.tmo = null;

				var jImages	= [];
				for( var i=0; i<ref.items.length; i++ ) {
					jImages.push( ref.items[ i ].preload );
				}
				ref.trigger( Loader.Events.Complete, jImages );
			}
		} , 50 );
	}

	this.items		= [];
	this.n_loaded	= 0;
	this.tmo		= null;
	this.callbacks	= jQuery.extend( {}, opts );
}
Loader.Events	= {
	Start:				'onStart',
	Complete:			'onComplete'
}
function ImagePreloader( imageSource, observer, alt ) {
	this.toString		= function() {
		return '[ImagePreloader]';
	}
	this.start			= function() {
		this.preload		= jQuery( '<img/>' );
		this.preload.bind(
			'load',
			{ ref: this },
			function( e ) {
				if ( null != e.data.ref.observer ) {
					e.data.ref.observer.onImageLoaded.call( e.data.ref.observer, e.data.ref.preload );
				}
			}
		).bind(
			'error',
			{ ref: this },
			function( e ) {
				if ( null != e.data.ref.observer ) {
					e.data.ref.observer.onImageError.call( e.data.ref.observer, e.data.ref.preload );
				}
			}
		).attr( 'src', imageSource ).attr( 'title', alt );
	}
	this.getSrc			= function() {
		return imageSource;
	}

	this.preload		= null;
	this.observer		= 'undefined' != typeof observer.onImageLoaded && 'undefined' != typeof observer.onImageError
		? observer : null;
	if ( null == this.observer ) {
		alert( 'onImageLoaded & onImageError handlers must be defined' );
	}
}
