Forum Replies Created
-
AuthorPosts
-
September 18, 2021 at 9:14 pm in reply to: Translation for the explanatory messages of eye-tracking module #7279
Jeremy
KeymasterHello Carlos,
My code only targets text nodes, you cannot target HTML tags like
<p>
with it.At this point editing PennController.js (in your project’s Modules folder) might be the way to go. What I said above is actually not true: all the text is in that file (PennController.js), the only content that’s fetched from a distant server are the illustrate images. You can look for
ambient
in PennController.js and you’ll find the section you need to edit in the fileJeremy
September 17, 2021 at 12:31 pm in reply to: Translation for the explanatory messages of eye-tracking module #7272Jeremy
KeymasterHello,
All this is fetched from a distant page: you won’t find its content locally in your project on the PCIbex Farm
As a general solution, you can create a new .js file in your project’s Scripts folder, with this at the top:
function textNodesUnder(el){ var n, a=[], walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false); while(n=walk.nextNode()) a.push(n); return a; } const targetNode = document.documentElement; const config = { attributes: false, childList: true, subtree: true }; const replaceTexts = new Map(); const callback = function(mutationsList, observer) { if (!document.body) return; for(const mutation of mutationsList) { if (mutation.type === 'childList') { const textNodes = textNodesUnder(document.body); var key, keys = replaceTexts.keys(); while ((key=keys.next()) && !key.done) for (let node of textNodes){ if (node.textContent) node.textContent = node.textContent.replace(key.value, replaceTexts.get(key.value)); } } } }; const observer = new MutationObserver(callback); observer.observe(targetNode, config);
Then you can list texts to replace below that piece of code this way:
replaceTexts.set( "It looks like we were not able to precisely calibrate the tracker:", "Il semble que nous n'avons pas pu calibrer le tracker précisément" ).set( /You calibration score is (\d+) and you need at least (\d+)/, "Votre score est de $1 et vous avez besoin d'au moins $2" )
As you can see, you can pass a simple string (first
set
command) or a regular expression (secondset
command)Jeremy
September 16, 2021 at 3:16 pm in reply to: "We're sorry, but something went wrong" – Can't enter one of my experiments #7269Jeremy
KeymasterThe error has been fixed, however I had to delete the file named “questions.html,” which was corrupted
Jeremy
September 16, 2021 at 1:32 pm in reply to: "We're sorry, but something went wrong" – Can't enter one of my experiments #7267Jeremy
KeymasterHello Jack,
Did you try clearing your browser’s cache and trying again? The servers were down for a few seconds not long before you posted your message here, so it might be that you tried accessing that project during that time interval and your browser remembers visiting an error page
If the problem persists, please share your project’s slug with me (the 6-character sequence between
experiments/
and/edit
in the URL when you try to edit your project)Jeremy
September 16, 2021 at 11:00 am in reply to: How to add a "come back" button to the practice trial #7265Jeremy
KeymasterHi,
Here’s a demo of what you need to do: https://farm.pcibex.net/r/OYjfNM/
In this demo project, all the trials but “instructions_E” are dummy trials for illustration purposes, but that doesn’t matter to the logic applied
Jeremy
Jeremy
KeymasterHi Juliana,
You have several occurrences of
"
s in the Frase column of your two Experimentais* tables that are not meant to delineate a text cell:Experimentais1.csv
"aproveita a noite" "Moby Dick"
Experimentais2.csv
"carpe_diem"
My suggestion is you use the
“
/”
characters instead, like you’re already doing for“carpe diem”
in Experimentais1.csvOnce you replace those characters, your experiment runs normally
Jeremy
Jeremy
KeymasterI was able to reproduce the bug: sometimes global_z.css is loaded before global_main.css, sometimes it’s the other way around. I’ll have to fix things so it always serves the files in the same order. In the meantime, I invite you to place everything in global_main.css (adding a global_z.css file was only *necessary* on the old farms, where you couldn’t edit global_main.css)
Jeremy
Jeremy
KeymasterHi Elise,
Thanks for reporting this bug. You’re missing a closing
;
at line 47 of global_z.css, but I doubt this could be causing the bug you describe. I’ve been unable to reproduce the problem so far, unfortunatelyJeremy
September 14, 2021 at 10:12 am in reply to: How to add a "come back" button to the practice trial #7255Jeremy
KeymasterHi,
You are
wait
ing for a click on the “come back” button, so whatever comes after thatwait
command won’t be executed until you click the “come back” button, but because thecallback
commands executed upon click on that buttonend
s the trial immediately, the commands belowwait
never get to be executed at all. Just remove.wait()
and things will workYou might want to replace
jump("instructions_D")
withjump(startsWith("p_trial_"))
if you don’t want to show the practice instructions again..callback("intructions_D")
won’t work because"instructions_D"
is not a command, it’s a plain stringJeremy
September 11, 2021 at 3:02 pm in reply to: Context Sentences removing for comprehension questions in SPR #7251Jeremy
KeymasterDear Kristina,
Most of your trials print a Text element on top of the DashedSentence Controller element, so naturally the two will appear together on the screen
Some trials have a Text element and a Scale element that get printed after the DashedSentence Controller element is removed, so you won’t see the latter with the former two together on the page at the same time
A few trials have both a Text element
print
ed before the DashedSentence Controller element, and a Text element + a Scale elementprint
ed after the Controller element’swait
andremove
commands: for those, the content of the Controller element (the dashed sentence) will disappear from the page once the script gets to itsremove
command, but because you never useremove
on the first Text element, it will stay on the page as the second Text element and the Scale element get printed below it. You could usegetText("sentences").remove()
just before printing the second Text element if you want it to disappear thenAs a side note, I strongly recommend you identify types of trials (seems like you have 4: some have no context sentences, some have one, some have a question, and some have both) and reference their content in a CSV table so you can use a
Template
rather than manually creating ~200newTrial
sJeremy
September 10, 2021 at 10:14 pm in reply to: How to add a "come back" button to the practice trial #7249Jeremy
KeymasterHello,
If you don’t mind the progress bar widening as the participant goes back in the sequence, since PennController 2.0 you can do this:
newButton("come back") .callback( jump("practice trial") , end() ) .print()
(that’s assuming your practice trial is literally labeled
practice trial
)Jeremy
Jeremy
KeymasterHi Elise,
The
randomizeNoMoreThan
function is based on exact match between labels: it will only make sure that, say,con-tst
is not followed by more than two othercon-tst
, however, it will be OK with a sequence likecon-tst
–con-fil
–con-tst
–con-fil
You can minimally edit it, so that you can pass it predicates after
n
to identify which trials from the set it should target:function RandomizeNoMoreThan(predicate,n,...filters) { this.args = [predicate]; this.run = function(arrays) { let moreThanN = true; let order; while (moreThanN){ order = randomize(predicate).run(arrays); moreThanN = false; let previousType = ""; let current_n = 0; for (let i = 0; i < order.length; i++){ let currentType = order[i][0].type; if (currentType==previousType || (filters.length&&filters.filter(f=>f(currentType)).length)){ current_n++; if (current_n > n){ moreThanN = true; break; } } else{ current_n = 1; previousType = currentType; } } } return order; }; } function randomizeNoMoreThan(predicate, n, ...filters) { return new RandomizeNoMoreThan(predicate,n, ...filters); }
Then you can use it like this:
Sequence( randomizeNoMoreThan( anyOf(startsWith("con-"), startsWith("incon-"), startsWith("neu-")), 3, startsWith("con-") ) )
and it will make sure that you have no subseries of more than three trials whose label starts with
con-
in a row (it will allow for 3+ series of trials with any other labels)Jeremy
Jeremy
KeymasterHi Noelia,
Your table uses
;
s as separators. It should use either,
s or tabsJeremy
Jeremy
KeymasterHi Diana,
1) I am not sure what you mean by FPS. It usually means “frames per second,” which is a measure of how “dense” is video rendering: the denser (higher FPS) the more fluid the video appears, the less dense (lower FPS) the jerkier the video appears. Do you mean using frame numbers instead of seconds to point to positions in the video? I’m not sure it is feasible, and if it is, it wouldn’t be easy. More importantly, I strongly suspect that your participants will have a hard time understanding how to use this alternative cursor, when all they’re given (and which they most likely are already familiar with) are timecodes of the form MM:ss:mm (MM for minutes, mm for milliseconds). I do think using unrounded seconds would be the most straightforward way to go
2) In this case PennController will not give you all you need for what you want to do out of the box. You will need to create as many new elements as the participant needs, not just elements named “…1” and “…2” as you have now. PennController interprets the
newX
commands at the beginning of the experiment and not at runtime, so you cannot just usenewX
to dynamically create an indefinite number of new elements. You would need to wrap thosenewX
commands in Function elements, so they can be interpreted during runtime. Here’s a very simplified illustration of the concept, not actually using a Video element for simplicity:newTrial( newCanvas("container", "auto", "auto").print() , newScale("dummy", 100).slider().print(getCanvas("container")) , newVar("time", -1), newVar("times", []).log() , newFunction( () => document.querySelector(".PennController-dummy input").addEventListener("change", e=>{ getVar("time").set(e.target.value)._runPromises(); const textSpan = document.querySelector(".PennController-Text-container:last-child span"); textSpan.innerHTML = textSpan.innerHTML.replace(/<strong>.*<\/strong>$/, "<strong>"+e.target.value+"</strong>"); }) ).call() , newButton("Add a segment") .callback( getVar("time").testNot.is(v=>v<0).success( getVar("times").set(v=>[...v,getVar("time").value]) ) , newFunction( ()=>new Promise(r => newText("The natural ending for the first meaningful event would be at: <strong></strong>") .print(getCanvas("container")) ._runPromises().then(r) )).call() ) .print() .click() // start with one segment already , newButton("Finish").print().wait() , getVar("time").testNot.is(v=>v<0).success( getVar("times").set(v=>[...v,getVar("time").value]) ) )
Note that in this example, you get warning about creating new Function and Text elements with the same names, but those are not fatal to the execution of the program
Jeremy
Jeremy
KeymasterHello Aymeric,
I think what you are looking for are
test
commands, one of which is illustrated in the Stroop task template (line 42)If you need to report whether the choice was correct as an extra column for every result line of the corresponding trial, you can use a global Var element:
Template( "table.csv" , row => newTrial( newVar("correct").global() , newText( row.Word ).bold().center().print() , newText("F: word ") .after(newText(" J: not a word")) .center() .print() , newKey("FJ") .log() .wait() .test.pressed( row.Key ) .success( getVar("correct").set(1) ) .failure( getVar("correct").set(0) ) ) .log( "correct" , getVar("correct") ) )
Sample of table.csv:
Word,Key Hello,F Wold,J
I don’t see any general argument for doing it one way or another, just do what’s more convenient for you
Jeremy
-
AuthorPosts