{"version":3,"file":"index.cjs","sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n  \"name\": \"@jspsych/plugin-survey-likert\",\n  \"version\": \"2.2.0\",\n  \"description\": \"a jspsych plugin for measuring items on a likert scale\",\n  \"type\": \"module\",\n  \"main\": \"dist/index.cjs\",\n  \"exports\": {\n    \"import\": \"./dist/index.js\",\n    \"require\": \"./dist/index.cjs\"\n  },\n  \"typings\": \"dist/index.d.ts\",\n  \"unpkg\": \"dist/index.browser.min.js\",\n  \"files\": [\n    \"src\",\n    \"dist\"\n  ],\n  \"source\": \"src/index.ts\",\n  \"scripts\": {\n    \"test\": \"jest\",\n    \"test:watch\": \"npm test -- --watch\",\n    \"tsc\": \"tsc\",\n    \"build\": \"rollup --config\",\n    \"build:watch\": \"npm run build -- --watch\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/jspsych/jsPsych.git\",\n    \"directory\": \"packages/plugin-survey-likert\"\n  },\n  \"author\": \"Josh de Leeuw\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/jspsych/jsPsych/issues\"\n  },\n  \"homepage\": \"https://www.jspsych.org/latest/plugins/survey-likert\",\n  \"peerDependencies\": {\n    \"jspsych\": \">=7.1.0\"\n  },\n  \"devDependencies\": {\n    \"@jspsych/config\": \"^3.2.0\",\n    \"@jspsych/test-utils\": \"^1.2.0\"\n  }\n}\n","import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n  name: \"survey-likert\",\n  version: version,\n  parameters: {\n    /** Array containing one or more objects with parameters for the question(s) that should be shown on the page. */\n    questions: {\n      type: ParameterType.COMPLEX,\n      array: true,\n      nested: {\n        /** Question prompt. */\n        prompt: {\n          type: ParameterType.HTML_STRING,\n          default: undefined,\n        },\n        /** Array of likert labels to display for this question. */\n        labels: {\n          type: ParameterType.STRING,\n          array: true,\n          default: undefined,\n        },\n        /** Whether or not a response to this question must be given in order to continue. */\n        required: {\n          type: ParameterType.BOOL,\n          default: false,\n        },\n        /** Name of the question in the trial data. If no name is given, the questions are named Q0, Q1, etc. */\n        name: {\n          type: ParameterType.STRING,\n          default: \"\",\n        },\n        /** Default response value for this question (0-based index of labels array). */\n        default_response: {\n          type: ParameterType.INT,\n          default: null,\n        },\n      },\n    },\n    /** If true, the order of the questions in the 'questions' array will be randomized. */\n    randomize_question_order: {\n      type: ParameterType.BOOL,\n      default: false,\n    },\n    /** HTML-formatted string to display at top of the page above all of the questions. */\n    preamble: {\n      type: ParameterType.HTML_STRING,\n      default: null,\n    },\n    /** Width of the likert scales in pixels. */\n    scale_width: {\n      type: ParameterType.INT,\n      default: null,\n    },\n    /** Label of the button to submit responses. */\n    button_label: {\n      type: ParameterType.STRING,\n      default: \"Continue\",\n    },\n    /** Setting this to true will enable browser auto-complete or auto-fill for the form. */\n    autocomplete: {\n      type: ParameterType.BOOL,\n      default: false,\n    },\n  },\n  data: {\n    /** An object containing the response for each question. The object will have a separate key (variable) for each question, with the first question in the trial being recorded in `Q0`, the second in `Q1`, and so on. The responses are recorded as integers, representing the position selected on the likert scale for that question. If the `name` parameter is defined for the question, then the response object will use the value of `name` as the key for each question. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */\n    response: {\n      type: ParameterType.OBJECT,\n    },\n    /** The response time in milliseconds for the participant to make a response. The time is measured from when the questions first appear on the screen until the participant's response(s) are submitted. */\n    rt: {\n      type: ParameterType.INT,\n    },\n    /** An array with the order of questions. For example `[2,0,1]` would indicate that the first question was `trial.questions[2]` (the third item in the `questions` parameter), the second question was `trial.questions[0]`, and the final question was `trial.questions[1]`. This will be encoded as a JSON string when data is saved using the `.json()` or `.csv()` functions. */\n    question_order: {\n      type: ParameterType.INT,\n      array: true,\n    },\n  },\n  // prettier-ignore\n  citations: '__CITATIONS__',\n};\n\ntype Info = typeof info;\n\n/**\n * The survey-likert plugin displays a set of questions with Likert scale responses. The participant responds\n * by selecting a radio button.\n *\n * @author Josh de Leeuw\n * @see {@link https://www.jspsych.org/latest/plugins/survey-likert/ survey-likert plugin documentation on jspsych.org}\n */\nclass SurveyLikertPlugin implements JsPsychPlugin<Info> {\n  static info = info;\n\n  constructor(private jsPsych: JsPsych) {}\n\n  trial(display_element: HTMLElement, trial: TrialType<Info>) {\n    if (trial.scale_width !== null) {\n      var w = trial.scale_width + \"px\";\n    } else {\n      var w = \"100%\";\n    }\n\n    var html = \"\";\n    // inject CSS for trial\n    html += '<style id=\"jspsych-survey-likert-css\">';\n    html +=\n      \".jspsych-survey-likert-statement { display:block; font-size: 16px; padding-top: 40px; margin-bottom:10px; }\" +\n      \".jspsych-survey-likert-opts { list-style:none; width:\" +\n      w +\n      \"; margin:auto; padding:0 0 35px; display:block; font-size: 14px; line-height:1.1em; }\" +\n      \".jspsych-survey-likert-opt-label { line-height: 1.1em; color: #444; }\" +\n      \".jspsych-survey-likert-opts:before { content: ''; position:relative; top:11px; /*left:9.5%;*/ display:block; background-color:#efefef; height:4px; width:100%; }\" +\n      \".jspsych-survey-likert-opts:last-of-type { border-bottom: 0; }\" +\n      \".jspsych-survey-likert-opts li { display:inline-block; /*width:19%;*/ text-align:center; vertical-align: top; }\" +\n      \".jspsych-survey-likert-opts li input[type=radio] { display:block; position:relative; top:0; left:50%; margin-left:-6px; }\";\n    html += \"</style>\";\n\n    // show preamble text\n    if (trial.preamble !== null) {\n      html +=\n        '<div id=\"jspsych-survey-likert-preamble\" class=\"jspsych-survey-likert-preamble\">' +\n        trial.preamble +\n        \"</div>\";\n    }\n\n    if (trial.autocomplete) {\n      html += '<form id=\"jspsych-survey-likert-form\">';\n    } else {\n      html += '<form id=\"jspsych-survey-likert-form\" autocomplete=\"off\">';\n    }\n\n    // add likert scale questions ///\n    // generate question order. this is randomized here as opposed to randomizing the order of trial.questions\n    // so that the data are always associated with the same question regardless of order\n    var question_order = [];\n    for (var i = 0; i < trial.questions.length; i++) {\n      question_order.push(i);\n    }\n    if (trial.randomize_question_order) {\n      question_order = this.jsPsych.randomization.shuffle(question_order);\n    }\n\n    for (var i = 0; i < trial.questions.length; i++) {\n      var question = trial.questions[question_order[i]];\n      // add question\n      html += '<label class=\"jspsych-survey-likert-statement\">' + question.prompt + \"</label>\";\n      // add options\n      var width = 100 / question.labels.length;\n      var options_string =\n        '<ul class=\"jspsych-survey-likert-opts\" data-name=\"' +\n        question.name +\n        '\" data-radio-group=\"Q' +\n        question_order[i] +\n        '\">';\n      for (var j = 0; j < question.labels.length; j++) {\n        options_string +=\n          '<li style=\"width:' +\n          width +\n          '%\"><label class=\"jspsych-survey-likert-opt-label\"><input type=\"radio\" name=\"Q' +\n          question_order[i] +\n          '\" value=\"' +\n          j +\n          '\"';\n        if (question.required) {\n          options_string += \" required\";\n        }\n        if (question.default_response === j) {\n          options_string += \" checked\";\n        }\n        options_string += \">\" + question.labels[j] + \"</label></li>\";\n      }\n      options_string += \"</ul>\";\n      html += options_string;\n    }\n\n    // add submit button\n    html +=\n      '<input type=\"submit\" id=\"jspsych-survey-likert-next\" class=\"jspsych-survey-likert jspsych-btn\" value=\"' +\n      trial.button_label +\n      '\"></input>';\n\n    html += \"</form>\";\n\n    display_element.innerHTML = html;\n\n    display_element.querySelector(\"#jspsych-survey-likert-form\").addEventListener(\"submit\", (e) => {\n      e.preventDefault();\n      // measure response time\n      var endTime = performance.now();\n      var response_time = Math.round(endTime - startTime);\n\n      // create object to hold responses\n      var question_data = {};\n      var matches = display_element.querySelectorAll<HTMLFormElement>(\n        \"#jspsych-survey-likert-form .jspsych-survey-likert-opts\"\n      );\n      for (var index = 0; index < matches.length; index++) {\n        var id = matches[index].dataset[\"radioGroup\"];\n        var el = display_element.querySelector<HTMLInputElement>(\n          'input[name=\"' + id + '\"]:checked'\n        );\n        if (el === null) {\n          var response: string | number = \"\";\n        } else {\n          var response: string | number = parseInt(el.value);\n        }\n        var obje = {};\n        if (matches[index].attributes[\"data-name\"].value !== \"\") {\n          var name = matches[index].attributes[\"data-name\"].value;\n        } else {\n          var name = id;\n        }\n        obje[name] = response;\n        Object.assign(question_data, obje);\n      }\n\n      // save data\n      var trial_data = {\n        rt: response_time,\n        response: question_data,\n        question_order: question_order,\n      };\n\n      // next trial\n      this.jsPsych.finishTrial(trial_data);\n    });\n\n    var startTime = performance.now();\n  }\n\n  simulate(\n    trial: TrialType<Info>,\n    simulation_mode,\n    simulation_options: any,\n    load_callback: () => void\n  ) {\n    if (simulation_mode == \"data-only\") {\n      load_callback();\n      this.simulate_data_only(trial, simulation_options);\n    }\n    if (simulation_mode == \"visual\") {\n      this.simulate_visual(trial, simulation_options, load_callback);\n    }\n  }\n\n  private create_simulation_data(trial: TrialType<Info>, simulation_options) {\n    const question_data = {};\n    let rt = 1000;\n\n    for (const q of trial.questions) {\n      const name = q.name ? q.name : `Q${trial.questions.indexOf(q)}`;\n      question_data[name] = this.jsPsych.randomization.randomInt(0, q.labels.length - 1);\n      rt += this.jsPsych.randomization.sampleExGaussian(1500, 400, 1 / 200, true);\n    }\n\n    const default_data = {\n      response: question_data,\n      rt: rt,\n      question_order: trial.randomize_question_order\n        ? this.jsPsych.randomization.shuffle([...Array(trial.questions.length).keys()])\n        : [...Array(trial.questions.length).keys()],\n    };\n\n    const data = this.jsPsych.pluginAPI.mergeSimulationData(default_data, simulation_options);\n\n    this.jsPsych.pluginAPI.ensureSimulationDataConsistency(trial, data);\n\n    return data;\n  }\n\n  private simulate_data_only(trial: TrialType<Info>, simulation_options) {\n    const data = this.create_simulation_data(trial, simulation_options);\n\n    this.jsPsych.finishTrial(data);\n  }\n\n  private simulate_visual(trial: TrialType<Info>, simulation_options, load_callback: () => void) {\n    const data = this.create_simulation_data(trial, simulation_options);\n\n    const display_element = this.jsPsych.getDisplayElement();\n\n    this.trial(display_element, trial);\n    load_callback();\n\n    const answers = Object.entries(data.response);\n    for (let i = 0; i < answers.length; i++) {\n      this.jsPsych.pluginAPI.clickTarget(\n        display_element.querySelector(\n          `input[type=\"radio\"][name=\"${answers[i][0]}\"][value=\"${answers[i][1]}\"]`\n        ),\n        ((data.rt - 1000) / answers.length) * (i + 1)\n      );\n    }\n\n    this.jsPsych.pluginAPI.clickTarget(\n      display_element.querySelector(\"#jspsych-survey-likert-next\"),\n      data.rt\n    );\n  }\n}\n\nexport default SurveyLikertPlugin;\n"],"names":[],"mappings":";;;;AAEE,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECiFA,SAAA,EAAA;AAAA;;GAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}