Hi all,
I made a script that can be used as a captcha. It shows a random amount of (roman) letters sliding by every 2000 miliseconds (thats why its called MatrixCaptcha. But I am not sure wether it is able to stop bots from spamming. Does anyone have a suggestion for improvement?
I know it's a bit long but you should try it out and see what you think about it!
The code:
<script type="text/javascript">
/*
=============================================================
=----------------===========================----------------=
=----------------==-----MatrixCaptcha-----==----------------=
=----------------===========================----------------=
=============================================================
###################---SCRIPT INFORMATION---###################
# #
# Script: MatrixCaptcha.js #
# #
# Description: a script written in javascript. It is used #
# to make sure that a human is filling in the form and not a #
# bot. #
# #
# Visit us at: www.symbolwebdesign.nl #
# Contact: info@symbolwebdesign.nl #
# #
##############################################################
########################---WARRANTY---########################
# #
# This script is written and created by Symbol Webdesign. #
# You are allowed to adjust this script to your own likings. #
# #
# DO NOT REMOVE THIS WARRANTY OR THE SCRIPT INFORMATION #
# AT ALL TIMES #
# #
# Created by Symbol Webdesign ©2009 #
##############################################################
Further information:
The HTML of the script is compatible with (X)HTML transistional,
in order for this script to function properly, it is required to
put it in a form (as this is the entire purpose of the script)
You are able to retrieve the "TRUE" or "FALSE" values from it
by doing so (PHP) :
$validUser = $_POST['captcha-valid'];
if ($validUser == "TRUE") {
// What happens if it is a human that filled in the form
} else {
// What happens if it is a bot that filled in the form (or if the user incorrectly entered the letters)
}
The input format within the captcha:
<input type="hidden" id="captcha-valid" name="captcha-valid" value="FALSE" />
*/
// Showing the captcha-container in the document
document.write('<div id="captcha-container" style="width:130px; height:150px; position:relative; top:0px; left:0px; font-family:Calibri; background-color:#EEEEEE; border: 5px ridge #EEEEEE;">');
document.write('<div id="captcha-display" style="background-color:black; position:absolute; top:5px; left:5px; height:110px; width:60px; border:1px solid darkgreen; overflow:hidden; padding:0px; spacing:3px;">');
document.write('<div id="line0" style="position:absolute; top:0px; left:3px; width:8px; overflow:hidden; font-weight:normal; font-style:normal; font-size:14px; font-family:sans-serif; color:darkgreen;"></div>');
document.write('<div id="line1" style="position:absolute; top:0px; left:11px; width:8px; overflow:hidden; font-weight:normal; font-style:normal; font-size:14px; font-family:sans-serif; color:darkgreen;"></div>');
document.write('<div id="line2" style="position:absolute; top:0px; left:19px; width:8px; overflow:hidden; font-weight:normal; font-style:normal; font-size:14px; font-family:sans-serif; color:darkgreen;"></div>');
document.write('<div id="line3" style="position:absolute; top:0px; left:27px; width:8px; overflow:hidden; font-weight:normal; font-style:normal; font-size:14px; font-family:sans-serif; color:darkgreen;"></div>');
document.write('<div id="line4" style="position:absolute; top:0px; left:35px; width:8px; overflow:hidden; font-weight:normal; font-style:normal; font-size:14px; font-family:sans-serif; color:darkgreen;"></div>');
document.write('<div id="line5" style="position:absolute; top:0px; left:43px; width:8px; overflow:hidden; font-weight:normal; font-style:normal; font-size:14px; font-family:sans-serif; color:darkgreen;"></div>');
document.write('<div id="line6" style="position:absolute; top:0px; left:51px; width:8px; overflow:hidden; font-weight:normal; font-style:normal; font-size:14px; font-family:sans-serif; color:darkgreen;"></div>');
document.write('</div>');
document.write('<div id="captcha-menu" style="position:absolute; top:5px; left:70px;">');
document.write('<input type="button" onclick="newCaptcha()" value="New" style="width:57px;" /><br />');
document.write('<input type="button" onclick="displayWord()" value="Again" style="width:57px;" /><br />');
document.write('<span style="font-size:13px;">');
document.write('Enter the shown letters below:');
document.write('</span>');
document.write('</div>');
document.write('<div id="captcha-input" style="position:absolute; top:120px; left:5px;">');
document.write('<input type="text" maxlength="12" id="captcha-textinput" onchange="checkAnswer(this.value)" style="width:115px; height:18px;" />');
document.write('</div>');
document.write('<input type="hidden" id="captcha-valid" name="captcha-valid" value="FALSE" />');
document.write('</div>');
// Setting variables
var alreadyRunning = "false"; // This prevents spam clicking
var word = ""; // The word that needs to be entered in order to return a TRUE value
var repChar = "_"; // Which character replaces the 0's inside the bit-lines
// Creating a new word, so that when it's empty, it won't be a correct answer
genNewWord();
function initCaptcha() {
genNewWord();
displayWord();
}
function convertLetterToMatrix(regularLetter) {
// This function returns 7 lines full of 7 bit codes, that represent the letter
switch (regularLetter) {
case "A" :
var line = new Array;
line[0] = "0000000";
line[1] = "0001000";
line[2] = "0010100";
line[3] = "0111110";
line[4] = "0100010";
line[5] = "0100010";
line[6] = "0000000";
return (line);
break;
case "B" :
var line = new Array;
line[0] = "0000000";
line[1] = "011110";
line[2] = "0100010";
line[3] = "0111100";
line[4] = "0100010";
line[5] = "0111100";
line[6] = "0000000";
return (line);
break;
case "C" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111110";
line[2] = "0100000";
line[3] = "0100000";
line[4] = "0100000";
line[5] = "0111110";
line[6] = "0000000";
return (line);
break;
case "D" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111100";
line[2] = "0100010";
line[3] = "0100010";
line[4] = "0100010";
line[5] = "0111100";
line[6] = "0000000";
return (line);
break;
case "E" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111110";
line[2] = "0100000";
line[3] = "0111110";
line[4] = "0100000";
line[5] = "0111110";
line[6] = "0000000";
return (line);
break;
case "F" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111110";
line[2] = "0100000";
line[3] = "0111100";
line[4] = "0100000";
line[5] = "0100000";
line[6] = "0000000";
return (line);
break;
case "G" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111110";
line[2] = "0100000";
line[3] = "01001100";
line[4] = "0100010";
line[5] = "0111110";
line[6] = "0000000";
return (line);
break;
case "H" :
var line = new Array;
line[0] = "0000000";
line[1] = "0100010";
line[2] = "0100010";
line[3] = "0111110";
line[4] = "0100010";
line[5] = "0100010";
line[6] = "0000000";
return (line);
break;
case "I" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111110";
line[2] = "0001000";
line[3] = "0001000";
line[4] = "0001000";
line[5] = "0111110";
line[6] = "0000000";
return (line);
break;
case "J" :
var line = new Array;
line[0] = "0000000";
line[1] = "0000010";
line[2] = "0000010";
line[3] = "0000010";
line[4] = "0100010";
line[5] = "0011100";
line[6] = "0000000";
return (line);
break;
case "K" :
var line = new Array;
line[0] = "0000000";
line[1] = "0100100";
line[2] = "0101000";
line[3] = "0110000";
line[4] = "0101000";
line[5] = "0100100";
line[6] = "0000000";
return (line);
break;
case "L" :
var line = new Array;
line[0] = "0000000";
line[1] = "0100000";
line[2] = "0100000";
line[3] = "0100000";
line[4] = "0100000";
line[5] = "0111110";
line[6] = "0000000";
return (line);
break;
case "M" :
var line = new Array;
line[0] = "0000000";
line[1] = "0110110";
line[2] = "0101010";
line[3] = "0100010";
line[4] = "0100010";
line[5] = "0100010";
line[6] = "0000000";
return (line);
break;
case "N" :
var line = new Array;
line[0] = "0000000";
line[1] = "0100010";
line[2] = "0110010";
line[3] = "0101010";
line[4] = "0100110";
line[5] = "0100010";
line[6] = "0000000";
return (line);
break;
case "O" :
var line = new Array;
line[0] = "0000000";
line[1] = "0011100";
line[2] = "0100010";
line[3] = "0100010";
line[4] = "0100010";
line[5] = "0011100";
line[6] = "0000000";
return (line);
break;
case "P" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111100";
line[2] = "0100100";
line[3] = "0111100";
line[4] = "0100000";
line[5] = "0100000";
line[6] = "0000000";
return (line);
break;
case "Q" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111000";
line[2] = "0101000";
line[3] = "0111010";
line[4] = "0001100";
line[5] = "0001000";
line[6] = "0000000";
return (line);
break;
case "R" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111100";
line[2] = "0100100";
line[3] = "0111100";
line[4] = "0100100";
line[5] = "0100010";
line[6] = "0000000";
return (line);
break;
case "S" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111110";
line[2] = "0100000";
line[3] = "0111110";
line[4] = "0000010";
line[5] = "0111110";
line[6] = "0000000";
return (line);
break;
case "T" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111110";
line[2] = "0001000";
line[3] = "0001000";
line[4] = "0001000";
line[5] = "0001000";
line[6] = "0000000";
return (line);
break;
case "U" :
var line = new Array;
line[0] = "0000000";
line[1] = "0100010";
line[2] = "0100010";
line[3] = "0100010";
line[4] = "0100010";
line[5] = "0111110";
line[6] = "0000000";
return (line);
break;
case "V" :
var line = new Array;
line[0] = "0000000";
line[1] = "0000000";
line[2] = "0100010";
line[3] = "001010";
line[4] = "0001000";
line[5] = "0000000";
line[6] = "0000000";
return (line);
break;
case "W" :
var line = new Array;
line[0] = "0000000";
line[1] = "0100010";
line[2] = "0100010";
line[3] = "0101010";
line[4] = "0101010";
line[5] = "0110110";
line[6] = "0000000";
return (line);
break;
case "X" :
var line = new Array;
line[0] = "0000000";
line[1] = "0100010";
line[2] = "0010100";
line[3] = "0001000";
line[4] = "0010100";
line[5] = "0100010";
line[6] = "0000000";
return (line);
break;
case "Y" :
var line = new Array;
line[0] = "0000000";
line[1] = "0100010";
line[2] = "0010100";
line[3] = "0001000";
line[4] = "0001000";
line[5] = "0001000";
line[6] = "0000000";
return (line);
break;
case "Z" :
var line = new Array;
line[0] = "0000000";
line[1] = "0111110";
line[2] = "0000100";
line[3] = "0001000";
line[4] = "0010000";
line[5] = "0111110";
line[6] = "0000000";
return (line);
break;
case "EMPTY" :
var line = new Array;
line[0] = "0000000";
line[1] = "0000000";
line[2] = "0000000";
line[3] = "0000000";
line[4] = "0000000";
line[5] = "0000000";
line[6] = "0000000";
return (line);
break;
default :
return "Invalid Letter";
break;
}
}
function mt_rand(min, max){
return Math.floor(Math.random()*(max-min+1)) + min;
}
function genNewWord() {
// This function creates a random new word
// The minimum wordlength is 5 and the maximum wordlength is 10
// And an new word can only be generated if there if the matrix isn't running
if (alreadyRunning == "false") {
var romanletters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var romanletter = new Array;
word = "";
var romanwordlength = mt_rand(6, 10);
for (i = 0; i < romanwordlength; i++) {
romanletter[i] = romanletters.substr(mt_rand(0, 26), 1);
word += romanletter[i];
}
}
}
function displayWord() {
document.getElementById("captcha-textinput").focus();
if (alreadyRunning == "false") {
alreadyRunning = "true";
var wordLength = strlen(word);
wordLetters = new Array;
for (i = 0; i < wordLength; i++) {
wordLetters[i] = word.substr(i,1);
}
var displayLength = 0;
if (wordLength > 0) {
setTimeout('displayLetter(wordLetters[0])', 0);
displayLength = 2000;
}
if (wordLength > 1) {
setTimeout('displayLetter(wordLetters[1])', 2000);
displayLength = 4000;
}
if (wordLength > 2) {
setTimeout('displayLetter(wordLetters[2])', 4000);
displayLength = 6000;
}
if (wordLength > 3) {
setTimeout('displayLetter(wordLetters[3])', 6000);
displayLength = 8000;
}
if (wordLength > 4) {
setTimeout('displayLetter(wordLetters[4])', 8000);
displayLength = 1000;
}
if (wordLength > 5) {
setTimeout('displayLetter(wordLetters[5])', 10000);
displayLength = 12000;
}
if (wordLength > 6) {
setTimeout('displayLetter(wordLetters[6])', 12000);
displayLength = 14000;
}
if (wordLength > 7) {
setTimeout('displayLetter(wordLetters[7])', 14000);
displayLength = 16000;
}
if (wordLength > 8) {
setTimeout('displayLetter(wordLetters[8])', 16000);
displayLength = 18000;
}
if (wordLength > 9) {
setTimeout('displayLetter(wordLetters[9])', 18000);
displayLength = 20000;
}
if (wordLength > 10) {
setTimeout('displayLetter(wordLetters[10])', 20000);
displayLength = 22000;
}
// Switching the ability to be able to run it again or to select a new word
setTimeout('switchRunningFalse()', displayLength);
}
}
function switchRunningFalse() {
alreadyRunning = "false";
}
function displayRow(pos0,pos1,pos2,pos3,pos4,pos5,pos6) {
// This function displays the bits in every line (7 total)
// They are numbered from 0-6
// Position 0 (column 0)
var column0 = document.getElementById('line0').innerHTML;
column0 = column0.substr(0,24);
column0 = pos0 + "\n" + column0;
column0 = column0.replace("0", repChar);
document.getElementById('line0').innerHTML = column0;
// Position 1 (column 1)
var column1 = document.getElementById('line1').innerHTML;
column1 = column1.substr(0,24);
column1 = pos1 + "\n" + column1;
column1 = column1.replace("0", repChar);
document.getElementById('line1').innerHTML = column1;
// Position 2 (column 2)
var column2 = document.getElementById('line2').innerHTML;
column2 = column2.substr(0,24);
column2 = pos2 + "\n" + column2;
column2 = column2.replace("0", repChar);
document.getElementById('line2').innerHTML = column2;
// Position 3 (column 3)
var column3 = document.getElementById('line3').innerHTML;
column3 = column3.substr(0,24);
column3 = pos3 + "\n" + column3;
column3 = column3.replace("0", repChar);
document.getElementById('line3').innerHTML = column3;
// Position 4 (column 4)
var column4 = document.getElementById('line4').innerHTML;
column4 = column4.substr(0,24);
column4 = pos4 + "\n" + column4;
column4 = column4.replace("0", repChar);
document.getElementById('line4').innerHTML = column4;
// Position 5 (column 5)
var column5 = document.getElementById('line5').innerHTML;
column5 = column5.substr(0,24);
column5 = pos5 + "\n" + column5;
column5 = column5.replace("0", repChar);
document.getElementById('line5').innerHTML = column5;
// Position 6 (column 6)
var column6 = document.getElementById('line6').innerHTML;
column6 = column6.substr(0,21);
column6 = pos6 + "\n" + column6;
column6 = column6.replace("0", repChar);
document.getElementById('line6').innerHTML = column6;
}
function displayLetter(letter) {
// This functions converts the strings received from convertLetterToMatrix() and puts every single
// bit into the array. Then it displays them in the captcha-display.
// This returns the seven bit-strings
var thelines = convertLetterToMatrix(letter);
// Declaring array
lineArray = new Array;
// For each line (total 7) and each character (also total 7), will be stored in the lineArray
for (var i = 0; i < 7; i++) {
lineArray[i] = new Array;
lineArray[i][0] = thelines[i].substr(0,1);
lineArray[i][1] = thelines[i].substr(1,1);
lineArray[i][2] = thelines[i].substr(2,1);
lineArray[i][3] = thelines[i].substr(3,1);
lineArray[i][4] = thelines[i].substr(4,1);
lineArray[i][5] = thelines[i].substr(5,1);
lineArray[i][6] = thelines[i].substr(6,1);
}
// Showing them all in the captcha-display
setTimeout('displayRow(lineArray[6][0],lineArray[6][1],lineArray[6][2],lineArray[6][3],lineArray[6][4],lineArray[6][5],lineArray[6][6])', 50); // First row (place 0)
setTimeout('displayRow(lineArray[5][0],lineArray[5][1],lineArray[5][2],lineArray[5][3],lineArray[5][4],lineArray[5][5],lineArray[5][6])', 100); // Second row (place 1)
setTimeout('displayRow(lineArray[4][0],lineArray[4][1],lineArray[4][2],lineArray[4][3],lineArray[4][4],lineArray[4][5],lineArray[4][6])', 150); // Third row (place 2)
setTimeout('displayRow(lineArray[3][0],lineArray[3][1],lineArray[3][2],lineArray[3][3],lineArray[3][4],lineArray[3][5],lineArray[3][6])', 200); // Fourth row (place 3)
setTimeout('displayRow(lineArray[2][0],lineArray[2][1],lineArray[2][2],lineArray[2][3],lineArray[2][4],lineArray[2][5],lineArray[2][6])', 250); // Fifth row (place 4)
setTimeout('displayRow(lineArray[1][0],lineArray[1][1],lineArray[1][2],lineArray[1][3],lineArray[1][4],lineArray[1][5],lineArray[1][6])', 300); // Sixth row (place 5)
setTimeout('displayRow(lineArray[0][0],lineArray[0][1],lineArray[0][2],lineArray[0][3],lineArray[0][4],lineArray[0][5],lineArray[0][6])', 350); // Seventh row (place 6)
}
function checkAnswer(answer) {
// Making sure that all the letters are uppercase
answer = answer.toUpperCase();
if (answer == word) {
// If the answer is correct:
document.getElementById('captcha-valid').value = "TRUE";
} else {
// If the answer is wrong:
document.getElementById('captcha-valid').value = "FALSE";
}
}
function strlen (string) {
// Get string length
//
// version: 909.322
// discuss at: http://phpjs.org/functions/strlen
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: Sakimori
// + input by: Kirk Strobeck
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// + revised by: Brett Zamir (http://brett-zamir.me)
// % note 1: May look like overkill, but in order to be truly faithful to handling all Unicode
// % note 1: characters and to this function in PHP which does not count the number of bytes
// % note 1: but counts the number of characters, something like this is really necessary.
// * example 1: strlen('Kevin van Zonneveld');
// * returns 1: 19
// * example 2: strlen('A\ud87e\udc04Z');
// * returns 2: 3
var str = string+'';
var i = 0, chr = '', lgth = 0;
var getWholeChar = function (str, i) {
var code = str.charCodeAt(i);
var next = '', prev = '';
if (0xD800 <= code && code <= 0xDBFF) { // High surrogate (could change last hex to 0xDB7F to treat high private surrogates as single characters)
if (str.length <= (i+1)) {
throw 'High surrogate without following low surrogate';
}
next = str.charCodeAt(i+1);
if (0xDC00 > next || next > 0xDFFF) {
throw 'High surrogate without following low surrogate';
}
return str.charAt(i)+str.charAt(i+1);
} else if (0xDC00 <= code && code <= 0xDFFF) { // Low surrogate
if (i === 0) {
throw 'Low surrogate without preceding high surrogate';
}
prev = str.charCodeAt(i-1);
if (0xD800 > prev || prev > 0xDBFF) { //(could change last hex to 0xDB7F to treat high private surrogates as single characters)
throw 'Low surrogate without preceding high surrogate';
}
return false; // We can pass over low surrogates now as the second component in a pair which we have already processed
}
return str.charAt(i);
};
for (i=0, lgth=0; i < str.length; i++) {
if ((chr = getWholeChar(str, i)) === false) {
continue;
} // Adapt this line at the top of any loop, passing in the whole string and the current iteration and returning a variable to represent the individual character; purpose is to treat the first part of a surrogate pair as the whole character and then ignore the second part
lgth++;
}
return lgth;
}
function newCaptcha() {
document.getElementById('captcha-textinput').focus();
if (alreadyRunning == "false") {
initCaptcha();
document.getElementById('captcha-textinput').value = "";
}
}
// Start the captcha
// initCaptcha();
</script>
~G