Skip to content

Instantly share code, notes, and snippets.

@makotom
Created June 3, 2012 05:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save makotom/2862070 to your computer and use it in GitHub Desktop.
Save makotom/2862070 to your computer and use it in GitHub Desktop.
JavaScript Stroop Test Extended
<!doctype html>
<meta charset="UTF-8">
<title>JSST-EX</title>
<!--
JavaScript Stroop Test Extended
Copyright © 2012 Makoto Mizukami.
http://makotom.org/
The main idea of this work is derived from JSStroop by Makoto Mizukami, although it was fully rewritten.
http://tomato.myftp.org/blog/?tag=JSStroop
This programme is licensed under the GNU General Public License, either version 3 or any later version.
This is free software, and THERE IS NO WARRANTY FOR THE PROGRAMME.
See the GNU General Public License (http://www.gnu.org/licenses/) for more details.
-->
<script>
window.addEventListener("DOMContentLoaded", function(){
"use strict";
var main = function(){
var charVars = ["あ", "し", "ぬ", "け", "よ"], colourVars = ["#f00", "#0f0", "#00f"], sizeVars = ["240px", "60px"], familyVars = ["sans-serif", "serif"],
maxCount = 10, minTimeout = 1500, maxTimeout = 2500,
UI = {subject : document.getElementById("subject"), keybind : [document.getElementById("key-pos"), document.getElementById("key-neg")], comp : document.getElementById("comp"), button : document.getElementById("button"), disp : document.getElementById("disp"), results : document.getElementById("results")},
state = {count : 0, time : {base : 0, now : 0}, secret : 0, allowInput : false, timeoutId : 0},
tests = [],
results = [],
keypressFunc = null,
flexIntRand = function(from, to){
return Math.floor(Math.random() * (to - from) + from);
},
clearChild = function(p){
var c;
while((c = p.firstChild) !== null){
p.removeChild(c);
}
return;
},
refreshSubject = function(ent){
var box = [UI.subject.firstChild, UI.subject.lastChild], i = 0;
UI.subject.style.visibility = "hidden";
for(i = 0; i < box.length; i += 1){
clearChild(box[i]);
box[i].appendChild(document.createTextNode(ent[i].char));
box[i].style.color = ent[i].colour;
box[i].style.fontSize = ent[i].size;
box[i].style.fontFamily = ent[i].family;
}
state.timeoutId = setTimeout(function(){
UI.subject.style.visibility = "inherit";
state.time.base = new Date().getTime();
state.allowInput = true;
}, flexIntRand(maxTimeout, minTimeout));
return;
},
refreshButtonLabel = function(string){
clearChild(UI.button);
UI.button.appendChild(document.createTextNode(string));
return;
},
refreshDisp = function(){
var i = 0, v = 0, timeSum = 0, minTime = 0, maxTime = 0;
clearChild(UI.disp);
UI.disp.appendChild(document.createTextNode(state.count.toString() + "/" + maxCount.toString() + " done."));
for(i = 0; i < results.length; i += 1){
if(results[i].isFeedbackCorrect === true && results[i].isPositive === true){
v += 1;
timeSum += results[i].feedbackTime;
minTime = results[i].feedbackTime < minTime || minTime === 0 ? results[i].feedbackTime : minTime;
maxTime = results[i].feedbackTime > maxTime ? results[i].feedbackTime : maxTime;
}
}
if(v > 2){
timeSum -= minTime + maxTime;
v -= 2;
}
if(v > 0){
UI.disp.appendChild(document.createTextNode(" Trimmed mean time for True Positive answers = " + Math.round(timeSum / v) + " ms"));
}
return;
},
appendResult = function(){
var res = results[results.length - 1];
UI.results.value += (UI.results.value !== "" ? "\n" : "") +
(res.isFeedbackCorrect === true ? "T" : "F") + "," +
(res.isPositive === true ? "P" : "N") + "," +
res.feedbackTime.toString();
return;
},
prepareTest = function(){
var i = 0, j = 0, k = 0, n = 0, m = 0, h = 0, extCharVars = [];
switch(parseInt(UI.comp.value, 10)){
case 0:
case 1:
for(i = 0; i < charVars.length; i += 1){
for(j = 0; j < colourVars.length; j += 1){
for(k = 0; k < colourVars.length; k += 1){
tests[tests.length] = {
ent : [
{char : charVars[i], colour : colourVars[j], size : sizeVars[0], family : familyVars[0]},
{char : charVars[i], colour : colourVars[k], size : sizeVars[0], family : familyVars[0]}
],
secret : (parseInt(UI.comp.value, 10) === 0 || j !== k ? 0 : 1)
};
}
}
}
break;
case 2:
for(i = 0; i < charVars.length; i += 1){
for(j = 0; j < colourVars.length; j += 1){
for(k = 0; k < sizeVars.length; k += 1){
for(n = 0; n < sizeVars.length; n += 1){
tests[tests.length] = {
ent : [
{char : charVars[i], colour : colourVars[j], size : sizeVars[k], family : familyVars[0]},
{char : charVars[i], colour : colourVars[j], size : sizeVars[n], family : familyVars[0]}
],
secret : (k !== n && i !== j ? 0 : 1)
};
}
}
}
}
break;
case 3:
for(i = 0; i < charVars.length; i += 1){
extCharVars[extCharVars.length] = String.fromCharCode(charVars[i].charCodeAt(0) + 96);
}
for(i = 0; i < charVars.length; i += 1){
for(j = 0; j < charVars.length; j += 1){
for(k = 0; k < colourVars.length; k += 1){
for(n = 0; n < colourVars.length; n += 1){
tests[tests.length] = {
ent : [
{char : charVars[i], colour : colourVars[k], size : sizeVars[0], family : familyVars[0]},
{char : extCharVars[j], colour : colourVars[n], size : sizeVars[0], family : familyVars[0]}
],
secret : (k !== n && i !== j ? 0 : 1)
};
tests[tests.length] = {
ent : [
{char : extCharVars[i], colour : colourVars[k], size : sizeVars[0], family : familyVars[0]},
{char : charVars[j], colour : colourVars[n], size : sizeVars[0], family : familyVars[0]}
],
secret : (k !== n && i !== j ? 0 : 1)
};
}
}
}
}
break;
case 4:
for(i = 0; i < charVars.length; i += 1){
extCharVars[extCharVars.length] = charVars[i];
extCharVars[extCharVars.length] = String.fromCharCode(charVars[i].charCodeAt(0) + 96);
}
for(i = 0; i < extCharVars.length; i += 1){
for(j = 0; j < extCharVars.length; j += 1){
for(k = 0; k < colourVars.length; k += 1){
for(n = 0; n < colourVars.length; n += 1){
tests[tests.length] = {
ent : [
{char : extCharVars[i], colour : colourVars[k], size : sizeVars[0], family : familyVars[0]},
{char : extCharVars[j], colour : colourVars[n], size : sizeVars[0], family : familyVars[0]}
],
secret : (k !== n && Math.floor(i / 2) !== Math.floor(j / 2) && (i % 2) !== (j % 2) ? 0 : 1)
};
}
}
}
}
break;
case 5:
for(i = 0; i < charVars.length; i += 1){
extCharVars[extCharVars.length] = charVars[i];
extCharVars[extCharVars.length] = String.fromCharCode(charVars[i].charCodeAt(0) + 96);
}
for(i = 0; i < extCharVars.length; i += 1){
for(j = 0; j < extCharVars.length; j += 1){
for(k = 0; k < colourVars.length; k += 1){
for(n = 0; n < colourVars.length; n += 1){
for(m = 0; m < familyVars.length; m += 1){
for(h = 0; h < familyVars.length; h += 1){
tests[tests.length] = {
ent : [
{char : extCharVars[i], colour : colourVars[k], size : sizeVars[0], family : familyVars[m]},
{char : extCharVars[j], colour : colourVars[n], size : sizeVars[0], family : familyVars[h]}
],
secret : (k !== n && Math.floor(i / 2) !== Math.floor(j / 2) && (i % 2) !== (j % 2) && m !== h ? 0 : 1)
};
}
}
}
}
}
}
break;
}
return;
},
setNextTest = function(){
var test = tests[flexIntRand(0, tests.length)], targetSecret = parseInt(UI.comp.value, 10) !== 0 ? flexIntRand(0, 2) : 0;
while(test.secret !== targetSecret){
test = tests[flexIntRand(0, tests.length)];
}
state.secret = test.secret;
return test.ent;
},
finish = function(){
var i;
window.removeEventListener("keypress", keypressFunc, false);
UI.subject.style.visibility = "hidden";
clearTimeout(state.timeoutId);
for(i = 0; i < UI.keybind.length; i += 1){
UI.keybind[i].disabled = false;
}
UI.comp.disabled = false;
UI.button.removeEventListener("click", finish, false);
refreshButtonLabel("Start");
UI.button.addEventListener("click", main, false);
return;
},
userFeedback = function(e){
state.time.now = new Date().getTime();
var inputCode = (!isNaN(parseInt(e.charCode, 10))) ? e.charCode : e.keyCode;
if(state.allowInput === false || (inputCode !== UI.keybind[0].value.charCodeAt(0) && inputCode !== UI.keybind[1].value.charCodeAt(0))){
return;
}
state.allowInput = false;
results[results.length] = {
isFeedbackCorrect : (inputCode === UI.keybind[state.secret].value.charCodeAt(0)),
isPositive : (state.secret === 0),
feedbackTime : state.time.now - state.time.base
};
if(results[results.length - 1].isFeedbackCorrect === true && results[results.length - 1].isPositive === true){
state.count += 1;
}
refreshDisp();
appendResult();
return state.count < maxCount ? refreshSubject(setNextTest()) : finish();
},
boot = function(){
var i = 0;
UI.button.removeEventListener("click", main, false);
refreshButtonLabel("Stop");
UI.button.addEventListener("click", finish, false);
for(i = 0; i < UI.keybind.length; i += 1){
if(UI.keybind[i].value.length !== 1){
UI.keybind[i].value = UI.keybind[i].defaultValue;
}
UI.keybind[i].disabled = true;
}
window.addEventListener("keypress", keypressFunc = userFeedback, false);
UI.results.value = "";
UI.comp.disabled = true;
prepareTest();
refreshDisp();
refreshSubject(setNextTest());
return;
};
boot();
return;
}, defStyle = document.createElement("style");
defStyle.appendChild(document.createTextNode("#subject{ height: 300px; visibility: hidden; }\n" +
"#subject > span{ display: inline-block; width: 30%; height: 300px; vertical-align: middle; line-height: 300px; text-align: center; }\n" +
" #disp{ font-weight: bold; }"
));
document.documentElement.appendChild(defStyle);
document.getElementById("button").addEventListener("click", main, false);
return;
}, false);
</script>
<div id="subject"><span></span><span></span></div>
<p><b>Rule</b>: <em>Except</em> for "Always Positive" mode, hit <strong>Positive</strong> only if all the conditions differ; otherwise hit <strong>Negative</strong>. For "Always Positive" mode, always hit <strong>Positive</strong>.</p>
<p>
Keybind: Positive = <input type="text" id="key-pos" value="[" size="1" maxlength="1">; Negative = <input type="text" id="key-neg" value="]" size="1" maxlength="1"><br>
Comparison: <select id="comp"><option value="0">None (Always Positive)<option value="1">Colour<option value="2">Size<option value="3">Colour + Sound<option value="4">Colour + Sound + Component<option value="5">Colour + Sound + Component + Font family</select><br>
<button id="button">Start</button>
</p>
<p id="disp"><noscript>This programme does not work on this environment. Please check that ECMAScript is enabled.</noscript></p>
<textarea id="results" rows="5"></textarea>
<footer>
<p>Copyright &copy; 2012 Makoto Mizukami &lt;<a href="http://makotom.org/">http://makotom.org</a>>. <a href="http://www.gnu.org/licenses/gpl.html">Licence</a></p>
</footer>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment