Global Commands

PennController

PennController( ... )

or PennController( "label" , ... ) (since version 1.0)

The PennController command lets you write the script of your trials. You can use it in three environments: on its own to create a trial (since version 1.0), within PennController.Template to generate items from a spreadsheet, or within the definition of the Ibex variable items to create single elements.

You pass a sequence of commands as its parameter to define the script of your trial. Optionally, you can also pass a string as the first parameter to give a label to your trial which you can refer to when defining the experiment’s sequence order in shuffleSequence or (since version 1.1) in PennController.Sequence. If you specify no label string, the trial will be labeled unlabeled.

Example:

PennController(
    newButton("helloworld", "Hello world!")
        .print()
        .wait()
);

PennController.Template(
    row => PennController( row.Type ,
        newButton("button", row.ButtonText)
            .print()
            .wait()
    )
);

Creates one trial labeled unlabeled with a single button on the screen reading Hello world!, and generate one-button trials from a spreadsheet, assigning the text from its Type cells as their label and the text from its ButtonText cells as the button’s text (see PennController.Template).

PennController.AddHost

PennController.AddHost( url )

You can use PennController.AddHost to spare you from specifying a URL when you create an element with newAudio, newVideo or newImage. The URL must end with /.

You can pass multiple URL strings (separated by comma) or use AddHost as many times as you want. If you do so, PennController will try to look for audio/images at the host URLs in the order they were provided, and stop looking as soon as it finds one.

Note that you can still directly specify URLs when creating a new element even if you added a host URL with AddHost.

Example:

PennController.AddHost("https://files.lab.florianschwarz.net/test/");
PennController.AddHost("http://spellout.net/ibexfarm/static/images/");

PennController(
    newImage("ibex", "ibex.jpg")
        .print()
    ,
    newImage("wait", "https://openclipart.org/image/300px/svg_to_png/23920/Anonymous-Sandglass.png")
        .print()
    ,
    newAudio("sentence", "test.mp3")
        .play()
        .wait()
);

The trial shows two images and plays one audio file. PennController will first try send a request for the file https://myserver/images/square.png and another one for the file https://myserver/audios/square.png. The same is true for the audio file: PennController will first send a request for the file https://myserver/images/shape.mp3 and another one for the file https://myserver/audios/shape.mp3. PennController uses the resource from the first successfully resolving request: in this example, assuming there is a file named square.png at https://myserver/images/ and a file named shape.mp3 at https://myserver/audios/ (and only there), those are the resources that PennController will use (it may still trigger warnings in the console for the unsuccessful requests). PennController will only look for the wait image at http://publicserver/wait.gif, since a full URL is provided there.

PennController.AddTable

PennController.AddTable( name, csv_string )

You can use this command to manually define a table to use with PennController.Template. You normally do not need to use this command since, as of PennController beta 0.4, you can directly upload your CSV file under chunk_includes.

Example:

PennController.AddTable( "myTable",  // Name of the table
    "Type,Sentence\n"+           // Column names (mind the \n)
    "practice,Hello world!\n"+   // First row (mind the \n)
    "test,Bye world!"            // Second row
);

PennController.Template( "myTable" ,
    row => PennController(
        newText("type", row.Type)
            .print()
        ,
        newButton("sentence", row.Sentence)
            .print()
            .wait()
    )
);

PennController.CheckPreloaded

PennController.CheckPreloaded()

or PennController.CheckPreloaded( labels )

or PennController.CheckPreloaded( delay )

or PennController.CheckPreloaded( labels , delay )

By default, PennController starts the preloading of every image, audio and video resource used in your experiment as soon as the experiment starts running and it will wait before running a trial that all the resources it uses are preloaded. Since this could result in undesirable random breaks in the flow of your experiment, you can control when PennController should check that the resources used by (subsets of) your trials are preloaded before proceeding by using PennController.CheckPreloaded.

Note that PennController.CheckPreloaded inserts a trial in your experiment the same way PennController does. You can label the generated trial by using the label command so you can refer to it within PennController.Sequence.

