Snippet: Simple PDO Wrapper

January 10, 2010

This simple wrapper stores a single instance of a PDO connection statically, so it can be recalled anywhere in an application without passing around a special variable. To use it, just call the getInstance method whenever a database connection is required.

/**
 * Simple PDO Wrapper
 * January 10, 2010
 * Corey Hart @ http://www.codenothing.com
 *
 *
 * The DB class provides a method to retrieve a singleton instance
 * of a database connection statically.
 *
 * Usage -
 * 	$conn = DB::getInstance();
 */ 

Class DB
{
	// Database Credentials
	const host = 'localhost';
	const user = 'someuser';
	const pass = 'somepass';
	const db = 'dbname';

	// Singleton Instance Storage
	private static $connection;

	// Returns singleton instance of a database connection
	public static function getInstance(){
		// Create a connection if not already done so
		if (!self::$connection){
			$config = "mysql:host=" . self::host . ";dbname=" . self::db;
			self::$connection = new PDO($config, self::user, self::pass);
		}

		// Return the connection instance
		return self::$connection;
	}
};

Javascript Random Color Generator

December 28, 2009

The Random Color Generator not only randomly creates colors, it also stores those colors so they are not repeated in the next request.

Usage

The generator takes up a single namespace, Color, with a defaults object extension attached to it. Those defaults are defined below.

  • predef: An array of hex values (prefixed with the '#' symbol) that cannot be used by the generator.
  • randMax: Highest number that the rand method can obtain. Defaults to 255.
  • randMin: Lowest number the the rand method can obtain. Defaults to 0.
  • levelUp: Upper level that one of the rgb values must pass to be valid. Defaults to -1 (to include all values).
  • levelDown: Lower level that one of the rgb values must pass to be valid. Defaults to 256 (to include all values).
  • recursionLimit: Number of loops the generator can call upon itself before failing (to prevent recursion errors). Defaults to 15.
  • recursion: Callback function for when the recursionLimit is reached. Default function throws an error.
Methods

The generator uses a few methods internally that have been exposed as they may be useful.

  • random: Returns a non-duplicate hex value.
  • rand: Returns a random integer between the randMin and randMax default values.
  • reset: Clears the stack cache, and resets the generator to start from scratch.
  • rgb2hex: Converts rgb values into hex code form. Takes three parameters, (red, green blue).
  • hex2rgb: Converts a hex code string into rgb values. Returns in array form [red, green, blue].
Example

Color Hex RGB

The source package includes a demo page that uses jQuery as a helper. The generator itself is framework independent.

/*!
 * Random Color Generator
 * December 28, 2009
 * Corey Hart @ http://www.codenothing.com
 */
