Show:

File: app/components/exp-frame-select/component.js

import ExpFrameBaseComponent from '../exp-frame-base/component';
import ExperimentParser from '../../utils/parse-experiment';

/**
 * @module exp-player
 * @submodule frames
 */

/**
 *
 * Frame that allows you to specify a list of possible frames to show, plus an index or
 * list of indices of which ones to actually show. The frame(s) will be inserted into the
 * sequence of frames for this study on the fly, so that you can use a custom
 * <a href="../classes/Exp-frame-base.html#property_generateProperties" class="crosslink">generateProperties</a>
 * function to select which frame(s) to show.
 *
 *
 * This frame serves as a wrapper for the randomizer <a href="../classes/Select.html" class="crosslink">select</a>,
 * which is evaluated during experiment parsing and cannot be modified on the fly.
 *
 *
 * For more information about making study behavior conditional on data collected,
 * see <a href="https://lookit.readthedocs.io/en/develop/researchers-create-experiment.html#conditional-logic">the Lookit docs</a>.
 *
 *
 * Example usage:
 ```json
        "study-procedure": {
            "kind": "exp-frame-select",
            "frameOptions": [
                {
                    "kind": "exp-lookit-text",
                    "blocks": [
                        {
                            "emph": true,
                            "text": "Cats are great"
                        },
                        {
                            "text": "We are measuring how much your child loves cats now. Beep boop!"
                        }
                    ]
                },
                {
                    "kind": "exp-lookit-text",
                    "blocks": [{
                            "emph": true,
                            "text": "Your child is not eligible for this study"
                        },
                        {
                            "text": "Either you do not have any cats or your child does not love cats."
                        }
                    ]
                }
            ],
            "generateProperties": "function(expData, sequence, child, pastSessions) {var formData = expData['0-eligibility-survey'].formData; if (formData.nCats >= 1 && formData.loveCats == 'yes') { console.log('eligible'); return { 'whichFrames': 0, 'ELIGIBLE': true } } else { console.log('ineligible'); return { 'whichFrames': 1,  'ELIGIBLE': false } } }"
        }

 * ```
 *
 *
 * **Warning:** to avoid unpredictable behavior, this frame does not itself use any
 * <a href="../classes/Exp-frame-base.html#property_selectNextFrame" class="crosslink">selectNextFrame</a> passed to it.
 * (Frames within the `frameOptions` list are welcome to make use of `selectNextFrame`, though!)
 *
 *
 * Data will be stored for this frame so that any ``generatedProperties`` are available
 * for future use; however, it will proceed immediately upon loading to the first frame
 * that is specified (or the next frame in the original sequence, if it turns out that `whichFrames` is an empty
 * list).
 *
 *
 * In `expData`, the frame keys for all frames generated by this frame will be prefixed
 * by this frame's ID, with an index within `whichFrames` appended to the end of the ID.
 * For instance, if this frame's ID is `1-study-procedure`, and it generates three frames,
 * we would have keys `1-study-procedure`, `1-study-procedure-0`, `1-study-procedure-1`, and
 * `1-study-procedure-2`.
 *
 *
 * @class Exp-frame-select
 * @extends Exp-frame-base
 *
 */
export default ExpFrameBaseComponent.extend({
    type: 'exp-frame-select',

    whichFrames: -1,

    frameSchemaProperties: {
        /**
         * List of frames that can be created by this randomizer. Each frame is an
         * object with any necessary frame-specific properties specified. The
         * 'kind' of frame can be specified either here (per frame) or in
         * commonFrameProperties. If a property is defined for a given frame both
         * in this frame list and in commonFrameProperties, the value in the frame
         * list will take precedence.
         *
         * (E.g., you could include 'kind': 'normal-frame' in
         * commmonFrameProperties, but for a single frame in frameOptions, include
         * 'kind': 'special-frame'.)
         *
         * @property {Object[]} frameOptions
         */
        frameOptions: {
            type: 'array',
            items: {
                type: 'object'
            },
            default: []
        },

        /**
         * Object describing common parameters to use in EVERY frame created
         * by this randomizer. Parameter names and values are as described in
         * the documentation for the frameType used.
         *
         * @property {Object} commonFrameProperties
         */

        commonFrameProperties: {
            type: 'object',
            default: {}
        },

        /**
         * Index or indices (0-indexed) within frameOptions to actually use. This can be either a number
         * (e.g., 0 or 1 to use the first or second option respectively) or an array providing
         * an ordered list of indices to use (e.g., [0, 1] or [1, 0] to use the first then
         * second or second then first options, respectively). All indices must be integers
         * in [0, frameOptions.length).
         *
         * If not provided or -1, the entire frameOptions list is used in order. (If empty
         * list is provided, however, that is respected and no frames are inserted by this
         * randomizer.)
         *
         * @property {Number} whichFrames
         */
        whichFrames: {
            type: 'number',
            default: -1
        }
    },

    meta: {
        data: {
            type: 'object',
            properties: {

            }
        }
    },

    didReceiveAttrs() {
        this._super(...arguments);

        // Convert current frame (w/ possibly updated parameters based on generateProperties)
        // to a single 'select' randomizer frame.

        var equivalentRandomizer = {
            'kind': 'choice',
            'sampler': 'select',
            'whichFrames': this.get('whichFrames') == -1 ? [...this.get('frameOptions').keys()] : this.get('whichFrames'),
            'frameOptions': this.get('frameOptions'),
            'commonFrameProperties': this.get('commonFrameProperties')
        };
        this.set('whichFrames', -1);

        var id = this.get('id');
        var parser = new ExperimentParser({
            structure: {
                'frames': {
                    [id]: equivalentRandomizer
                },
                'sequence': [
                    id
                ]
            },
            pastSessions: this.parentView.get('pastSessions').toArray()
        });
        const prependFrameInds = false;
        var [frameConfigs, conditions] = parser.parse(prependFrameInds);
        var frames = this.parentView.get('frames');
        frames.splice(this.get('frameIndex') + 1, 0, ...frameConfigs);
        this.parentView.set('frames', frames);
        var existingConditions = this.parentView.get('conditions');
        if (existingConditions) {
            Object.assign(existingConditions, conditions);
        }
        this.parentView.set('conditions', existingConditions);
    },

    didRender() {
        this._super(...arguments);
        this.send('next');
    }

});