You can also define a delay after which the trial sequence will proceed anyway if preloading fails, by passing a number as the last parameter of the command (in milliseconds). Each resource that PennController fails to preload will be reported in a new line in the results file.

Example:

PennController.CheckPreloaded("audio", "video");

PennController( "audio" ,
    newAudio("audio file", "test.mp3")
        .print()
        .play()
        .wait()
);

PennController( "video" ,
    newVideo("video file", "skate.mp4")
        .print()
        .play()
        .wait()
);

PennController.CheckPreloaded();

PennController( "filler" ,
    newImage("image", "square.png")
        .print()
    ,
    newKey("fj", "FJ")
        .wait()
);

PennController( "test" ,
    newImage("image", "triangle.png")
        .print()
    ,
    newKey("fj", "FJ")
        .wait()
);

Will make sure that test.mp3 and skate.mp4 are fully preloaded before running the very first PennController trial, labeled audio. After the PennController trial labeled video has ended, will make sure that all image, audio and video resources ever used in the experiment are preloaded before running the trial labeled filler (which practically means it will check that square.png and triangle.png are preloaded, since the first preloader made sure the audio and video resources were already preloaded).

PennController.Debug

PennController.Debug() (since PennController 1.2)

Runs your experiment in Debug mode.

A pop-in console appears at the bottom-left corner of the page when your experiment runs in Debug mode. The Debug console gives you information on the experiment’s structure and on the current trial. Buttons in the Sequence and Log tabs let you force the experiment to move on to the next steps/trials. The Tables tab lists the tables available to your script, which you can click to explore.

Each time you try to quit/refresh the experiment page while in Debug mode, you will be prompted a confirmation message.

In Debug mode, the items variable containing the content of all your trials is publicly accessible. This means that, for example, you can open your browser Javascript’s console and type items to take a look at how each of your trials is defined.

Make sure to delete any PennController.Debug() command before running the final version of your experiment (the items variable will be undefined during runtime).

Example:

PennController.Debug()

PennController(
    newButton("hello", "Hello world")
        .print()
        .wait()
)

PennController.AddTable( "Words" ,
    "item,Word\n"  +
       "1,Hello\n" +
       "2,World"
)

PennController.Template( "Words" ,
    row => PennController(
        newButton(row.Word)
            .print()
            .wait()
    )
)

PennController.DownloadVoiceButton

PennController.DownloadVoiceButton( text )

Generates a <button> that, when clicked, proposes to download a ZIP archive containing all the audio recordings collected during the experiment.

This should be used only after the recordings have been sent to the server you specified (see this page). You would typically send the results early with PennController.SendResults and show an exit page with the button after that.

Example:

PennController.InitiateRecorder("https://myserver/uploadVoices/saveRecordings.php");

PennController( "record" ,
    newVoiceRecorder("recorder")
        .print()
        .wait()
);

PennController.SendResults();

PennController(
    newFunction("check upload", ()=>PennController.uploadRecordingsError)
        .test.is()
        .success(
            newText("confirmation", "The recordings were sent to the server. ")
                .print()
        )
        .failure(
            newText("error", "There was a problem sending the recordings to the server. ") 
                .settings.color("red")
                .print()
        )
    ,
    newText("download", PennController.DownloadVoiceButton("Click here to download an archive of your recordings.") )
        .print()
    ,
    newTimer("inifinite", 0)
        .wait()
);

Invites the participant to make a voice recording and send the results to the server. In this case, we specified a dummy URL, so uploading the recordings will fail, and PennController.uploadRecordingsError will accordingly be defined. The last screen will inform the participant of the error (since PennController.uploadRecordingsError is not void/undefined) and will show a button to download an archive of the recordings.

PennController.FeedItems

Deprecated since version 1.0. See PennController.Template.

PennController.Footer

PennController.Footer( sequenceOfCommands ) (since beta 0.3)

