{"version":3,"file":"index.cjs","sources":["../package.json","../src/index.ts"],"sourcesContent":["{\n  \"name\": \"@jspsych/plugin-external-html\",\n  \"version\": \"2.1.0\",\n  \"description\": \"jsPsych plugin to load and display external html pages\",\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-external-html\"\n  },\n  \"author\": \"Erik Weitnauer\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/jspsych/jsPsych/issues\"\n  },\n  \"homepage\": \"https://www.jspsych.org/latest/plugins/external-html\",\n  \"peerDependencies\": {\n    \"jspsych\": \">=7.1.0\"\n  },\n  \"devDependencies\": {\n    \"@jspsych/config\": \"^3.2.0\",\n    \"@jspsych/test-utils\": \"^1.2.0\",\n    \"jest-fetch-mock\": \"^3.0.3\"\n  }\n}\n","import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\n\nimport { version } from \"../package.json\";\n\nconst info = <const>{\n  name: \"external-html\",\n  version: version,\n  parameters: {\n    /** The URL of the page to display. */\n    url: {\n      type: ParameterType.STRING,\n      default: undefined,\n    },\n    /** The key character the participant can use to advance to the next trial. If left as null, then the participant will not be able to advance trials using the keyboard. */\n    cont_key: {\n      type: ParameterType.KEY,\n      default: null,\n    },\n    /** The ID of a clickable element on the page. When the element is clicked, the trial will advance. */\n    cont_btn: {\n      type: ParameterType.STRING,\n      default: null,\n    },\n    /** `function(){ return true; }` | This function is called with the jsPsych `display_element` as the only argument when the participant attempts to advance the trial. The trial will only advance if the function return `true`. This can be used to verify that the participant has correctly filled out a form before continuing, for example. */\n    check_fn: {\n      type: ParameterType.FUNCTION,\n      default: () => true,\n    },\n    /** If `true`, then the plugin will avoid using the cached version of the HTML page to load if one exists. */\n    force_refresh: {\n      type: ParameterType.BOOL,\n      default: false,\n    },\n    /** If `true`, then scripts on the remote page will be executed. */\n    execute_script: {\n      type: ParameterType.BOOL,\n      pretty_name: \"Execute scripts\",\n      default: false,\n    },\n  },\n  data: {\n    /** The url of the page. */\n    url: {\n      type: ParameterType.STRING,\n    },\n    /** The response time in milliseconds for the participant to finish the trial. */\n    rt: {\n      type: ParameterType.INT,\n    },\n  },\n  // prettier-ignore\n  citations: '__CITATIONS__',\n};\n\ntype Info = typeof info;\n\n/**\n * The HTML plugin displays an external HTML document (often a consent form). Either a keyboard response or a button press can be used to continue to the next trial. It allows the experimenter to check if conditions are met (such as indicating informed consent) before continuing.\n *\n * @author Erik Weitnauer\n * @see {@link https://www.jspsych.org/latest/plugins/external-html/ external-html plugin documentation on jspsych.org}\n */\nclass ExternalHtmlPlugin implements JsPsychPlugin<Info> {\n  static info = info;\n\n  constructor(private jsPsych: JsPsych) {}\n\n  trial(display_element: HTMLElement, trial: TrialType<Info>, on_load: () => void) {\n    // hold the .resolve() function from the Promise that ends the trial\n    let trial_complete;\n\n    var url = trial.url;\n    if (trial.force_refresh) {\n      url = trial.url + \"?t=\" + performance.now();\n    }\n\n    fetch(url)\n      .then((response) => {\n        return response.text();\n      })\n      .then((html) => {\n        display_element.innerHTML = html;\n        on_load();\n        var t0 = performance.now();\n\n        const key_listener = (e) => {\n          if (this.jsPsych.pluginAPI.compareKeys(e.key, trial.cont_key)) {\n            finish();\n          }\n        };\n\n        const finish = () => {\n          if (trial.check_fn && !trial.check_fn(display_element)) {\n            return;\n          }\n          if (trial.cont_key) {\n            display_element.removeEventListener(\"keydown\", key_listener);\n          }\n          var trial_data = {\n            rt: Math.round(performance.now() - t0),\n            url: trial.url,\n          };\n          this.jsPsych.finishTrial(trial_data);\n          trial_complete();\n        };\n\n        // by default, scripts on the external page are not executed with XMLHttpRequest().\n        // To activate their content through DOM manipulation, we need to relocate all script tags\n        if (trial.execute_script) {\n          // changed for..of getElementsByTagName(\"script\") here to for i loop due to TS error:\n          // Type 'HTMLCollectionOf<HTMLScriptElement>' must have a '[Symbol.iterator]()' method that returns an iterator.ts(2488)\n          var all_scripts = display_element.getElementsByTagName(\"script\");\n          for (var i = 0; i < all_scripts.length; i++) {\n            const relocatedScript = document.createElement(\"script\");\n            const curr_script = all_scripts[i];\n            relocatedScript.text = curr_script.text;\n            curr_script.parentNode.replaceChild(relocatedScript, curr_script);\n          }\n        }\n\n        if (trial.cont_btn) {\n          display_element.querySelector(\"#\" + trial.cont_btn).addEventListener(\"click\", finish);\n        }\n\n        if (trial.cont_key) {\n          display_element.addEventListener(\"keydown\", key_listener);\n        }\n      })\n      .catch((err) => {\n        console.error(`Something went wrong with fetch() in plugin-external-html.`, err);\n      });\n\n    // helper to load via XMLHttpRequest\n    /*const load = (element, file, callback) => {\n      var xmlhttp = new XMLHttpRequest();\n      xmlhttp.open(\"GET\", file, true);\n      xmlhttp.onload = () => {\n        console.log(`loaded ${xmlhttp.status}`)\n        if (xmlhttp.status == 200 || xmlhttp.status == 0) {\n          //Check if loaded\n          element.innerHTML = xmlhttp.responseText;\n          console.log(`made it ${xmlhttp.responseText}`);\n          callback();\n        }\n      };\n      xmlhttp.send();\n    };\n\n    load(display_element, url, () => {\n      \n    });\n*/\n    return new Promise((resolve) => {\n      trial_complete = resolve;\n    });\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 default_data = {\n      url: trial.url,\n      rt: this.jsPsych.randomization.sampleExGaussian(2000, 200, 1 / 200, true),\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      if (trial.cont_key) {\n        this.jsPsych.pluginAPI.pressKey(trial.cont_key, data.rt);\n      } else if (trial.cont_btn) {\n        this.jsPsych.pluginAPI.clickTarget(\n          display_element.querySelector(\"#\" + trial.cont_btn),\n          data.rt\n        );\n      }\n    });\n  }\n}\n\nexport default ExternalHtmlPlugin;\n"],"names":[],"mappings":";;;;AAEE,IAAW,OAAA,GAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECiDA,SAAA,EAAA;AAAA;;GAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}