remove editable option

Closes #72
This commit is contained in:
Josh de Leeuw 2014-09-29 14:02:04 -04:00
parent 940c5580f8
commit 9b617435e8

View File

@ -16,406 +16,385 @@
*/ */
(function($) { (function($) {
jsPsych.palmer = (function() { jsPsych.palmer = (function() {
var plugin = {}; var plugin = {};
plugin.create = function(params) { plugin.create = function(params) {
params = jsPsych.pluginAPI.enforceArray(params, ['data']); var trials = [];
for (var i = 0; i < params.configurations.length; i++) {
var trials = []; var trial = {
for (var i = 0; i < params.configurations.length; i++) { configurations: params.configurations[i],
var trial = { show_feedback: (typeof params.show_feedback === 'undefined') ? false : params.show_feedback,
configurations: params.configurations[i], grid_spacing: params.grid_spacing || 75,
editable: (typeof params.editable === 'undefined') ? false : params.editable, square_size: params.square_size || 3,
show_feedback: (typeof params.show_feedback === 'undefined') ? false : params.show_feedback, circle_radius: params.circle_radius || 20,
grid_spacing: params.grid_spacing || 75, timing_item: params.timing_item || 1000,
square_size: params.square_size || 3, timing_feedback: params.timing_feedback || 1000,
circle_radius: params.circle_radius || 20, prompt: (typeof params.prompt === 'undefined') ? "" : params.prompt
timing_item: params.timing_item || 1000, };
timing_feedback: params.timing_feedback || 1000,
prompt: (typeof params.prompt === 'undefined') ? "" : params.prompt trials.push(trial);
}; }
return trials;
trials.push(trial); };
}
return trials; plugin.trial = function(display_element, trial) {
};
// if any trial variables are functions
plugin.trial = function(display_element, trial) { // this evaluates the function and replaces
// it with the output of the function
// if any trial variables are functions trial = jsPsych.pluginAPI.normalizeTrialVariables(trial);
// this evaluates the function and replaces
// it with the output of the function // variables to keep track of user interaction
trial = jsPsych.pluginAPI.normalizeTrialVariables(trial); var start_circle = -1;
var end_circle = -1;
// variables to keep track of user interaction var line_started = false;
var start_circle = -1;
var end_circle = -1; var size = trial.grid_spacing * (trial.square_size + 1);
var line_started = false;
display_element.append($("<div id='jspsych-palmer-raphaelCanvas'>", {
var size = trial.grid_spacing * (trial.square_size + 1); css: {
width: size + "px",
display_element.append($("<div id='jspsych-palmer-raphaelCanvas'>", { height: size + "px"
css: { }
width: size + "px", }));
height: size + "px"
} var paper = Raphael("jspsych-palmer-raphaelCanvas", size, size);
}));
// create the circles at the vertices.
var paper = Raphael("jspsych-palmer-raphaelCanvas", size, size); var circles = [];
var node_idx = 0;
// create the circles at the vertices. for (var i = 1; i <= trial.square_size; i++) {
var circles = []; for (var j = 1; j <= trial.square_size; j++) {
var node_idx = 0; var circle = paper.circle(trial.grid_spacing * j, trial.grid_spacing * i, trial.circle_radius);
for (var i = 1; i <= trial.square_size; i++) { circle.attr("fill", "#000").attr("stroke-width", "0").attr("stroke", "#000").data("node", node_idx);
for (var j = 1; j <= trial.square_size; j++) {
var circle = paper.circle(trial.grid_spacing * j, trial.grid_spacing * i, trial.circle_radius);
circle.attr("fill", "#000").attr("stroke-width", "0").attr("stroke", "#000").data("node", node_idx); circle.hover(
if (trial.editable) { function() {
circle.hover( this.attr("stroke-width", "2");
//this.attr("stroke", "#000");
function() { },
this.attr("stroke-width", "2");
//this.attr("stroke", "#000"); function() {
}, this.attr("stroke-width", "0");
//this.attr("stroke", "#fff")
function() { }).click(
this.attr("stroke-width", "0");
//this.attr("stroke", "#fff") function() {
}).click( if (!line_started) {
line_started = true;
function() { start_circle = this.data("node");
if (!line_started) { this.attr("fill", "#777").attr("stroke", "#777");
line_started = true; } else {
start_circle = this.data("node"); end_circle = this.data("node");
this.attr("fill", "#777").attr("stroke", "#777"); draw_connection(start_circle, end_circle);
} }
else { });
end_circle = this.data("node");
draw_connection(start_circle, end_circle); node_idx++;
} circles.push(circle);
}); }
} }
node_idx++;
circles.push(circle); function draw_connection(start_circle, end_circle) {
} var the_line = getLineIndex(start_circle, end_circle);
} if (the_line > -1) {
toggle_line(the_line);
function draw_connection(start_circle, end_circle) { }
var the_line = getLineIndex(start_circle, end_circle); // reset highlighting on circles
if (the_line > -1) { for (var i = 0; i < circles.length; i++) {
toggle_line(the_line); circles[i].attr("fill", "#000").attr("stroke", "#000");
} }
// reset highlighting on circles // cleanup the variables
for (var i = 0; i < circles.length; i++) { line_started = false;
circles[i].attr("fill", "#000").attr("stroke", "#000"); start_circle = -1;
} end_circle = -1;
// cleanup the variables }
line_started = false;
start_circle = -1; // create all possible lines that connect circles
end_circle = -1; var horizontal_lines = [];
} var vertical_lines = [];
var backslash_lines = [];
// create all possible lines that connect circles var forwardslash_lines = [];
var horizontal_lines = [];
var vertical_lines = []; for (var i = 0; i < trial.square_size; i++) {
var backslash_lines = []; for (var j = 0; j < trial.square_size; j++) {
var forwardslash_lines = []; var current_item = (i * trial.square_size) + j;
// add horizontal connections
for (var i = 0; i < trial.square_size; i++) { if (j < (trial.square_size - 1)) {
for (var j = 0; j < trial.square_size; j++) { horizontal_lines.push([current_item, current_item + 1]);
var current_item = (i * trial.square_size) + j; }
// add horizontal connections // add vertical connections
if (j < (trial.square_size - 1)) { if (i < (trial.square_size - 1)) {
horizontal_lines.push([current_item, current_item + 1]); vertical_lines.push([current_item, current_item + trial.square_size]);
} }
// add vertical connections // add diagonal backslash connections
if (i < (trial.square_size - 1)) { if (i < (trial.square_size - 1) && j < (trial.square_size - 1)) {
vertical_lines.push([current_item, current_item + trial.square_size]); backslash_lines.push([current_item, current_item + trial.square_size + 1]);
} }
// add diagonal backslash connections // add diagonal forwardslash connections
if (i < (trial.square_size - 1) && j < (trial.square_size - 1)) { if (i < (trial.square_size - 1) && j > 0) {
backslash_lines.push([current_item, current_item + trial.square_size + 1]); forwardslash_lines.push([current_item, current_item + trial.square_size - 1]);
} }
// add diagonal forwardslash connections }
if (i < (trial.square_size - 1) && j > 0) { }
forwardslash_lines.push([current_item, current_item + trial.square_size - 1]);
} var lines = horizontal_lines.concat(vertical_lines).concat(backslash_lines).concat(forwardslash_lines);
}
} // actually draw the lines
var lineIsVisible = [];
var lines = horizontal_lines.concat(vertical_lines).concat(backslash_lines).concat(forwardslash_lines); var lineElements = [];
// actually draw the lines for (var i = 0; i < lines.length; i++) {
var lineIsVisible = []; var line = paper.path("M" + circles[lines[i][0]].attr("cx") + " " + circles[lines[i][0]].attr("cy") + "L" + circles[lines[i][1]].attr("cx") + " " + circles[lines[i][1]].attr("cy")).attr("stroke-width", "8").attr("stroke", "#000");
var lineElements = []; line.hide();
lineElements.push(line);
for (var i = 0; i < lines.length; i++) { lineIsVisible.push(0);
var line = paper.path("M" + circles[lines[i][0]].attr("cx") + " " + circles[lines[i][0]].attr("cy") + "L" + circles[lines[i][1]].attr("cx") + " " + circles[lines[i][1]].attr("cy")).attr("stroke-width", "8").attr("stroke", "#000"); }
line.hide();
lineElements.push(line); // define some helper functions to toggle lines on and off
lineIsVisible.push(0);
} // this function gets the index of a line based on the two circles it connects
// define some helper functions to toggle lines on and off function getLineIndex(start_circle, end_circle) {
var the_line = -1;
// this function gets the index of a line based on the two circles it connects for (var i = 0; i < lines.length; i++) {
function getLineIndex(start_circle, end_circle) { if ((start_circle == lines[i][0] && end_circle == lines[i][1]) || (start_circle == lines[i][1] && end_circle == lines[i][0])) {
var the_line = -1; the_line = i;
for (var i = 0; i < lines.length; i++) { break;
if ((start_circle == lines[i][0] && end_circle == lines[i][1]) || (start_circle == lines[i][1] && end_circle == lines[i][0])) { }
the_line = i; }
break; return the_line;
} }
}
return the_line; // this function turns a line on/off based on the index (the_line)
}
function toggle_line(the_line) {
// this function turns a line on/off based on the index (the_line) if (the_line > -1) {
function toggle_line(the_line) { if (lineIsVisible[the_line] === 0) {
if (the_line > -1) { lineElements[the_line].show();
if (lineIsVisible[the_line] === 0) { lineElements[the_line].toBack();
lineElements[the_line].show(); lineIsVisible[the_line] = 1;
lineElements[the_line].toBack(); } else {
lineIsVisible[the_line] = 1; lineElements[the_line].hide();
} lineElements[the_line].toBack();
else { lineIsVisible[the_line] = 0;
lineElements[the_line].hide(); }
lineElements[the_line].toBack(); }
lineIsVisible[the_line] = 0; }
}
} // this function takes an array of length = num lines, and displays the line whereever there
} // is a 1 in the array.
// this function takes an array of length = num lines, and displays the line whereever there function showConfiguration(configuration) {
// is a 1 in the array. for (var i = 0; i < configuration.length; i++) {
function showConfiguration(configuration) { if (configuration[i] != lineIsVisible[i]) {
for (var i = 0; i < configuration.length; i++) { toggle_line(i);
if (configuration[i] != lineIsVisible[i]) { }
toggle_line(i); }
} }
}
} // highlight a line
// highlight a line function highlightLine(line) {
function highlightLine(line) { lineElements[line].attr("stroke", "#f00");
lineElements[line].attr("stroke", "#f00"); }
}
// start recording the time
// start recording the time var startTime = (new Date()).getTime();
var startTime = (new Date()).getTime();
display_element.append($('<button id="jspsych-palmer-submitButton" type="button">Submit Answer</button>'));
// what kind of trial are we doing? $('#jspsych-palmer-submitButton').click(function() {
// if trial.editable is true, then we will let the user interact with the stimulus to create save_data();
// something, e.g. for a reconstruction probe. });
// need a way for the user to submit when they are done in that case...
if (trial.editable) { if (trial.prompt !== "") {
display_element.append($('<button id="jspsych-palmer-submitButton" type="button">Submit Answer</button>')); display_element.append($('<div id="jspsych-palmer-prompt">'));
$('#jspsych-palmer-submitButton').click(function() { $("#jspsych-palmer-prompt").html(trial.prompt);
save_data(); }
});
} function arrayDifferences(arr1, arr2) {
var n_diff = 0;
// if trial.editable is false, then we are just showing a pre-determined configuration. for (var i = 0; i < arr1.length; i++) {
// for now, the only option will be to display for a fixed amount of time. if (arr1[i] != arr2[i]) {
// future ideas: allow for key response, to enable things like n-back, same/different, etc.. n_diff++;
if (!trial.editable) { }
showConfiguration(trial.configurations); }
return n_diff;
setTimeout(function() { }
save_data();
}, trial.timing_item); // save data
}
function save_data() {
if (trial.prompt !== "") {
display_element.append($('<div id="jspsych-palmer-prompt">')); // measure RT
$("#jspsych-palmer-prompt").html(trial.prompt); var endTime = (new Date()).getTime();
} var response_time = endTime - startTime;
function arrayDifferences(arr1, arr2) { // check if configuration is correct
var n_diff = 0; // this is meaningless for trials where the user can't edit
for (var i = 0; i < arr1.length; i++) { var n_diff = arrayDifferences(trial.configurations, lineIsVisible);
if (arr1[i] != arr2[i]) { var correct = (n_diff === 0);
n_diff++;
} jsPsych.data.write($.extend({}, {
} "configuration": JSON.stringify(lineIsVisible),
return n_diff; "target_configuration": JSON.stringify(trial.configurations),
} "rt": response_time,
"correct": correct,
// save data "num_wrong": n_diff,
function save_data() { }, trial.data));
// measure RT if (trial.show_feedback) {
var endTime = (new Date()).getTime(); // hide the button
var response_time = endTime - startTime; $('#jspsych-palmer-submitButton').hide();
$('#jspsych-palmer-prompt').hide();
// check if configuration is correct
// this is meaningless for trials where the user can't edit showConfiguration(trial.configurations);
var n_diff = arrayDifferences(trial.configurations, lineIsVisible); var feedback = "";
var correct = (n_diff === 0); if (correct) {
feedback = "Correct!";
jsPsych.data.write($.extend({}, { } else {
"configuration": JSON.stringify(lineIsVisible), if (n_diff > 1) {
"target_configuration": JSON.stringify(trial.configurations), feedback = "You missed " + n_diff + " lines. The correct symbol is shown above.";
"rt": response_time, } else {
"correct": correct, feedback = "You missed 1 line. The correct symbol is shown above.";
"num_wrong": n_diff, }
}, trial.data)); }
display_element.append($.parseHTML("<p id='jspsych-palmer-feedback'>" + feedback + "</p>"));
if (trial.editable && trial.show_feedback) {
// hide the button setTimeout(function() {
$('#jspsych-palmer-submitButton').hide(); next_trial();
$('#jspsych-palmer-prompt').hide(); }, trial.timing_feedback);
showConfiguration(trial.configurations); } else {
var feedback = ""; next_trial();
if (correct) { }
feedback = "Correct!"; }
}
else { function next_trial() {
if (n_diff > 1) {
feedback = "You missed " + n_diff + " lines. The correct symbol is shown above."; display_element.html('');
}
else { // next trial
feedback = "You missed 1 line. The correct symbol is shown above."; if (trial.timing_post_trial > 0) {
} setTimeout(function() {
} jsPsych.finishTrial();
display_element.append($.parseHTML("<p id='jspsych-palmer-feedback'>" + feedback + "</p>")); }, trial.timing_post_trial);
} else {
setTimeout(function() { jsPsych.finishTrial();
next_trial(); }
}, trial.timing_feedback);
}
}
else {
next_trial(); };
}
} // method for drawing palmer stimuli.
// returns the string description of svg element containing the stimulus
function next_trial() { // requires raphaeljs library -> www.raphaeljs.com
display_element.html(''); plugin.generate_stimulus = function(square_size, grid_spacing, circle_radius, configuration) {
// next trial // create a div to hold the generated svg object
if (trial.timing_post_trial > 0) { var stim_div = $('body').append('<div id="jspsych-palmer-temp-stim"></div>');
setTimeout(function() {
jsPsych.finishTrial(); var size = grid_spacing * (square_size + 1);
}, trial.timing_post_trial);
} // create the svg raphael object
else { var paper = Raphael("jspsych-palmer-temp-stim", size, size);
jsPsych.finishTrial();
} // create the circles at the vertices.
var circles = [];
} var node_idx = 0;
for (var i = 1; i <= square_size; i++) {
for (var j = 1; j <= square_size; j++) {
}; var circle = paper.circle(grid_spacing * j, grid_spacing * i, circle_radius);
circle.attr("fill", "#000").attr("stroke-width", "0").attr("stroke", "#000").data("node", node_idx);
// method for drawing palmer stimuli. node_idx++;
// returns the string description of svg element containing the stimulus circles.push(circle);
// requires raphaeljs library -> www.raphaeljs.com }
}
plugin.generate_stimulus = function(square_size, grid_spacing, circle_radius, configuration) {
// create all possible lines that connect circles
// create a div to hold the generated svg object var horizontal_lines = [];
var stim_div = $('body').append('<div id="jspsych-palmer-temp-stim"></div>'); var vertical_lines = [];
var backslash_lines = [];
var size = grid_spacing * (square_size + 1); var forwardslash_lines = [];
// create the svg raphael object for (var i = 0; i < square_size; i++) {
var paper = Raphael("jspsych-palmer-temp-stim", size, size); for (var j = 0; j < square_size; j++) {
var current_item = (i * square_size) + j;
// create the circles at the vertices. // add horizontal connections
var circles = []; if (j < (square_size - 1)) {
var node_idx = 0; horizontal_lines.push([current_item, current_item + 1]);
for (var i = 1; i <= square_size; i++) { }
for (var j = 1; j <= square_size; j++) { // add vertical connections
var circle = paper.circle(grid_spacing * j, grid_spacing * i, circle_radius); if (i < (square_size - 1)) {
circle.attr("fill", "#000").attr("stroke-width", "0").attr("stroke", "#000").data("node", node_idx); vertical_lines.push([current_item, current_item + square_size]);
node_idx++; }
circles.push(circle); // add diagonal backslash connections
} if (i < (square_size - 1) && j < (square_size - 1)) {
} backslash_lines.push([current_item, current_item + square_size + 1]);
}
// create all possible lines that connect circles // add diagonal forwardslash connections
var horizontal_lines = []; if (i < (square_size - 1) && j > 0) {
var vertical_lines = []; forwardslash_lines.push([current_item, current_item + square_size - 1]);
var backslash_lines = []; }
var forwardslash_lines = []; }
}
for (var i = 0; i < square_size; i++) {
for (var j = 0; j < square_size; j++) { var lines = horizontal_lines.concat(vertical_lines).concat(backslash_lines).concat(forwardslash_lines);
var current_item = (i * square_size) + j;
// add horizontal connections // actually draw the lines
if (j < (square_size - 1)) { var lineIsVisible = [];
horizontal_lines.push([current_item, current_item + 1]); var lineElements = [];
}
// add vertical connections for (var i = 0; i < lines.length; i++) {
if (i < (square_size - 1)) { var line = paper.path("M" + circles[lines[i][0]].attr("cx") + " " + circles[lines[i][0]].attr("cy") + "L" + circles[lines[i][1]].attr("cx") + " " + circles[lines[i][1]].attr("cy")).attr("stroke-width", "8").attr("stroke", "#000");
vertical_lines.push([current_item, current_item + square_size]); line.hide();
} lineElements.push(line);
// add diagonal backslash connections lineIsVisible.push(0);
if (i < (square_size - 1) && j < (square_size - 1)) { }
backslash_lines.push([current_item, current_item + square_size + 1]);
} // define some helper functions to toggle lines on and off
// add diagonal forwardslash connections
if (i < (square_size - 1) && j > 0) { // this function turns a line on/off based on the index (the_line)
forwardslash_lines.push([current_item, current_item + square_size - 1]);
} function toggle_line(the_line) {
} if (the_line > -1) {
} if (lineIsVisible[the_line] === 0) {
lineElements[the_line].show();
var lines = horizontal_lines.concat(vertical_lines).concat(backslash_lines).concat(forwardslash_lines); lineElements[the_line].toBack();
lineIsVisible[the_line] = 1;
// actually draw the lines } else {
var lineIsVisible = []; lineElements[the_line].hide();
var lineElements = []; lineElements[the_line].toBack();
lineIsVisible[the_line] = 0;
for (var i = 0; i < lines.length; i++) { }
var line = paper.path("M" + circles[lines[i][0]].attr("cx") + " " + circles[lines[i][0]].attr("cy") + "L" + circles[lines[i][1]].attr("cx") + " " + circles[lines[i][1]].attr("cy")).attr("stroke-width", "8").attr("stroke", "#000"); }
line.hide(); }
lineElements.push(line);
lineIsVisible.push(0); // displays the line wherever there
} // is a 1 in the array.
// showConfiguration(configuration)
// define some helper functions to toggle lines on and off for (var i = 0; i < configuration.length; i++) {
if (configuration[i] == 1) {
// this function turns a line on/off based on the index (the_line) toggle_line(i);
function toggle_line(the_line) { }
if (the_line > -1) { }
if (lineIsVisible[the_line] === 0) {
lineElements[the_line].show();
lineElements[the_line].toBack(); var svg = $("#jspsych-palmer-temp-stim").html();
lineIsVisible[the_line] = 1;
} $('#jspsych-palmer-temp-stim').remove();
else {
lineElements[the_line].hide(); return svg;
lineElements[the_line].toBack(); };
lineIsVisible[the_line] = 0;
} return plugin;
} })();
}
// displays the line wherever there
// is a 1 in the array.
// showConfiguration(configuration)
for (var i = 0; i < configuration.length; i++) {
if (configuration[i] == 1) {
toggle_line(i);
}
}
var svg = $("#jspsych-palmer-temp-stim").html();
$('#jspsych-palmer-temp-stim').remove();
return svg;
};
return plugin;
})();
})(jQuery); })(jQuery);