var Color = {
	defaults: {
		// Predefined hex codes that cant be used as random colors
		// All must be prefixed with the '#' indicator
		predef: [],

		// Maximum & Minimum random range values
		rangeMax: 255,
		rangeMin: 0,

		// Upper and lower level values that must be 
		// passed for random color acceptance
		//
		// By setting levelUp: 200, levelDown: 100; Neutral
		// colors like White, Gray, and Black can be somewhat weeded
		// out and your random colors will be full spectrum based.
		// Note*: Doing so increases likely hood of recursion
		levelUp: -1,
		levelDown: 256,

		// Recursion handlers
		recursionLimit: 15,
		recursion: function(){
			throw 'Recursion Error in Random Color Generator, ' +
				'too many tries on finding random color, ' +
				'[Limit ' + this.recursionLimit + ']';
		}
	},

	// Caching of random colors
	stack: {},

	// Returns a random color in hex code form, and caches
	// find in the stack.
	random: function(i){
		var self = this,
			defaults = self.defaults,
			r = self.rand(),
			g = self.rand(),
			b = self.rand(),
			hex = self.rgb2hex(r, g, b),
			levels = true;

		// Check for recursion
		if (i === undefined || typeof i !== 'number') i = 0;
		else if (i++ > defaults.recursionLimit) return defaults.recursion();

		// Color already used, try another one
		if (self.stack[hex]) hex = self.random(i);

		// Ensure one of the vals is above levelUp and another is below levelDown
		// Check defaults comments for better understanding
		levels = !!(
			(r > defaults.levelUp || g > defaults.levelUp || b > defaults.levelUp) &&
			(r < defaults.levelDown || g < defaults.levelDown || b < defaults.levelDown)
		);
		if (! levels) hex = self.random(i);

		// Store on stack to help prevent repeat
		self.stack[hex] = [r,g,b];

		// Return hex code in #
		return hex;
	},

	// Returns random number within range
	rand: function(){
		var defaults = this.defaults;
		return defaults.rangeMin + Math.floor(Math.random()*(defaults.rangeMax+1));
	},

	// Clears the stack
	reset: function(){
		var self = this,
			predef = self.defaults.predef,
			i = -1, l = predef.length;
		self.stack = {};
		if (l > 0)
			for ( ; ++i < l; )
				self.stack[ predef[i] ] = true;
	},

	// Returns hex code
	rgb2hex: function(r, g, b){
		var str = '0123456789ABCDEF';
		return '#' + [
			str.charAt((r-r%16)/16) + str.charAt(r%16),
			str.charAt((g-g%16)/16) + str.charAt(g%16),
			str.charAt((b-b%16)/16) + str.charAt(b%16)
		].join('');
	},

	// Returns in array form [red, green, blue]
	hex2rgb: function(hex){
		if (hex.substr(0, 1) === '#')
			hex = hex.substr(1);

		// Use the stack if possible to reduce processing
		return this.stack['#'+hex] ? this.stack['#'+hex] : 
			hex.length === 6 ? [
				parseInt(hex.substr(0, 2), 16),
				parseInt(hex.substr(2, 2), 16),
				parseInt(hex.substr(4, 2), 16)
			] : hex.length === 3 ? [
				parseInt(hex.substr(0, 1), 16),
				parseInt(hex.substr(1, 1), 16),
				parseInt(hex.substr(2, 1), 16)
			] : [];
	}
};

jQuery Plugin: Activity

December 20, 2009

The activity plugin takes care of all the underlying work in tracking user's (in)activity with the smallest amount of interference as possible. The plugin utilizes jQuery's one method on specified intervals to track key strokes and mouse movement in relation to the current window.

Usage

To start tracking user's activity, call the init method described later in this page. Simplicity is key with this hundred line script, with only four possible options to manipulate.

  • inactive: Time in milliseconds of user inactivity allowed (defaults to 30 mins)
  • inactiveFn: Callback function when above inactive time is reached
  • interval: Time in milliseconds to check user activity in intervals (defaults to 5 mins)
  • intervalFn: Callback function when above interval time is reached
Methods

The activity object comes with a few methods that may be of use. Remember that the init function needs to be called before the plugin will start tracking the users activity.

  • init: Initializes the activity tracker. Takes object of settings as it's single parameter.
  • update: Updates the users last active time to the current time stamp, or one that is provided.
  • isActive: Returns a boolean value of the user's current active state.
  • getActivity: Returns an object containing the users activity information.
  • reActivate: Re-activates the activity plugin after a user has gone inactive.
  • now: Returns the current time stamp.
Example
jQuery(function($){
	// Initialize the activity check
	$.activity.init({
		// Set interval check to every 5 seconds
		interval: 1000*5,
		intervalFn: function(info){
			console.log('Interval Check - Last Active:', info.lastActive, ', Difference in milliseconds to current time:', info.diff);
		},
		// Set inactive check to every 15 seconds
		inactive: 1000*5*3,
		inactiveFn: function(info){
			console.warn('Inactive Triggered - Last Active:', info.lastActive, ', Difference in milliseconds to current time:', info.diff);
		}
	});

	// Either reactivate, or update the current timestamp when user clicks on the page
	$(document).click(function(){
		if ( $.activity.isActive() )
			$.activity.update();
		else
			$.activity.reActivate();
	});
});