Will append sequenceOfCommands to the end of the sequence of commands of each trial created with PennController.

Example:

PennController.Footer(
    newText("validation instructions", "Click to go to next trial")
        .print()
    ,
    newButton("validation button", "Validate")
        .print()
        .wait()
);

PennController(
    newText("test sentence", "The cat chases the mouse")
        .print()
    ,
    newScale("natural", 5)
        .settings.slider()
        .settings.before( newText("left", "Unnatural") )
        .settings.after(  newText("right", "Natural")  )
        .print()
);

Without the footer, the trial defined above would be validated immediately after it has started running, since it includes no wait command. Thankfully, the footer adds a button at the bottom of the page that must be clicked for validation.

PennController.GetTable

PennController.GetTable( tablename )

Refers to a table, where tablename can be the filename of a CSV file you uploaded to chunk_includes or the name of a table that was created with PennController.AddTable.

You would typically use GetTable within PennController.Template when your project contains more than one table and/or when you need to use only a subset of a table, using PennController.GetTable().filter.

Example:

PennController.Template( PennController.GetTable( "spreadsheet.csv" ) ,
    row => PennController( "button trial" , 
        newButton("test button", row.ButtonText)
            .print()
            .wait()
    )
    .log( "Group" , row.Item )
    .log( "Text"  , row.ButtonText )
);

Generate two trials from the table spreadsheet.csv which contains two rows and the columns Item and ButtonText.

PennController.GetTable().filter

PennController.GetTable( tablename ).filter( "column" , "match" )

or PennController.GetTable( tablename ).filter( "column" , /match/ )

or PennController.GetTable( tablename ).filter( function )

Returns a filtered version of the table, containing only rows whose specified column’s value is a match.

If you use a string then the column’s value must match the text exactly. Alternatively, you can use a regular expression to test the column’s value. You can also use a function that will take each row as an argument and should return true to keep the row or false to exclude it.

You can use several filters in chain.

Example:

PennController.Template( 
    PennController.GetTable( "spreadsheet.csv" )
        .filter( row => row.Item > 0 )      // 'Item' should be greater than 0, and
        .filter( "ButtonText" , /second/ )  // 'ButtonText' should contain 'second'
    ,
    row => PennController( "button trial" ,
        newButton("test button", row.ButtonText)
            .print()
            .wait()
    )
    .log( "Item" , row.Item       )
    .log( "Text" , row.ButtonText )
);

Generates only one trial from a subset of the table spreadsheet.csv: first we only consider rows where the value in the Item column is a number greater than 0 (this is practically ineffective, for both rows in spreadsheet.csv already satisfy this condition) and we further consider only rows among those rows where the value of the ButtonText column is a text containing the string second (only the second row satisfies this condition).

PennController.GetTable().setGroupColumn

PennController.GetTable( tablename ).setGroupColumn( columnname )

Tells which column in the table assigns each row to a given group of participants, i.e. which subset of rows PennController will keep for each group of participants listed in the column.

Example:

PennController.SetCounter();

PennController.AddTable( "test_table" , 
    "Spelling,Text\n"+
    "Normal,Hello\n"+
    "Reversed,olleH\n"+
    "Normal,World\n"+
    "Reversed,dlroW"
);

PennController.Template( 
    PennController.GetTable( "test_table" ) 
                  .setGroupColumn( "Spelling" )
    ,
    row => PennController( "button trial" ,
        newButton("the button", row.Text )
            .print()
            .wait()
    )
    .log( "Group" , row.Spelling )
    .log( "Text"  , row.Text     )
);

Creates a 2×4 table called test_table with column names Spelling and Text (manual creation for purpose of illustration—you would normally upload a CSV file to chunk_includes). Uses the column Spelling from this table to identify which rows go with which groups of participants.

Every other time the experiment is run, participants will see trials generated either from the two rows where Spelling is Normal or from the two rows where Spelling is Reversed.

