├── README.md ├── bootstrap-captcha.js ├── exampleUse.html ├── examples ├── asElement.html ├── asModal.html ├── css │ └── examplesCSS.css ├── index.html └── textPromptOff.html └── javascripts ├── bootstrap-captcha-min.js └── bootstrap-captcha.js /README.md: -------------------------------------------------------------------------------- 1 | bootstrap-captcha 2 | ================= 3 | 4 | A jQuery plugin that along with Bootstrap and Font-Awesome will create a stylish, secure, modal validation window. 5 | Your users will appreciate the use of clear visual (randomized) icons instead of hard to read `captchas`. The mouse movment validation will allow developers to discover that the user is indeed a human and not a bot! 6 | Work in progress begun 6/6/2013 much more to follow including mobile/touch version and updating the $.fn namespace. 7 | -------------------------------------------------------------------------------- /bootstrap-captcha.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | "use strict"; 3 | $.fn.bootstrapCaptcha = function (userOptions) { 4 | var that; 5 | this.attr('data-valid', 'false'); 6 | this.attr('data-mouseUsed', 'false'); 7 | this.iconSize = '3x'; 8 | this.resetInvalidDrop = false; 9 | this.clearWhenCorrect = true; 10 | this.textPrompt = true; 11 | this.displayTargetArrows = true; 12 | that = $.extend(true, this, userOptions); 13 | if (that.onDrop && typeof that.onDrop === 'function') { 14 | that.callback = true; 15 | } else { 16 | that.callback = false; 17 | } 18 | that.icons = [ 19 | 'envelope', 20 | 'anchor', 21 | 'pencil', 22 | 'bullhorn', 23 | 'fire-extinguisher', 24 | 'camera', 25 | 'wrench', 26 | 'cut', 27 | 'beaker', 28 | 'magic', 29 | 'heart', 30 | 'cogs', 31 | 'trophy', 32 | 'fire', 33 | 'bell', 34 | 'money', 35 | 'truck', 36 | 'coffee', 37 | 'lightbulb', 38 | 'paper-clip', 39 | 'lock', 40 | 'credit-card', 41 | 'headphones', 42 | 'microphone', 43 | 'rocket', 44 | 'fighter-jet', 45 | 'search', 46 | 'beer', 47 | 'eye-open', 48 | 'magnet', 49 | 'ambulance', 50 | 'home', 51 | 'glass', 52 | 'facetime-video', 53 | 'thumbs-up', 54 | 'gift', 55 | 'book', 56 | 'road', 57 | 'star', 58 | 'music', 59 | 'user', 60 | 'shield', 61 | 'puzzle-piece', 62 | 'bolt', 63 | 'briefcase', 64 | 'globe', 65 | 'leaf', 66 | 'circle-blank', 67 | 'calendar', 68 | 'frown', 69 | 'question', 70 | 'print', 71 | 'check', 72 | 'smile', 73 | 'key', 74 | 'keyboard' 75 | ]; 76 | that.iconNames = { 77 | envelope: 'envelope', 78 | anchor: 'anchor', 79 | pencil: 'pencil', 80 | bullhorn: 'bullhorn', 81 | 'fire-extinguisher': 'fire extinguisher', 82 | camera: 'camera', 83 | wrench: 'wrench', 84 | cut: 'scissors', 85 | beaker: 'beaker', 86 | magic: 'magic wand', 87 | heart: 'heart', 88 | cogs: 'cogs', 89 | trophy: 'trophy', 90 | fire: 'fire', 91 | bell: 'bell', 92 | money: 'money', 93 | truck: 'truck', 94 | coffee: 'coffee cup', 95 | lightbulb: 'lightbulb', 96 | 'paper-clip': 'paper clip', 97 | lock: 'lock', 98 | 'credit-card': 'credit card', 99 | headphones: 'headphones', 100 | microphone: 'microphone', 101 | smile: 'smily face', 102 | rocket: 'rocket', 103 | 'fighter-jet': 'fighter jet', 104 | search: 'magnifying glass', 105 | beer: 'beer mug', 106 | 'eye-open': 'eye', 107 | magnet: 'magnet', 108 | ambulance: 'ambulance', 109 | home: 'house', 110 | glass: 'glass', 111 | 'facetime-video': 'movie camera', 112 | 'thumbs-up': 'thumbs-up', 113 | gift: 'gift', 114 | book: 'book', 115 | keyboard: 'keyboard', 116 | road: 'road', 117 | star: 'star', 118 | music: 'musical notes', 119 | user: 'person', 120 | shield: 'shield', 121 | 'puzzle-piece': 'puzzle piece', 122 | bolt: 'lightning bolt', 123 | question: 'question mark', 124 | briefcase: 'briefcase', 125 | globe: 'globe', 126 | leaf: 'leaf', 127 | 'circle-blank': 'circle', 128 | calendar: 'calendar', 129 | check: 'check mark', 130 | frown: 'frown', 131 | key: 'key', 132 | print: 'printer' 133 | }; 134 | that.used = []; 135 | that.storedIcons = []; 136 | that.mouseUsed = false; 137 | that.bsValid = ''; 138 | that.str = ''; 139 | that.validate = function ($icon) { 140 | var klass = 'icon-' + that.bsValid, 141 | x = { 142 | valid: false, 143 | mouseUsed: false 144 | }; 145 | that.attr('data-valid', 'false'); 146 | that.attr('data-mouseUsed', 'false'); 147 | if (!that.mouseUsed) { 148 | if (that.callback === true) { 149 | that.onDrop(x); 150 | } 151 | return; 152 | } 153 | x.mouseUsed = true; 154 | that.attr('data-mouseUsed', 'true'); 155 | if ($icon.hasClass(klass)) { 156 | that.attr('data-valid', 'true'); 157 | x.valid = true; 158 | $('#bsBoop').append($('', { 159 | 'class': 'hide valid-icon icon-' + that.iconSize + ' icon-' + that.bsValid, 160 | id: 'bsValidIcon' 161 | })); 162 | $icon.hide(); 163 | $('#bsTargetSpan').empty().append('Correct!'); 164 | $('.icon-bullseye').fadeOut(function () { 165 | $('#bsCaptchaTarget').removeClass('alert-danger').addClass('alert-success'); 166 | $('#bsValidIcon').fadeIn(); 167 | }); 168 | if (that.clearWhenCorrect === true) { 169 | $('.bsCaptchaRemove').slideUp(); 170 | } 171 | if (that.callback === true) { 172 | that.onDrop(x); 173 | } 174 | return; 175 | } 176 | if (that.resetInvalidDrop === true) { 177 | that.makeLayout(); 178 | return; 179 | } 180 | $('#bsCaptchaError').empty().append('Try again'); 181 | if (that.callback === true) { 182 | that.onDrop(x); 183 | } 184 | }; 185 | that.getRandomInt = function (min, max) { 186 | return Math.floor(Math.random() * (max - min + 1)) + min; 187 | }; 188 | that.addIcon = function () { 189 | var randomnumber = that.getRandomInt(0, (that.icons.length - 1)); 190 | if ($.inArray(randomnumber, that.used) === -1) { 191 | that.used.push(randomnumber); 192 | $('', { 193 | 'class': 'bsDraggable icon-' + that.iconSize + ' icon-' + that.icons[randomnumber] 194 | }).appendTo('#bsCaptchaOut'); 195 | $('#bsCaptchaOut').append(' '); 196 | that.storedIcons.push(that.icons[randomnumber]); 197 | } 198 | if (that.used.length < 6) { 199 | that.addIcon(); 200 | return; 201 | } 202 | randomnumber = that.getRandomInt(0, (that.storedIcons.length - 1)); 203 | if (that.textPrompt === true) { 204 | $('#bsWhat').empty().append('' + that.iconNames[that.storedIcons[randomnumber]] + ' icon'); 205 | } else { 206 | $('#bsWhat').empty().append($('', { 207 | 'class': 'icon-' + that.iconSize + ' icon-' + that.storedIcons[randomnumber] 208 | })); 209 | $('#bsWhat').append(' icon '); 210 | } 211 | $('#bsCaptchaTarget').append($('
', { 212 | id: 'bsBoop' 213 | })); 214 | 215 | that.bsValid = that.storedIcons[randomnumber]; 216 | $('.bsDraggable').draggable({ 217 | revert: true, 218 | cursor: "move", 219 | helper: "clone" 220 | }).on("mousedown", function () { 221 | that.mouseUsed = true; 222 | }); 223 | $('#bsCaptchaTarget').droppable({ 224 | accept: ".bsDraggable", 225 | activeClass: "ui-state-highlight", 226 | drop: function (event, ui) { 227 | that.validate(ui.draggable); 228 | } 229 | }); 230 | $('#bootstrapCaptchaDiv').slideDown(); 231 | 232 | }; 233 | // credit: http://sedition.com/perl/javascript-fy.html 234 | that.fisherYates = function () { 235 | var i = that.icons.length, 236 | j, temp; 237 | if (i === 0) { 238 | return false; 239 | } 240 | while (--i) { 241 | j = Math.floor(Math.random() * (i + 1)); 242 | temp = that.icons[i]; 243 | that.icons[i] = that.icons[j]; 244 | that.icons[j] = temp; 245 | } 246 | that.addIcon(); 247 | }; 248 | that.makeLayout = function () { 249 | that.used = []; 250 | that.storedIcons = []; 251 | that.mouseUsed = false; 252 | that.bsValid = ''; 253 | that.str = ''; 254 | that.empty(); 255 | $('', { 256 | 'class': 'hide', 257 | id: "bootstrapCaptchaDiv" 258 | }).appendTo(that); 259 | $('', { 260 | 'class': 'row-fluid bsCaptchaDiv bsCaptchaRemove' 261 | }).appendTo('#bootstrapCaptchaDiv'); 262 | $('