Forum Replies Created
-
AuthorPosts
-
Jeremy
KeymasterHi Jun,
What version of PennController are you using? Also, what does your HTML file look like?
Using PennController 2.0 and this HTML code, things work just fine for me:
<script> function newCode() { const randomCode = [...Array(8)].map(()=>Math.round(Math.random()*10)).join(''); document.querySelector("#code").value = randomCode; document.querySelector("#codeDisplay").innerText = randomCode; } </script> <button onclick="newCode()">Generate code</button> <input id='code' type='text' name='code' style='display:none;' class='obligatory' /> <p>Your code is <span id='codeDisplay'></span></p>
Jeremy
Jeremy
KeymasterHi Ebru,
You could get there by using the Selector element, if you created one per cell, but the code would quickly become cumbersome. A more concise solution would be to use three Scale elements, one per row, that you set as checkboxes. Then you manipulate the aesthetics to make a checked option appear as a black square, and an unchecked option as a white square
The test part in
wait
is a little tricky, because up to PennController 2.0,.test.selected
does not behave as expected on a checkbox Scale. However, one can write some concise javascript code to test that the right boxes are (un)selectedHere’s the script:
Template("dotmemorization.csv", row => newTrial("dot-pattern", defaultScale.checkbox().center().log().print() , newScale("row1", " ", " ", " "),newScale("row2", " ", " ", " "),newScale("row3", " ", " ", " ") , getScale("row1").select(row.r1c1==1?0:-1).select(row.r1c2==1?1:-1).select(row.r1c3==1?2:-1), getScale("row2").select(row.r2c1==1?0:-1).select(row.r2c2==1?1:-1).select(row.r2c3==1?2:-1), getScale("row3").select(row.r3c1==1?0:-1).select(row.r3c2==1?1:-1).select(row.r3c3==1?2:-1) , newTimer(850).start().wait() , getScale("row1").unselect(0,"log").unselect(1,"log").unselect(2,"log").remove(), getScale("row2").unselect(0,"log").unselect(1,"log").unselect(2,"log").remove(), getScale("row3").unselect(0,"log").unselect(1,"log").unselect(2,"log").remove() , newText('inst', 'Now produce the pattern').css("margin-bottom","20px").print().center() , getScale("row1").print(),getScale("row2").print(),getScale("row3").print() , newButton("press") .print() .wait( newFunction( ()=>[...document.querySelectorAll(".PennController-Scale input")].map(c=>Number(c.checked)).join('') ) .test.is([row.r1c1,row.r1c2,row.r1c3,row.r2c1,row.r2c2,row.r2c3,row.r3c1,row.r3c2,row.r3c3].join('') ) .failure( newText("Try again!").print() ) ) .center() ) )
And here’s what to put in global_main.css to make the checkboxes appear as white and black boxes:
.PennController-row1 input, .PennController-row2 input, .PennController-row3 input { display: none; } .PennController-row1 input + label, .PennController-row2 input + label, .PennController-row3 input + label { display: inline-block; width: 40px; height: 40px; background-color: white; border: solid 1px grey; } .PennController-row1 input:checked + label, .PennController-row2 input:checked + label, .PennController-row3 input:checked + label { background-color: black; }
Jeremy
Jeremy
KeymasterHi,
I apologize for the issue: I brought some modifications to the server’s configuration yesterday and mistakenly cut communication between the farm and the database
Things should be back to normal now
Jeremy
Jeremy
KeymasterHi,
My bad, the code should have been
newTimer("callback",1) .callback( getAudio("audio").wait().remove() , getText("replay").remove() ) .start()
Jeremy
-
This reply was modified 2 years, 5 months ago by
Jeremy. Reason: fix order
Jeremy
KeymasterHi,
1) You can not use
getVar
inside anewX
command. Instead, you need to use it inside the Text element’stext
command, like this:newText("sentence", "").text( getVar("segmentation") )
2) The easiest solution would be to create as many TextInput elements that you will allow the user to add, and dynamically add/remove them to/from the page. Here is an example with the possibility of adding up to 5 extra input boxes: https://farm.pcibex.net/r/kZmhKa/
Jeremy
Jeremy
KeymasterHi Laia,
Your code actually works pretty well already, the only problem is that the text on the left overflows onto the text on the right. This is because you didn’t explicitly set a width to the text element, so it inherits the width of its container, the Canvas element
Just give an explicit width to your Text elements and you’ll be good:
getText(oneFirst?"sentence1":"sentence2").size(300,"auto").print( 20,0,getCanvas("frases")), getText(oneFirst?"sentence2":"sentence1").size(300,"auto").print(350,0,getCanvas("frases")),
Jeremy
Jeremy
KeymasterHi,
Remove the Text element in the same callback, and don’t
wait
for it (you cannotwait
for Text elements):newTimer("callback",1) .callback( getText("replay").remove() , getAudio("audio").wait().remove() ) .start()
Jeremy
January 22, 2023 at 2:59 pm in reply to: Presenting self-defined regions in self-paced reading #9853Jeremy
KeymasterHi,
PennController is a simple extension to IBEX, so you can still do what you used to do. For example, this is still valid:
items = [ ["label", "DashedSentence", {s: ["This is", "what", "I", "have", "in mind"]}] ];
However, you can only store strings in a CSV file, you cannot directly store a javascript array (which is what
["This is", "what", "I", "have", "in mind"]
is) so to accomplish the same using a string from a CSV file, you need to split it into an array, using a character of your choice to serve as the splitting character. Say you choose to use ~, then you can haveThis is~what~I~have~in mind
in your CSV file, and then do this in yourTemplate
command:Template( row => newTrial( newController("DashedSentence", {s: row.sentence.split("~")}) .print() .log() .wait() ) )
Jeremy
Jeremy
KeymasterHi Yev,
PennController always gives focus to the most recently added TextInput element. If at one point in your trial, you want focus to go to a given TextInput element, you can use the Function element to execute some javascript code during runtime:
newTrial( newTextInput("first").print() , newTextInput("second").print() , newFunction( ()=>document.querySelector(".PennController-first").focus() ).call() , newButton("Next").print().wait() )
Jeremy
Jeremy
KeymasterHi Nico,
As long as you have the exact same list of groups in your two tables, you can use a
Template
command for one table and another command for the other table, and participants will be assigned the same group across tables. So this is how I would do things:Template("Mix.csv", row => newTrial("Global", // etc. Template("CT.csv", row => newTrial("Random", // etc. Sequence( ...( Math.random() > 0.5 ? [ randomize("Global") , randomize("Random") ] : [ randomize("Random") , randomize("Global") ] ) )
Jeremy
January 17, 2023 at 2:38 pm in reply to: Randomizing the sentences in a self-paced reading experiment #9846Jeremy
KeymasterHello,
If you want to use the method in that project, you would just need to repeat the lines from
var count=2;
tonewArray.push(originalArray[i]);
using new variable names (eg.count2
,newArray2
,originalArray2
) and in yournewTrial
command, reference the newnewArray
variable in place of the sentences followed by a question, just like you’re currently doing with the other ones, eg.newController("DashedSentence", {s : newArray2[0]})
However you may be interested in creating one trial per sentence(/question) instead of including them all in a single trial. To do so, you would list your sentences and questions in a CSV file and use
Template
to create as many trial as you have rows in the CSV file. You can conditionally include a question in the trial by looking up the value of a column, as described in this post. Then you can simply userandomize
orrshuffle
inSequence
to randomize the order in which the sentences(+questions) appearJeremy
January 11, 2023 at 6:45 pm in reply to: Pseudorandomization with no two items of the same type in a row #9840Jeremy
KeymasterHi Dari,
The method used by the function in that post is too brute force to efficiently return a sequence every time the experiment is run
Use this instead:
function rnmt(toFill,fillFrom,n){ if (fillFrom.length<1) return toFill; let lst = toFill[toFill.length-1], nxt = fillFrom[0]; if (toFill.length>=n) { if (nxt.type == lst.type) { let lstN = 0; for (let i=toFill.length-1; i>=0 && toFill[i].type==lst.type; i--) lstN += 1; if (lstN>=n) { for (let i=0; i<fillFrom.length && nxt.type==lst.type; i++) { fillFrom = [...fillFrom.slice(1,),fillFrom[0]]; nxt = fillFrom[0]; } if (nxt.type==lst.type) return false; } } } return rnmt([...toFill,nxt],fillFrom.slice(1,),n); } function RandomizeNoMoreThan(predicate,n) { this.args = [predicate]; this.run = function(arrays) { let ret = false; while (!ret){ fisherYates(arrays[0]); ret = rnmt([],[...arrays[0]],n); } return ret }; } function randomizeNoMoreThan(predicate, n) { return new RandomizeNoMoreThan(predicate,n); }
Jeremy
Jeremy
KeymasterHi,
You have two options. One is to use one column per sentence, so if you have two sentences you need two columns instead of one for the sentences in your CSV file. Let’s say you name your first column text_1 and the second column text_2, then you would do
newController("DashedSentence", {s : [row.text_1, row.text_2]})
Another option is to use javascript to split the string coming from the table into multiple sub-strings. Assuming you are separating your sentences with the comma character (meaning no comma will ever occur as an actual punctuation in a sentence) then you would proceed like this:
newController("DashedSentence", {s : row.text_1.split(',') })
Jeremy
January 6, 2023 at 4:12 pm in reply to: adding comprehension quesiton to self paced reading task #9836Jeremy
KeymasterHi Doğan,
Apologies for the late reply. The native Ibex controller called DashedAcceptabilityJudgment readily provides these two options. Here’s an example of a PennController trial using it:
newTrial( newController("DashedAcceptabilityJudgment", { s: "While Alex was watching TV, Sue was washing the dishes", mode: "self-paced reading", q: "Who was watching TV?", as: ["Alex", "Sue"] }) .log() .print() .wait() )
Jeremy
Jeremy
KeymasterHi Emiel,
If you want to prevent the experiment from sending the results when the recordings fail to upload (even though the two things are separate, and as you found out results can still be saved even though recordings aren’t uploaded) then you can do something like this:
Sequence( "init", sepWith("async", rshuffle("letter","picture")), "sync", "last" ) UploadRecordings("sync") newTrial("last", newFunction("check upload", ()=>PennController.UploadRecordingsError) .test.is(undefined) .failure( newText("error", "There was a problem sending the recordings to the server. ") .color("red") .print() , newButton().wait() ) )
The idea is you manually insert a synchronous
UploadRecordings
in yourSequence
and check in the next trial whetherPennController.UploadRecordingsError
reports an error: if so, then you usenewButton().wait()
to prevent the trial from completing and proceeding to the end of the experiment (=sending the results)Jeremy
-
This reply was modified 2 years, 5 months ago by
-
AuthorPosts