Преглед изворни кода

Add accessible format, add support for multiple CAPTCHAS on a page (use class instead of id for container)

Skylar Ittner пре 1 година
родитељ
комит
a62d1b85e0
6 измењених фајлова са 131 додато и 61 уклоњено
  1. 25
    6
      api.php
  2. 24
    0
      captcheck.css
  3. 1
    1
      captcheck.dist.js
  4. 74
    47
      captcheck.js
  5. 5
    5
      readme.md
  6. 2
    2
      test.html

+ 25
- 6
api.php Прегледај датотеку

@@ -35,9 +35,15 @@ switch ($VARS['action']) {
35 35
             $scrambled_insert[] = ["sid" => $sid, "aid" => $scrambled['real'][$i], "acode" => $scrambled['fake'][$i]];
36 36
         }
37 37
         $database->insert("scrambled_answers", $scrambled_insert);
38
+        $questions = ["Please click on the [].", "Click the [].", "Find the []."];
39
+        $accessible_questions = ["Please type [] here.", "Enter [] into the box.", "Type []."];
40
+        shuffle($questions);
41
+        shuffle($accessible_questions);
38 42
         $resp = [
39 43
             "session" => $skey,
40
-            "question" => $correct_answer['aname'],
44
+            "id_prefix" => substr(hash("md5", mt_rand()), 3, 5),
45
+            "question_i" => str_replace("[]", $correct_answer['aname'], $questions[0]),
46
+            "question_a" => str_replace("[]", $correct_answer['aname'], $accessible_questions[0]),
41 47
             "answers" => $scrambled["fake"]
42 48
         ];
43 49
         exit(json_encode($resp));
@@ -88,15 +94,28 @@ switch ($VARS['action']) {
88 94
             echo json_encode(["session" => $VARS['session_id'], "result" => false, "msg" => "Session key already used."]);
89 95
             exit();
90 96
         }
