Jeremy

Forum Replies Created

Viewing 15 posts - 736 through 750 (of 1,522 total)
  • Author
    Posts
  • in reply to: Translation for the explanatory messages of eye-tracking module #7279
    Jeremy
    Keymaster

    Hello 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 file

    Jeremy

    in reply to: Translation for the explanatory messages of eye-tracking module #7272
    Jeremy
    Keymaster

    Hello,

    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 (second set command)

    Jeremy

    Jeremy
    Keymaster

    The error has been fixed, however I had to delete the file named “questions.html,” which was corrupted

    Jeremy

    Jeremy
    Keymaster

    Hello 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

    in reply to: How to add a "come back" button to the practice trial #7265
    Jeremy
    Keymaster

    Hi,

    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

    in reply to: Not loading … #7262
    Jeremy
    Keymaster

    Hi 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.csv

    Once you replace those characters, your experiment runs normally

    Jeremy

    in reply to: Inconsistent css behavior between reloads #7258
    Jeremy
    Keymaster

    I 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

    in reply to: Inconsistent css behavior between reloads #7256
    Jeremy
    Keymaster

    Hi 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, unfortunately

    Jeremy

    in reply to: How to add a "come back" button to the practice trial #7255
    Jeremy
    Keymaster

    Hi,

    You are waiting for a click on the “come back” button, so whatever comes after that wait command won’t be executed until you click the “come back” button, but because the callback commands executed upon click on that button ends the trial immediately, the commands below wait never get to be executed at all. Just remove .wait() and things will work

    You might want to replace jump("instructions_D") with jump(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 string

    Jeremy

    in reply to: Context Sentences removing for comprehension questions in SPR #7251
    Jeremy
    Keymaster

    Dear 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 printed before the DashedSentence Controller element, and a Text element + a Scale element printed after the Controller element’s wait and remove commands: for those, the content of the Controller element (the dashed sentence) will disappear from the page once the script gets to its remove command, but because you never use remove 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 use getText("sentences").remove() just before printing the second Text element if you want it to disappear then

    As 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 ~200 newTrials

    Jeremy

    in reply to: How to add a "come back" button to the practice trial #7249
    Jeremy
    Keymaster

    Hello,

    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

    in reply to: RandomizeNoMoreThan #7248
    Jeremy
    Keymaster

    Hi 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 other con-tst, however, it will be OK with a sequence like con-tstcon-filcon-tstcon-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

    in reply to: Troubleshooting #7243
    Jeremy
    Keymaster

    Hi Noelia,

    Your table uses ;s as separators. It should use either ,s or tabs

    Jeremy

    in reply to: Event segmentation task #7240
    Jeremy
    Keymaster

    Hi 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 use newX to dynamically create an indefinite number of new elements. You would need to wrap those newX 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

    in reply to: Compute accuracy for each trial #7239
    Jeremy
    Keymaster

    Hello 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

Viewing 15 posts - 736 through 750 (of 1,522 total)