跳转至

开发扩展

扩展的要求

在7.0版本中,扩展通过JavaScript类实现。扩展中需要实现:

模板

可以使用JavaScript或TypeScript编写扩展。JavaScriptTypeScript的模板文件可以在jspsych-contrib仓库中找到。

扩展的构成

constructor()

扩展的constructor()会接收一个JsPsych的实例,constructor函数需要将其保存下来用于访问。

class MyAwesomeExtension {
  constructor(jsPsych){
    this.jsPsych = jsPsych;
  }
}

initialize()

initialize()函数在初始化jsPsych实例的时候调用 (通过initJsPsych()new JsPsych())。初始化扩展的代码应该下载这个函数里。不同于其他事件每个试次都会触发,每个实验只会初始化一次。params对象中包含了配置扩展所需要的参数,它会在调用传递给initialize()方法,此时initialize()需要返回一个Promise对象,在扩展完成初始化后执行。

//... experiment code ...//
let jsPsych = initJsPsych({
  extensions: [
    {type: myAwesomeExtension, params: {demo: 'value'}}
  ]
});

//... extension code ...//
class MyAwesomeExtension {

  initialize(params){
    return new Promise((resolve, reject)=>{
      console.log(params.demo); // will output 'value'

      resolve(); // finish initialzing
    })
  }
}

on_start()

on_start()在插件开始执行时先于plugin.trial调用。和试次相关的初始化代码应该写在这里,例如创建用于存储数据的对象、重置内部状态等。params对象通过扩展的声明传入试次对象,我们可以使用该对象自定义扩展在各个试次中的行为。

//... experiment code ...//
let trial = {
  type: htmlKeyboardResponse,
  stimulus: "You're awesome!",
  extensions: [
    {type: myAwesomeExtension, params: {demo: 'value'}}
  ]
});

//... extension code ...//
class MyAwesomeExtension {

  initialize(params){ ... }

  on_start(params){
    console.log(params.demo); // outputs 'value' before the trial begins.
  }
}

on_load()

on_load()在插件的on_load()完成后调用,通常是在插件完成对DOM元素的修改以及创建事件的监听器之后。和DOM交互、存储数据的代码应该写在这里。params对象通过扩展的声明传入试次对象,我们可以使用该对象自定义扩展在各个试次中的行为。

//... experiment code ...//
let trial = {
  type: htmlKeyboardResponse,
  stimulus: "You're awesome!",
  extensions: [
    {type: myAwesomeExtension, params: {demo: 'value'}}
  ]
});

//... extension code ...//
class MyAwesomeExtension {

  initialize(params){ ... }

  on_start(params){ ... }

  on_load(params){
    // replaces the contents of the display with 'value';
    this.jsPsych.getDisplayElement().innerHTML = params.demo;
  }
}

on_finish()

on_finish()在插件调用jsPsych.finishTrial()后调用。该方法应该返回一个数据对象,用于添加到插件的数据对象。请注意,这个事件在插件的on_finish 之前 触发,所以其数据可以在试次的on_finish中使用。params对象通过扩展的声明传入试次对象,我们可以使用该对象自定义扩展在各个试次中的行为。

//... experiment code ...//
let trial = {
  type: htmlKeyboardResponse,
  stimulus: "You're awesome!",
  extensions: [
    {type: myAwesomeExtension, params: {demo: 'value'}}
  ],
  on_finish: (data) => {
    console.log(data.awesome); // will output 'value'.
  }
});

//... extension code ...//
class MyAwesomeExtension {

  initialize(params){ ... }

  on_start(params){ ... }

  on_load(params){ ... }

  on_finish(params){
    return {
      awesome: params.value
    }
  }
}

static .info

info属性是一个对象,必须有name属性作为扩展的唯一名称、标记了版本的version属性以及包含了扩展生成的数据相关信息的data属性。

import { version } from '../package.json';

class MyAwesomeExtension {

}

MyAwesomeExtension.info = {
  name: 'awesome',
  version: version, // Should be hardcoded as `version: "1.0.1"` if not using build tools.
  data: {
    /** This will be scraped as metadata describing tracking_data and used to create the JsPsych docs */
    tracking_data: {
      type: ParameterType.STRING,
    }
  }
}

version属性标记了扩展的版本,且会被保存在数据中。其作用是产生相应的元数据且有助于维Psych-DS标准。它应该从package.json文件引入,做法是在index.ts文件顶部添加import语句。这样,version会在changeset中自动更新。如果你没有使用构建环境,而是直接编写JS文件,则可以手动写version

data属性是一个包含了插件产生的数据的对象。每一个数据对象都包含typedefault属性。额外说一句,这个属性只应该用于你希望产生的数据。如果你选择生成元数据,jsdoc(/* /内的注释)都会作为元数据用来生成JsPsych文档。

可选方法

扩展还可以额外加入一些方法。参见webgazer扩展

关于编写扩展的建议

如果你希望将开发的扩展加入到jsPsych的主仓库中,请参照贡献代码指南

总的来说,扩展应该适配所有插件,对于DOM中除了实验元素之外还包括了什么不应该有太多限制。如果你开发的扩展只针对少数几个插件,可以考虑修改插件代码。