The very first line runs a trial incrementing Ibex’s internal counter at the beginning of the experiment, which determines which group is run, so you can cycle through the rows if you re-run the experiment.

PennController.GetURLParameter

PennController.GetURLParameter( parametername )

Retrieves the value of a parameter that was passed after ? in the URL. This is particularly useful if you pass a uniquely identifying code to the URL when you recruit your participants, like http://spellout.net/ibexexps/PennController/Demo/experiment.html?id=abcdefgh

Example:

PennController( "my trial" ,
    newButton("helloworld", "Hello world!")
        .settings.log()
        .print()
        .wait()
)
.log( "ID" , PennController.GetURLParameter( "id" ) );

Will add a column to each result line logged by my trial reporting the value that was passed after id= in the URL.

PennController.Header

PennController.Header( sequenceOfCommands ) (since beta 0.3)

Will evaluate and run sequenceOfCommands at the beginning of each trial created with PennController.

Note that default commands are immediately evaluated (rather than evaluating upon running). As a consequence, any default command will only have an effect on PennController trials defined after the Header has been set.

Example:

PennController.Header(
    defaultText
        .print()
);

PennController(
    newText("test sentence", "The cat is chasing the mouse")
    ,
    newText("instructions", "How natural is this sentence?")
    ,
    newScale("natural" 5)
        .settings.slider()
        .settings.before( newText("left", "Unnatural") )
        .settings.after(  newText("right", "Natural")  )
        .print()
        .wait()
);

PennController(
    newText("test sentence", "The mouse is being chased by the cat")
    ,
    newText("instructions", "How natural is this sentence?")
    ,
    newScale("natural" 5)
        .settings.slider()
        .settings.before( newText("left", "Unnatural") )
        .settings.after(  newText("right", "Natural")  )
        .print()
        .wait()
);

Though no print command explicitly appears in the trials themselves, the Text elements will be printed onto the page because the header defines print as a default command for all Text elements.

PennController.InitiateRecorder

PennController.InitiateRecorder( url )

or PennController.InitiateRecorder( url , message )

Note: you cannot use this command if you install a custom build of PennController that does not include the VoiceRecorder element.

Use this to specify the URL of the PHP file to upload the audio recordings collected during the experiment.

PennController.InitiateRecorder creates a PennController trial asking for the participant’s consent to record audio samples. You can specify a message to replace the default one. Use the label command on it to determine when it should appear in your sequence of trials.

Example:

PennController.InitiateRecorder(
    "https://myserver/uploadVoices/saveRecordings.php"
    ,
    "This experiment collects audio recordings. \u003Cstrong\u003EOnce you grant it access to your recording device, you will be notified of whether you are being recorded by a label at the top of the page\u003C/strong\u003E"
);

PennController(
    newVoiceRecorder("recorder")
        .print()
        .wait()
);

Adds a page asking for the participant’s authorization to use their recording device, using a custom text message.

PennController().label

PennController().label( name ) (since PennController 1.1)

This is another way to assign a label to a PennController item so you can refer to it in PennController.Sequence. It is most useful called on PennController.CheckPreloaded or PennController.InitiateRecorder since those commands do not take labels as arguments.

Example:

PennController.Sequence( "practice" , "preload-exp" , rshuffle("filler","test") )

PennController.CheckPreloaded( 
  "filler"
  ,
  "test"
).label( "preload-exp" )

Creates an item labeled preload-exp checking that all the resources used in the trials labeled filler or test are preloaded, and refers to its label in PennController.Sequence so it will be run before the filler and test trials, but after the practice trials.

PennController().log

PennController().log( name, value ) (since beta 0.3)

You can use the .log method to add columns to every line corresponding to this trial that is logged in the results file. You can add as many .log as you want.

Example:

PennController(
    newButton("helloworld", "Hello world!")
        .settings.log()
        .print()
        .wait()
)
.log("Trial type", "One-Button")
.log("Text on button", "Hello world!");

Will add One-Button and Hello world! to the end of every line saved to the results file for this trial.

