├── 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 | $('
      ', { 263 | 'class': 'text-center span6' 264 | }).appendTo('.bsCaptchaDiv:last').append('
    • Please drag and drop the below:
    • '); 265 | $('
      ', { 266 | 'class': "row-fluid bsCaptchaDiv bsCaptchaRemove" 267 | }).appendTo('#bootstrapCaptchaDiv'); 268 | $('
        ', { 269 | 'class': "span6 text-center" 270 | }).appendTo('.bsCaptchaDiv:last').append('
      • '); 271 | $('
        ', { 272 | 'class': 'row-fluid bsCaptchaRemove' 273 | }).appendTo('#bootstrapCaptchaDiv').append($('

        ', { 274 | 'class': 'span6', 275 | html: '


        ' 276 | })); 277 | $('
        ', { 278 | 'class': 'row-fluid bsCaptchaDiv' 279 | }).appendTo('#bootstrapCaptchaDiv'); 280 | $('
          ', { 281 | 'class': "span6 text-center" 282 | }).appendTo('.bsCaptchaDiv:last').append('
        •  
        • '); 283 | that.str = ''; 284 | if (that.displayTargetArrows) { 285 | that.str += ''; 286 | that.str += ' to this target '; 287 | that.str += ''; 288 | } else { 289 | that.str += 'to that target'; 290 | } 291 | that.str += ''; 292 | $('#bsTargetSpanLI').append(that.str); 293 | $('
          ', { 294 | 'class': 'row-fluid bsCaptchaDiv' 295 | }).appendTo('#bootstrapCaptchaDiv'); 296 | $('
            ', { 297 | 'class': "span2 offset2 text-center well well-small alert alert-danger", 298 | id: 'bsCaptchaTarget' 299 | }).appendTo('.bsCaptchaDiv:last').append('
          • '); 300 | that.fisherYates(); 301 | }; 302 | if ($('body').hasClass('bootstrapCaptcha')) { 303 | return this; 304 | } 305 | $('body').addClass('bootstrapCaptcha'); 306 | this.makeLayout(); 307 | return this; 308 | }; 309 | }(jQuery)); 310 | -------------------------------------------------------------------------------- /exampleUse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* make sure you include jquery, bootstrap and font-awesome librarys */ 4 | 5 | 6 |
            7 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/asElement.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Example Boostrap Captcha 7 | 8 | 9 | 10 | 13 | 14 | 15 |
            16 |
            17 |
            18 |

            Example Boostrap Captcha as document element

            19 | 20 |

            21 |
            22 |
            23 |
            24 | 25 | 26 |
            27 | 28 | 29 | 30 | 31 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /examples/asModal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Example Boostrap Captcha 7 | 8 | 9 | 10 | 13 | 14 | 15 |
            16 |
            17 |
            18 |

            Example Boostrap Captcha

            19 | 20 |

            21 |
            22 |
            23 | 24 | 36 |
            37 | 38 | 39 | 40 | 41 | 42 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /examples/css/examplesCSS.css: -------------------------------------------------------------------------------- 1 | .topDiv { 2 | margin-top: 4em; 3 | } 4 | ul { 5 | list-display-style: none; 6 | } 7 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Example Boostrap Captcha 7 | 8 | 9 | 10 | 11 |
            12 |
            13 | 14 | 15 | 16 | 23 | 24 | 25 | 26 | 50 | 51 | 52 | 53 | 62 | 63 | 64 | 65 | 72 | 73 |
            bootstrap-captcha:
            Version 0.1 Created 6/6/2013
            A 17 | jQuery plug-in that along with 18 | Bootstrap and 19 | Font-Awesome 20 | will create a stylish, secure, modal validation window. 21 | Your users will appreciate the use of clear visual (randomized) icons instead of hard to read captchas. The mouse movement validation will allow developers 22 | to discover that the user is indeed a human and not a bot!
            Requirements: 27 |
              28 |
            • 29 | The required javascript libraries for this plug-in are: 30 |
                31 |
              • jQuery
              • 32 |
              • jQuery UI
              • 33 |
              • Bootstrap
              • 34 |
              • bootstrap-captcha.js (of course)
              • 35 |
              36 |
            • 37 |
            • 38 | The required css libraries for this plug-in are: 39 |
                40 |
              • jQuery UI css
              • 41 |
              • Bootstrap-combined css
              • 42 |
              • Font Awesome css
              • 43 |
              44 |
            • 45 |
            46 |

            Note 47 | All of the javascript and css (with the exception of the bootstrap-captcha.js) are included in the examples via cdn urls. I don't believe in bloating 48 | systems with download after download of standard libraries

            49 |
            Initialization: 54 |
             55 |                            $('#someDiv').bootstrapCaptcha({
             56 |                                 user: 'user',
             57 |                                 options: 'options',
             58 |                                 here: 'here'
             59 |                            });
             60 |                        
            61 |
            Examples: 66 | 71 |
            74 | 75 | 76 | 77 | 78 |
            bootstrapCaptcha Options
            79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 91 | 94 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 109 | 112 | 115 | 118 | 119 | 120 | 121 | 122 | 123 | 126 | 127 | 128 | 131 | 134 | 137 | 140 | 141 | 142 | 145 | 148 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 |
            ValueExampleDefaultExplanation
            onDrop 89 | onDrop: function(results) 90 | 92 | Optional 93 | 95 | Function returns Object with values Object.valid (true/false) and Object.mouseUsed (true/false) 96 | Callback function triggered when the user has dropped an icon on to the target. 97 |
            iconSize{ iconSize: '4x'} (String)Default: '2x'Sets the size of the icons for the bootstrap captcha, any variation of the Font-awesome values are permissiable, such as '3x','4x' ect.
            107 | resetInvalidDrop 108 | 110 | { resetInvalidDrop: false } (Boolean) 111 | 113 | Default: true 114 | 116 | When a user drops an incorrect icon on the target, by default, all the icons are randomized and shuffled again to help prevent `guessing`. 117 |
            resetTitle{ resetTitle: 'I know you can do it!' } (String)Default: 'Oops..try again.' 124 | If you choose to set resetInvalidDrop to false you can customize the message the user receives with they drop the incorrect icon using resetTitle 125 |
            129 | textPrompt example here 130 | 132 | { textPrompt: false} (Boolean) 133 | 135 | Default: true 136 | 138 | When set to false, a copy of the correct icon, (not the default wording) prompts the user for the correct icon. 139 |
            143 | clearWhenCorrect 144 | 146 | { clearWhenCorrect: false} (Boolean) 147 | 149 | Default: true 150 | By default if the user drags the correct icon, the remaining icons and prompts disappear. You can change that by setting this to false.
            displayTargetArrows { displayTargetArrows: false } (Boolean)Default: true This can turn the arrows highlighting the target on or off
            160 |
            161 |
            162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /examples/textPromptOff.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Example Boostrap Captcha 7 | 8 | 9 | 10 | 13 | 14 | 15 |
            16 |
            17 |
            18 |

            Example Boostrap Captcha

            19 | 20 |

            21 |
            22 |
            23 | 24 | 36 |
            37 | 38 | 39 | 40 | 41 | 42 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /javascripts/bootstrap-captcha-min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.bootstrapCaptcha=function(f){var a;this.attr("data-valid","false");this.attr("data-mouseUsed","false");this.iconSize="3x";this.displayTargetArrows=this.textPrompt=this.clearWhenCorrect=this.resetInvalidDrop=!0;this.resetTitle="Oops...try again";a=b.extend(!0,this,f);a.callback=a.onDrop&&"function"===typeof a.onDrop?!0:!1;a.guessedWrong=!1;a.icons="envelope anchor pencil bullhorn fire-extinguisher camera wrench cut beaker magic heart cogs trophy fire bell money truck coffee lightbulb paper-clip lock credit-card headphones microphone rocket fighter-jet search beer eye-open magnet ambulance home glass facetime-video thumbs-up gift book road star music user shield puzzle-piece bolt briefcase globe leaf circle-blank calendar frown question print check smile key keyboard".split(" "); 2 | a.iconNames={envelope:"envelope",anchor:"anchor",pencil:"pencil",bullhorn:"bullhorn","fire-extinguisher":"fire extinguisher",camera:"camera",wrench:"wrench",cut:"scissors",beaker:"beaker",magic:"magic wand",heart:"heart",cogs:"cogs",trophy:"trophy",fire:"fire",bell:"bell",money:"money",truck:"truck",coffee:"coffee cup",lightbulb:"lightbulb","paper-clip":"paper clip",lock:"lock","credit-card":"credit card",headphones:"headphones",microphone:"microphone",smile:"smily face",rocket:"rocket","fighter-jet":"fighter jet", 3 | search:"magnifying glass",beer:"beer mug","eye-open":"eye",magnet:"magnet",ambulance:"ambulance",home:"house",glass:"glass","facetime-video":"movie camera","thumbs-up":"thumbs-up",gift:"gift",book:"book",keyboard:"keyboard",road:"road",star:"star",music:"musical notes",user:"person",shield:"shield","puzzle-piece":"puzzle piece",bolt:"lightning bolt",question:"question mark",briefcase:"briefcase",globe:"globe",leaf:"leaf","circle-blank":"circle",calendar:"calendar",check:"check mark",frown:"frown", 4 | key:"key",print:"printer"};a.used=[];a.storedIcons=[];a.mouseUsed=!1;a.bsValid="";a.str="";a.validate=function(c){var e="icon-"+a.bsValid,d={valid:!1,mouseUsed:!1};a.attr("data-valid","false");a.attr("data-mouseUsed","false");if(a.mouseUsed)if(d.mouseUsed=!0,a.attr("data-mouseUsed","true"),c.hasClass(e)){if(a.attr("data-valid","true"),d.valid=!0,b("#bsBoop").append(b("",{"class":"hide valid-icon icon-"+a.iconSize+" icon-"+a.bsValid,id:"bsValidIcon"})),c.hide(),b("#bsTargetSpan").empty().append("Correct!"), 5 | b(".icon-bullseye").fadeOut(function(){b("#bsCaptchaTarget").removeClass("alert-danger").addClass("alert-success");b("#bsValidIcon").fadeIn()}),!0===a.clearWhenCorrect&&b(".bsCaptchaRemove").slideUp(),!0===a.callback)a.onDrop(d)}else if(!0===a.resetInvalidDrop)a.guessedWrong=!0,a.makeLayout();else{if(b("#bsCaptchaError").empty().append("Try again"),!0===a.callback)a.onDrop(d)}else if(!0===a.callback)a.onDrop(d)};a.getRandomInt=function(a,b){return Math.floor(Math.random()*(b-a+1))+a};a.addIcon=function(){var c= 6 | a.getRandomInt(0,a.icons.length-1);-1===b.inArray(c,a.used)&&(a.used.push(c),b("",{"class":"bsDraggable icon-"+a.iconSize+" icon-"+a.icons[c]}).appendTo("#bsCaptchaOut"),b("#bsCaptchaOut").append("  "),a.storedIcons.push(a.icons[c]));6>a.used.length?a.addIcon():(c=a.getRandomInt(0,a.storedIcons.length-1),!0===a.textPrompt?b("#bsWhat").empty().append(""+a.iconNames[a.storedIcons[c]]+" icon"):(b("#bsWhat").empty().append(b("",{"class":"icon-"+a.iconSize+" icon-"+a.storedIcons[c]})), 7 | b("#bsWhat").append(" icon ")),b("#bsCaptchaTarget").append(b("
          • ",{id:"bsBoop"})),a.bsValid=a.storedIcons[c],b(".bsDraggable").draggable({revert:!0,cursor:"move",helper:"clone"}).on("mousedown",function(){a.mouseUsed=!0}),b("#bsCaptchaTarget").droppable({accept:".bsDraggable",activeClass:"ui-state-highlight",drop:function(b,c){a.validate(c.draggable)}}),b("#bootstrapCaptchaDiv").slideDown())};a.fisherYates=function(){var b=a.icons.length,e,d;if(0===b)return!1;for(;--b;)e=Math.floor(Math.random()* 8 | (b+1)),d=a.icons[b],a.icons[b]=a.icons[e],a.icons[e]=d;a.addIcon()};a.makeLayout=function(){a.used=[];a.storedIcons=[];a.mouseUsed=!1;a.bsValid="";a.str="";a.empty();b("
            ",{"class":"hide",id:"bootstrapCaptchaDiv"}).appendTo(a);b("
            ",{"class":"row-fluid bsCaptchaDiv bsCaptchaRemove"}).appendTo("#bootstrapCaptchaDiv");b("
              ",{"class":"text-center span12",id:"bsInstructions"}).appendTo(".bsCaptchaDiv:last").append('
            • Please drag and drop the below:
            • ');!0=== 9 | a.guessedWrong&&b("#bsInstructions").prepend("
            • "+a.resetTitle+"
            • ");b("
              ",{"class":"row-fluid bsCaptchaDiv bsCaptchaRemove"}).appendTo("#bootstrapCaptchaDiv");b("
                ",{"class":"span12 text-center"}).appendTo(".bsCaptchaDiv:last").append('
              • ');b("
                ",{"class":"row-fluid bsCaptchaRemove"}).appendTo("#bootstrapCaptchaDiv").append(b("

                ",{"class":"span12",html:"


                "}));b("
                ",{"class":"row-fluid bsCaptchaDiv"}).appendTo("#bootstrapCaptchaDiv");b("
                  ", 10 | {"class":"span12 text-center"}).appendTo(".bsCaptchaDiv:last").append('
                •  
                • ');a.str='';a.displayTargetArrows?(a.str+='',a.str+=' to this target ',a.str+=''):a.str+='to that target';a.str+="";b("#bsTargetSpanLI").append(a.str);b("
                  ", 11 | {"class":"row-fluid bsCaptchaDiv"}).appendTo("#bootstrapCaptchaDiv");b("
                    ",{"class":"span4 offset4 text-center well well-small alert alert-danger",id:"bsCaptchaTarget"}).appendTo(".bsCaptchaDiv:last").append('
                  • ');a.fisherYates()};if(b("body").hasClass("bootstrapCaptcha"))return this;b("body").addClass("bootstrapCaptcha");this.makeLayout();return this}})(jQuery); 12 | -------------------------------------------------------------------------------- /javascripts/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 = true; 9 | this.clearWhenCorrect = true; 10 | this.textPrompt = true; 11 | this.displayTargetArrows = true; 12 | this.resetTitle = 'Oops...try again'; 13 | that = $.extend(true, this, userOptions); 14 | if (that.onDrop && typeof that.onDrop === 'function') { 15 | that.callback = true; 16 | } else { 17 | that.callback = false; 18 | } 19 | that.guessedWrong = false; 20 | that.icons = [ 21 | 'envelope', 22 | 'anchor', 23 | 'pencil', 24 | 'bullhorn', 25 | 'fire-extinguisher', 26 | 'camera', 27 | 'wrench', 28 | 'cut', 29 | 'beaker', 30 | 'magic', 31 | 'heart', 32 | 'cogs', 33 | 'trophy', 34 | 'fire', 35 | 'bell', 36 | 'money', 37 | 'truck', 38 | 'coffee', 39 | 'lightbulb', 40 | 'paper-clip', 41 | 'lock', 42 | 'credit-card', 43 | 'headphones', 44 | 'microphone', 45 | 'rocket', 46 | 'fighter-jet', 47 | 'search', 48 | 'beer', 49 | 'eye-open', 50 | 'magnet', 51 | 'ambulance', 52 | 'home', 53 | 'glass', 54 | 'facetime-video', 55 | 'thumbs-up', 56 | 'gift', 57 | 'book', 58 | 'road', 59 | 'star', 60 | 'music', 61 | 'user', 62 | 'shield', 63 | 'puzzle-piece', 64 | 'bolt', 65 | 'briefcase', 66 | 'globe', 67 | 'leaf', 68 | 'circle-blank', 69 | 'calendar', 70 | 'frown', 71 | 'question', 72 | 'print', 73 | 'check', 74 | 'smile', 75 | 'key', 76 | 'keyboard' 77 | ]; 78 | that.iconNames = { 79 | envelope: 'envelope', 80 | anchor: 'anchor', 81 | pencil: 'pencil', 82 | bullhorn: 'bullhorn', 83 | 'fire-extinguisher': 'fire extinguisher', 84 | camera: 'camera', 85 | wrench: 'wrench', 86 | cut: 'scissors', 87 | beaker: 'beaker', 88 | magic: 'magic wand', 89 | heart: 'heart', 90 | cogs: 'cogs', 91 | trophy: 'trophy', 92 | fire: 'fire', 93 | bell: 'bell', 94 | money: 'money', 95 | truck: 'truck', 96 | coffee: 'coffee cup', 97 | lightbulb: 'lightbulb', 98 | 'paper-clip': 'paper clip', 99 | lock: 'lock', 100 | 'credit-card': 'credit card', 101 | headphones: 'headphones', 102 | microphone: 'microphone', 103 | smile: 'smily face', 104 | rocket: 'rocket', 105 | 'fighter-jet': 'fighter jet', 106 | search: 'magnifying glass', 107 | beer: 'beer mug', 108 | 'eye-open': 'eye', 109 | magnet: 'magnet', 110 | ambulance: 'ambulance', 111 | home: 'house', 112 | glass: 'glass', 113 | 'facetime-video': 'movie camera', 114 | 'thumbs-up': 'thumbs-up', 115 | gift: 'gift', 116 | book: 'book', 117 | keyboard: 'keyboard', 118 | road: 'road', 119 | star: 'star', 120 | music: 'musical notes', 121 | user: 'person', 122 | shield: 'shield', 123 | 'puzzle-piece': 'puzzle piece', 124 | bolt: 'lightning bolt', 125 | question: 'question mark', 126 | briefcase: 'briefcase', 127 | globe: 'globe', 128 | leaf: 'leaf', 129 | 'circle-blank': 'circle', 130 | calendar: 'calendar', 131 | check: 'check mark', 132 | frown: 'frown', 133 | key: 'key', 134 | print: 'printer' 135 | }; 136 | that.used = []; 137 | that.storedIcons = []; 138 | that.mouseUsed = false; 139 | that.bsValid = ''; 140 | that.str = ''; 141 | that.validate = function ($icon) { 142 | var klass = 'icon-' + that.bsValid, 143 | x = { 144 | valid: false, 145 | mouseUsed: false 146 | }; 147 | that.attr('data-valid', 'false'); 148 | that.attr('data-mouseUsed', 'false'); 149 | if (!that.mouseUsed) { 150 | if (that.callback === true) { 151 | that.onDrop(x); 152 | } 153 | return; 154 | } 155 | x.mouseUsed = true; 156 | that.attr('data-mouseUsed', 'true'); 157 | if ($icon.hasClass(klass)) { 158 | that.attr('data-valid', 'true'); 159 | x.valid = true; 160 | $('#bsBoop').append($('', { 161 | 'class': 'hide valid-icon icon-' + that.iconSize + ' icon-' + that.bsValid, 162 | id: 'bsValidIcon' 163 | })); 164 | $icon.hide(); 165 | $('#bsTargetSpan').empty().append('Correct!'); 166 | $('.icon-bullseye').fadeOut(function () { 167 | $('#bsCaptchaTarget').removeClass('alert-danger').addClass('alert-success'); 168 | $('#bsValidIcon').fadeIn(); 169 | }); 170 | if (that.clearWhenCorrect === true) { 171 | $('.bsCaptchaRemove').slideUp(); 172 | } 173 | if (that.callback === true) { 174 | that.onDrop(x); 175 | } 176 | return; 177 | } 178 | if (that.resetInvalidDrop === true) { 179 | that.guessedWrong = true; 180 | that.makeLayout(); 181 | return; 182 | } 183 | $('#bsCaptchaError').empty().append('Try again'); 184 | if (that.callback === true) { 185 | that.onDrop(x); 186 | } 187 | }; 188 | that.getRandomInt = function (min, max) { 189 | return Math.floor(Math.random() * (max - min + 1)) + min; 190 | }; 191 | that.addIcon = function () { 192 | var randomnumber = that.getRandomInt(0, (that.icons.length - 1)); 193 | if ($.inArray(randomnumber, that.used) === -1) { 194 | that.used.push(randomnumber); 195 | $('', { 196 | 'class': 'bsDraggable icon-' + that.iconSize + ' icon-' + that.icons[randomnumber] 197 | }).appendTo('#bsCaptchaOut'); 198 | $('#bsCaptchaOut').append('  '); 199 | that.storedIcons.push(that.icons[randomnumber]); 200 | } 201 | if (that.used.length < 6) { 202 | that.addIcon(); 203 | return; 204 | } 205 | randomnumber = that.getRandomInt(0, (that.storedIcons.length - 1)); 206 | if (that.textPrompt === true) { 207 | $('#bsWhat').empty().append('' + that.iconNames[that.storedIcons[randomnumber]] + ' icon'); 208 | } else { 209 | $('#bsWhat').empty().append($('', { 210 | 'class': 'icon-' + that.iconSize + ' icon-' + that.storedIcons[randomnumber] 211 | })); 212 | $('#bsWhat').append(' icon '); 213 | } 214 | $('#bsCaptchaTarget').append($('
                  • ', { 215 | id: 'bsBoop' 216 | })); 217 | 218 | that.bsValid = that.storedIcons[randomnumber]; 219 | $('.bsDraggable').draggable({ 220 | revert: true, 221 | cursor: "move", 222 | helper: "clone" 223 | }).on("mousedown", function () { 224 | that.mouseUsed = true; 225 | }); 226 | $('#bsCaptchaTarget').droppable({ 227 | accept: ".bsDraggable", 228 | activeClass: "ui-state-highlight", 229 | drop: function (event, ui) { 230 | that.validate(ui.draggable); 231 | } 232 | }); 233 | $('#bootstrapCaptchaDiv').slideDown(); 234 | 235 | }; 236 | // credit: http://sedition.com/perl/javascript-fy.html 237 | that.fisherYates = function () { 238 | var i = that.icons.length, 239 | j, temp; 240 | if (i === 0) { 241 | return false; 242 | } 243 | while (--i) { 244 | j = Math.floor(Math.random() * (i + 1)); 245 | temp = that.icons[i]; 246 | that.icons[i] = that.icons[j]; 247 | that.icons[j] = temp; 248 | } 249 | that.addIcon(); 250 | }; 251 | that.makeLayout = function () { 252 | that.used = []; 253 | that.storedIcons = []; 254 | that.mouseUsed = false; 255 | that.bsValid = ''; 256 | that.str = ''; 257 | that.empty(); 258 | $('
                    ', { 259 | 'class': 'hide', 260 | id: "bootstrapCaptchaDiv" 261 | }).appendTo(that); 262 | $('
                    ', { 263 | 'class': 'row-fluid bsCaptchaDiv bsCaptchaRemove' 264 | }).appendTo('#bootstrapCaptchaDiv'); 265 | $('
                      ', { 266 | 'class': 'text-center span12', 267 | id: 'bsInstructions' 268 | }).appendTo('.bsCaptchaDiv:last').append('
                    • Please drag and drop the below:
                    • '); 269 | if(that.guessedWrong === true){ 270 | $('#bsInstructions').prepend('
                    • ' + that.resetTitle + '
                    • '); 271 | } 272 | $('
                      ', { 273 | 'class': "row-fluid bsCaptchaDiv bsCaptchaRemove" 274 | }).appendTo('#bootstrapCaptchaDiv'); 275 | $('
                        ', { 276 | 'class': "span12 text-center" 277 | }).appendTo('.bsCaptchaDiv:last').append('
                      • '); 278 | $('
                        ', { 279 | 'class': 'row-fluid bsCaptchaRemove' 280 | }).appendTo('#bootstrapCaptchaDiv').append($('

                        ', { 281 | 'class': 'span12', 282 | html: '


                        ' 283 | })); 284 | $('
                        ', { 285 | 'class': 'row-fluid bsCaptchaDiv' 286 | }).appendTo('#bootstrapCaptchaDiv'); 287 | $('
                          ', { 288 | 'class': "span12 text-center" 289 | }).appendTo('.bsCaptchaDiv:last').append('
                        •  
                        • '); 290 | that.str = ''; 291 | if (that.displayTargetArrows) { 292 | that.str += ''; 293 | that.str += ' to this target '; 294 | that.str += ''; 295 | } else { 296 | that.str += 'to that target'; 297 | } 298 | that.str += ''; 299 | $('#bsTargetSpanLI').append(that.str); 300 | $('
                          ', { 301 | 'class': 'row-fluid bsCaptchaDiv' 302 | }).appendTo('#bootstrapCaptchaDiv'); 303 | $('
                            ', { 304 | 'class': "span4 offset4 text-center well well-small alert alert-danger", 305 | id: 'bsCaptchaTarget' 306 | }).appendTo('.bsCaptchaDiv:last').append('
                          • '); 307 | that.fisherYates(); 308 | }; 309 | if ($('body').hasClass('bootstrapCaptcha')) { 310 | return this; 311 | } 312 | $('body').addClass('bootstrapCaptcha'); 313 | this.makeLayout(); 314 | return this; 315 | }; 316 | }(jQuery)); 317 | --------------------------------------------------------------------------------