From 92b6c2e78e06fa24bd068fcd0e3ee09792b13122 Mon Sep 17 00:00:00 2001
From: "Peter J. Kohler"
Date: Sat, 29 Aug 2020 14:55:25 -0400
Subject: [PATCH 01/13] updates to free-sort plugin, new functionality
---
plugins/jspsych-free-sort.js | 276 +++++++++++++++++++++++++++--------
1 file changed, 213 insertions(+), 63 deletions(-)
diff --git a/plugins/jspsych-free-sort.js b/plugins/jspsych-free-sort.js
index e498d7e3f2..f3af378484 100644
--- a/plugins/jspsych-free-sort.js
+++ b/plugins/jspsych-free-sort.js
@@ -22,19 +22,25 @@ jsPsych.plugins['free-sort'] = (function() {
pretty_name: 'Stimuli',
default: undefined,
array: true,
- description: 'Images to be displayed.'
+ description: 'items to be displayed.'
},
stim_height: {
type: jsPsych.plugins.parameterType.INT,
pretty_name: 'Stimulus height',
default: 100,
- description: 'Height of images in pixels.'
+ description: 'Height of items in pixels.'
},
stim_width: {
type: jsPsych.plugins.parameterType.INT,
pretty_name: 'Stimulus width',
default: 100,
- description: 'Width of images in pixels'
+ description: 'Width of items in pixels'
+ },
+ scale_factor: {
+ type: jsPsych.plugins.parameterType.FLOAT,
+ pretty_name: 'Stimulus scaling factor',
+ default: 1.5,
+ description: 'How much larger to make the stimulus while moving (1 = no scaling)'
},
sort_area_height: {
type: jsPsych.plugins.parameterType.INT,
@@ -48,6 +54,13 @@ jsPsych.plugins['free-sort'] = (function() {
default: 800,
description: 'The width of the container that subjects can move the stimuli in.'
},
+ sort_area_shape: {
+ type: jsPsych.plugins.parameterType.STRING,
+ pretty_name: 'Sort area shape',
+ options: ['square','ellipse'],
+ default: 'ellipse',
+ description: 'The shape of the sorting area'
+ },
prompt: {
type: jsPsych.plugins.parameterType.STRING,
pretty_name: 'Prompt',
@@ -64,7 +77,7 @@ jsPsych.plugins['free-sort'] = (function() {
button_label: {
type: jsPsych.plugins.parameterType.STRING,
pretty_name: 'Button label',
- default: 'Continue',
+ default: 'continue',
description: 'The text that appears on the button to continue to the next trial.'
}
}
@@ -74,36 +87,91 @@ jsPsych.plugins['free-sort'] = (function() {
var start_time = performance.now();
- var html = "";
- // check if there is a prompt and if it is shown above
- if (trial.prompt !== null && trial.prompt_location == "above") {
- html += trial.prompt;
- }
-
- html += '';
+ 'style="position: relative; width:'+trial.sort_area_width+'px; height:'+trial.sort_area_height+'px; margin: auto; line-height: 0em"';
- // check if prompt exists and if it is shown below
- if (trial.prompt !== null && trial.prompt_location == "below") {
- html += trial.prompt;
+ // variable that has the prompt text, counter and button
+ const html_text = '
' + trial.prompt +
+ '
You still need to place ' + trial.stimuli.length + ' items inside the arena.
' +
+ '
'
+
+ // position prompt above or below
+ if (trial.prompt_location == "below") {
+ html += html_text
+ } else {
+ html = html_text + html
}
display_element.innerHTML = html;
+ // another div for border
+ let border_div = ''
+ } else {
+ border_div += 'webkit-border-radius: 0%; moz-border-radius: 0%; border-radius: 0%">'
+ }
+
+ // add border div code
+ display_element.querySelector("#jspsych-free-sort-arena").innerHTML += border_div
+
// store initial location data
- var init_locations = [];
+ let init_locations = [];
- for (var i = 0; i < trial.stimuli.length; i++) {
- var coords = random_coordinate(trial.sort_area_width - trial.stim_width, trial.sort_area_height - trial.stim_height);
+ // determine number of rows and colums, must be a even number
+ let num_rows = Math.ceil(Math.sqrt(trial.stimuli.length))
+ if ( num_rows % 2 != 0) {
+ num_rows = num_rows + 1
+ }
+ // compute coords for left and right side of arena
+ let r_coords = [];
+ let l_coords = [];
+ for (const x of make_arr(0, trial.sort_area_width - trial.stim_width, num_rows) ) {
+ for (const y of make_arr(0, trial.sort_area_height - trial.stim_height, num_rows) ) {
+ if ( x > ( (trial.sort_area_width - trial.stim_width) * .5 ) ) {
+ //r_coords.push({ x:x, y:y } )
+ r_coords.push({ x:x + (trial.sort_area_width) * .5 , y:y });
+ } else {
+ l_coords.push({ x:x - (trial.sort_area_width) * .5 , y:y });
+ //l_coords.push({ x:x, y:y } )
+ }
+ }
+ }
+
+ // repeat coordinates until you have enough coords (may be obsolete)
+ while ( ( r_coords.length + l_coords.length ) < trial.stimuli.length ) {
+ r_coords = r_coords.concat(r_coords)
+ l_coords = l_coords.concat(l_coords)
+ }
+ // reverse left coords, so that coords closest to arena is used first
+ l_coords = l_coords.reverse()
+
+ // shuffle stimuli, so that starting positions are random
+ trial.stimuli = shuffle(trial.stimuli);
+
+ let inside = []
+ for (let i = 0; i < trial.stimuli.length; i++) {
+ let coords = []
+ if ( (i % 2) == 0 ) {
+ coords = r_coords[Math.floor(i * .5)];
+ } else {
+ coords = l_coords[Math.floor(i * .5)];
+ }
display_element.querySelector("#jspsych-free-sort-arena").innerHTML += ''+
'';
@@ -112,31 +180,74 @@ jsPsych.plugins['free-sort'] = (function() {
"x": coords.x,
"y": coords.y
});
+ inside.push(false);
}
+ // moves within a trial
+ let moves = [];
- display_element.innerHTML += '';
+ // are objects currently inside
+ let cur_in = false
- var maxz = 1;
+ // draggable items
+ const draggables = display_element.querySelectorAll('.jspsych-free-sort-draggable');
- var moves = [];
+ // button (will show when all items are inside) and border (will change color)
+ const border = display_element.querySelector("#jspsych-free-sort-border")
+ const button = display_element.querySelector('#jspsych-free-sort-done-btn')
- var draggables = display_element.querySelectorAll('.jspsych-free-sort-draggable');
-
- for(var i=0;i 1 ) {
+ display_element.querySelector("#jspsych-free-sort-counter").innerHTML = "You still need to place " + (inside.length - inside.filter(Boolean).length) + " items inside the arena."
+ } else {
+ display_element.querySelector("#jspsych-free-sort-counter").innerHTML = "You still need to place " + (inside.length - inside.filter(Boolean).length) + " item inside the arena."
+ }
+ }
}
document.addEventListener('mousemove', mousemoveevent);
var mouseupevent = function(e){
document.removeEventListener('mousemove', mousemoveevent);
+ elem.style.transform = "scale(1, 1)";
+ if (inside.every(Boolean)) {
+ border.style.background = "#a1d99b";
+ border.style.borderColor = "#a1d99b";
+ } else {
+ border.style.background = "none";
+ border.style.borderColor = "#fc9272";
+ }
moves.push({
"src": elem.dataset.src,
"x": elem.offsetLeft,
@@ -148,47 +259,86 @@ jsPsych.plugins['free-sort'] = (function() {
});
}
- display_element.querySelector('#jspsych-free-sort-done-btn').addEventListener('click', function(){
-
- var end_time = performance.now();
- var rt = end_time - start_time;
- // gather data
- // get final position of all objects
- var final_locations = [];
- var matches = display_element.querySelectorAll('.jspsych-free-sort-draggable');
- for(var i=0; i
Date: Sat, 29 Aug 2020 17:08:38 -0400
Subject: [PATCH 02/13] updated free-sort-example
---
examples/jspsych-free-sort.html | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/examples/jspsych-free-sort.html b/examples/jspsych-free-sort.html
index 5b91d33f9b..91569bcc1f 100644
--- a/examples/jspsych-free-sort.html
+++ b/examples/jspsych-free-sort.html
@@ -10,7 +10,11 @@
var trials = {
type: 'free-sort',
- stimuli: ['img/happy_face_1.jpg','img/happy_face_2.jpg','img/happy_face_3.jpg','img/happy_face_4.jpg']
+ stimuli: ['img/happy_face_1.jpg','img/happy_face_2.jpg','img/happy_face_3.jpg','img/happy_face_4.jpg'],
+ stim_height: 150,
+ stim_width: 200,
+ scale_factor: 1.3,
+ sort_area_shape: "ellipse"
};
From c2132ab4d73c3c1160482bf7ed592b517100f274 Mon Sep 17 00:00:00 2001
From: "Peter J. Kohler"
Date: Sat, 29 Aug 2020 17:13:05 -0400
Subject: [PATCH 03/13] updated free-sort docs
---
docs/plugins/jspsych-free-sort.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/plugins/jspsych-free-sort.md b/docs/plugins/jspsych-free-sort.md
index 2064915437..d55ee98845 100644
--- a/docs/plugins/jspsych-free-sort.md
+++ b/docs/plugins/jspsych-free-sort.md
@@ -15,13 +15,14 @@ Parameter | Type | Default Value | Description
stimuli | array | *undefined* | Each element of this array is an image path.
stim_height | numeric | 100 | The height of the images in pixels.
stim_width | numeric | 100 | The width of the images in pixels.
+scale_factor | numeric | 1.5 | How much larger to make the stimulus while moving (1 = no scaling).
sort_area_height | numeric | 800 | The height of the container that subjects can move the stimuli in. Stimuli will be constrained to this area.
sort_area_width | numeric | 800 | The width of the container that subjects can move the stimuli in. Stimuli will be constrained to this area.
+sort_area_shape | string | "ellipse" | The shape of the sorting area, can be "ellipse" or "square".
prompt | string | null | This string can contain HTML markup. The intention is that it can be used to provide a reminder about the action the subject is supposed to take (e.g., which key to press).
prompt_location | string | "above" | Indicates whether to show the prompt `"above"` or `"below"` the sorting area.
button_label | string | 'Continue' | The text that appears on the button to continue to the next trial.
-
## Data Generated
In addition to the [default data collected by all plugins](overview#datacollectedbyplugins), this plugin collects the following data for each trial.
From 5740d0a316d0147890b81d062635157ffefcd6e5 Mon Sep 17 00:00:00 2001
From: "Peter J. Kohler"
Date: Sun, 30 Aug 2020 10:41:19 -0400
Subject: [PATCH 04/13] added my name to contributors
---
contributors.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/contributors.md b/contributors.md
index 9b5ad9b350..5651bdbe58 100644
--- a/contributors.md
+++ b/contributors.md
@@ -5,6 +5,7 @@ The following people have contributed to the development of jsPsych by writing c
* Krisitn Diep - https://github.com/kristiyip
* Becky Gilbert - https://github.com/becky-gilbert
* Jana Klaus - https://github.com/janakl4us
+* Peter Jes Kohler - https://github.com/pjkohler
* Jonas Lambers
* Shane Martin - https://github.com/shamrt
* Adrian Oesch - https://github.com/adrianoesch
From a1ca6a8406e5d72561c07b9ec25eedfdb2e1a49a Mon Sep 17 00:00:00 2001
From: "Peter J. Kohler"
Date: Wed, 18 Nov 2020 19:41:43 -0500
Subject: [PATCH 05/13] fixed above/below
---
examples/jspsych-free-sort.html | 3 ++-
plugins/jspsych-free-sort.js | 41 ++++++++++++++++++---------------
2 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/examples/jspsych-free-sort.html b/examples/jspsych-free-sort.html
index 91569bcc1f..336d6a7f7d 100644
--- a/examples/jspsych-free-sort.html
+++ b/examples/jspsych-free-sort.html
@@ -14,7 +14,8 @@
stim_height: 150,
stim_width: 200,
scale_factor: 1.3,
- sort_area_shape: "ellipse"
+ sort_area_shape: "ellipse",
+ prompt_location: "below"
};
diff --git a/plugins/jspsych-free-sort.js b/plugins/jspsych-free-sort.js
index f3af378484..80ff49d8d9 100644
--- a/plugins/jspsych-free-sort.js
+++ b/plugins/jspsych-free-sort.js
@@ -64,7 +64,7 @@ jsPsych.plugins['free-sort'] = (function() {
prompt: {
type: jsPsych.plugins.parameterType.STRING,
pretty_name: 'Prompt',
- default: null,
+ default: '',
description: 'It can be used to provide a reminder about the action the subject is supposed to take.'
},
prompt_location: {
@@ -87,15 +87,34 @@ jsPsych.plugins['free-sort'] = (function() {
var start_time = performance.now();
- var html =
+ let html =
'
';
+ // another div for border
+ html += ''
+ } else {
+ html += 'webkit-border-radius: 0%; moz-border-radius: 0%; border-radius: 0%">
'
+ }
+
+ if ( trial.prompt ) {
+ trial.prompt = ' '
+ }
+ else {
+ trial.prompt = ''
+ }
+
// variable that has the prompt text, counter and button
const html_text = '
' + trial.prompt +
- '
You still need to place ' + trial.stimuli.length + ' items inside the arena.
' +
+ '
You still need to place ' + trial.stimuli.length + ' items inside the arena.
'+
'
'
@@ -106,23 +125,9 @@ jsPsych.plugins['free-sort'] = (function() {
html = html_text + html
}
+ console.log(html)
display_element.innerHTML = html;
- // another div for border
- let border_div = ''
- } else {
- border_div += 'webkit-border-radius: 0%; moz-border-radius: 0%; border-radius: 0%">'
- }
-
- // add border div code
- display_element.querySelector("#jspsych-free-sort-arena").innerHTML += border_div
-
// store initial location data
let init_locations = [];
From 8f24267aae6a5652c02c554b21b30f3a6e770aa3 Mon Sep 17 00:00:00 2001
From: Becky Gilbert
Date: Thu, 19 Nov 2020 14:40:44 -0800
Subject: [PATCH 06/13] update expected regex to handle plugin modifications
---
tests/plugins/plugin-free-sort.test.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tests/plugins/plugin-free-sort.test.js b/tests/plugins/plugin-free-sort.test.js
index 6f578042a3..a8294cc1aa 100644
--- a/tests/plugins/plugin-free-sort.test.js
+++ b/tests/plugins/plugin-free-sort.test.js
@@ -59,7 +59,7 @@ describe('free-sort plugin', function(){
auto_preload: false
});
- expect(jsPsych.getDisplayElement().innerHTML).toMatch(new RegExp('This is a prompt
'
// position prompt above or below
if (trial.prompt_location == "below") {
@@ -125,7 +165,6 @@ jsPsych.plugins['free-sort'] = (function() {
html = html_text + html
}
- console.log(html)
display_element.innerHTML = html;
// store initial location data
@@ -176,7 +215,7 @@ jsPsych.plugins['free-sort'] = (function() {
'data-src="'+trial.stimuli[i]+'" '+
'class="jspsych-free-sort-draggable" '+
'draggable="false" '+
- 'id="'+i+'" '+
+ 'id="jspsych-free-sort-draggable-'+i+'" '+
'style="position: absolute; cursor: move; width:'+trial.stim_width+'px; height:'+trial.stim_height+'px; top:'+coords.y+'px; left:'+coords.x+'px;">'+
'';
@@ -215,20 +254,25 @@ jsPsych.plugins['free-sort'] = (function() {
elem.style.left = Math.min(trial.sort_area_width*1.5 - trial.stim_width, Math.max(-trial.sort_area_width*.5, (e.clientX - x)))+ 'px';
// modify border while items is being moved
- if (cur_in) {
- border.style.borderColor = "#a1d99b";
- border.style.background = "None";
- } else {
- border.style.borderColor = "#fc9272";
- border.style.background = "None";
+ if (trial.change_border_background_color) {
+ if (cur_in) {
+ border.style.borderColor = trial.border_color_in;
+ border.style.background = "None";
+ } else {
+ border.style.borderColor = trial.border_color_out;
+ border.style.background = "None";
+ }
}
- // replace in overall array, grab idx from item id
- inside.splice(elem.id, true, cur_in)
+ // replace in overall array, grab index from item id
+ var elem_number = elem.id.split("jspsych-free-sort-draggable-")[1];
+ inside.splice(elem_number, true, cur_in)
// modify text and background if all items are inside
if (inside.every(Boolean)) {
- border.style.background = "#a1d99b";
+ if (trial.change_border_background_color) {
+ border.style.background = trial.border_color_in;
+ }
button.style.display = "inline-block";
display_element.querySelector("#jspsych-free-sort-counter").innerHTML = "All items placed. Feel free to reposition any item if necessary. Otherwise, click here to "
} else {
@@ -246,12 +290,14 @@ jsPsych.plugins['free-sort'] = (function() {
var mouseupevent = function(e){
document.removeEventListener('mousemove', mousemoveevent);
elem.style.transform = "scale(1, 1)";
- if (inside.every(Boolean)) {
- border.style.background = "#a1d99b";
- border.style.borderColor = "#a1d99b";
- } else {
- border.style.background = "none";
- border.style.borderColor = "#fc9272";
+ if (trial.change_border_background_color) {
+ if (inside.every(Boolean)) {
+ border.style.background = "#a1d99b";
+ border.style.borderColor = "#a1d99b";
+ } else {
+ border.style.background = "none";
+ border.style.borderColor = "#fc9272";
+ }
}
moves.push({
"src": elem.dataset.src,
From 8cc2454cfd9e5ba47e98e2f3b858be76c0a17470 Mon Sep 17 00:00:00 2001
From: Becky Gilbert
Date: Thu, 19 Nov 2020 18:22:08 -0800
Subject: [PATCH 10/13] add counter text and stim_starts_inside parameters
---
docs/plugins/jspsych-free-sort.md | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/docs/plugins/jspsych-free-sort.md b/docs/plugins/jspsych-free-sort.md
index fc4be64e7c..992176242b 100644
--- a/docs/plugins/jspsych-free-sort.md
+++ b/docs/plugins/jspsych-free-sort.md
@@ -1,6 +1,6 @@
# jspsych-free-sort plugin
-The free-sort plugin displays one or more images on the screen that the participant can interact with by clicking and dragging. All images must be moved into the sorting area before the participant can click a button to end the trial. All of the moves that the participant performs are recorded, as well as the final positions of all images. This plugin could be useful when asking participants to position images based on similarity to one another, or to recall image spatial locations.
+The free-sort plugin displays one or more images on the screen that the participant can interact with by clicking and dragging. When the trial starts, the images can be positioned outside or inside the sort area. All images must be moved into the sorting area before the participant can click a button to end the trial. All of the moves that the participant performs are recorded, as well as the final positions of all images. This plugin could be useful when asking participants to position images based on similarity to one another, or to recall image spatial locations.
## Parameters
@@ -22,6 +22,9 @@ change_border_background_color | boolean | true | If `true`, the sort area borde
border_color_in | string | '#a1d99b' | If `change_border_background_color` is `true`, the sort area border will change to this color when an item is being moved into the sort area, and the background will change to this color when all of the items have been moved into the sort area.
border_color_out | string | '#fc9272' | If `change_border_background_color` is `true`, this will be the color of the sort area border when there are one or more items that still need to be moved into the sort area.
border_width | numeric | null | The width in pixels of the border around the sort area. If `null`, the border width will be 3% of the `sort_area_height`.
+counter_text_unfinished | string | You still need to place %n% item%s% inside the sort area. | Text to display when there are one or more items that still need to be placed in the sort area. If "%n%" is included in the string, it will be replaced with the number of items that still need to be moved inside. If "%s%" is included in the string, a "s" will be included when the number of items remaining is greater than one.
+counter_text_finished | string | All items placed. Feel free to reposition items if necessary. | Text that will take the place of the counter_text_unfinished text when all items have been moved inside the sort area.
+stim_starts_inside | boolean | false | If `false`, the images will be positioned to the left and right of the sort area when the trial loads. If `true`, the images will be positioned at random locations inside the sort area when the trial loads.
## Data Generated
From dc00efd0ea170491d0c7ff517d619cec6423edd3 Mon Sep 17 00:00:00 2001
From: Becky Gilbert
Date: Thu, 19 Nov 2020 18:24:44 -0800
Subject: [PATCH 11/13] add counter text and stim_starts_inside params, fix
missed hardcoded colors
---
plugins/jspsych-free-sort.js | 173 ++++++++++++++++++++++-------------
1 file changed, 111 insertions(+), 62 deletions(-)
diff --git a/plugins/jspsych-free-sort.js b/plugins/jspsych-free-sort.js
index f13d7b294b..6176907125 100644
--- a/plugins/jspsych-free-sort.js
+++ b/plugins/jspsych-free-sort.js
@@ -45,13 +45,13 @@ jsPsych.plugins['free-sort'] = (function() {
sort_area_height: {
type: jsPsych.plugins.parameterType.INT,
pretty_name: 'Sort area height',
- default: 800,
+ default: 700,
description: 'The height in pixels of the container that subjects can move the stimuli in.'
},
sort_area_width: {
type: jsPsych.plugins.parameterType.INT,
pretty_name: 'Sort area width',
- default: 800,
+ default: 700,
description: 'The width in pixels of the container that subjects can move the stimuli in.'
},
sort_area_shape: {
@@ -109,6 +109,27 @@ jsPsych.plugins['free-sort'] = (function() {
default: null,
description: 'The width in pixels of the border around the sort area. If null, the border width '+
'defaults to 3% of the sort area height.'
+ },
+ counter_text_unfinished: {
+ type: jsPsych.plugins.parameterType.STRING,
+ pretty_name: 'Counter text unfinished',
+ default: 'You still need to place %n% item%s% inside the sort area.',
+ description: 'Text to display when there are one or more items that still need to be placed in the sort area. '+
+ 'If "%n%" is included in the string, it will be replaced with the number of items that still need to be moved inside. '+
+ 'If "%s%" is included in the string, a "s" will be included when the number of items remaining is greater than one.'
+ },
+ counter_text_finished: {
+ type: jsPsych.plugins.parameterType.STRING,
+ pretty_name: 'Counter text finished',
+ default: 'All items placed. Feel free to reposition items if necessary.',
+ description: 'Text that will take the place of the counter_text_unfinished text when all items have been moved inside the sort area.'
+ },
+ stim_starts_inside: {
+ type: jsPsych.plugins.parameterType.BOOL,
+ pretty_name: 'Stim starts inside',
+ default: false,
+ description: 'If false, the images will be positioned to the left and right of the sort area when the trial loads. '+
+ 'If true, the images will be positioned at random locations inside the sort area when the trial loads.'
}
}
}
@@ -129,7 +150,7 @@ jsPsych.plugins['free-sort'] = (function() {
'