PennController().logAppend

PennController().logAppend( name, value ) (replaced with log since beta 0.3)

PennController().noFooter

PennController().noFooter() (since beta 0.3)

Will not run the footer sequence at the end of the trial.

Example:

PennController.Footer(
    newButton("validate", "Got it!")
        .print()
        .wait()
);

PennController( "with footer" ,
    newScale("score", 5)
        .settings.before( newText("left","Score:") )
        .print()
        .wait()
);
  
PennController( "without footer" ,
    newScale("score", 5)
        .settings.before( newText("left","Score:") )
        .print()
        .wait()
)
.noFooter();

The first trial (labeled with footer) will show a radio-button scale on the screen and reveal a button reading Got it! to be clicked when a radio button is selected. The second trial (labeled without footer) will end right after a radio button is selected.

PennController().noHeader

PennController().noHeader() (since beta 0.3)

Will not run the header sequence at the beginning of the trial. Note that this also concerns default commands defined in the header: those will not be run in trials where you use noHeader.

Example:

PennController.Header(
    defaultButton
        .print()
        .wait()
    ,
    newText("please", "Please give a score")
        .print()
);

PennController( "twoClicks" ,
    newScale("score", 5)
        .print()
    ,
    newButton("validate", "Validate")
        .wait( getScale("score").test.selected() )
);

PennController( "oneClick" ,
    newScale("score", 5)
        .print()
    ,
    newButton("validate", "Validate")
        .print()
        .wait( getScale("score").test.selected() )
)
.noHeader();

The first trial (labeled twoClicks) will show a text at the top of the page, then a radio-button scale and a button. Because the header already contains a wait command, two clicks will be necessary to validate the button (the second one having to happen after a radio-button is selected). On the contrary, the second trial (labeled oneClick) which uses noHeader, will show no text at the top of the page and will only require one click on the button to validate it (assuming a radio-button is selected), because it will only evaluate and execute one wait command.

PennController.PreloadZip

PennController.PreloadZip( url )

or PennController.PreloadZip( url1 , url2, ... )

Instead of fetching audio and images from a distant URL for every single one of your PennController trials using one, you can choose to store them in ZIP archives that you upload on your server. Use PennController.PreloadZip to tell where to look the ZIP archives up.

See this page for more details.

PennController.ResetPrefix

PennController.ResetPrefix( "prefix" )

or PennController.ResetPrefix( null )

NOTE: until beta 0.2, a bug prevented from defining a custom prefix (only null worked)—this bug was fixed in beta 0.3. As of beta 0.3, the default prefix is no longer PennController.instructions, but PennController.Elements.

By default, all the commands to create and refer back to elements in a PennController trial should be preceded by a prefix (PennController.Elements. as of beta 0.3). It is standard practice that every command added by a javascript module (which is what PennController is) uses a specific prefix (in this case, PennController). This is to avoid inadvertently overwriting (or being overwritten by) commands of the same names defined somewhere else, outside of the module. For instance, it could be that you or another module defined a function getImage which, say, takes a filename and adds it at the end of a certain URL (this would be useful if you defined many items using built-in Ibex controllers that contain images and you do not want to type the host URL each time).

Yet, having to write PennController.Elements. before each creation of/reference to an element would be particularly long, painful and not necessarily fit to your aesthetic preferences. Consider this:

PennController(
    PennController.Elements.defaultImage
        .settings.size(80, 80)
    ,
    PennController.Elements.newText("helloworld", "Hello world!")
        .print()
    ,
    PennController.Elements.newCanvas("geometry", 200, 100)
        .settings.add(  10, 10, PennController.Elements.newImage("left image", "triangle.png") )
        .settings.add( 110, 10, PennController.Elements.newImage("right image", "square.png") )
        .print()
    ,
    PennController.Elements.newSelector("shapes")
        .settings.add( PennController.Elements.getImage("left image") , PennController.Elements.getImage("right image") )
        .print()
        .wait()
);

