Graphix 68 ---

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

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.