jsPsych/packages/plugin-survey-html-form/src/index.ts

211 lines
6.8 KiB
TypeScript

import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";
import { version } from "../package.json";
const info = <const>{
name: "survey-html-form",
version: version,
parameters: {
/** HTML formatted string containing all the input elements to display. Every element has to have its own distinctive name attribute. The <form> tag must not be included and is generated by the plugin. */
html: {
type: ParameterType.HTML_STRING,
default: null,
},
/** HTML formatted string to display at the top of the page above all the questions. */
preamble: {
type: ParameterType.HTML_STRING,
default: null,
},
/** The text that appears on the button to finish the trial. */
button_label: {
type: ParameterType.STRING,
default: "Continue",
},
/** The HTML element ID of a form field to autofocus on. */
autofocus: {
type: ParameterType.STRING,
default: "",
},
/** Retrieve the data as an array e.g. [{name: "INPUT_NAME", value: "INPUT_VALUE"}, ...] instead of an object e.g. {INPUT_NAME: INPUT_VALUE, ...}. */
dataAsArray: {
type: ParameterType.BOOL,
default: false,
},
/** Setting this to true will enable browser auto-complete or auto-fill for the form. */
autocomplete: {
type: ParameterType.BOOL,
default: false,
},
},
data: {
/** An object containing the response for each input. The object will have a separate key (variable) for the response to each input, with each variable being named after its corresponding input element. Each response is a string containing whatever the participant answered for this particular input. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */
response: {
type: ParameterType.COMPLEX,
nested: {
identifier: {
type: ParameterType.STRING,
},
response: {
type:
ParameterType.STRING |
ParameterType.INT |
ParameterType.FLOAT |
ParameterType.BOOL |
ParameterType.OBJECT,
},
},
},
/** The response time in milliseconds for the participant to make a response. */
rt: {
type: ParameterType.INT,
},
},
};
type Info = typeof info;
/**
*
* The survey-html-form plugin displays a set of `<inputs>` from a HTML string. The type of input can be freely
* chosen, for a list of possible input types see the [MDN page on inputs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
* The participant provides answers to the input fields.
* @author Jan Simson
* @see {@link https://www.jspsych.org/latest/plugins/survey-html-form/ survey-html-form plugin documentation on jspsych.org}
*/
class SurveyHtmlFormPlugin implements JsPsychPlugin<Info> {
static info = info;
constructor(private jsPsych: JsPsych) {}
trial(display_element: HTMLElement, trial: TrialType<Info>) {
var html = "";
// show preamble text
if (trial.preamble !== null) {
html +=
'<div id="jspsych-survey-html-form-preamble" class="jspsych-survey-html-form-preamble">' +
trial.preamble +
"</div>";
}
// start form
if (trial.autocomplete) {
html += '<form id="jspsych-survey-html-form">';
} else {
html += '<form id="jspsych-survey-html-form" autocomplete="off">';
}
// add form HTML / input elements
html += trial.html;
// add submit button
html +=
'<input type="submit" id="jspsych-survey-html-form-next" class="jspsych-btn jspsych-survey-html-form" value="' +
trial.button_label +
'"></input>';
html += "</form>";
display_element.innerHTML = html;
if (trial.autofocus !== "") {
var focus_elements = display_element.querySelectorAll<HTMLInputElement>(
"#" + trial.autofocus
);
if (focus_elements.length === 0) {
console.warn("No element found with id: " + trial.autofocus);
} else if (focus_elements.length > 1) {
console.warn('The id "' + trial.autofocus + '" is not unique so autofocus will not work.');
} else {
focus_elements[0].focus();
}
}
display_element
.querySelector("#jspsych-survey-html-form")
.addEventListener("submit", (event) => {
// don't submit form
event.preventDefault();
// measure response time
var endTime = performance.now();
var response_time = Math.round(endTime - startTime);
var this_form = display_element.querySelector("#jspsych-survey-html-form");
var question_data = serializeArray(this_form);
if (!trial.dataAsArray) {
question_data = objectifyForm(question_data);
}
// save data
var trialdata = {
rt: response_time,
response: question_data,
};
// next trial
this.jsPsych.finishTrial(trialdata);
});
var startTime = performance.now();
/**
* Serialize all form data into an array
* @copyright (c) 2018 Chris Ferdinandi, MIT License, https://gomakethings.com
* @param {Node} form The form to serialize
* @return {String} The serialized form data
*/
function serializeArray(form) {
// Setup our serialized data
var serialized = [];
// Loop through each field in the form
for (var i = 0; i < form.elements.length; i++) {
var field = form.elements[i];
// Don't serialize fields without a name, submits, buttons, file and reset inputs, and disabled fields
if (
!field.name ||
field.disabled ||
field.type === "file" ||
field.type === "reset" ||
field.type === "submit" ||
field.type === "button"
)
continue;
// If a multi-select, get all selections
if (field.type === "select-multiple") {
for (var n = 0; n < field.options.length; n++) {
if (!field.options[n].selected) continue;
serialized.push({
name: field.name,
value: field.options[n].value,
});
}
}
// Convert field data to a query string
else if ((field.type !== "checkbox" && field.type !== "radio") || field.checked) {
serialized.push({
name: field.name,
value: field.value,
});
}
}
return serialized;
}
// from https://stackoverflow.com/questions/1184624/convert-form-data-to-javascript-object-with-jquery
function objectifyForm(formArray) {
//serialize data function
var returnArray = <any>{};
for (var i = 0; i < formArray.length; i++) {
returnArray[formArray[i]["name"]] = formArray[i]["value"];
}
return returnArray;
}
}
}
export default SurveyHtmlFormPlugin;