If you pop open firebug, There will be log statements indicating either intervals or inactivity on the current window (depending on how long it took to get to this point). If there is a warning log, this means that the inactive flag was triggered, and the plugin has stopped tracking. To reactivate the tracker, simply click on the screen.

As always the source code is provided below, along with a zip file containing an example page to test on locally.

/*!
 * jQuery Activity
 * December 20, 2009
 * Corey Hart @ http://www.codenothing.com
 */
(function($, undefined){
	// Current Timestamp
 	function now(){
		return (new Date).getTime();
	}

	// Attach activity object to jQuery base object
	$.activity = {
		// Variable Storage
		defaults: {
			// Users last active timestamp
			lastActive: now(),

			// Interval times in milliseconds
			inactive: 1000*60*30, // Default inactive at 30 minutes
			interval: 1000*60*5, // Default interval check every 5 minutes

			// Callback functions when intervals are reached
			inactiveFn: function(){},
			intervalFn: function(){}
		},

		// Timerid
		timer: undefined,

		// Expose the current time fn
		now: now,

		// Starts the interval
		init: function(options){
			// Update vars with options
			var self = this;
			self.defaults = $.extend(self.defaults, options||{});
			self.defaults.lastActive = now();

			// Check activity on a continuous interval
			self.bind();
			return self.timer = setInterval(function(){ self.check(); }, self.defaults.interval);
		},

		// Updates lastActive to current/provided timestamp
		update: function(time){
			return (this.defaults.lastActive = time === undefined ? now() : time);
		},

		// Returns boolean indicator of activity
		isActive: function(){
			return (this.timer !== undefined);
		},

		// Getter for current activity
		getActivity: function(){
			var self = this, time = now();
			return {diff: time-self.defaults.lastActive, time: time, lastActive: self.defaults.lastActive};
		},

		// Re-activates the interval (options only required if change to defaults wanted)
		reActivate: function(options){
			var self = this;
			// Clear interval if still running
			if (self.timer)
				self.timer = clearInterval(self.timer);
			// Reactivation
			return self.init(options||{});
		},

		// Interval check function
		check: function(){
			var self = this,
				time = now(),
				diff = time - self.defaults.lastActive;

			// Trigger appropriate callback
			if (diff > self.defaults.inactive){
				self.defaults.inactiveFn.call(self, {diff: diff, time: time, lastActive: self.defaults.lastActive});
				return self.timer = clearInterval(self.timer);
			}
			else if (diff > self.defaults.interval){
				self.defaults.intervalFn.call(self, {diff: diff, time: time, lastActive: self.defaults.lastActive});
			}

			// Rebind mouse tracking
			return self.bind();
		},

		// Rebinds activity events 
		bind: function(){
			var self = this;
			$(window).unbind('.activity').one('mousemove.activity keyup.activity', function(event){
				self.defaults.lastActive = event.timeStamp;
			});
			return self;
		}
	};
})(jQuery);

jQuery Plugin: Event Filter

December 14, 2009

Event's are a large part of what makes jQuery so powerful. A lesser known piece of information is that all event's binded through jQuery, are actually stored in the 'events' portion of that elements data object.

jQuery(function($){
	var $events = $('.event-filter-example');
	// Bind some events to the example
	$events.filter(':odd').click(function(){
		alert('This is an example click event');
	})
	$events.filter(':even').bind('mouseover.event-namespace', function(){
		$(this).css('color', 'purple');
	})
	$events.eq(0).bind('mouseover.event-new-namespace', function(){
		$(this).css('color', 'yellow');
	})
	$events.eq(1).bind('mouseout', function(){
		$(this).css('color', 'black');
	})
	$events.eq(2).bind('myevent.namespace', function(){
		alert('This is the ' + event.type + ' event');
	});

	// Log info into firebug if present
	if (console){
		console.group( 'Events of Filter Example: - ');
		console.log( 'First DIV - ', $events.eq(0).data('events') );
		console.log( 'Second DIV - ', $events.eq(1).data('events') );
		console.log( 'Third DIV - ', $events.eq(2).data('events') );
		console.groupEnd( 'Events of Filter Example: - ');
	}
});