91
-        if (!$database->has("scrambled_answers", ["AND" => ["sid" => $sid, "acode" => $VARS['answer_id']]])) {
97
+
98
+        $image = false;
99
+        if ($database->has("scrambled_answers", ["AND" => ["sid" => $sid, "acode" => $VARS['answer_id']]])) {
100
+            // Image maybe correct
101
+            $image = true;
102
+        } else if ($database->has("sessions", ["[>]answers" => ["aid" => "aid"]], ["AND" => ["sid" => $sid, "aname" => $VARS['answer_id']]])) {
103
+            // Accessible text correct
104
+            $image = false;
105
+        } else {
106
+            // Invalid answer
92 107
             echo json_encode(["session" => $VARS['session_id'], "result" => false, "msg" => "Answer invalid."]);
93 108
             exit();
94 109
         }
95
-        $aid = $database->get('scrambled_answers', 'aid', ["AND" => ["sid" => $sid, "acode" => $VARS['answer_id']]]);
96
-        if ($database->has('sessions', ["AND" => ["sid" => $sid, "aid" => $aid]])) {
97
-            echo json_encode(["session" => $VARS['session_id'], "result" => true]);
110
+        if ($image) {
111
+            $aid = $database->get('scrambled_answers', 'aid', ["AND" => ["sid" => $sid, "acode" => $VARS['answer_id']]]);
112
+            if ($database->has('sessions', ["AND" => ["sid" => $sid, "aid" => $aid]])) {
113
+                echo json_encode(["session" => $VARS['session_id'], "result" => true]);
114
+            } else {
115
+                echo json_encode(["session" => $VARS['session_id'], "result" => false, "msg" => "Answer incorrect."]);
116
+            }
98 117
         } else {
99
-            echo json_encode(["session" => $VARS['session_id'], "result" => false, "msg" => "Answer incorrect."]);
118
+            echo json_encode(["session" => $VARS['session_id'], "result" => true]);
100 119
         }
101 120
         $database->update("sessions", ['expired' => 1], ["sid" => $sid]);
102 121
         exit();

+ 24
- 0
captcheck.css Прегледај датотеку

@@ -42,4 +42,28 @@ Don't use this file in your site; captcheck.js contains it.
42 42
 
43 43
 .captcheck_error_message {
44 44
     color: red;
45
+}
46
+
47
+.captcheck_question_image {
48
+    display: initial;
49
+}
50
+
51
+.captcheck_question_access {
52
+    display: none;
53
+}
54
+
55
+.captcheck_alt_question_button {
56
+    float: right;
57
+    font-size: 80%;
58
+    cursor: pointer;
59
+    color: inherit;
60
+    text-decoration: inherit;
61
+}
62
+
63
+.captcheck_answer_images {
64
+    display: initial;
65
+}
66
+
67
+.captcheck_answer_access {
68
+    display: none;
45 69
 }

+ 1
- 1
captcheck.dist.js Прегледај датотеку

@@ -1 +1 @@
1
-function chooseAnswer(e){var a=document.getElementById("captcheck_answer_"+e);a.checked=!0}window.onload=function(){var e="https://captcheck.netsyms.com/api.php",a=function(e,a){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){4==this.readyState&&a(this.status,this.responseText)},n.send()};a(e+"?action=new",function(a,n){var c=document.createElement("style");c.innerHTML=".captcheck_box,.captcheck_label_message,.captcheck_label_message b{color:#000;font-family:Ubuntu,Arial,sans-serif}.captcheck_box{border:1px solid #e0e0e0;border-radius:3px;display:inline-block;padding:3px;margin:5px 2px 5px 1px;background-color:#f5f5f5}.captcheck_answer_label>input{visibility:hidden;position:absolute}.captcheck_answer_label>input+img{cursor:pointer;border:2px solid transparent;border-radius:3px;min-width:32px;width:18%;max-width:64px}.captcheck_answer_label>input:checked+img{cursor:pointer;border:2px solid #424242;border-radius:3px}.captcheck_error_message{color:red}",document.body.appendChild(c);var t=document.getElementById("captcheck_container"),s=document.createElement("div");if(s.setAttribute("class","captcheck_box"),t.appendChild(s),200==a){for(var r=JSON.parse(n),i="",o=0,p=r.answers.length;p>o;o++){var d=e+"?action=img&s="+r.session+"&c="+r.answers[o];i+="<span class='captcheck_answer_label' onclick='chooseAnswer(\""+r.answers[o]+"\")'><input id='captcheck_answer_"+r.answers[o]+"' type='radio' name='captcheck_selected_answer' value='"+r.answers[o]+"' /><img src='"+d+"' /></span>"}var l=document.createElement("div");l.innerHTML=i;var h=document.createElement("div");h.setAttribute("class","captcheck_label_message"),h.innerHTML="Click on the <b>"+r.question+"</b>:",s.appendChild(h),s.appendChild(l);var u=document.createElement("span");u.innerHTML="<input type='hidden' name='captcheck_session_code' value='"+r.session+"' />",s.appendChild(u)}else s.innerHTML="<span class='captcheck_error_message'>There was a problem loading the CAPTCHA.</span>"})};
1
+function chooseAnswer(e,t){var a=document.getElementById("captcheck_"+e+"_answer_"+t);return a.checked=!0,!1}function switchMode(e){var t=document.getElementById("captcheck_"+e+"_alt_question_button"),a=document.getElementById("captcheck_"+e+"_question_image"),c=document.getElementById("captcheck_"+e+"_question_access"),n=document.getElementById("captcheck_"+e+"_answer_images"),s=document.getElementById("captcheck_"+e+"_answer_access");"&gt; Text mode"==t.innerHTML?(t.innerHTML="&gt; Image mode",a.style.display="none",c.style.display="initial",n.style.display="none",s.style.display="initial",s.innerHTML="<input type='text' name='captcheck_selected_answer' aria-label='Type your answer here.' autocomplete='off' autofill='off'/>"):(t.innerHTML="&gt; Text mode",a.style.display="initial",c.style.display="none",n.style.display="initial",s.style.display="none",s.innerHTML="")}window.onload=function(){var e="https://captcheck.netsyms.com/api.php",t=document.createElement("style");t.innerHTML=".captcheck_box,.captcheck_label_message,.captcheck_label_message b{color:#000;font-family:Ubuntu,Arial,sans-serif}.captcheck_box{border:1px solid #e0e0e0;border-radius:3px;display:inline-block;padding:3px;margin:5px 2px 5px 1px;background-color:#f5f5f5}.captcheck_answer_label>input{visibility:hidden;position:absolute}.captcheck_answer_label>input+img{cursor:pointer;border:2px solid transparent;border-radius:3px;min-width:32px;width:18%;max-width:64px}.captcheck_answer_label>input:checked+img{cursor:pointer;border:2px solid #424242;border-radius:3px}.captcheck_error_message{color:red}.captcheck_question_image{display:initial}.captcheck_question_access{display:none}.captcheck_alt_question_button{float:right;font-size:80%;cursor:pointer;color:inherit;text-decoration:inherit}.captcheck_answer_images{display:initial}.captcheck_answer_access{display:none}",document.body.appendChild(t),Array.prototype.forEach.call(document.getElementsByClassName("captcheck_container"),function(t){var a=new XMLHttpRequest;a.open("GET",e+"?action=new",!0),a.onreadystatechange=function(){if(4==this.readyState){var a=this.status,c=this.responseText,n=document.createElement("div");if(n.setAttribute("class","captcheck_box"),t.appendChild(n),200==a){for(var s=JSON.parse(c),i=s.id_prefix,r="<div class='captcheck_answer_images' id='captcheck_"+i+"_answer_images'>",o=0,l=s.answers.length;l>o;o++){var p=e+"?action=img&s="+s.session+"&c="+s.answers[o];r+="<a class='captcheck_answer_label' href='' tabindex='0' onClick='chooseAnswer(\""+i+'","'+s.answers[o]+"\"); return false;' onEnter='chooseAnswer(\""+i+'","'+s.answers[o]+"\"); return false;'><input id='captcheck_"+i+"_answer_"+s.answers[o]+"' type='radio' name='captcheck_selected_answer' value='"+s.answers[o]+"' /><img src='"+p+"' /></a>"}r+="</div>";var d=document.createElement("div");d.innerHTML=r+"<div class='captcheck_answer_access' id='captcheck_"+i+"_answer_access'></div>";var _=document.createElement("div");_.setAttribute("class","captcheck_label_message"),_.setAttribute("id","captcheck_"+i+"_label_message"),_.innerHTML="<span class='captcheck_question_image' id='captcheck_"+i+"_question_image'>"+s.question_i+"</span><span class='captcheck_question_access' id='captcheck_"+i+"_question_access'>"+s.question_a+"</span><a href='' class='captcheck_alt_question_button' onClick='switchMode(\""+i+"\"); return false;' onEnter='switchMode(\""+i+"\"); return false;' id='captcheck_"+i+"_alt_question_button' aria-label='Switch between image and text question' tabindex='0'>&gt; Text mode</a>",n.appendChild(_),n.appendChild(d);var h=document.createElement("span");h.innerHTML="<input type='hidden' name='captcheck_session_code' value='"+s.session+"' />",n.appendChild(h)}else n.innerHTML="<span class='captcheck_error_message'>There was a problem loading the CAPTCHA.</span>"}},a.send()})};

+ 74
- 47
captcheck.js Прегледај датотеку

@@ -1,60 +1,87 @@
1 1
 window.onload = function () {
2 2
     var api_url = "https://captcheck.netsyms.com/api.php";
3
-    var getJSON = function (url, callback) {
3
+
4
+    /* Add custom styles */
5
+    var styles = document.createElement('style');
6
+    /* Remove newlines/comments from captcheck.css and put it here */
7
+    styles.innerHTML = ".captcheck_box,.captcheck_label_message,.captcheck_label_message b{color:#000;font-family:Ubuntu,Arial,sans-serif}.captcheck_box{border:1px solid #e0e0e0;border-radius:3px;display:inline-block;padding:3px;margin:5px 2px 5px 1px;background-color:#f5f5f5}.captcheck_answer_label>input{visibility:hidden;position:absolute}.captcheck_answer_label>input+img{cursor:pointer;border:2px solid transparent;border-radius:3px;min-width:32px;width:18%;max-width:64px}.captcheck_answer_label>input:checked+img{cursor:pointer;border:2px solid #424242;border-radius:3px}.captcheck_error_message{color:red}.captcheck_question_image{display:initial}.captcheck_question_access{display:none}.captcheck_alt_question_button{float:right;font-size:80%;cursor:pointer;color:inherit;text-decoration:inherit}.captcheck_answer_images{display:initial}.captcheck_answer_access{display:none}";
8
+    document.body.appendChild(styles);
9
+
10
+    /* Loop over all the CAPTCHA containers on the page, setting up a different CAPTCHA in each */
11
+    Array.prototype.forEach.call(document.getElementsByClassName("captcheck_container"), function (container) {
4 12
         var xhr = new XMLHttpRequest();
5
-        xhr.open('GET', url, true);
13
+        xhr.open('GET', api_url + "?action=new", true);
6 14
         xhr.onreadystatechange = function () {
7 15
             if (this.readyState == 4) {
8
-                callback(this.status, this.responseText);
9
-            }
10
-        };
11
-        xhr.send();
12
-    };
13
-    getJSON(api_url + "?action=new", function (status, json) {
14
-        /* Add custom styles */
15
-        var styles = document.createElement('style');
16
-        /* Remove newlines/comments from captcheck.css and put it here */
17
-        styles.innerHTML = ".captcheck_box {font-family: Ubuntu, Arial, sans-serif; color: black; border: 1px solid #e0e0e0; border-radius: 3px; display: inline-block; padding: 3px; margin: 5px 2px 5px 1px; background-color: #f5f5f5;} .captcheck_label_message, .captcheck_label_message b {color: black; font-family: Ubuntu, Arial, sans-serif;} .captcheck_answer_label > input {visibility: hidden; position: absolute;} .captcheck_answer_label > input + img {cursor: pointer; border: 2px solid transparent; border-radius: 3px; min-width: 32px; width: 18%; max-width: 64px;} .captcheck_answer_label > input:checked + img {cursor: pointer; border: 2px solid #424242; border-radius: 3px;} .captcheck_error_message {color: red;}";
18
-        document.body.appendChild(styles);
16
+                var status = this.status;
17
+                var json = this.responseText;
18
+                /* Create captcha div */
19
+                var captcha = document.createElement("div");
20
+                captcha.setAttribute("class", "captcheck_box");
21
+                container.appendChild(captcha);
19 22
 
20
-        /* Get captcha container div */
21
-        var container = document.getElementById("captcheck_container");
22
-        /* Create captcha div */
23
-        var captcha = document.createElement("div");
24
-        captcha.setAttribute("class", "captcheck_box");
25
-        container.appendChild(captcha);
26
-        
27
-        if (status == 200) {
28
-            var data = JSON.parse(json);
29
-            /* Create answer buttons */
30
-            var answers = "";
31
-            for (var i = 0, len = data.answers.length; i < len; i++) {
32
-                var src = api_url + "?action=img&s=" + data.session + "&c=" + data.answers[i];
33
-                answers += "<span class='captcheck_answer_label' onclick='chooseAnswer(\"" + data.answers[i] + "\")'><input id='captcheck_answer_" + data.answers[i] + "' type='radio' name='captcheck_selected_answer' value='" + data.answers[i] + "' /><img src='" + src + "' /></span>";
34
-            }
35
-            var answer_div = document.createElement("div");
36
-            answer_div.innerHTML = answers;
37
-            /* Create question */
38
-            var question_div = document.createElement("div");
39
-            question_div.setAttribute("class", "captcheck_label_message");
40
-            question_div.innerHTML = "Click on the <b>" + data.question + "</b>:";
23
+                if (status == 200) {
24
+                    var data = JSON.parse(json);
25
+                    // ID prefix to use for this instance
26
+                    var idp = data.id_prefix;
27
+                    /* Create answer buttons */
28
+                    var answers = "<div class='captcheck_answer_images' id='captcheck_" + idp + "_answer_images'>";
29
+                    for (var i = 0, len = data.answers.length; i < len; i++) {
30
+                        var src = api_url + "?action=img&s=" + data.session + "&c=" + data.answers[i];
31
+                        answers += "<a class='captcheck_answer_label' href='' tabindex='0' onClick='chooseAnswer(\"" + idp + "\",\"" + data.answers[i] + "\"); return false;' onEnter='chooseAnswer(\"" + idp + "\",\"" + data.answers[i] + "\"); return false;'><input id='captcheck_" + idp + "_answer_" + data.answers[i] + "' type='radio' name='captcheck_selected_answer' value='" + data.answers[i] + "' /><img src='" + src + "' /></a>";
32
+                    }
33
+                    answers += "</div>";
34
+                    var answer_div = document.createElement("div");
35
+                    answer_div.innerHTML = answers + "<div class='captcheck_answer_access' id='captcheck_" + idp + "_answer_access'></div>";
36
+                    /* Create question */
37
+                    var question_div = document.createElement("div");
38
+                    question_div.setAttribute("class", "captcheck_label_message");
39
+                    question_div.setAttribute("id", "captcheck_" + idp + "_label_message")
40
+                    question_div.innerHTML = "<span class='captcheck_question_image' id='captcheck_" + idp + "_question_image'>" + data.question_i + "</span><span class='captcheck_question_access' id='captcheck_" + idp + "_question_access'>" + data.question_a + "</span><a href='' class='captcheck_alt_question_button' onClick='switchMode(\"" + idp + "\"); return false;' onEnter='switchMode(\"" + idp + "\"); return false;' id='captcheck_" + idp + "_alt_question_button' aria-label='Switch between image and text question' tabindex='0'>&gt; Text mode</a>";
41 41
 
42
-            /* Add question and answers */
43
-            captcha.appendChild(question_div);
44
-            captcha.appendChild(answer_div);
42
+                    /* Add question and answers */
43
+                    captcha.appendChild(question_div);
44
+                    captcha.appendChild(answer_div);
45 45
 
46
-            /* Add hidden session ID element */
47
-            var skey_input = document.createElement("span");
48
-            skey_input.innerHTML = "<input type='hidden' name='captcheck_session_code' value='" + data.session + "' />";
49
-            captcha.appendChild(skey_input);
50
-        } else {
51
-            /* Add error message */
52
-            captcha.innerHTML = "<span class='captcheck_error_message'>There was a problem loading the CAPTCHA.</span>";
53
-        }
46
+                    /* Add hidden session ID element */
47
+                    var skey_input = document.createElement("span");
48
+                    skey_input.innerHTML = "<input type='hidden' name='captcheck_session_code' value='" + data.session + "' />";
49
+                    captcha.appendChild(skey_input);
50
+                } else {
51
+                    /* Add error message */
52
+                    captcha.innerHTML = "<span class='captcheck_error_message'>There was a problem loading the CAPTCHA.</span>";
53
+                }
54
+            }
55
+        };
56
+        xhr.send();
54 57
     });
55 58
 }
56 59
 
57
-function chooseAnswer(ans) {
58
-    var box = document.getElementById("captcheck_answer_" + ans);
60
+function chooseAnswer(idp, ans) {
61
+    var box = document.getElementById("captcheck_" + idp + "_answer_" + ans);
59 62
     box.checked = true;
63
+    return false;
64
+}
65
+
66
+function switchMode(idp) {
67
+    var switch_label = document.getElementById("captcheck_" + idp + "_alt_question_button");
68
+    var img_q = document.getElementById("captcheck_" + idp + "_question_image");
69
+    var acc_q = document.getElementById("captcheck_" + idp + "_question_access");
70
+    var img_a = document.getElementById("captcheck_" + idp + "_answer_images");
71
+    var acc_a = document.getElementById("captcheck_" + idp + "_answer_access");
72
+    if (switch_label.innerHTML == "&gt; Text mode") {
73
+        switch_label.innerHTML = "&gt; Image mode";
74
+        img_q.style.display = "none";
75
+        acc_q.style.display = "initial";
76
+        img_a.style.display = "none";
77
+        acc_a.style.display = "initial";
78
+        acc_a.innerHTML = "<input type='text' name='captcheck_selected_answer' aria-label='Type your answer here.' autocomplete='off' autofill='off'/>";
79
+    } else {
80
+        switch_label.innerHTML = "&gt; Text mode";
81
+        img_q.style.display = "initial";
82
+        acc_q.style.display = "none";
83
+        img_a.style.display = "initial";
84
+        acc_a.style.display = "none";
85
+        acc_a.innerHTML = "";
86
+    }
60 87
 }

+ 5
- 5
readme.md Прегледај датотеку

@@ -8,8 +8,8 @@ IE9+.  Uses icons from Font-Awesome.
8 8
 How to use
9 9
 ----------
10 10
 
11
-In your form, put an empty div with the ID "captcheck_container". 
12
-Add `captcheck.js` into your page.
11
+In your form, put an empty div with the class "captcheck_container". 
12
+Add `captcheck.js` (or `captcheck.dist.js`) into your page.
13 13
 
14 14
     <!DOCTYPE html>
15 15
     <html>
@@ -22,7 +22,7 @@ Add `captcheck.js` into your page.
22 22
         <body>
23 23
             <form action="submit.php">
24 24
                 <input type="text" name="form_field" placeholder="Some random form field" />
25
-                <div id="captcheck_container">
25
+                <div class="captcheck_container">
26 26
                 </div>
27 27
                 <button type="submit">Submit Form</button>
28 28
             </form>
@@ -65,7 +65,7 @@ Execution Flow
65 65
     JS -> API:      Request session ID, question, and answers (with scrambled random codes)
66 66
     API -> JS:      Sends info, saves session ID, correct answer, and scrambled answer codes in DB
67 67
     JS -> API:      Requests answer images by sending scrambled value and session ID
68
-    JS -> FORM:     Adds hidden field with value=session ID, displays question and images
68
+    JS -> FORM:     Adds hidden field with value=session ID, displays question and images (or text box)
69 69
     [USER SUBMITS FORM]
70
-    SITE -> API:    Sends session ID and scrambled answer
70
+    SITE -> API:    Sends session ID and answer
71 71
     API -> SITE:    Responds with true/false to indicate if the answer is valid, marks session as expired to prevent CAPTCHA reuse

+ 2
- 2
test.html Прегледај датотеку

@@ -7,9 +7,9 @@
7 7
         <script src="captcheck.js"></script>
8 8
     </head>
9 9
     <body>
10
-        <form action="submit.php" method="POST">
10
+        <form action="test.php" method="POST">
11 11
             <input type="text" name="form_field" placeholder="Some random form field" />
12
-            <div id="captcheck_container">
12
+            <div class="captcheck_container">
13 13
             </div>
14 14
             <button type="submit">Submit Form</button>
15 15
         </form>

Loading…
Откажи
Сачувај