Long, painful and a little messy, right?

Now if you already have a getImage function defined somewhere else and you do not want to rename it, but you do not want PennController to erase it either, you can choose to reset the prefix to make everything shorter while keeping your own getImage function. For instance, you can choose to use the prefix E for Element (assuming no script defined a global object named E otherwise):

PennController.ResetPrefix("E");

PennController(
    E.defaultImage
        .settings.size(80, 80)
    ,
    E.newText("helloworld", "Hello world!")
        .print()
    ,
    E.newCanvas("geometry", 200, 100)
        .settings.add(  10, 10, E.newImage("left image", "triangle.png") )
        .settings.add( 110, 10, E.newImage("right image", "square.png") )
        .print()
    ,
    E.newSelector("shapes")
        .settings.add( E.getImage("left image") , E.getImage("right image") )
        .print()
        .wait()
);

This is already much better. But to the extent that PennController’s element commands have names that are not used by default Ibex projects, you may want to straight out drop the prefix using PennController.ResetPrefix( null ), which is what is assumed throughout this documentation (no longer assuming the pre-existence of a getImage function in your project).

PennController.SendResults

PennController.SendResults() (since PennController 1.1)

or PennController.SendResults( "label" ) (since PennController 1.1)

Creates an item that will send the results to the server when it is run. You can give a label to the item by passing a string as an argument to PennController.SendResults and then refer to it in PennController.Sequence in order to send the results early.

Sending the results early is useful if you want your participants to see a confirmation screen at the end.

Example:

PennController(
    newButton("hello", "Hello")
        .settings.log()
        .print()
        .wait()
);

PennController.SendResults();

PennController(
    newText("thanks", "Thank your for participating in this experiment.")
        .print()
    ,
    newText("link", "\u003Ca href='somelink'\u003EClick here to confirm your participation.\u003C/a\u003E")
        .print()
    ,
    newTimer("forever", 1)
        .wait()            // Timer never started: will wait forever
)
.setOption("countsForProgressBar",false);

PennController.Sequence

PennController.Sequence( labels )

Determines the order in which your trials will be run. Use your trials’ labels to manipulate the running order.

PennController.Sequence is a handler for the definition of Ibex’s shuffleSequence variable. As such, its arguments follow the same format as those of Ibex’s seq function. See Ibex’s documentation manual, section called Shuffle sequences.

Example:

PennController.Sequence( "hello" , randomize("world") );

PennController( "world" ,
    newButton("world", "Earth")
        .print()
        .wait()
);
PennController( "world" ,
    newButton("world", "Moon")
        .print()
        .wait()
);
PennController( "world" ,
    newButton("world", "Mars")
        .print()
        .wait()
);

PennController( "hello" ,
    newButton("world", "Hello...")
        .print()
        .wait()
);

Will run the trial labeled hello first, even though it is defined below the world ones, and then will run all three trials labeled world in a random order.

PennController.SetCounter

PennController.SetCounter() (since PennController 1.1)

or PennController.SetCounter( "label" ) (since PennController 1.1)

or PennController.SetCounter( number ) (since PennController 1.1)

or PennController.SetCounter( "label" , number ) (since PennController 1.1)

Creates an item that will set Ibex’s internal counter when it is run.

Ibex has an internal counter which keeps track of how many people participated in your experiment in order to automatically handle group designs. By default, the counter is incremented at the end of the experiment, which has the undesirable effect of assigning the same group to all the participants who click your link before anyone has completed your experiment. You can choose to run PennController.SetCounter at the very beginning of your experiment instead.

You can give a label to the item by passing a string as an argument which you can refer to in PennController.Sequence (e.g. you can make it the first item to run and therefore increment the counter at each click).

You can also pass a number, as the single argument if you define no label, or as the second argument if the first is a string for the label. The number will be used to increment (if positive) or decrement (if negative) the counter by its value.

Example:

PennController.SetCounter();

PennController.AddTable( "myTable" , 
  "Group,Button\n"+
  "A,Hello\n"+
  "B,World"
);

