﻿/* http://keith-wood.name/realPerson.html

   Real Person Form Submission for jQuery v1.0.1.

   Written by Keith Wood (kwood{at}iinet.com.au) June 2009.

   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 

   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 

   Please attribute the author if you use it. */



(function($) { // Hide scope, no $ conflict



var PROP_NAME = 'realPerson';



/* Real person manager. */

function RealPerson() {

	this._defaults = {

		length: 6, // Number of characters to use

		includeNumbers: false, // True to use numbers as well as letters

		regenerate: 'Click to change', // Instruction text to regenerate

		hashName: '{n}Hash' // Name of the hash value field to compare with,

			// use {n} to substitute with the original field name

	};

}



var CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

var DOTS = [

	['   *   ', '  * *  ', '  * *  ', ' *   * ', ' ***** ', '*     *', '*     *'],

	['****** ', '*     *', '*     *', '****** ', '*     *', '*     *', '****** '],

	[' ***** ', '*     *', '*      ', '*      ', '*      ', '*     *', ' ***** '],

	['****** ', '*     *', '*     *', '*     *', '*     *', '*     *', '****** '],

	['*******', '*      ', '*      ', '****   ', '*      ', '*      ', '*******'],

	['*******', '*      ', '*      ', '****   ', '*      ', '*      ', '*      '],

	[' ***** ', '*     *', '*      ', '*      ', '*   ***', '*     *', ' ***** '],

	['*     *', '*     *', '*     *', '*******', '*     *', '*     *', '*     *'],

	['*******', '   *   ', '   *   ', '   *   ', '   *   ', '   *   ', '*******'],

	['      *', '      *', '      *', '      *', '      *', '*     *', ' ***** '],

	['*     *', '*   ** ', '* **   ', '**     ', '* **   ', '*   ** ', '*     *'],

	['*      ', '*      ', '*      ', '*      ', '*      ', '*      ', '*******'],

	['*     *', '**   **', '* * * *', '*  *  *', '*     *', '*     *', '*     *'],

	['*     *', '**    *', '* *   *', '*  *  *', '*   * *', '*    **', '*     *'],

	[' ***** ', '*     *', '*     *', '*     *', '*     *', '*     *', ' ***** '],

	['****** ', '*     *', '*     *', '****** ', '*      ', '*      ', '*      '],

	[' ***** ', '*     *', '*     *', '*     *', '*   * *', '*    * ', ' **** *'],

	['****** ', '*     *', '*     *', '****** ', '*   *  ', '*    * ', '*     *'],

	[' ***** ', '*     *', '*      ', ' ***** ', '      *', '*     *', ' ***** '],

	['*******', '   *   ', '   *   ', '   *   ', '   *   ', '   *   ', '   *   '],

	['*     *', '*     *', '*     *', '*     *', '*     *', '*     *', ' ***** '],

	['*     *', '*     *', ' *   * ', ' *   * ', '  * *  ', '  * *  ', '   *   '],

	['*     *', '*     *', '*     *', '*  *  *', '* * * *', '**   **', '*     *'],

	['*     *', ' *   * ', '  * *  ', '   *   ', '  * *  ', ' *   * ', '*     *'],

	['*     *', ' *   * ', '  * *  ', '   *   ', '   *   ', '   *   ', '   *   '],

	['*******', '     * ', '    *  ', '   *   ', '  *    ', ' *     ', '*******'],

	['  ***  ', ' *   * ', '*     *', '*     *', '*     *', ' *   * ', '  ***  '],

	['   *   ', '  **   ', ' * *   ', '   *   ', '   *   ', '   *   ', '*******'],

	[' ***** ', '*     *', '      *', '     * ', '   **  ', ' **    ', '*******'],

	[' ***** ', '*     *', '      *', '    ** ', '      *', '*     *', ' ***** '],

	['    *  ', '   **  ', '  * *  ', ' *  *  ', '*******', '    *  ', '    *  '],

	['*******', '*      ', '****** ', '      *', '      *', '*     *', ' ***** '],

	['  **** ', ' *     ', '*      ', '****** ', '*     *', '*     *', ' ***** '],

	['*******', '     * ', '    *  ', '   *   ', '  *    ', ' *     ', '*      '],

	[' ***** ', '*     *', '*     *', ' ***** ', '*     *', '*     *', ' ***** '],

	[' ***** ', '*     *', '*     *', ' ******', '      *', '     * ', ' ****  ']];



$.extend(RealPerson.prototype, {

	/* Class name added to elements to indicate already configured with real person. */

	markerClassName: 'hasRealPerson',



	/* Override the default settings for all real person instances.

	   @param  settings  (object) the new settings to use as defaults

	   @return  (RealPerson) this object */

	setDefaults: function(settings) {

		$.extend(this._defaults, settings || {});

		return this;

	},



	/* Attach the real person functionality to an input field.

	   @param  target    (element) the control to affect

	   @param  settings  (object) the custom options for this instance */

	_attachRealPerson: function(target, settings) {

		target = $(target);

		if (target.hasClass(this.markerClassName)) {

			return;

		}

		target.addClass(this.markerClassName);

		var inst = {settings: $.extend({}, this._defaults)};

		$.data(target[0], PROP_NAME, inst);

		this._changeRealPerson(target, settings);

	},



	/* Reconfigure the settings for a real person control.

	   @param  target    (element) the control to affect

	   @param  settings  (object) the new options for this instance or

	                     (string) an individual property name

	   @param  value     (any) the individual property value (omit if settings is an object) */

	_changeRealPerson: function(target, settings, value) {

		target = $(target);

		if (!target.hasClass(this.markerClassName)) {

			return;

		}

		settings = settings || {};

		if (typeof settings == 'string') {

			var name = settings;

			settings = {};

			settings[name] = value;

		}

		var inst = $.data(target[0], PROP_NAME);

		$.extend(inst.settings, settings);

		target.prevAll('.realperson-challenge,.realperson-hash').remove().end().

			before(this._generateHTML(target, inst));

	},



	/* Generate the additional content for this control.

	   @param  target  (jQuery) the input field

	   @param  inst    (object) the current instance settings

	   @return  (string) the additional content */

	_generateHTML: function(target, inst) {

		var text = '';

		for (var i = 0; i < inst.settings.length; i++) {

			text += CHARS.charAt(Math.floor(Math.random() *

				(inst.settings.includeNumbers ? 36 : 26)));

		}

		var html = '<div class="realperson-challenge"><div class="realperson-text">';

		for (var i = 0; i < DOTS[0].length; i++) {

			for (var j = 0; j < text.length; j++) {

				html += DOTS[CHARS.indexOf(text.charAt(j))][i].replace(/ /g, '&nbsp;') +

					'&nbsp;&nbsp;';

			}

			html += '<br>';

		}

		html += '</div><div class="realperson-regen">' + inst.settings.regenerate +

			'</div></div><input type="hidden" class="realperson-hash" name="' +

			inst.settings.hashName.replace(/\{n\}/, target.attr('name')) +

			'" value="' + this._hash(text) + '">';

		return html;

	},



	/* Remove the real person functionality from a control.

	   @param  target  (element) the control to affect */

	_destroyRealPerson: function(target) {

		target = $(target);

		if (!target.hasClass(this.markerClassName)) {

			return;

		}

		target.removeClass(this.markerClassName).

			prevAll('.realperson-challenge,.realperson-hash').remove();

		$.removeData(target[0], PROP_NAME);

	},



	/* Compute a hash value for the given text.

	   @param  value  (string) the text to hash

	   @return  the corresponding hash value */

	_hash: function(value) {

		var hash = 5381;

		for (var i = 0; i < value.length; i++) {

			hash = ((hash << 5) + hash) + value.charCodeAt(i);

		}

		return hash;

	}

});



/* Attach the real person functionality to a jQuery selection.

   @param  command  (string) the command to run (optional, default 'attach')

   @param  options  (object) the new settings to use for these instances (optional)

   @return  (jQuery) for chaining further calls */

$.fn.realperson = function(options) {

	var otherArgs = Array.prototype.slice.call(arguments, 1);

	return this.each(function() {

		if (typeof options == 'string') {

			$.realperson['_' + options + 'RealPerson'].

				apply($.realperson, [this].concat(otherArgs));

		}

		else {

			$.realperson._attachRealPerson(this, options || {});

		}

	});

};



/* Initialise the real person functionality. */

$.realperson = new RealPerson(); // singleton instance



$('.realperson-challenge').live('click', function() {

	$(this).next().next().realperson('change');

});



})(jQuery);


