1 /* http://keith-wood.name/realPerson.html
2 Real Person Form Submission for jQuery v1.1.1.
3 Written by Keith Wood (kwood{at}iinet.com.au) June 2009.
4 Available under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license.
5 Please attribute the author if you use it. */
7 (function($) { // Hide scope, no $ conflict
9 /* Real person manager. */
10 function RealPerson() {
12 length: 6, // Number of characters to use
13 includeNumbers: false, // True to use numbers as well as letters
14 regenerate: 'Click to change', // Instruction text to regenerate
15 hashName: '{n}Hash' // Name of the hash value field to compare with,
16 // use {n} to substitute with the original field name
20 var CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
22 [' * ', ' * * ', ' * * ', ' * * ', ' ***** ', '* *', '* *'],
23 ['****** ', '* *', '* *', '****** ', '* *', '* *', '****** '],
24 [' ***** ', '* *', '* ', '* ', '* ', '* *', ' ***** '],
25 ['****** ', '* *', '* *', '* *', '* *', '* *', '****** '],
26 ['*******', '* ', '* ', '**** ', '* ', '* ', '*******'],
27 ['*******', '* ', '* ', '**** ', '* ', '* ', '* '],
28 [' ***** ', '* *', '* ', '* ', '* ***', '* *', ' ***** '],
29 ['* *', '* *', '* *', '*******', '* *', '* *', '* *'],
30 ['*******', ' * ', ' * ', ' * ', ' * ', ' * ', '*******'],
31 [' *', ' *', ' *', ' *', ' *', '* *', ' ***** '],
32 ['* *', '* ** ', '* ** ', '** ', '* ** ', '* ** ', '* *'],
33 ['* ', '* ', '* ', '* ', '* ', '* ', '*******'],
34 ['* *', '** **', '* * * *', '* * *', '* *', '* *', '* *'],
35 ['* *', '** *', '* * *', '* * *', '* * *', '* **', '* *'],
36 [' ***** ', '* *', '* *', '* *', '* *', '* *', ' ***** '],
37 ['****** ', '* *', '* *', '****** ', '* ', '* ', '* '],
38 [' ***** ', '* *', '* *', '* *', '* * *', '* * ', ' **** *'],
39 ['****** ', '* *', '* *', '****** ', '* * ', '* * ', '* *'],
40 [' ***** ', '* *', '* ', ' ***** ', ' *', '* *', ' ***** '],
41 ['*******', ' * ', ' * ', ' * ', ' * ', ' * ', ' * '],
42 ['* *', '* *', '* *', '* *', '* *', '* *', ' ***** '],
43 ['* *', '* *', ' * * ', ' * * ', ' * * ', ' * * ', ' * '],
44 ['* *', '* *', '* *', '* * *', '* * * *', '** **', '* *'],
45 ['* *', ' * * ', ' * * ', ' * ', ' * * ', ' * * ', '* *'],
46 ['* *', ' * * ', ' * * ', ' * ', ' * ', ' * ', ' * '],
47 ['*******', ' * ', ' * ', ' * ', ' * ', ' * ', '*******'],
48 [' *** ', ' * * ', '* * *', '* * *', '* * *', ' * * ', ' *** '],
49 [' * ', ' ** ', ' * * ', ' * ', ' * ', ' * ', '*******'],
50 [' ***** ', '* *', ' *', ' * ', ' ** ', ' ** ', '*******'],
51 [' ***** ', '* *', ' *', ' ** ', ' *', '* *', ' ***** '],
52 [' * ', ' ** ', ' * * ', ' * * ', '*******', ' * ', ' * '],
53 ['*******', '* ', '****** ', ' *', ' *', '* *', ' ***** '],
54 [' **** ', ' * ', '* ', '****** ', '* *', '* *', ' ***** '],
55 ['*******', ' * ', ' * ', ' * ', ' * ', ' * ', '* '],
56 [' ***** ', '* *', '* *', ' ***** ', '* *', '* *', ' ***** '],
57 [' ***** ', '* *', '* *', ' ******', ' *', ' * ', ' **** ']];
59 $.extend(RealPerson.prototype, {
60 /* Class name added to elements to indicate already configured with real person. */
61 markerClassName: 'hasRealPerson',
62 /* Name of the data property for instance settings. */
63 propertyName: 'realperson',
65 /* Override the default settings for all real person instances.
66 @param options (object) the new settings to use as defaults
67 @return (RealPerson) this object */
68 setDefaults: function(options) {
69 $.extend(this._defaults, options || {});
73 /* Attach the real person functionality to an input field.
74 @param target (element) the control to affect
75 @param options (object) the custom options for this instance */
76 _attachPlugin: function(target, options) {
78 if (target.hasClass(this.markerClassName)) {
81 var inst = {options: $.extend({}, this._defaults)};
82 target.addClass(this.markerClassName).data(this.propertyName, inst);
83 this._optionPlugin(target, options);
86 /* Retrieve or reconfigure the settings for a control.
87 @param target (element) the control to affect
88 @param options (object) the new options for this instance or
89 (string) an individual property name
90 @param value (any) the individual property value (omit if options
91 is an object or to retrieve the value of a setting)
92 @return (any) if retrieving a value */
93 _optionPlugin: function(target, options, value) {
95 var inst = target.data(this.propertyName);
96 if (!options || (typeof options == 'string' && value == null)) { // Get option
98 options = (inst || {}).options;
99 return (options && name ? options[name] : options);
102 if (!target.hasClass(this.markerClassName)) {
105 options = options || {};
106 if (typeof options == 'string') {
109 options[name] = value;
111 $.extend(inst.options, options);
112 target.prevAll('.' + this.propertyName + '-challenge,.' + this.propertyName + '-hash').
113 remove().end().before(this._generateHTML(target, inst));
116 /* Generate the additional content for this control.
117 @param target (jQuery) the input field
118 @param inst (object) the current instance settings
119 @return (string) the additional content */
120 _generateHTML: function(target, inst) {
122 for (var i = 0; i < inst.options.length; i++) {
123 text += CHARS.charAt(Math.floor(Math.random() *
124 (inst.options.includeNumbers ? 36 : 26)));
126 var html = '<div class="' + this.propertyName + '-challenge">' +
127 '<div class="' + this.propertyName + '-text">';
128 for (var i = 0; i < DOTS[0].length; i++) {
129 for (var j = 0; j < text.length; j++) {
130 html += DOTS[CHARS.indexOf(text.charAt(j))][i].replace(/ /g, ' ') +
135 html += '</div><div class="' + this.propertyName + '-regen">' + inst.options.regenerate +
136 '</div></div><input type="hidden" class="' + this.propertyName + '-hash" name="' +
137 inst.options.hashName.replace(/\{n\}/, target.attr('name')) +
138 '" value="' + this._hash(text) + '">';
142 /* Enable the plugin functionality for a control.
143 @param target (element) the control to affect */
144 _enablePlugin: function(target) {
146 if (!target.hasClass(this.markerClassName)) {
149 target.removeClass(this.propertyName + '-disabled').prop('disabled', false).
150 prevAll('.' + this.propertyName + '-challenge').removeClass(this.propertyName + '-disabled');
153 /* Disable the plugin functionality for a control.
154 @param target (element) the control to affect */
155 _disablePlugin: function(target) {
157 if (!target.hasClass(this.markerClassName)) {
160 target.addClass(this.propertyName + '-disabled').prop('disabled', true).
161 prevAll('.' + this.propertyName + '-challenge').addClass(this.propertyName + '-disabled');
164 /* Remove the plugin functionality from a control.
165 @param target (element) the control to affect */
166 _destroyPlugin: function(target) {
168 if (!target.hasClass(this.markerClassName)) {
171 target.removeClass(this.markerClassName).
172 removeData(this.propertyName).
173 prevAll('.' + this.propertyName + '-challenge,.' + this.propertyName + '-hash').remove();
176 /* Compute a hash value for the given text.
177 @param value (string) the text to hash
178 @return the corresponding hash value */
179 _hash: function(value) {
181 for (var i = 0; i < value.length; i++) {
182 hash = ((hash << 5) + hash) + value.charCodeAt(i);
188 // The list of commands that return values and don't permit chaining
191 /* Determine whether a command is a getter and doesn't permit chaining.
192 @param command (string, optional) the command to run
193 @param otherArgs ([], optional) any other arguments for the command
194 @return true if the command is a getter, false if not */
195 function isNotChained(command, otherArgs) {
196 if (command == 'option' && (otherArgs.length == 0 ||
197 (otherArgs.length == 1 && typeof otherArgs[0] == 'string'))) {
200 return $.inArray(command, getters) > -1;
203 /* Attach the real person functionality to a jQuery selection.
204 @param options (object) the new settings to use for these instances (optional) or
205 (string) the command to run (optional)
206 @return (jQuery) for chaining further calls or
207 (any) getter value */
208 $.fn.realperson = function(options) {
209 var otherArgs = Array.prototype.slice.call(arguments, 1);
210 if (isNotChained(options, otherArgs)) {
211 return plugin['_' + options + 'Plugin'].apply(plugin, [this[0]].concat(otherArgs));
213 return this.each(function() {
214 if (typeof options == 'string') {
215 if (!plugin['_' + options + 'Plugin']) {
216 throw 'Unknown command: ' + options;
218 plugin['_' + options + 'Plugin'].apply(plugin, [this].concat(otherArgs));
221 plugin._attachPlugin(this, options || {});
226 /* Initialise the real person functionality. */
227 var plugin = $.realperson = new RealPerson(); // Singleton instance
229 $(document).on('click', 'div.' + plugin.propertyName + '-challenge', function() {
230 if (!$(this).hasClass(plugin.propertyName + '-disabled')) {
231 $(this).nextAll('.' + plugin.markerClassName).realperson('option', {});