PennController.Template(
  row => PennController(
    newButton( "greetings" , row.Button )
        .print()
        .wait()
  )
);

Increments the counter as soon as the experiment starts running. Since we use a table defining two groups (A and B) the button will read Hello or World every other time the experiment is run.

PennController().setOption

PennController().setOption(option, value) (since beta 0.3)

Lets you modify a parameter of the controller, as you would for any other controller in Ibex.

This can be helpful if you want to override some default settings, such as countsForProgressBar which is used by Ibex.

Example:

PennController(
    newScale("Score", 8)
        .settings.slider()
        .print()
        .wait()
)
.setOption("countsForProgressBar", false);

This trial will not count for the progress bar at the top of the Ibex experiment page (see the Ibex documentation manual).

PennController.Template

(PennController.FeedItems has become deprecated since version 1.0)

PennController.Template( row => PennController() )

or PennController.Template( table , row => PennController() )

or PennController.Template( row => ["controllername", options, "controllername", options, ...] )

or PennController.Template( table , row => ["controllername", options, "controllername", options, ...] )

Generates Ibex items from a table. See this page to learn how to use it. You can have as many PennController.Template as you want (which is helpful if you use different trial templates).

You can pass the name of the table you want to use as a string, or use PennController.GetTable. If you do not explicitly specify a table, PennController.Template will use the table whose name comes first in the alpha-numerical order (if any—if you added only one table, it will automatically use that table).

The argument of Template is a function: you can use arrow functions, as illustrated in this documentation, or old-style functions: PennController.Template( function (row) { return PennController( ... ); } ).

The variable (named row here, but you could also name it item, or however pleases you) is iteratively fed with the content of each row of the table (modulo filtering, see PennController.GetTable().filter). You retrieve the value of a given column with row.columnName or row["columnName"] (use the latter if you column’s name contains special characters, like spaces, commas, periods or dashes).

The argument function can directly return PennController(), defining a PennController template for the trials to be generated. It can also return an array of Ibex item-elements: in this case, it should follow the same format as the definition of a standard Ibex item, modulo the label name (see Ibex’s documentation manual).

Examples:

PennController.AddTable( "mytable" ,
    "Type,Sentence,TargetPicture,CompetitorPicture\n"+
    "Test,This is a square,square.png,triangle.png\n"+
    "Filler,This is a triangle,triangle.png,pear.png"
);

PennController.Template( "mytable" ,
    row => PennController( row.Type ,
        newText("sentence", row.Sentence)
            .print()
        ,
        newSelector("choice")
        ,
        defaultImage
            .settings.size(200,200)
            .settings.selector("choice")
        ,
        newCanvas( "images", 500, 200 )
            .settings.add(   0, 0, newImage("target", row.TargetPicture)     ) 
            .settings.add( 300, 0, newImage("target", row.CompetitorPicture) ) 
            .print()
        ,
        getSelector("choice")
            .shuffle()
            .wait()
    )
);

Generates trials labeled according to the Type column of the table, showing a sentence whose text is retrieved from the Sentence column of the table, and two pictures side by side retrieved from the TargetPicture and CompetitorPicture columns.

PennController.AddTable( "mytable" ,
    "Type,Sentence\n"+
    "Filler,The cat that is chasing the mouse runs fast\n"+
    "Test,The mouse that the cat is chasing runs fast"
);

PennController.Template( "mytable" ,
    row => [
        "DashedSentence", {s: row.Sentence},
        "PennController", PennController(
            newText("question", "How natural did you find this sentence?")
                .print()
            ,
            newScale("natural", 7)
                .settings.before( newText("left", "Completely unnatural") )
                .settings.after( newText("right", "Completely natural") )
                .print()
                .wait()
        )
    ]
);

Generates two-screen trials, the first screen using the native Ibex DashedSentence with the value from the Sentence column passed as its s parameter, the second screen using PennController to show a naturalness scale.