From 6b0f5ebd903f7d90f629761831df6668f9624802 Mon Sep 17 00:00:00 2001 From: Xiaolu Bai Date: Mon, 16 Jan 2017 18:23:10 -0800 Subject: [PATCH 01/87] click pic go plug_in --- jspsych.js | 1 - plugins/jspsych-html.js | 2 +- plugins/jspsych-survey-multi-choice.js | 23 ++-- plugins/jspsych-text.js | 2 +- plugins/test.js | 158 +++++++++++++++++++++++++ 5 files changed, 167 insertions(+), 19 deletions(-) create mode 100644 plugins/test.js diff --git a/jspsych.js b/jspsych.js index e9164a6c..5edf4f3b 100755 --- a/jspsych.js +++ b/jspsych.js @@ -186,7 +186,6 @@ window.jsPsych = (function() { }; core.getDisplayElement = function() { - console.log("yo in getDisplayElement", DOM_target); return DOM_target; }; diff --git a/plugins/jspsych-html.js b/plugins/jspsych-html.js index 5decafce..46b65623 100644 --- a/plugins/jspsych-html.js +++ b/plugins/jspsych-html.js @@ -80,7 +80,7 @@ jsPsych.plugins.html = (function() { var key_listener = function(e) { if (e.which == trial.cont_key) finish(); }; - display_element.attachEventListener('keydown', key_listener); + display_element.addEventListener('keydown', key_listener); } }); }; diff --git a/plugins/jspsych-survey-multi-choice.js b/plugins/jspsych-survey-multi-choice.js index ef14f90e..dd3aafec 100644 --- a/plugins/jspsych-survey-multi-choice.js +++ b/plugins/jspsych-survey-multi-choice.js @@ -82,10 +82,8 @@ jsPsych.plugins['survey-multi-choice'] = (function() { // form element var trial_form_id = _join(plugin_id_name, "form"); - console.log("trial_form_id", trial_form_id) display_element.innerHTML += '
'; var trial_form = display_element.querySelector("#" + trial_form_id); - console.log("trial_form", trial_form) // show preamble text var preamble_id_name = _join(plugin_id_name, 'preamble'); trial_form.innerHTML += '
'+trial.preamble+'
'; @@ -116,11 +114,12 @@ jsPsych.plugins['survey-multi-choice'] = (function() { // add label and question text var option_label = ''; display_element.querySelector(option_id_selector).innerHTML += option_label; + console.log("option label", option_label) // create radio button var input_id_name = _join(plugin_id_name, 'response', i); display_element.querySelector(option_id_selector + " label").innerHTML = - '' + + '' + display_element.querySelector(option_id_selector + " label").innerHTML; } @@ -129,7 +128,7 @@ jsPsych.plugins['survey-multi-choice'] = (function() { display_element.querySelector(question_selector + " p").innerHMTL += "*"; // add required property - display_element.querySelector(question_selector + " input").required = true; + display_element.querySelector(question_selector + " input[type=radio]").required = true; } } // add submit button @@ -137,8 +136,6 @@ jsPsych.plugins['survey-multi-choice'] = (function() { trial_form.addEventListener('submit', function(event) { event.preventDefault(); - var matches = display_element.querySelectorAll("div." + plugin_id_name + "-question"); - console.log("are you getting called") // measure response time var endTime = (new Date()).getTime(); var response_time = endTime - startTime; @@ -146,17 +143,11 @@ jsPsych.plugins['survey-multi-choice'] = (function() { // create object to hold responses var question_data = {}; var matches = display_element.querySelectorAll("div." + plugin_id_name + "-question"); - var inputs = document.getElementsByTagName('input'); - var radios = [] - for(var i = 0; i < inputs.length; i++){ - if(inputs[i].checked){ - radios.push(inputs[i].value) - } - } - matches.forEach(function(currentEl ,index){ - var id = "Q" + index; + matches.forEach(function(match, index) { + var id = 'answer' + var val = match.querySelector("input[type=radio]:checked").value; var obje = {}; - obje[id] = radios[index]; + obje[id] = val; Object.assign(question_data, obje); }) // save data diff --git a/plugins/jspsych-text.js b/plugins/jspsych-text.js index a4393d38..edb0e823 100644 --- a/plugins/jspsych-text.js +++ b/plugins/jspsych-text.js @@ -74,7 +74,7 @@ jsPsych.plugins.text = (function() { // check if key is 'mouse' if (trial.choices == 'mouse') { - display_element.attachEventListener(mouse_listener); + display_element.addEventListener(mouse_listener); var start_time = (new Date()).getTime(); } else { jsPsych.pluginAPI.getKeyboardResponse({ diff --git a/plugins/test.js b/plugins/test.js new file mode 100644 index 00000000..194e4ef2 --- /dev/null +++ b/plugins/test.js @@ -0,0 +1,158 @@ +/** + * jspsych-test + * a jspsych plugin for multiple choice survey questions + * + * Shane Martin + * + * documentation: docs.jspsych.org + * + */ + + +jsPsych.plugins['test'] = (function() { + var plugin = {}; + + plugin.info = { + name: 'test', + description: '', + parameters: { + questions: { + type: [jsPsych.plugins.parameterType.STRING], + array: true, + default: undefined, + no_function: false, + description: '' + }, + options: { + type: [jsPsych.plugins.parameterType.STRING], + array: true, + default: undefined, + no_function: false, + description: '' + }, + required: { + type: [jsPsych.plugins.parameterType.BOOL], + array: true, + default: false, + no_function: false, + description: '' + }, + horitzontal: { + type: [jsPsych.plugins.parameterType.BOOL], + default: false, + no_function: false, + description: '' + }, + preamble: { + type: [jsPsych.plugins.parameterType.STRING], + default: '', + no_function: false, + description: '' + } + } + } + plugin.trial = function(display_element, trial) { + var plugin_id_name = "jspsych-test"; + var plugin_id_selector = '#' + plugin_id_name; + var _join = function( /*args*/ ) { + var arr = Array.prototype.slice.call(arguments, _join.length); + return arr.join(separator = '-'); + } + + // trial defaults + trial.preamble = typeof trial.preamble == 'undefined' ? "" : trial.preamble; + trial.required = typeof trial.required == 'undefined' ? null : trial.required; + trial.horizontal = typeof trial.required == 'undefined' ? false : trial.horizontal; + + // if any trial variables are functions + // this evaluates the function and replaces + // it with the output of the function + trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial); + + // inject CSS for trial + var node = display_element.innerHTML += ''; - var cssstr = ".jspsych-survey-likert-statement { display:block; font-size: 18px; padding-top: 30px; margin-bottom:10px; }"+ + var cssstr = ".jspsych-survey-likert-statement { display:block; font-size: 16px; padding-top: 40px; margin-bottom:10px; }"+ ".jspsych-survey-likert-opts { list-style:none; width:100%; margin:0; padding:0 0 35px; display:block; font-size: 14px; line-height:1.1em; }"+ - ".jspsych-survey-likert-opt-label { line-height: 1.1em; }"+ + ".jspsych-survey-likert-opt-label { line-height: 1.1em; color: #444; }"+ ".jspsych-survey-likert-opts:before { content: ''; position:relative; top:11px; /*left:9.5%;*/ display:block; background-color:#efefef; height:4px; width:100%; }"+ ".jspsych-survey-likert-opts:last-of-type { border-bottom: 0; }"+ ".jspsych-survey-likert-opts li { display:inline-block; /*width:19%;*/ text-align:center; vertical-align: top; }"+ From f339d6d558223ae304808eb7775b1b101fd6be51 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Mon, 20 Mar 2017 09:44:11 -0400 Subject: [PATCH 52/87] update docs on data storage --- docs/markdown_docs/features/data.md | 121 ++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 14 deletions(-) diff --git a/docs/markdown_docs/features/data.md b/docs/markdown_docs/features/data.md index 532c7e76..b6da7430 100644 --- a/docs/markdown_docs/features/data.md +++ b/docs/markdown_docs/features/data.md @@ -15,8 +15,8 @@ In most cases, data collection will be automatic and hidden. Plugins save data o Often it is useful to add a piece of data to *all* of the trials in the experiment. For example, appending the subject ID to each trial. This can be done with the `jsPsych.data.addProperties()` function. Here is an example: ```javascript -// generate a random subject ID -var subject_id = Math.floor(Math.random()*100000); +// generate a random subject ID with 15 characters +var subject_id = jsPsych.randomization.randomID(15); // pick a random condition for the subject at the start of the experiment var condition_assignment = jsPsych.randomization.sample(['conditionA', 'conditionB', 'conditionC'],1)[0]; @@ -143,28 +143,121 @@ The `file_put_contents($filename, $data)` method requires permission to write ne To use the PHP script, the JavaScript that runs jsPsych needs to send the `filename` and `filedata` information. This is done through an [AJAX](http://www.w3schools.com/xml/ajax_intro.asp) call. ```javascript -function saveData(filename, filedata){ - $.ajax({ - type:'post', - cache: false, - url: 'save_data.php', // this is the path to the above PHP script - data: {filename: filename, filedata: filedata} - }); +function saveData(name, data){ + var xhr = new XMLHttpRequest(); + xhr.open('POST', 'write_data.php'); // 'write_data.php' is the path to the php file described above. + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.send(JSON.stringify({filename: filename, filedata: filedata})); } // call the saveData function after the experiment is over jsPsych.init({ - // code to define the experiment structure would go here... - - on_finish: function(data){ saveData("filename.csv", jsPsych.data.getDataAsCSV()) } + on_finish: function(){ saveData("experiment_data.csv", jsPsych.data.get().csv()); } }); ``` -To use this in an actual experiment, it would be important to tie the filename to some unique identifier like a subject number. +To use this in an actual experiment, it would be important to tie the filename to some unique identifier like a subject number. Otherwise the file may be overwritten by collecting new data. ## Storing data permanently in a MySQL database The ideal solution for storing data generated by jsPsych is to write it to a database. -There are dozens of database options. MySQL is one of the most popular [relational databases](http://en.wikipedia.org/wiki/Relational_database), is free to use, and relatively easy [to install](https://www.google.com/search?q=how+to+install+mysql). This page will assume that you have a MySQL database installed on your server that is hosting the jsPsych experiment, and that your server is able to execute PHP code. If you are trying to run on a local machine, you'll need to install a local server environment like [XAMPP](https://www.apachefriends.org/index.html). +There are dozens of database options. MySQL is one of the most popular [relational databases](http://en.wikipedia.org/wiki/Relational_database), is free to use, and relatively easy [to install](https://www.google.com/search?q=how+to+install+mysql). This code will assume that you have a MySQL database installed on your server that is hosting the jsPsych experiment, and that your server is able to execute PHP code. If you are trying to run on a local machine, you'll need to install a local server environment like [XAMPP](https://www.apachefriends.org/index.html). + +You'll need two PHP scripts. The first is a configuration file for your database. Save it as `database_config.php` on your server. Within this file are configuration options for the database. You'll need to change these according to how you have configured your MySQL installation. + +```php + +``` + +The second PHP file will write data to the database. This script reads the database to discover what columns are in the table, and then only allows data to be entered in that matches those columns. This is a security feature. Save this file as `write_data.php` on your server. + +```php +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + // First stage is to get all column names from the table and store + // them in $col_names array. + $stmt = $conn->prepare("SHOW COLUMNS FROM `$table`"); + $stmt->execute(); + $col_names = array(); + while($row = $stmt->fetchColumn()) { + $col_names[] = $row; + } + // Second stage is to create prepared SQL statement using the column + // names as a guide to what values might be in the JSON. + // If a value is missing from a particular trial, then NULL is inserted + $sql = "INSERT INTO $table VALUES("; + for($i = 0; $i < count($col_names); $i++){ + $name = $col_names[$i]; + $sql .= ":$name"; + if($i != count($col_names)-1){ + $sql .= ", "; + } + } + $sql .= ");"; + $insertstmt = $conn->prepare($sql); + for($i=0; $i < count($data_array); $i++){ + for($j = 0; $j < count($col_names); $j++){ + $colname = $col_names[$j]; + if(!isset($data_array[$i][$colname])){ + $insertstmt->bindValue(":$colname", null, PDO::PARAM_NULL); + } else { + $insertstmt->bindValue(":$colname", $data_array[$i][$colname]); + } + } + $insertstmt->execute(); + } + echo '{"success": true}'; +} catch(PDOException $e) { + echo '{"success": false, "message": ' . $e->getMessage(); +} +$conn = null; +?> +``` + +To send the data, we use an AJAX request in JavaScript. +```JavaScript +function saveData() { + var xhr = new XMLHttpRequest(); + xhr.open('POST', 'write_data.php'); // change 'write_data.php' to point to php script. + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.onload = function() { + if(xhr.status == 200){ + var response = JSON.parse(xhr.responseText); + console.log(response.success); + } + }; + xhr.send(jsPsych.data.getData().json()); +} +``` + +You can call the `saveData()` function using the `on_finish` handler for the experiment, or by using the `call-function` plugin. + +```javascript +// with on_finish handler +jsPsych.init({ + on_finish: saveData +}); + +// with call-function plugin +timeline.push({ + type: 'call-function', + func: saveData +}); +``` From 84de9aed7e55b9a6bb9d3f6e519b3dbfe1904ecf Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Mon, 20 Mar 2017 13:58:41 -0400 Subject: [PATCH 53/87] add resize plugin #352 --- docs/markdown_docs/plugins/jspsych-resize.md | 30 +++++ docs/markdown_docs/plugins/overview.md | 3 +- docs/mkdocs.yml | 1 + examples/jspsych-resize.html | 34 +++++ plugins/jspsych-resize.js | 135 +++++++++++++++++++ 5 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 docs/markdown_docs/plugins/jspsych-resize.md create mode 100644 examples/jspsych-resize.html create mode 100644 plugins/jspsych-resize.js diff --git a/docs/markdown_docs/plugins/jspsych-resize.md b/docs/markdown_docs/plugins/jspsych-resize.md new file mode 100644 index 00000000..d8b05d7c --- /dev/null +++ b/docs/markdown_docs/plugins/jspsych-resize.md @@ -0,0 +1,30 @@ +# jspsych-resize + +This plugin displays a resizable div container that allows the user to drag until the container is the same size as the item being measured. Once the user measures the item as close as possible, clicking the button sets a scaling factor for the div containing jsPsych content. This causes the stimuli that follow to have a known size, independent of monitor resolution. + +## Parameters + +This table lists the parameters associated with this plugin. Parameters with a default value of *undefined* must be specified. Other parameters can be left unspecified if the default value is acceptable. + +Parameter | Type | Default Value | Description +----------|------|---------------|------------ +item_height | numeric | 1 | The height of the item to be measured. Any units can be used as long as you are consistent with using the same units for all parameters. +item_width | numeric | 1 | The width of the item to be measured. +pixels_per_unit | numeric | 100 | After the scaling factor is applied, this many pixels will equal one unit of measurement. +prompt | string | `''` | HTML content to display below the resizable box, and above the button. +button_text | string | `'Done'` | Label to display on the button to complete calibration. +starting_size | numeric | 100 | The initial size of the box, in pixels, along the largest dimension. The aspect ratio will be set automatically to match the item width and height. + +## Examples + +#### Measuring a credit card and resizing the display to have 150 pixels equal an inch. + +```javascript +var inputs = { + type: 'resize', + item_width: 3 + 3/8, + item_height: 2 + 1/8, + prompt: "

Click and drag the lower right corner of the box until the box is the same size as a credit card held up to the screen.

", + pixels_per_unit: 150 +}; +``` diff --git a/docs/markdown_docs/plugins/overview.md b/docs/markdown_docs/plugins/overview.md index 402bbd91..7f837a1e 100644 --- a/docs/markdown_docs/plugins/overview.md +++ b/docs/markdown_docs/plugins/overview.md @@ -75,7 +75,8 @@ This table is a description of all plugins that are currently bundled with jsPsy [jspsych‑instructions](jspsych-instructions) | For displaying instructions to the subject. [jspsych‑multi‑stim‑multi‑response](jspsych-multi-stim-multi-response) | A more generalized version of the single-stim plugin. Can display multiple stimuli in a single trial, and collect multiple responses in a single trial. [jspsych‑palmer](jspsych-palmer) | Shows grid-like stimuli inspired by Stephen Palmer's work. The stimuli are editable: subjects can add and subtract parts interactively. Also contains a method for generating the HTML code to render the stimuli, allowing them to be used in other plugins. - [jspsych‑reconstruction](jspsych-reconstruction) | The subject interacts with a stimulus by modifying a parameter of the stimulus and observing the change in the stimulus in real-time. + [jspsych‑reconstruction](jspsych-reconstruction) | The subject interacts with a stimulus by modifying a parameter of the stimulus and observing the change in the stimulus in real-time. + [jspsych‑resize](jspsych-resize) | Calibrate the display so that materials display with a known physical size. [jspsych‑same‑different](jspsych-same-different) | A same-different judgment task. A stimulus is shown, followed by a brief gap, and then another stimulus is shown. The subject indicates whether the stimuli are the same or different. [jspsych‑similarity](jspsych-similarity) | Two stimuli are shown sequentially, and the subject indicates how similar they are by dragging a slider object. [jspsych‑single‑audio](jspsych-single-audio) | A basic plugin for playing an audio stimulus and getting a keyboard response. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 6a223683..89cdcf2e 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -46,6 +46,7 @@ pages: - 'jspsych-multi-stim-multi-response': 'plugins/jspsych-multi-stim-multi-response.md' - 'jspsych-palmer': 'plugins/jspsych-palmer.md' - 'jspsych-reconstruction': 'plugins/jspsych-reconstruction.md' + - 'jspsych-resize': 'plugins/jspsych-resize.md' - 'jspsych-same-different': 'plugins/jspsych-same-different.md' - 'jspsych-similarity': 'plugins/jspsych-similarity.md' - 'jspsych-single-audio': 'plugins/jspsych-single-audio.md' diff --git a/examples/jspsych-resize.html b/examples/jspsych-resize.html new file mode 100644 index 00000000..2b8bf251 --- /dev/null +++ b/examples/jspsych-resize.html @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/plugins/jspsych-resize.js b/plugins/jspsych-resize.js new file mode 100644 index 00000000..5590c905 --- /dev/null +++ b/plugins/jspsych-resize.js @@ -0,0 +1,135 @@ +/** +* jspsych-resize +* Steve Chao +* +* plugin for controlling the real world size of the display +* +* documentation: docs.jspsych.org +* +**/ + +jsPsych.plugins["resize"] = (function() { + + var plugin = {}; + + plugin.trial = function(display_element, trial) { + + // if any trial variables are functions + // this evaluates the function and replaces + // it with the output of the function + trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial); + + // default trial paramters + trial.item_height = trial.item_height || 1; + trial.item_width = trial.item_width || 1; + trial.prompt = trial.prompt || ' '; + trial.pixels_per_unit = trial.pixels_per_unit || 100; + trial.starting_size = trial.starting_size || 100; + trial.button_text = trial.button_text || "Done"; + + var aspect_ratio = trial.item_width / trial.item_height; + + // variables to determine div size + if(trial.item_width >= trial.item_height){ + var start_div_width = trial.starting_size; + var start_div_height = Math.round(trial.starting_size / aspect_ratio); + } else { + var start_div_height = trial.starting_size; + var start_div_width = Math.round(trial.starting_size * aspect_ratio); + } + + // create html for display + var html ='
'; + html += '
'; + html += '
'; + html += trial.prompt; + html += ''+trial.button_text+''; + + // render + display_element.innerHTML = html; + + // listens for the click + document.getElementById("jspsych-resize-btn").addEventListener('click', function() { + scale(); + end_trial(); + }); + + var dragging = false; + var origin_x, origin_y; + var cx, cy; + + var mousedownevent = function(e){ + e.preventDefault(); + e.stopPropagation(); + dragging = true; + origin_x = e.pageX; + origin_y = e.pageY; + cx = parseInt(scale_div.style.width); + cy = parseInt(scale_div.style.height); + } + + display_element.querySelector('#jspsych-resize-handle').addEventListener('mousedown', mousedownevent); + + var mouseupevent = function(e){ + dragging = false; + } + + document.addEventListener('mouseup', mouseupevent); + + var scale_div = display_element.querySelector('#jspsych-resize-div'); + + var resizeevent = function(e){ + if(dragging){ + var dx = (e.pageX - origin_x)*2; + var dy = (e.pageY - origin_y)*2; + + if(dx >= dy){ + scale_div.style.width = Math.max(10, cx+dx) + "px"; + scale_div.style.height = Math.round(Math.max(10, cx+dx) / aspect_ratio ) + "px"; + } else { + scale_div.style.height = Math.max(10, cy+dy) + "px"; + scale_div.style.width = Math.round(aspect_ratio * Math.max(10, cy+dy)) + "px"; + } + } + } + + document.addEventListener('mousemove', resizeevent); + + // scales the stimulus + var scale_factor; + var final_height_px, final_width_px; + function scale() { + final_width_px = scale_div.offsetWidth; + final_height_px = scale_div.offsetHeight; + + var pixels_unit_screen = final_width_px / trial.item_width; + + scale_factor = pixels_unit_screen / trial.pixels_per_unit; + document.getElementById("jspsych-content").style.transform = "scale(" + scale_factor + ")"; + }; + + + // function to end trial + function end_trial() { + + // clear document event listeners + document.removeEventListener('mousemove', resizeevent); + document.removeEventListener('mouseup', mouseupevent); + + // clear the screen + display_element.innerHTML = ''; + + // finishes trial + + var trial_data = { + 'final_height_px': final_height_px, + 'final_width_px': final_width_px, + 'scale_factor': scale_factor + } + + jsPsych.finishTrial(trial_data); + } + }; + + return plugin; +})(); From cd67e061ecf99ef84ed91eff7616118a6d5562b6 Mon Sep 17 00:00:00 2001 From: rivasd Date: Mon, 20 Mar 2017 14:32:02 -0400 Subject: [PATCH 54/87] Fixing crucial bug in instructions plugin *the navigation buttons were not functioning and had syntax errors, replaced their onClick listener with one that actually advances the pages and removes itself automatically * other small syntax corrections --- plugins/jspsych-instructions.js | 27 ++++++++++++++------------- plugins/jspsych-reconstruction.js | 2 +- plugins/jspsych-vsl-grid-scene.js | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/plugins/jspsych-instructions.js b/plugins/jspsych-instructions.js index c889fcfe..e657a0c5 100644 --- a/plugins/jspsych-instructions.js +++ b/plugins/jspsych-instructions.js @@ -77,7 +77,17 @@ jsPsych.plugins.instructions = (function() { var start_time = (new Date()).getTime(); var last_page_update_time = start_time; - + + function btnListener(evt){ + evt.target.removeEventListener('click', btnListener); + if(this.id === "jspsych-instructions-back"){ + back(); + } + else if(this.id === 'jspsych-instructions-next'){ + next(); + } + } + function show_current_page() { display_element.innerHTML = trial.pages[current_page]; @@ -90,24 +100,15 @@ jsPsych.plugins.instructions = (function() { nav_html += "" display_element.innerHTML += nav_html; - + if (current_page != 0 && trial.allow_backward) { - display_element.querySelector('#jspsych-instructions-back').addEventListener('click', function() { - clear_button_handlers(); - }); + display_element.querySelector('#jspsych-instructions-back').addEventListener('click', btnListener); } - display_element.querySelector('#jspsych-instructions-next').addEventListener('click', function() { - clear_button_handlers(); - }); + display_element.querySelector('#jspsych-instructions-next').addEventListener('click', btnListener); } } - function clear_button_handlers() { - display_element.querySelector('#jspsych-instructions-next').removeEventListener('click'); - display_element.querySelector('#jspsych-instructions-back').removeEventListener('click'); - } - function next() { add_current_page_to_view_history() diff --git a/plugins/jspsych-reconstruction.js b/plugins/jspsych-reconstruction.js index bb8bde3d..ab1e1cdb 100644 --- a/plugins/jspsych-reconstruction.js +++ b/plugins/jspsych-reconstruction.js @@ -102,7 +102,7 @@ jsPsych.plugins['reconstruction'] = (function() { //console.log(param); - display_element.innerHTML = '
'+trial.stim_function(param)+'
'); + display_element.innerHTML = '
'+trial.stim_function(param)+'
'; // add submit button display_element.innerHTML += ''; diff --git a/plugins/jspsych-vsl-grid-scene.js b/plugins/jspsych-vsl-grid-scene.js index aaf330c8..6ded60ce 100644 --- a/plugins/jspsych-vsl-grid-scene.js +++ b/plugins/jspsych-vsl-grid-scene.js @@ -90,7 +90,7 @@ jsPsych.plugins['vsl-grid-scene'] = (function() { for (var col = 0; col < ncols; col++) { display_element.querySelector('#jspsych-vsl-grid-scene-table-row-' + row).innerHTML += ''+ - '
'; + '
'; } } From b6ed388713bb4f5aadce9e339ccbf10021006fd2 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Mon, 20 Mar 2017 14:58:59 -0400 Subject: [PATCH 55/87] update contributors --- contributors.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/contributors.md b/contributors.md index 1828c803..c57235c6 100644 --- a/contributors.md +++ b/contributors.md @@ -1,11 +1,16 @@ -The following people have contributed to the development of jsPsych by writing code, documentation, and/or suggesting major improvements (in alphabetical order): +The following people have contributed to the development of jsPsych by writing code, documentation, and/or suggesting improvements (in alphabetical order): +* Xiaolu Bai - https://github.com/lbai001 * Jason Carpenter -* Josh de Leeuw - https://github.com/jodeleeuw +* Steve Chao - https://github.com/stchao * Jana Klaus - https://github.com/janakl4us * Jonas Lambers * Shane Martin - https://github.com/shamrt * Adrian Oesch - https://github.com/adrianoesch * Junyan Qi - https://github.com/GavinQ1 +* Dan Rivas - https://github.com/rivasd * Marian Sauter - https://github.com/mariansauter * Tim Vergenz - https://github.com/vergenzt +* Matteo Visconti di Oleggio Castello - https://github.com/mvdoc +* Wolfgang Walther - https://github.com/wolfgangwalther * Erik Weitnauer - https://github.com/eweitnauer +* Rob Wilkinson - https://github.com/RobAWilkinson From b5bdcfa34560099abd9c014aed76b92daa08b7bd Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Mon, 20 Mar 2017 15:03:25 -0400 Subject: [PATCH 56/87] update ref, text --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 93af6f32..56731aa4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![logo](jspsych-logo-readme.jpg) -jsPsych is a JavaScript library for creating and running behavioral experiments in a web browser. jsPsych simplifies the process of coding browser-based experiments by providing a set of flexible plugins that define different kinds of tasks a subject could complete during an experiment. By assembling different plugins together and customizing the parameters of each, it is possible to create many different types of experiments. +jsPsych is a JavaScript library for creating behavioral experiments that run in a web browser. jsPsych creates a framework for defining experiments and provides a set of flexible plugins that create different kinds of tasks a subject could complete during an experiment. By assembling different plugins together and customizing the parameters of each, it is possible to create many different types of experiments. Documentation ------------- @@ -27,10 +27,10 @@ de Leeuw, J.R. (2015). jsPsych: A JavaScript library for creating behavioral exp Response times -------------- -Wondering if jsPsych can be used for research that depends on accurate response time measurement? In general, the answer is yes. Response time measurements in jsPsych (and JavaScript in general) are comparable to those taken in standard lab software like Psychophysics Toolbox and E-Prime. Response times measured in JavaScript tend to be a little bit longer (10-40ms), but have similar variance. See the following references for extensive work on this topic. +Wondering if jsPsych can be used for research that depends on accurate response time measurement? For most purposes, the answer is yes. Response time measurements in jsPsych (and JavaScript in general) are comparable to those taken in standard lab software like Psychophysics Toolbox and E-Prime. Response times measured in JavaScript tend to be a little bit longer (10-40ms), but have similar variance. See the following references for extensive work on this topic. * [de Leeuw, J. R., & Motz, B. A. (2016). Psychophysics in a Web browser? Comparing response times collected with JavaScript and Psychophysics Toolbox in a visual search task. *Behavior Research Methods*, *48*(1), 1-12.](http://link.springer.com/article/10.3758%2Fs13428-015-0567-2) -* [Hilbig, B. E. (in press). Reaction time effects in lab- versus web-based research: Experimental evidence. *Behavior Research Methods*.](http://dx.doi.org/10.3758/s13428-015-0678-9) +* [Hilbig, B. E. (2016). Reaction time effects in lab- versus web-based research: Experimental evidence. *Behavior Research Methods*, *48*(4), 1718-1724.](http://dx.doi.org/10.3758/s13428-015-0678-9) * [Pinet, S., Zielinski, C., Mathôt, S. et al. (in press). Measuring sequences of keystrokes with jsPsych: Reliability of response times and interkeystroke intervals. *Behavior Research Methods*.](http://link.springer.com/article/10.3758/s13428-016-0776-3) * [Reimers, S., & Stewart, N. (2015). Presentation and response time accuracy in Adobe Flash and HTML5/JavaScript Web experiments. *Behavior Research Methods*, *47*(2), 309-327.](http://link.springer.com/article/10.3758%2Fs13428-014-0471-1) From b0ecd233d9dd27834dd2e706e7a709174e54a218 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Mon, 20 Mar 2017 22:22:50 -0400 Subject: [PATCH 57/87] fixes #340 --- plugins/jspsych-single-audio.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/jspsych-single-audio.js b/plugins/jspsych-single-audio.js index 3e9cf80d..451e8f0c 100644 --- a/plugins/jspsych-single-audio.js +++ b/plugins/jspsych-single-audio.js @@ -112,10 +112,13 @@ jsPsych.plugins["single-audio"] = (function() { jsPsych.pluginAPI.clearAllTimeouts(); // stop the audio file if it is playing + // remove end event listeners if they exist if(context !== null){ source.stop(); + source.onended = function() { } } else { audio.pause(); + audio.removeEventListener('end', end_trial); } // kill keyboard listeners From 5a4b0e453bf3b011a65b2a9fa62c1e610e1220e0 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Mon, 20 Mar 2017 22:34:17 -0400 Subject: [PATCH 58/87] implements test for #336, works. --- tests/timelines.test.js | 120 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 tests/timelines.test.js diff --git a/tests/timelines.test.js b/tests/timelines.test.js new file mode 100644 index 00000000..aa1a4fd6 --- /dev/null +++ b/tests/timelines.test.js @@ -0,0 +1,120 @@ +const root = '../'; + +beforeEach(function(){ + require(root + 'jspsych.js'); + require(root + 'plugins/jspsych-text.js'); +}); + +describe('loop function', function(){ + + test('repeats a timeline when returns true', function(){ + + var count = 0; + + var trial = { + timeline: [{ + type: 'text', + text: 'foo' + }], + loop_function: function(){ + if(count < 1){ + count++; + return true; + } else { + return false; + } + } + } + + jsPsych.init({ + timeline: [trial] + }); + + // first trial + document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32})); + document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32})); + + expect(jsPsych.data.get().count()).toBe(1); + + // second trial + document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32})); + document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32})); + + expect(jsPsych.data.get().count()).toBe(2); + + }); + + test('does not repeat when returns false', function(){ + + var count = 0; + + var trial = { + timeline: [{ + type: 'text', + text: 'foo' + }], + loop_function: function(){ + return false + } + } + + jsPsych.init({ + timeline: [trial] + }); + + // first trial + document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32})); + document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32})); + + expect(jsPsych.data.get().count()).toBe(1); + + // second trial + document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32})); + document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32})); + + expect(jsPsych.data.get().count()).toBe(1); + + }); + + test('gets the data from the most recent iteration', function(){ + + var data_count = []; + var count = 0; + + var trial = { + timeline: [{ + type: 'text', + text: 'foo' + }], + loop_function: function(data){ + data_count.push(data.count()); + if(count < 2){ + count++; + return true; + } else { + return false; + } + } + } + + jsPsych.init({ + timeline: [trial] + }); + + // first trial + document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32})); + document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32})); + + // second trial + document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32})); + document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32})); + + // third trial + document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 32})); + document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 32})); + + expect(data_count).toEqual([1,1,1]); + expect(jsPsych.data.get().count()).toBe(3); + + }); +}); From ad980e345b83cddb477360e2d75394eb2d7f880d Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Sun, 26 Mar 2017 19:42:29 -0400 Subject: [PATCH 59/87] adjust progress bar style --- css/jspsych.css | 1 - 1 file changed, 1 deletion(-) diff --git a/css/jspsych.css b/css/jspsych.css index 5694b600..6cd03b62 100644 --- a/css/jspsych.css +++ b/css/jspsych.css @@ -98,7 +98,6 @@ input[type="text"] { #jspsych-progressbar-outer { background-color: #dedede; border-radius: 5px; - padding: 1px; width: 800px; margin: auto; } From 163bf8f06b285d3f2f61c92da4d8cf415321e3b1 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Wed, 29 Mar 2017 09:39:15 -0400 Subject: [PATCH 60/87] remove references to .forEach for IE11 compatibility --- plugins/jspsych-survey-multi-choice.js | 7 ++++--- plugins/jspsych-survey-multi-picture.js | 9 +++++---- plugins/jspsych-survey-multi-select.js | 10 ++++++---- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/plugins/jspsych-survey-multi-choice.js b/plugins/jspsych-survey-multi-choice.js index 7a47f92c..ae36919b 100644 --- a/plugins/jspsych-survey-multi-choice.js +++ b/plugins/jspsych-survey-multi-choice.js @@ -149,8 +149,9 @@ jsPsych.plugins['survey-multi-choice'] = (function() { // create object to hold responses var question_data = {}; var matches = display_element.querySelectorAll("div." + plugin_id_name + "-question"); - matches.forEach(function(match, index) { - var id = "Q" + index; + for(var i=0; i Date: Wed, 29 Mar 2017 23:04:01 -0400 Subject: [PATCH 61/87] Added button_label parameter to survey-likert and survey-text. Also addes values parameter to survey-text --- plugins/jspsych-survey-likert.js | 3 ++- plugins/jspsych-survey-text.js | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/plugins/jspsych-survey-likert.js b/plugins/jspsych-survey-likert.js index e3c356be..59bbcafa 100644 --- a/plugins/jspsych-survey-likert.js +++ b/plugins/jspsych-survey-likert.js @@ -38,6 +38,7 @@ jsPsych.plugins['survey-likert'] = (function() { // default parameters for the trial trial.preamble = typeof trial.preamble === 'undefined' ? "" : trial.preamble; trial.required = typeof trial.required === 'undefined' ? false : trial.required; + trial.button_label = typeof trial.button_label === 'undefined' ? 'Submit Answers' : trial.button_label; // if any trial variables are functions // this evaluates the function and replaces @@ -80,7 +81,7 @@ jsPsych.plugins['survey-likert'] = (function() { } // add submit button - form_element.innerHTML += ''; + form_element.innerHTML += ''; form_element.addEventListener('submit', function(e){ e.preventDefault(); diff --git a/plugins/jspsych-survey-text.js b/plugins/jspsych-survey-text.js index ac473abc..f452520f 100644 --- a/plugins/jspsych-survey-text.js +++ b/plugins/jspsych-survey-text.js @@ -50,6 +50,8 @@ jsPsych.plugins['survey-text'] = (function() { plugin.trial = function(display_element, trial) { trial.preamble = typeof trial.preamble == 'undefined' ? "" : trial.preamble; + trial.button_label = typeof trial.button_label === 'undefined' ? 'Submit Answers' : trial.button_label; + if (typeof trial.rows == 'undefined') { trial.rows = []; for (var i = 0; i < trial.questions.length; i++) { @@ -62,6 +64,12 @@ jsPsych.plugins['survey-text'] = (function() { trial.columns.push(40); } } + if (typeof trial.values == 'undefined') { + trial.values = []; + for (var i = 0; i < trial.questions.length; i++) { + trial.values.push(""); + } + } // if any trial variables are functions // this evaluates the function and replaces @@ -75,12 +83,12 @@ jsPsych.plugins['survey-text'] = (function() { for (var i = 0; i < trial.questions.length; i++) { display_element.innerHTML += '
'+ '

' + trial.questions[i] + '

'+ - ''+ + ''+ '
'; } // add submit button - display_element.innerHTML += ''; + display_element.innerHTML += ''; display_element.querySelector('#jspsych-survey-text-next').addEventListener('click', function() { // measure response time From 4b0932303dd6aa5ffa29bb4ba91058533052c566 Mon Sep 17 00:00:00 2001 From: Hugues Lacroix Date: Fri, 31 Mar 2017 23:43:13 -0400 Subject: [PATCH 62/87] added button_label parameter for all relevant plugins (except for jspsych-resize, as it already had a button_text parameter). --- plugins/jspsych-free-sort.js | 9 ++++++- plugins/jspsych-instructions.js | 42 ++++++++++++++++++++----------- plugins/jspsych-palmer.js | 9 ++++++- plugins/jspsych-reconstruction.js | 9 ++++++- plugins/jspsych-similarity.js | 10 +++++++- plugins/jspsych-survey-text.js | 15 ++++++++++- 6 files changed, 75 insertions(+), 19 deletions(-) diff --git a/plugins/jspsych-free-sort.js b/plugins/jspsych-free-sort.js index 97cb3fb2..183b94ae 100644 --- a/plugins/jspsych-free-sort.js +++ b/plugins/jspsych-free-sort.js @@ -60,6 +60,12 @@ jsPsych.plugins['free-sort'] = (function() { default: 'above', no_function: false, description: '' + }, + button_label: { + type: [jsPsych.plugins.parameterType.STRING], + default: 'Done', + no_function: false, + description: '' } } } @@ -73,6 +79,7 @@ jsPsych.plugins['free-sort'] = (function() { trial.prompt_location = trial.prompt_location || "above"; trial.sort_area_width = trial.sort_area_width || 800; trial.sort_area_height = trial.sort_area_height || 800; + trial.button_label = typeof trial.button_label === 'undefined' ? 'Done' : trial.button_label; // if any trial variables are functions // this evaluates the function and replaces @@ -118,7 +125,7 @@ jsPsych.plugins['free-sort'] = (function() { }); } - display_element.innerHTML += ''; + display_element.innerHTML += ''; var maxz = 1; diff --git a/plugins/jspsych-instructions.js b/plugins/jspsych-instructions.js index e657a0c5..80d39e5c 100644 --- a/plugins/jspsych-instructions.js +++ b/plugins/jspsych-instructions.js @@ -53,6 +53,18 @@ jsPsych.plugins.instructions = (function() { default: false, no_function: false, description: '' + }, + button_label_previous: { + type: [jsPsych.plugins.parameterType.STRING], + default: 'Previous', + no_function: false, + description: '' + }, + button_label_next: { + type: [jsPsych.plugins.parameterType.STRING], + default: 'Next', + no_function: false, + description: '' } } } @@ -64,6 +76,8 @@ jsPsych.plugins.instructions = (function() { trial.allow_backward = (typeof trial.allow_backward === 'undefined') ? true : trial.allow_backward; trial.allow_keys = (typeof trial.allow_keys === 'undefined') ? true : trial.allow_keys; trial.show_clickable_nav = (typeof trial.show_clickable_nav === 'undefined') ? false : trial.show_clickable_nav; + trial.button_label_previous = (typeof trial.button_label_previous === 'undefined') ? 'Previous' : trial.button_label_previous; + trial.button_label_next = (typeof trial.button_label_next === 'undefined') ? 'Next' : trial.button_label_next; // if any trial variables are functions // this evaluates the function and replaces @@ -77,7 +91,7 @@ jsPsych.plugins.instructions = (function() { var start_time = (new Date()).getTime(); var last_page_update_time = start_time; - + function btnListener(evt){ evt.target.removeEventListener('click', btnListener); if(this.id === "jspsych-instructions-back"){ @@ -87,7 +101,7 @@ jsPsych.plugins.instructions = (function() { next(); } } - + function show_current_page() { display_element.innerHTML = trial.pages[current_page]; @@ -95,41 +109,41 @@ jsPsych.plugins.instructions = (function() { var nav_html = "
"; if (current_page != 0 && trial.allow_backward) { - nav_html += ""; + nav_html += ""; } - nav_html += "
" + nav_html += "" display_element.innerHTML += nav_html; - + if (current_page != 0 && trial.allow_backward) { display_element.querySelector('#jspsych-instructions-back').addEventListener('click', btnListener); } - + display_element.querySelector('#jspsych-instructions-next').addEventListener('click', btnListener); } } function next() { - + add_current_page_to_view_history() - + current_page++; - + // if done, finish up... if (current_page >= trial.pages.length) { endTrial(); } else { show_current_page(); } - + } - + function back() { - + add_current_page_to_view_history() - + current_page--; - + show_current_page(); } diff --git a/plugins/jspsych-palmer.js b/plugins/jspsych-palmer.js index 30dbd024..a6c15951 100644 --- a/plugins/jspsych-palmer.js +++ b/plugins/jspsych-palmer.js @@ -66,6 +66,12 @@ jsPsych.plugins.palmer = (function() { default: '', no_function: false, description: '' + }, + button_label: { + type: [jsPsych.plugins.parameterType.STRING], + default: 'Done', + no_function: false, + description: '' } } } @@ -80,6 +86,7 @@ jsPsych.plugins.palmer = (function() { trial.timing_item = trial.timing_item || 1000; trial.timing_feedback = trial.timing_feedback || 1000; trial.prompt = (typeof trial.prompt === 'undefined') ? "" : trial.prompt; + trial.button_label = typeof trial.button_label === 'undefined' ? 'Submit Answers' : trial.button_label; // if any trial variables are functions // this evaluates the function and replaces @@ -246,7 +253,7 @@ jsPsych.plugins.palmer = (function() { // start recording the time var startTime = (new Date()).getTime(); - display_element.innerHTML += '

'; + display_element.innerHTML += '

'; display_element.querySelector('#jspsych-palmer-submitButton').addEventListener('click', function() { save_data(); }); diff --git a/plugins/jspsych-reconstruction.js b/plugins/jspsych-reconstruction.js index ab1e1cdb..6cc35a33 100644 --- a/plugins/jspsych-reconstruction.js +++ b/plugins/jspsych-reconstruction.js @@ -47,6 +47,12 @@ jsPsych.plugins['reconstruction'] = (function() { default: 'g', no_function: false, description: '' + }, + button_label: { + type: [jsPsych.plugins.parameterType.STRING], + default: '', + no_function: false, + description: 'Submit Answers' } } } @@ -58,6 +64,7 @@ jsPsych.plugins['reconstruction'] = (function() { trial.step_size = trial.step_size || 0.05; trial.key_increase = trial.key_increase || 'h'; trial.key_decrease = trial.key_decrease || 'g'; + trial.button_label = typeof trial.button_label === 'undefined' ? 'Submit Answers' : trial.button_label; // if any trial variables are functions // this evaluates the function and replaces @@ -105,7 +112,7 @@ jsPsych.plugins['reconstruction'] = (function() { display_element.innerHTML = '
'+trial.stim_function(param)+'
'; // add submit button - display_element.innerHTML += ''; + display_element.innerHTML += ''; display_element.querySelector('#jspsych-reconstruction-next').addEventListener('click', endTrial); } diff --git a/plugins/jspsych-similarity.js b/plugins/jspsych-similarity.js index 1433f4a3..3d7341ee 100644 --- a/plugins/jspsych-similarity.js +++ b/plugins/jspsych-similarity.js @@ -81,6 +81,12 @@ jsPsych.plugins.similarity = (function() { default: '', no_function: false, description: '' + }, + button_label: { + type: [jsPsych.plugins.parameterType.STRING], + default: '', + no_function: false, + description: 'Submit Answers' } } } @@ -100,6 +106,8 @@ jsPsych.plugins.similarity = (function() { trial.is_html = (typeof trial.is_html === 'undefined') ? false : trial.is_html; trial.prompt = (typeof trial.prompt === 'undefined') ? '' : trial.prompt; + + trial.button_label = typeof trial.button_label === 'undefined' ? 'Submit Answers' : trial.button_label; // if any trial variables are functions // this evaluates the function and replaces @@ -231,7 +239,7 @@ jsPsych.plugins.similarity = (function() { display_element.append($(''; + var listener = display_element.querySelector('#jspsych-fullscreen-btn').addEventListener('click', function() { + var element = document.documentElement; + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen(); + } + }); + } else { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } + } + endTrial(); + } + + function endTrial() { + jsPsych.pluginAPI.setTimeout(function(){ + + display_element.innerHTML = ''; + + var trial_data = { + success: !keyboardNotAllowed + }; + + jsPsych.finishTrial(trial_data); + + }, trial.delay_after); + } + + }; + + return plugin; +})(); From 6f032f189e198a468e2d4522189389c4192d31c8 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Mon, 1 May 2017 16:31:08 -0400 Subject: [PATCH 69/87] modifying info method --- plugins/jspsych-fullscreen.js | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/plugins/jspsych-fullscreen.js b/plugins/jspsych-fullscreen.js index c2421496..f73b335b 100644 --- a/plugins/jspsych-fullscreen.js +++ b/plugins/jspsych-fullscreen.js @@ -13,21 +13,42 @@ jsPsych.plugins.fullscreen = (function() { name: 'fullscreen', description: '', parameters: { - pages: { - type: [jsPsych.plugins.parameterType.STRING], - default: undefined, - array: true, + fullscreen_mode: { + type: [jsPsych.plugins.parameterType.BOOL], + default: true, + array: false, no_function: false, description: '' - } + }, + message: { + type: [jsPsych.plugins.parameterType.STRING], + default: '

The experiment will switch to full screen mode when you press the button below

', + array: false, + no_function: false, + description: '' + }, + button_label: { + type: [jsPsych.plugins.parameterType.STRING], + default: "Go", + array: false, + no_function: false, + description: '' + }, + delay_after: { + type: [jsPsych.plugins.parameterType.INT], + default: 1000, + array: false, + no_function: false, + description: '' + }, } } plugin.trial = function(display_element, trial) { trial.fullscreen_mode = typeof trial.fullscreen_mode === 'undefinded' ? true : trial.fullscreen_mode; - trial.message = trial.message || ''; - trial.button_label = trial.button_label || ''; + trial.message = trial.message || '

The experiment will switch to full screen mode when you press the button below

'; + trial.button_label = trial.button_label || 'Go'; trial.delay_after = trial.delay_after || 1000; // if any trial variables are functions From d5fd16be7c98172d5f4a79a66ea0c5728e05676c Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Mon, 1 May 2017 16:35:41 -0400 Subject: [PATCH 70/87] working example of fullscreen --- examples/jspsych-fullscreen.html | 44 ++++++++++++++++++++++++++++++++ plugins/jspsych-fullscreen.js | 7 ++--- 2 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 examples/jspsych-fullscreen.html diff --git a/examples/jspsych-fullscreen.html b/examples/jspsych-fullscreen.html new file mode 100644 index 00000000..171fdc2e --- /dev/null +++ b/examples/jspsych-fullscreen.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + diff --git a/plugins/jspsych-fullscreen.js b/plugins/jspsych-fullscreen.js index f73b335b..227dcda5 100644 --- a/plugins/jspsych-fullscreen.js +++ b/plugins/jspsych-fullscreen.js @@ -76,6 +76,7 @@ jsPsych.plugins.fullscreen = (function() { } else if (element.msRequestFullscreen) { element.msRequestFullscreen(); } + endTrial(); }); } else { if (document.exitFullscreen) { @@ -87,14 +88,14 @@ jsPsych.plugins.fullscreen = (function() { } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } + endTrial(); } - endTrial(); } function endTrial() { - jsPsych.pluginAPI.setTimeout(function(){ + display_element.innerHTML = ''; - display_element.innerHTML = ''; + jsPsych.pluginAPI.setTimeout(function(){ var trial_data = { success: !keyboardNotAllowed From fb8b7d22005e0c1bc99096c8b0cc99f1e2305de8 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Thu, 18 May 2017 11:28:06 -0400 Subject: [PATCH 71/87] show text input instead of textarea when rows=1 --- examples/jspsych-survey-text.html | 6 ++++-- plugins/jspsych-survey-text.js | 20 +++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/examples/jspsych-survey-text.html b/examples/jspsych-survey-text.html index 9db3b6b1..87d093c6 100644 --- a/examples/jspsych-survey-text.html +++ b/examples/jspsych-survey-text.html @@ -1,7 +1,7 @@ - + @@ -14,7 +14,9 @@ var survey_block = { type: 'survey-text', - questions: page_1_questions + questions: page_1_questions, + rows: [1, 8], + columns: [50, 20] }; jsPsych.init({ diff --git a/plugins/jspsych-survey-text.js b/plugins/jspsych-survey-text.js index 179dda81..5d4c3f9f 100644 --- a/plugins/jspsych-survey-text.js +++ b/plugins/jspsych-survey-text.js @@ -90,18 +90,24 @@ jsPsych.plugins['survey-text'] = (function() { trial = jsPsych.pluginAPI.evaluateFunctionParameters(trial); // show preamble text - display_element.innerHTML += '
'+trial.preamble+'
'; + var html = '
'+trial.preamble+'
'; // add questions for (var i = 0; i < trial.questions.length; i++) { - display_element.innerHTML += '
'+ - '

' + trial.questions[i] + '

'+ - ''+ - '
'; + html += '
'; + html += '

' + trial.questions[i] + '

'; + if(trial.rows[i] == 1){ + html += ''+trial.values[i]+''; + } else { + html += ''; + } + html += '
'; } // add submit button - display_element.innerHTML += ''; + html += ''; + + display_element.innerHTML = html; display_element.querySelector('#jspsych-survey-text-next').addEventListener('click', function() { // measure response time @@ -113,7 +119,7 @@ jsPsych.plugins['survey-text'] = (function() { var matches = display_element.querySelectorAll('div.jspsych-survey-text-question'); for(var index=0; index Date: Fri, 19 May 2017 14:42:10 +0200 Subject: [PATCH 72/87] Minor error corrected --- examples/demo-simple-rt-task.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo-simple-rt-task.html b/examples/demo-simple-rt-task.html index f9bcccf4..babd629e 100644 --- a/examples/demo-simple-rt-task.html +++ b/examples/demo-simple-rt-task.html @@ -14,7 +14,7 @@ var timeline = []; /* define welcome message trial */ - var welcome = { + var welcome_block = { type: "text", text: "Welcome to the experiment. Press any key to begin." }; From 6dc62aec0eebde397a9a1fa1c5422658c7296f4b Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Wed, 24 May 2017 22:18:16 -0400 Subject: [PATCH 73/87] remove fullscreen from jsPsych.init #380 --- jspsych.js | 58 ++++++++---------------------------------------------- 1 file changed, 8 insertions(+), 50 deletions(-) diff --git a/jspsych.js b/jspsych.js index 44abf952..afd19c0f 100755 --- a/jspsych.js +++ b/jspsych.js @@ -74,7 +74,6 @@ window.jsPsych = (function() { 'show_progress_bar': false, 'auto_preload': true, 'max_load_time': 60000, - 'fullscreen': false, 'default_iti': 0 }; @@ -705,49 +704,18 @@ window.jsPsych = (function() { loaded = true; - var fullscreen = opts.fullscreen; - - // fullscreen setup - if (fullscreen) { - // check if keys are allowed in fullscreen mode - var keyboardNotAllowed = typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element; - if (keyboardNotAllowed) { - go(); - } else { - DOM_target.innerHTML = '

The experiment will launch in fullscreen mode when you click the button below.

'; - var listener = DOM_target.querySelector('#jspsych-fullscreen-btn').addEventListener('click', function() { - var element = document.documentElement; - if (element.requestFullscreen) { - element.requestFullscreen(); - } else if (element.mozRequestFullScreen) { - element.mozRequestFullScreen(); - } else if (element.webkitRequestFullscreen) { - element.webkitRequestFullscreen(); - } else if (element.msRequestFullscreen) { - element.msRequestFullscreen(); - } - DOM_target.querySelector('#jspsych-fullscreen-btn').removeEventListener('click', listener); - DOM_target.innerHTML = ''; - setTimeout(go, 1000); - }); - } - } else { - go(); + // show progress bar if requested + if (opts.show_progress_bar === true) { + drawProgressBar(); } - function go() { - // show progress bar if requested - if (opts.show_progress_bar === true) { - drawProgressBar(); - } + // record the start time + exp_start_time = new Date(); - // record the start time - exp_start_time = new Date(); + // begin! + timeline.advance(); + doTrial(timeline.trial()); - // begin! - timeline.advance(); - doTrial(timeline.trial()); - } } function finishExperiment() { @@ -756,16 +724,6 @@ window.jsPsych = (function() { DOM_target.innerHTML = timeline.end_message; } - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } - opts.on_finish(jsPsych.data.get()); } From e129f832fc083dda05688546fcc1d4201fab556a Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Wed, 24 May 2017 22:23:54 -0400 Subject: [PATCH 74/87] update docs for removing fullscreen from ini #380 --- .../core_library/jspsych-core.md | 1 - docs/markdown_docs/features/fullscreen.md | 32 ++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/docs/markdown_docs/core_library/jspsych-core.md b/docs/markdown_docs/core_library/jspsych-core.md index 482e8818..3569db98 100644 --- a/docs/markdown_docs/core_library/jspsych-core.md +++ b/docs/markdown_docs/core_library/jspsych-core.md @@ -306,7 +306,6 @@ show_progress_bar | boolean | If true, then [a progress bar](../features/progres preload_audio | array | An array of audio files to preload before starting the experiment. preload_images | array | An array of image files to preload before starting the experiment. max_load_time | numeric | The maximum number of milliseconds to wait for content to preload. If the wait time is exceeded an error message is displayed and the experiment stops. The default value is 60 seconds. -fullscreen | boolean | If true, the experiment will run in fullscreen mode. See the [feature page](../features/fullscreen.md) for more details. default_iti | numeric | The default inter-trial interval in ms. The default value if none is specified is 0ms. Possible values for the exclusions parameter above. diff --git a/docs/markdown_docs/features/fullscreen.md b/docs/markdown_docs/features/fullscreen.md index 5e308d01..9f4a944f 100644 --- a/docs/markdown_docs/features/fullscreen.md +++ b/docs/markdown_docs/features/fullscreen.md @@ -1,14 +1,36 @@ # Fullscreen Experiments -You can run your experiment in fullscreen mode by setting the `fullscreen` parameter in the `jsPsych.init` call that launches the experiment. +You can run your experiment in fullscreen mode by using the jspsych-fullscreen plugin. ```javascript +var timeline = []; + +timeline.push({ + type: 'fullscreen', + fullscreen_mode: true +}); + +timeline.push({ + type: 'text', + text: 'This trial will be in fullscreen mode.' +}); + +// exit fullscreen mode +timeline.push({ + type: 'fullscreen', + fullscreen_mode: false +}); + +timeline.push({ + type: 'text', + text: 'This trial will NOT be in fullscreen mode.' +}); + jsPsych.init({ - timeline: timeline, - fullscreen: true + timeline: timeline }); ``` -For security reasons, launching the browser into fullscreen mode requires that the user take an action. Therefore, if fullscreen mode is requested, a button will be displayed on the page to launch the experiment into fullscreen mode. The experiment will not begin until this button is pressed. +For security reasons, web browsers require that users initiate an action to launch fullscreen mode. The fullscreen plugin displays a button that the user must click to change the display to fullscreen. -Safari does not support keyboard input when the browser is in fullscreen mode. Therefore, the function will not launch fullscreen mode on Safari. +Safari does not support keyboard input when the browser is in fullscreen mode. Therefore, the function will not launch fullscreen mode on Safari. The experiment will ignore any trials using the fullscreen plugin in Safari. From f2304c2b79a7b40a5ed09f4aaeac4504a197143e Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Wed, 24 May 2017 23:25:29 -0400 Subject: [PATCH 75/87] fixes #333 , adds same-different testing --- plugins/jspsych-same-different.js | 17 +++-- tests/plugins/plugin-same-different.test.js | 70 +++++++++++++++++++++ 2 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 tests/plugins/plugin-same-different.test.js diff --git a/plugins/jspsych-same-different.js b/plugins/jspsych-same-different.js index fbf71c88..7552a99c 100644 --- a/plugins/jspsych-same-different.js +++ b/plugins/jspsych-same-different.js @@ -82,6 +82,7 @@ jsPsych.plugins['same-different'] = (function() { // default parameters trial.same_key = trial.same_key || 81; // default is 'q' trial.different_key = trial.different_key || 80; // default is 'p' + trial.advance_key = trial.advance_key || jsPsych.ALL_KEYS // timing parameters trial.timing_first_stim = trial.timing_first_stim || 1000; // if -1, the first stim is shown until any key is pressed trial.timing_second_stim = trial.timing_second_stim || 1000; // if -1, then second stim is shown until response. @@ -112,11 +113,17 @@ jsPsych.plugins['same-different'] = (function() { first_stim_info = info; showBlankScreen(); } - jsPsych.pluginAPI.getKeyboardResponse(afterKeyboardResponse, [], 'date', false); + jsPsych.pluginAPI.getKeyboardResponse({ + callback_function: afterKeyboardResponse, + valid_responses: trial.advance_key, + rt_method: 'date', + persist: false, + allow_held_key: false + }); } function showBlankScreen() { - display_element.querySelector('.jspsych-same-different-stimulus').outerHTML = ''; + display_element.innerHTML = ''; jsPsych.pluginAPI.setTimeout(function() { showSecondStim(); @@ -125,14 +132,14 @@ jsPsych.plugins['same-different'] = (function() { function showSecondStim() { if (!trial.is_html) { - display_element.innerHTML += ''; + display_element.innerHTML += ''; } else { - display_element.innerHTML += '
'+trial.stimuli[1]+'
'; + display_element.innerHTML += '
'+trial.stimuli[1]+'
'; } if (trial.timing_second_stim > 0) { jsPsych.pluginAPI.setTimeout(function() { - display_element.querySelector('#jspsych-same-different-second-stimulus').style.visibility = 'hidden'; + display_element.querySelector('.jspsych-same-different-stimulus').style.visibility = 'hidden'; }, trial.timing_second_stim); } diff --git a/tests/plugins/plugin-same-different.test.js b/tests/plugins/plugin-same-different.test.js new file mode 100644 index 00000000..bf15e0a2 --- /dev/null +++ b/tests/plugins/plugin-same-different.test.js @@ -0,0 +1,70 @@ +const root = '../../'; + +jest.useFakeTimers(); + +describe('same-different plugin', function(){ + + beforeEach(function(){ + require(root + 'jspsych.js'); + require(root + 'plugins/jspsych-same-different.js'); + }); + + test('loads correctly', function(){ + expect(window.jsPsych.plugins['same-different']).not.toBeUndefined(); + }); + + test('works with default parameters', function(){ + var trial = { + type: 'same-different', + stimuli: ['../media/blue.png','../media/blue.png'], + answer: 'same' + } + + jsPsych.init({ + timeline: [trial] + }); + + expect(jsPsych.getDisplayElement().innerHTML).toBe(''); + jest.runTimersToTime(1000); + expect(jsPsych.getDisplayElement().innerHTML).toBe(''); + jest.runTimersToTime(500); + expect(jsPsych.getDisplayElement().innerHTML).toBe(''); + + document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 81})); + document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 81})); + + expect(jsPsych.getDisplayElement().innerHTML).toBe(''); + + expect(jsPsych.data.get().values()[0].correct).toBe(true); + + }); + + test('check when timing_first_stim is -1', function(){ + var trial = { + type: 'same-different', + stimuli: ['../media/blue.png','../media/blue.png'], + answer: 'same', + timing_first_stim: -1 + } + + jsPsych.init({ + timeline: [trial] + }); + + expect(jsPsych.getDisplayElement().innerHTML).toBe(''); + document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 81})); + document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 81})); + expect(jsPsych.getDisplayElement().innerHTML).toBe(''); + jest.runTimersToTime(500); + expect(jsPsych.getDisplayElement().innerHTML).toBe(''); + + document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 81})); + document.dispatchEvent(new KeyboardEvent('keyup', {keyCode: 81})); + + expect(jsPsych.getDisplayElement().innerHTML).toBe(''); + + expect(jsPsych.data.get().values()[0].correct).toBe(true); + + }); + +}); From 90a17b82ec669329bfa0b24a0cbfe224c0832bb7 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Wed, 24 May 2017 23:28:11 -0400 Subject: [PATCH 76/87] add timeline variables test shell --- tests/timeline-variables.test.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/timeline-variables.test.js diff --git a/tests/timeline-variables.test.js b/tests/timeline-variables.test.js new file mode 100644 index 00000000..e3575c5c --- /dev/null +++ b/tests/timeline-variables.test.js @@ -0,0 +1,24 @@ +const root = '../'; + +beforeEach(function(){ + require(root + 'jspsych.js'); + require(root + 'plugins/jspsych-text.js'); +}); + +describe('randomize order', function(){ + test('holder', function(){ + expect(true).toBe(true); + }); +}); + +describe('repetitons', function(){ + test('holder', function(){ + expect(true).toBe(true); + }); +}); + +describe('sampling', function(){ + test('holder', function(){ + expect(true).toBe(true); + }); +}); From 930d5cdff93e8e2657a0b2539bd20809f488b591 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Wed, 24 May 2017 23:28:46 -0400 Subject: [PATCH 77/87] fix typo in fullscreen plugin #380 --- plugins/jspsych-fullscreen.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/jspsych-fullscreen.js b/plugins/jspsych-fullscreen.js index 227dcda5..940a938a 100644 --- a/plugins/jspsych-fullscreen.js +++ b/plugins/jspsych-fullscreen.js @@ -46,7 +46,7 @@ jsPsych.plugins.fullscreen = (function() { plugin.trial = function(display_element, trial) { - trial.fullscreen_mode = typeof trial.fullscreen_mode === 'undefinded' ? true : trial.fullscreen_mode; + trial.fullscreen_mode = typeof trial.fullscreen_mode === 'undefined' ? true : trial.fullscreen_mode; trial.message = trial.message || '

The experiment will switch to full screen mode when you press the button below

'; trial.button_label = trial.button_label || 'Go'; trial.delay_after = trial.delay_after || 1000; @@ -93,6 +93,7 @@ jsPsych.plugins.fullscreen = (function() { } function endTrial() { + display_element.innerHTML = ''; jsPsych.pluginAPI.setTimeout(function(){ @@ -104,6 +105,7 @@ jsPsych.plugins.fullscreen = (function() { jsPsych.finishTrial(trial_data); }, trial.delay_after); + } }; From 25b4f28f405b6d9816f8a3c15c399d32fbd3bf9a Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Thu, 25 May 2017 10:33:23 -0400 Subject: [PATCH 78/87] move utility functions to .utils module for better testing --- jspsych.js | 88 +++++++++++++++---------- plugins/jspsych-serial-reaction-time.js | 2 +- 2 files changed, 53 insertions(+), 37 deletions(-) diff --git a/jspsych.js b/jspsych.js index afd19c0f..956e5a51 100755 --- a/jspsych.js +++ b/jspsych.js @@ -931,7 +931,7 @@ jsPsych.data = (function() { } data_collection.readOnly = function(){ - return DataCollection(deepExtend([], trials)); + return DataCollection(jsPsych.utils.deepExtend([], trials)); } data_collection.addToAll = function(properties){ @@ -956,9 +956,9 @@ jsPsych.data = (function() { // [{p1: v1, p2:v2}, {p1:v2}] // {p1: v1} if(!Array.isArray(filters)){ - var f = deepExtend([], [filters]); + var f = jsPsych.utils.deepExtend([], [filters]); } else { - var f = deepExtend([], filters); + var f = jsPsych.utils.deepExtend([], filters); } var filtered_data = []; @@ -1012,7 +1012,7 @@ jsPsych.data = (function() { if(!Array.isArray(columns)){ columns = [columns]; } - var o = deepExtend([], trials); + var o = jsPsych.utils.deepExtend([], trials); for (var i = 0; i < o.length; i++) { for (var j in columns) { delete o[i][columns[j]]; @@ -2011,7 +2011,7 @@ jsPsych.pluginAPI = (function() { module.preloadAudioFiles = function(files, callback_complete, callback_load) { - files = flatten(files); + files = jsPsych.utils.flatten(files); var n_loaded = 0; var loadfn = (typeof callback_load === 'undefined') ? function() {} : callback_load; @@ -2077,7 +2077,7 @@ jsPsych.pluginAPI = (function() { module.preloadImages = function(images, callback_complete, callback_load) { // flatten the images array - images = flatten(images); + images = jsPsych.utils.flatten(images); var n_loaded = 0; var loadfn = (typeof callback_load === 'undefined') ? function() {} : callback_load; @@ -2144,9 +2144,9 @@ jsPsych.pluginAPI = (function() { if (typeof trials[j][param] !== 'undefined' && typeof trials[j][param] !== 'function') { if ( typeof func == 'undefined' || func(trials[j]) ){ if (media == 'image') { - images = images.concat(flatten([trials[j][param]])); + images = images.concat(jsPsych.utils.flatten([trials[j][param]])); } else if (media == 'audio') { - audio = audio.concat(flatten([trials[j][param]])); + audio = audio.concat(jsPsych.utils.flatten([trials[j][param]])); } } } @@ -2193,40 +2193,56 @@ jsPsych.pluginAPI = (function() { })(); // methods used in multiple modules // +jsPsych.utils = (function() { -function flatten(arr, out) { - out = (typeof out === 'undefined') ? [] : out; - for (var i = 0; i < arr.length; i++) { - if (Array.isArray(arr[i])) { - flatten(arr[i], out); - } else { - out.push(arr[i]); - } - } - return out; -} + var module = {}; -function deepExtend(out) { - out = out || {}; + module.flatten = function(arr, out) { + out = (typeof out === 'undefined') ? [] : out; + for (var i = 0; i < arr.length; i++) { + if (Array.isArray(arr[i])) { + module.flatten(arr[i], out); + } else { + out.push(arr[i]); + } + } + return out; + } - for (var i = 1; i < arguments.length; i++) { - var obj = arguments[i]; + module.unique = function(arr) { + var out = []; + for (var i = 0; i < out.length; i++) { + if (arr.indexOf(arr[i]) == i) { + out.push(arr[i]); + } + } + return out; + } - if (!obj) - continue; + module.deepExtend = function(out, obj) { + out = out || {}; - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - if (typeof obj[key] === 'object') - out[key] = deepExtend(out[key], obj[key]); - else - out[key] = obj[key]; - } - } - } + for (var i = 1; i < arguments.length; i++) { + var obj = arguments[i]; - return out; -}; + if (!obj) + continue; + + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + if (typeof obj[key] === 'object') + out[key] = module.deepExtend(out[key], obj[key]); + else + out[key] = obj[key]; + } + } + } + + return out; + } + + return module; +})(); // polyfill for Object.assign to support IE if (typeof Object.assign != 'function') { diff --git a/plugins/jspsych-serial-reaction-time.js b/plugins/jspsych-serial-reaction-time.js index 0f1a3215..3f69dd0d 100644 --- a/plugins/jspsych-serial-reaction-time.js +++ b/plugins/jspsych-serial-reaction-time.js @@ -29,7 +29,7 @@ jsPsych.plugins["serial-reaction-time"] = (function() { trial.prompt = (typeof trial.prompt === 'undefined') ? "" : trial.prompt; // create a flattened version of the choices array - var flat_choices = flatten(trial.choices); + var flat_choices = jsPsych.utils.flatten(trial.choices); while(flat_choices.indexOf('') > -1){ flat_choices.splice(flat_choices.indexOf(''),1); } From 41f6f4d8ed11d46d3acd74733e73f406f193f1c3 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Thu, 25 May 2017 10:38:55 -0400 Subject: [PATCH 79/87] fix .unique bug --- jspsych.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jspsych.js b/jspsych.js index 956e5a51..4d70d3b8 100755 --- a/jspsych.js +++ b/jspsych.js @@ -2211,7 +2211,7 @@ jsPsych.utils = (function() { module.unique = function(arr) { var out = []; - for (var i = 0; i < out.length; i++) { + for (var i = 0; i < arr.length; i++) { if (arr.indexOf(arr[i]) == i) { out.push(arr[i]); } From 8be4e13cdae80def718e4ea6b686127f4575ae56 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Thu, 25 May 2017 10:39:03 -0400 Subject: [PATCH 80/87] implement testing for utils --- tests/jsPsych.utils/utils.test.js | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/jsPsych.utils/utils.test.js diff --git a/tests/jsPsych.utils/utils.test.js b/tests/jsPsych.utils/utils.test.js new file mode 100644 index 00000000..506fe536 --- /dev/null +++ b/tests/jsPsych.utils/utils.test.js @@ -0,0 +1,32 @@ +const root = '../../'; + +require(root + 'jspsych.js'); + +describe('unique', function(){ + test('generates unique array when there are duplicates', function(){ + var arr = [1,1,2,2,3,3]; + var out = jsPsych.utils.unique(arr); + expect(out).toEqual([1,2,3]); + expect(out).not.toEqual(arr); + }); + + test('generates same array when there are no duplicates', function(){ + var arr = [1,2,3]; + var out = jsPsych.utils.unique(arr); + expect(out).toEqual(arr); + }) +}); + +describe('flatten', function(){ + test('generates flat array from flat input', function(){ + var arr = [1,1,2,2,3,3]; + var out = jsPsych.utils.flatten(arr); + expect(out).toEqual(arr); + }); + + test('generates flat array from nested input', function(){ + var arr = [1,[1,2,2],[3],3]; + var out = jsPsych.utils.flatten(arr); + expect(out).toEqual([1,1,2,2,3,3]); + }); +}); From 87e064d9c9d5ae1c5c5bd4a9f0214c05726e1b15 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Thu, 25 May 2017 10:39:13 -0400 Subject: [PATCH 81/87] adjust loading test --- tests/loads.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/loads.test.js b/tests/loads.test.js index 31eabe21..fa1275db 100644 --- a/tests/loads.test.js +++ b/tests/loads.test.js @@ -1,7 +1,6 @@ const root = '../'; require(root + 'jspsych.js'); -require(root + 'plugins/jspsych-single-stim.js'); test('jsPsych should be in the window object', function(){ expect(typeof window.jsPsych).not.toBe('undefined'); From 2a900c7d3e74323cb98f8fb8fc6658f76c2bc610 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Thu, 25 May 2017 10:39:29 -0400 Subject: [PATCH 82/87] add fullscreen partial test --- tests/plugins/plugin-fullscreen.test.js | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/plugins/plugin-fullscreen.test.js diff --git a/tests/plugins/plugin-fullscreen.test.js b/tests/plugins/plugin-fullscreen.test.js new file mode 100644 index 00000000..8a725ccb --- /dev/null +++ b/tests/plugins/plugin-fullscreen.test.js @@ -0,0 +1,41 @@ +const root = '../../'; + +jest.useFakeTimers(); + +describe('fullscreen plugin', function(){ + + beforeEach(function(){ + require(root + 'jspsych.js'); + require(root + 'plugins/jspsych-fullscreen.js'); + require(root + 'plugins/jspsych-text.js'); + }); + + test('loads correctly', function(){ + expect(typeof window.jsPsych.plugins['fullscreen']).not.toBe('undefined'); + }); + + // can't test this right now because jsdom doesn't support fullscreen API. + + /*test('launches fullscreen mode by default', function(){ + var trial = { + type: 'fullscreen', + delay_after: 0 + } + + var text = { + type: 'text', + text: 'fullscreen' + } + + jsPsych.init({ + timeline: [trial, text] + }); + + expect(document.fullscreenElement).toBeUndefined(); + console.log(jsPsych.getDisplayElement().requestFullscreen); + document.querySelector('#jspsych-fullscreen-btn').dispatchEvent(new MouseEvent('click', {})); + + expect(document.fullscreenElement).not.toBeUndefined(); + });*/ + +}); From a0ecd6dc2367e152df06ae1d601f360aeb928f96 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Thu, 25 May 2017 10:40:59 -0400 Subject: [PATCH 83/87] only preload unique files; fixes #377 --- jspsych.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jspsych.js b/jspsych.js index 4d70d3b8..079758d9 100755 --- a/jspsych.js +++ b/jspsych.js @@ -2012,6 +2012,7 @@ jsPsych.pluginAPI = (function() { module.preloadAudioFiles = function(files, callback_complete, callback_load) { files = jsPsych.utils.flatten(files); + files = jsPsych.utils.unique(files); var n_loaded = 0; var loadfn = (typeof callback_load === 'undefined') ? function() {} : callback_load; @@ -2078,6 +2079,7 @@ jsPsych.pluginAPI = (function() { // flatten the images array images = jsPsych.utils.flatten(images); + images = jsPsych.utils.unique(images); var n_loaded = 0; var loadfn = (typeof callback_load === 'undefined') ? function() {} : callback_load; From 6b53fced9da48648f4a4e7fd4d2cb6eb63457351 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Thu, 25 May 2017 10:51:08 -0400 Subject: [PATCH 84/87] reset variables when timeline loops #343 --- jspsych.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jspsych.js b/jspsych.js index 079758d9..082f3ad3 100755 --- a/jspsych.js +++ b/jspsych.js @@ -468,7 +468,7 @@ window.jsPsych = (function() { // if we're all done with the repetitions, check if there is a loop function. else if (typeof timeline_parameters.loop_function !== 'undefined') { if (timeline_parameters.loop_function(this.generatedData())) { - this.reset(); // TODO: fix this probably... + this.reset(); return parent_node.advance(); } else { progress.done = true; @@ -558,6 +558,7 @@ window.jsPsych = (function() { progress.current_variable_set = 0; progress.current_iteration++; progress.done = false; + this.setTimelineVariablesOrder(); if (typeof timeline_parameters != 'undefined') { for (var i = 0; i < timeline_parameters.timeline.length; i++) { timeline_parameters.timeline[i].reset(); From d287f7a4d3cf95accb126f8279ec124abbd56ee4 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Thu, 25 May 2017 11:17:56 -0400 Subject: [PATCH 85/87] fixes #343 --- jspsych.js | 6 +++++ tests/timeline-variables.test.js | 45 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/jspsych.js b/jspsych.js index 082f3ad3..4b51bc44 100755 --- a/jspsych.js +++ b/jspsych.js @@ -359,6 +359,12 @@ window.jsPsych = (function() { // set the order for going through the timeline variables array // TODO: this is where all the sampling options can be implemented this.setTimelineVariablesOrder = function() { + + // check to make sure this node has variables + if(typeof timeline_parameters === 'undefined' || typeof timeline_parameters.timeline_variables === 'undefined'){ + return; + } + var order = []; for(var i=0; i Date: Thu, 25 May 2017 13:59:39 -0400 Subject: [PATCH 86/87] add example file for testing #288 , #370 --- examples/flex-layout-testing.html | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 examples/flex-layout-testing.html diff --git a/examples/flex-layout-testing.html b/examples/flex-layout-testing.html new file mode 100644 index 00000000..0b088826 --- /dev/null +++ b/examples/flex-layout-testing.html @@ -0,0 +1,30 @@ + + + + + + + + + + From 629f9915f7e8fa9debc1827395ed7c238cc52ca3 Mon Sep 17 00:00:00 2001 From: Josh de Leeuw Date: Fri, 26 May 2017 10:08:52 -0400 Subject: [PATCH 87/87] update jest to version 20 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b7112007..0e63ee33 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,6 @@ }, "homepage": "https://github.com/jodeleeuw/jsPsych#readme", "devDependencies": { - "jest": "^19.0.2" + "jest": "^20.0.4" } }