html code with embedded script

PennController for IBEX Forums Support html code with embedded script

This topic contains 11 replies, has 4 voices, and was last updated by Avatar abraver 12 months ago.

Viewing 12 posts - 1 through 12 (of 12 total)
  • Author
    Posts
  • #5044
    Avatar
    EricP
    Participant

    Hi,

    I’m trying to add a standardized headphone check as part of my experiment. There’s an html script here: https://github.com/mcdermottLab/HeadphoneCheck (code pasted below) that can run it, but I can’t figure out how (if) this can be incorporated into a PCIbex script. So, that’s my question: is there an easy (lazy) way to make this work in PCIbex, or do I need to decompose it and build from the parts? Probably good to add that I’m pretty clueless when it comes to javascript. Thanks!–for any answer and for your work on PCIbex in general.

    Eric

    <!DOCTYPE html>
    <html>
    <head>
    <!– 1) Import jQuery from Google CDN –>
    <script src=”https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js”></script>
    <!– 2) Import HeadphoneCheck.js (minified) from McDermott S3 server –>
    <script type=”text/javascript” src=”https://s3.amazonaws.com/mcd-headphone-check/v1.0/src/HeadphoneCheck.min.js”></script>
    <!– 2) Import HeadphoneCheck.css from McDermott S3 server –>
    <link rel=”stylesheet” type=”text/css” href=”https://s3.amazonaws.com/mcd-headphone-check/v1.0/src/HeadphoneCheckStyle.css”>

    <script>
    $(document).ready(function() {
    /* 4) Define what to do when the headphone check finishes */
    $(document).on(‘hcHeadphoneCheckEnd’, function(event, data) {
    var headphoneCheckDidPass = data.didPass;
    var headphoneCheckData = data.data;
    var didPassMessage = headphoneCheckDidPass ? ‘passed’ : ‘failed’;
    alert(‘Screening task ‘ + ‘.’);
    });

    var headphoneCheckConfig = {};
    /* 5) Run the headphone check, with customization options defined in headphoneCheckConfig */
    HeadphoneCheck.runHeadphoneCheck(headphoneCheckConfig);
    });
    </script>
    </head>
    <body>
    <!– 3) Create a container div for the headphone check –>
    <div id=”hc-container”></div>
    </body>
    </html>

    #5049
    Jeremy
    Jeremy
    Keymaster

    Hi,

    Yes, it’s possible, I actually implemented an earlier version of this script before myself

    Since you said you’re clueless when it comes to javascript, I don’t know how much of the script below you’ll understand, but it is a full, functional script:

    PennController.ResetPrefix(null)
    
    // The Farm's jQuery library is outdated, we need to polyfill a couple methods
    jQuery.prototype.on = function(...args) { return jQuery.prototype.bind.apply(this, args); }
    jQuery.prototype.prop = function(...args) { return jQuery.prototype.attr.apply(this, args); }
    // Let's dynamically load the HeadphoneCheck script
    var HeadphoneCheckScriptTag = document.createElement("script");
    HeadphoneCheckScriptTag.src = "https://s3.amazonaws.com/mcd-headphone-check/v1.0/src/HeadphoneCheck.min.js";
    document.head.appendChild( HeadphoneCheckScriptTag );
    
    newTrial(
        newButton("check", "Start Heaphone Check")
            .print()
        ,
        // This Canvas will contain the test itself
        newCanvas("headphonecheck", 500,500)
            .print()
        ,
        // The HeadphoneCheck module fills the element whose id is "hc-container"
        newFunction( () => getCanvas("headphonecheck")._element.jQueryElement.attr("id", "hc-container") ).call()
        ,
        getButton("check")
            .wait()
            .remove()
        ,
        // Create this Text element, but don't print it just yet
        newText("failure", "Sorry, you failed the heaphone check")
        ,
        // This is where it all happens
        newFunction( () => {
            $(document).on('hcHeadphoneCheckEnd', function(event, data) {
                getCanvas("headphonecheck").remove()._runPromises();
                if (data.didPass) getButton("dummy").click()._runPromises();
                else getText("failure").print()._runPromises()
            });
            HeadphoneCheck.runHeadphoneCheck() 
        }).call()
        ,
        // This is an invisible button that's clicked in the function above upon success
        newButton("dummy").wait()
    )

    Jeremy

    #5051
    Avatar
    EricP
    Participant

    Awesome. I added it to my script and it works as advertised. Thanks!

    #5560
    Avatar
    Ncomeau
    Participant

    Is there any way to customize the displayed instructional text for this implementation of the headphone check?

    #5562
    Jeremy
    Jeremy
    Keymaster

    You would need to upload HeadphoneCheck.js to your project and use that instead of directly loading it from S3. Then you could edit the text in HeadphoneCheck.js.

    Jeremy

    #5565
    Avatar
    Ncomeau
    Participant

    Can I refer to HeadphoneCheck.js explicitly if I have it in my scripts (HeadphoneCheckScriptTag.src = “HeadphoneCheck.js”;) or would I need to point it to the URL representing my experiment’s scripts directory?

    #5566
    Jeremy
    Jeremy
    Keymaster

    No as long as you have it in your scripts it will load, so you no longer need:

    var HeadphoneCheckScriptTag = document.createElement("script");
    HeadphoneCheckScriptTag.src = "https://s3.amazonaws.com/mcd-headphone-check/v1.0/src/HeadphoneCheck.min.js";
    document.head.appendChild( HeadphoneCheckScriptTag );

    But maybe move this to the top of HeadphoneCheck.js:

    jQuery.prototype.on = function(...args) { return jQuery.prototype.bind.apply(this, args); }
    jQuery.prototype.prop = function(...args) { return jQuery.prototype.attr.apply(this, args); }

    Jeremy

    #5574
    Avatar
    Ncomeau
    Participant

    Removing those lines works, but moving the jQuery lines to the top of HeadphoneCheck.js yields “Uncaught TypeError: $(…).prop is not a function” in my PennController debugger error tab right after I play the calibration sound and try to click continue. Things work fine when I leave them at the top of my main.js though.

    #5865
    Avatar
    abraver
    Participant

    Hi—

    I’m trying to implement the code in the above comment, but I’d like to send the result to the log, rather than have the page serve as a gate for people not using headphones.

    I’ve tried everything I can think of—for example, setting a newVar inside the function and trying to get that with .log at the end of the trial—but no luck.

    How can I get a JavaScript variable from inside a function to be saved to the log?

    Thanks in advance.

    #5866
    Avatar
    abraver
    Participant

    For more context, here’s the trial in question. I can print the value I’m looking for to the JavaScript console (console.log(data.data.totalCorrect) but I figure out how to get that into the PCIBex log no matter where I put the .log() function, and no matter whether I try to use data.data.totalCorrect as an argument of .log directly, or if I try to set a new variable and call getVar inside of .log.

    newTrial("headphonecheck",
        newButton("check", "Start Heaphone Check")
            .print()
        ,
        // This Canvas will contain the test itself
        newCanvas("headphonecheck", 500,500)
            .print()
        ,
        // The HeadphoneCheck module fills the element whose id is "hc-container"
        newFunction( () => getCanvas("headphonecheck")._element.jQueryElement.attr("id", "hc-container") ).call()
        ,
        getButton("check")
            .wait()
            .remove()
        ,
        // Create this Text element, but don't print it just yet
        newText("failure", "Sorry, you failed the heaphone check")
        ,
        // This is where it all happens
        newFunction( () => {
            $(document).on('hcHeadphoneCheckEnd', function(event, data) {
                getCanvas("headphonecheck").remove()._runPromises();
                
                    getButton("dummy").click()._runPromises();
                    
                    console.log("totalCorrect");
                    console.log(data.data.totalCorrect);
    
            });
            HeadphoneCheck.runHeadphoneCheck({totalTrials: 1,
                trialsPerPage: 1,
                doCalibration: false // we definitely want to change this for the real one
            })
    
        }).call()
    
        ,
        
        // This is an invisible button that's clicked in the function above upon success
        newButton("dummy").wait()
        
    )
    
    #5870
    Jeremy
    Jeremy
    Keymaster

    Hi,

    You cannot directly send messages to the Logs (or Errors) tab of the Debug window. Your console.log commands will send messages to the javascript console.

    Note that neither console.log nor a line appearing in the Logs tab of the Debug window means that the relevant event/value will be logged in the results file.

    If you want to save totalCorrect in the results file, you will need to sort of “hack” into how PennController-native elements are handled. In practice, it basically just means adding ._runPromises() after the series of PennController commands (like the code’s already doing with the dummy Button element):

    newTrial("headphonecheck",
        newButton("check", "Start Heaphone Check")
            .print()
        ,
        // This Canvas will contain the test itself
        newCanvas("headphonecheck", 500,500)
            .print()
        ,
        // The HeadphoneCheck module fills the element whose id is "hc-container"
        newFunction( () => getCanvas("headphonecheck")._element.jQueryElement.attr("id", "hc-container") ).call()
        ,
        getButton("check")
            .wait()
            .remove()
        ,
        // Create this Text element, but don't print it just yet
        newText("failure", "Sorry, you failed the heaphone check")
        ,
        newVar("totalCorrect")
            .global()
        ,
        // This is where it all happens
        newFunction( () => {
            $(document).on('hcHeadphoneCheckEnd', function(event, data) {
                getCanvas("headphonecheck").remove()._runPromises();
                    getButton("dummy").click()._runPromises();
                    getVar("totalCorrect").set( data.data.totalCorrect )._runPromises();
            });
            HeadphoneCheck.runHeadphoneCheck({totalTrials: 1,
                trialsPerPage: 1,
                doCalibration: false // we definitely want to change this for the real one
            })
    
        }).call()
        ,
        // This is an invisible button that's clicked in the function above upon success
        newButton("dummy").wait()
    )
    .log( "totalCorrect" , getVar("totalCorrect") )

    Note that you won’t get any reports of totalCorrect in your results file for participants who fail the headphone check, as the experiment will never move to the next trial, so nothing will get sent to the server.

    Jeremy

    #5871
    Avatar
    abraver
    Participant

    Many thanks Jeremy (and thanks for this great tool in general). Works like a charm.

    (And re: console.log, I was just making sure that the headphone check script was actually reporting *something* and the issue wasn’t its fault.)

Viewing 12 posts - 1 through 12 (of 12 total)

You must be logged in to reply to this topic.