This is an the example div
This is an the example div
This is an the example div


If you open up firebug, you will see a grouping with the 'Events of Filter Example' as the title. The group contains all events attached to each div, including the custom event's.

The Event Filter

The Event filter gives the ability to filter out a set of elements that only contain (or don't contain) the event specified. Here is an example with the above set of DIV's.

jQuery(function($){
	// Console only function
	if (!console) return;

	// Cache events
	var $events = $('.event-filter-example');

	// Log info into firebug
	console.group('Using the Event Filter');

	console.log( $events.filter(':Event(click)'), "Elements with 'click' event" );
	console.log( $events.filter(':Event(!click)'), "Elements with NO 'click' event" );
	console.log( $events.filter(':Event(mouseout)'), "Elements with 'mouseout' events" );
	console.log( $events.filter(':Event(mouseover.event-new-namespace)'), "Elements with 'mouseover.event-new-namespace' events" );
	console.log( $events.filter(':Event(myevent)'), "Elements with 'myevent' events" );
	console.log( $events.filter(':Event(.event-namespace)'), "Elements with '.event-namespace' event" );

	console.groupEnd('Using the Event Filter');
});

As shown above, the Event filter supports namespace searches along with searching for elements without the event with the 'not' operator. For events that contain the '!' at the start of their name, simply escape out the operator. Remember though, double slashes are required since javascript renders a single slash as an escaper, and won't pass it on to the filter function.

Note*: The reason the 'Event' filter is capitalized is so the :even filter doesn't overwrite it. jQuery uses regex on it's filters, and the even filter takes precedence over custom filter's.

The source code is provided below, along with a zip file containing a small example page of usage.

/*!
 * jQuery Event Filter
 * December 13, 2009
 * Corey Hart @ http://www.codenothing.com
 */ 
(function($, undefined){
	// Initialize vars together
	var original, notOperator = false, name, namespace, events, firstChar, i, k;

	// Event filter needs to be capitalized so it doesn't clash with 'even' filter
	$.expr[':'].Event = function(elem, index, params, group){
		// Return false if element doesn't exist, or no query string wasn't provided
                if (elem === undefined || ! params[3] || params[3] == '')
                        return false;
                // Run parsing if new query
                else if (original !== params[3]){
			// Will replace original var later
			original = params[3].split('.');
			// First is the name of the event
			name = original.shift();
			// Add ability to filter by elements that don't have the event
			notOperator = ((firstChar = name.substr(0,1)) === '!');
			// If not operator exists or user is escaping it out as it's part of the event name,
			// remove the name's first character
			if (notOperator || firstChar === "\\") name = name.substr(1);
			// Sort the namespace's like jQuery does in the event module
			namespace = original.sort().join('.');
			// Restore the original param for less processing
			original = params[3];
		}

		// Ensure events exist on element
		if (! (events = $(elem).data('events')) )
			return notOperator;
		// Run check for when only the namespace is passed
		// (Slower, especially when lots of events are attached)
		else if (! name && namespace.length){
			// Search for purely namespaced based event
			for (i in events)
				if (events[i])
					for (k in events[i])
						if (events[i][k].type && events[i][k].type === namespace)
							return !notOperator;
			// Event namespace not found
			return notOperator;
		}
		// Check to ensure event exists on the event object
		else if ( !events[name] )
			return notOperator;
		// Name && namespace passed
		else if ( namespace.length ){
			// Search for namespaced event
			for (i in events[name])
				if (events[name][i].type && events[name][i].type == namespace)
					return !notOperator;
			// Event not found
			return notOperator;
		}

		// No namespace was was defined, so only the event was desired
		return !notOperator;
	};
})(jQuery);

jQuery Event's, My Take

December 5, 2009

One of the more powerful parts of jQuery, is it's event's module. There's a lot more to it than just adding mouse handlers to give a hover effect. In fact, the best part of jQuery event's, is namespacing.

The Basics
// Base events from jQuery-1.3.2.js
jQuery.each( ("blur focus load resize scroll unload click dblclick " +
        "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
        "change select submit keydown keypress keyup error").split(" "), function( i, name ) {

        // Handle event binding
        jQuery.fn[ name ] = function( fn ) {
                return fn ? this.bind( name, fn ) : this.trigger( name );
        };
});

The above snippet was taken straight from the source of jQuery-1.3.2.js. What's happening is all the base event plugins are set-up with the bind method and ran with the trigger method. This just creates short-cuts from jQuery objects for things like setting up a key handler with: $('input').keyup(fn..), or triggering a focus event with $('textarea').focus().

Namespacing Events

Namespacing come into play when a few different event's of the same type are attached to the same element. Take the following example:

$('a').click(function(){
	$('#elem').css('color', 'red');
})
.click(function(){
	$('#elem').css('background-color', 'blue');
})
.click(function(){
	$('#elem').css('font-size', '8pt');
});

If the user actions only require for the background of the #elem to turn blue or the color to turn red on click, then this set of events wont work. What's needed is a way to trigger these events separately:

$('a').bind('click.color', function(){
	$('#elem').css('color', 'red');
})
.bind('click.bgcolor', function(){
	$('#elem').css('background-color', 'blue');
})
.bind('click.font', function(){
	$('#elem').css('font-size', '8pt');
});

// Changes the color only
$('a').trigger('click.color');
// Changes background color only
$('a').trigger('click.bgcolor');
// Changes font-size only
$('a').trigger('click.font');

With namespacing, there is access to triggering a single namespace of a certain event, and more specifically, triggering that event, without triggering every other event of the same type.

Custom Event's

Another bonus to the bind method, is the ability to create your own custom event's. Taking the same example from above, here's a new way to do it with custom event's:

$('a').bind('change-color', function(){
	$('#elem').css('color', 'red');
})
.bind('change-background', function(){
	$('#elem').css('background-color', 'blue');
})
.bind('change-font', function(){
	$('#elem').css('font-size', '8pt');
});

// Changes the color only
$('a').trigger('change-color');
// Changes background color only
$('a').trigger('change-background');
// Changes font-size only
$('a').trigger('change-font');

In this scope, the change-color, change-background, and change-font event's are attached to the 'a' element, and are triggered separately. Remember that these functions need to be manually triggered, as these aren't native event's, and won't be triggered natively by the browser. For some practical use case's, I will refer to the autoComplete plugin:

// Taken from autoComplete-5.0 source

// Add enabling event (only applicable after disable) 
.bind('autoComplete.enable', function(event){ 
	$input.data('ac-active', Active = TRUE); 
	// Store & return event 
	return LastEvent = event; 
}) 
// Add disable event 
.bind('autoComplete.disable', function(event){ 
	// Store event 
	$input.data('ac-active', Active = FALSE); 
	$ul.html('').hide(event); 
	// Store & return event 
	return LastEvent = event; 
}) 

There are a few things going on in this example, but the focus should be on the bind method presented. The autoComplete custom event is being use with namespacing so that only the autoComplete namespace is used in the event module of this input.

If enable and disable were attached directly to the input, and another developer added their own enable and disable event's, then triggering would fire both of them:

/**
 * Developer 1
 */
$('#elem').bind('enable', function(){
	// Some Code
})
.bind('disable', function(){
	// Some Code
});

/**
 * Developer 2
 */
$('#elem').bind('enable', function(){
	// Some other code
}).bind('disable', function(){
	// Some other code
});

// Later on in the application, triggering
// enable will run both developer 1&2's code
$('#elem').trigger('enable');
Finishing Up

It's fully recommended to download a copy of the source, or even more importantly, keep up with the latest changes and see how the pro's work. Please direct any question's or comments to corey@codeNothing.com, I am more than happy to help.