Skip to content
Snippets Groups Projects
Commit 273a53e2 authored by Alexander Meinhold's avatar Alexander Meinhold
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 514 additions and 0 deletions
.babelrc 0 → 100644
{
"presets": ["@babel/preset-env"],
"plugins": [
"@babel/plugin-proposal-class-properties",
[
"@babel/plugin-transform-classes",
{
"loose": true
}
]
]
}
node_modules/
dist/
packaged/
.vscode/
{
"trailingComma": "es5",
"tabWidth": 2,
"printWidth": 100
}
LICENSE 0 → 100644
MIT License
Copyright (c) 2019 bjoluc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# jsPsych Boilerplate
A boilerplate to quickly build [jsPsych](https://www.jspsych.org/) experiments
and package them for [JATOS](https://www.jatos.org/)
## Motivation
[jsPsych](https://www.jspsych.org/) is a great JavaScript library for the
creation of browser-based psychological experiments.
[JATOS](https://www.jatos.org/) is a server application that can make
browser-based experiments available to subjects via the internet and collect the
results via an API.
While setting up jsPsych experiments for JATOS is certainly possible with only a
text editor and a file manager, there are a few redundant steps that have to be
done for every experiment. Some of these are:
* Downloading and extracting jsPsych
* Setting up an index.html file
* Adding JATOS API calls to the experiment script to save the results to JATOS
* Uploading the resulting .html, .js, .css, and image files to a JATOS server
* Configuring a JATOS experiment that uses the files
Additionally, you would have to think about JavaScript and CSS browser
compatibility: Which features can you use such that the experiment is compatible
with all browsers?
jsPsych Boilerplate comes to the rescue and automates pretty much all of these
steps with common web development tools. In the end, you get a JATOS experiment
zip file which you can upload via the JATOS web user interface.
## Requirements
This project requires [Node.js](https://nodejs.org) 10.x.x to be installed on your machine.
## Getting started
1. Clone the repository and install the dependencies with npm
```bash
$ git clone https://github.com/bjoluc/jspsych-boilerplate.git
$ cd jspsych-boilerplate
$ npm install
```
2. Configure a new experiment
1. Duplicate [src/meta/empty.yml](src/meta/empty.yml) and rename it to your
experiment's name. This file is where the metadata (title and description)
of your new experiment are configured.
2. Replace the METAFILE value in [config.yml](config.yml) with your filename
from the previous step, so the build script knows which meta file to read.
3. Duplicate [src/js/experiments/empty.js](src/js/experiments/empty.js) and
rename it to your experiment's name. This is where you will write your
jsPsych experiment.
4. In the first line of [src/js/app.js](src/js/app.js), modify the right
handside of the import statement to import your experiment.
3. Run ```npm run start```. This will build your experiment and watch the source
files for changes. Whenever you modify a source file, the build will be
modified too. You should now have an `index.html` file within your `dist`
folder. Open it with a browser and you will see your new jsPsych experiment in action!
4. If you are new to jsPsych, you might have a look at the jsPsych [demo
experiment
tutorial](https://www.jspsych.org/tutorials/rt-task/#part-2-display-welcome-message).
You can skip part 1 there, as jspsych-boilerplate has you covered.
Once you have finished writing your experiment, you can run ```npm run
package```. This will create a JATOS study zip file of your experiment within
the `packaged` directory.
METAFILE: empty.yml
# Set to true if you want static asset revisioning, helpful for cache busting
REVISIONING: false
# Gulp will reference these paths when it copies files
PATHS:
# Path to dist folder
dist: "dist"
# Paths to static assets that aren't images, CSS, or JavaScript
assets:
- "src/**/*"
- "!src/{js,scss,html}/**"
# Paths to Sass libraries, which can then be loaded with @import
sass:
# Paths to JavaScript entry points for webpack to bundle modules
entries:
- "src/js/app.js"
externaljs:
- "node_modules/jspsych/jspsych.js"
externalcss:
- "node_modules/jspsych/css/jspsych.css"
"use strict";
import plugins from "gulp-load-plugins";
import gulp from "gulp";
import yargs from "yargs";
import rimraf from "rimraf";
import fs from "fs";
import dateFormat from "dateformat";
import yaml from "js-yaml";
import webpackStream from "webpack-stream";
import webpack2 from "webpack";
import named from "vinyl-named";
import log from "fancy-log";
import colors from "ansi-colors";
import uuidv4 from "uuid/v4";
import slugify from "slugify";
// Load all Gulp plugins into one variable
const $ = plugins();
// Check for --production flag
const PRODUCTION = !!yargs.argv.production;
// Check for --development flag
const DEV = !!yargs.argv.dev;
// Function to load a YAML config file
function loadYmlFile(filename) {
log("Loading", colors.bold(colors.cyan(filename)), "...");
let ymlFile = fs.readFileSync(filename, "utf8");
return yaml.load(ymlFile);
}
// Load settings from settings.yml
const { METAFILE, REVISIONING, PATHS } = loadYmlFile("config.yml");
const REV = REVISIONING && (PRODUCTION || DEV);
const META = loadYmlFile("src/meta/" + METAFILE);
// Auto-generate a slug if no slug is specified
if (META.slug == null) {
META.slug = slugify(META.title, {
lower: true,
replacement: "_",
remove: /"<>#%\{\}\|\\\^~\[\]`;\?:@=&/g,
});
}
sass.compiler = require("node-sass");
function clean(done) {
rimraf(PATHS.dist, done);
}
// Copy files out of the assets folder
// This task skips over the "images", "js", and "scss" folders, which are parsed separately
function copy() {
return gulp.src(PATHS.assets).pipe(gulp.dest(PATHS.dist));
}
const html = {
_build(includeJatos) {
let htmlReplacements = {
title: {
src: META.title,
tpl: "<title>%s</title>",
},
};
if (includeJatos) {
htmlReplacements.jatosjs = "/assets/javascripts/jatos.js";
}
return gulp
.src("src/html/**/*")
.pipe($.htmlReplace(htmlReplacements))
.pipe(
$.inject(gulp.src(PATHS.dist + "/css/**/*"), {
addRootSlash: false,
ignorePath: "dist",
})
)
.pipe($.removeEmptyLines({ removeComments: true }))
.pipe(gulp.dest(PATHS.dist));
},
local() {
return html._build(false);
},
jatos() {
return html._build(true);
},
};
// Compile Sass into CSS
// In production, the CSS is compressed
function sass() {
return gulp
.src(["src/scss/app.scss"])
.pipe(
$.sass({
includePaths: PATHS.sass,
}).on("error", $.sass.logError)
)
.pipe($.autoprefixer())
.pipe($.if(PRODUCTION, $.cleanCss({ compatibility: "ie9" })))
.pipe($.if(!PRODUCTION, $.sourcemaps.write()))
.pipe($.if(REV, $.rev()))
.pipe(gulp.dest(PATHS.dist + "/css"))
.pipe($.if(REV, $.rev.manifest()))
.pipe(gulp.dest(PATHS.dist + "/css"));
}
// Combine JavaScript into one file
// In production, the file is minified
const webpack = {
config: {
module: {
rules: [
{
test: /.js$/,
loader: "babel-loader",
exclude: /node_modules(?![\\\/]jspsych)/,
},
],
},
mode: PRODUCTION ? "production" : "development",
optimization: PRODUCTION ? { minimize: true } : {},
},
changeHandler(err, stats) {
log(
"[webpack]",
stats.toString({
colors: true,
})
);
},
build() {
return gulp
.src(PATHS.entries)
.pipe(named())
.pipe(webpackStream(webpack.config, webpack2))
.pipe($.if(REV, $.rev()))
.pipe(gulp.dest(PATHS.dist + "/js"))
.pipe($.if(REV, $.rev.manifest()))
.pipe(gulp.dest(PATHS.dist + "/js"));
},
watch() {
const watchConfig = Object.assign(webpack.config, {
watch: true,
devtool: "inline-source-map",
});
return gulp
.src(PATHS.entries)
.pipe(named())
.pipe(
webpackStream(watchConfig, webpack2, webpack.changeHandler).on("error", err => {
log(
"[webpack:error]",
err.toString({
colors: true,
})
);
})
)
.pipe(gulp.dest(PATHS.dist + "/js"));
},
};
function externaljs() {
return gulp
.src(PATHS.externaljs)
.pipe($.if(!PRODUCTION, $.sourcemaps.init()))
.pipe($.uglify())
.pipe($.if(!PRODUCTION, $.sourcemaps.write()))
.pipe(gulp.dest(PATHS.dist + "/js"));
}
function externalcss() {
return gulp
.src(PATHS.externalcss)
.pipe($.if(PRODUCTION, $.cleanCss({ compatibility: "ie9" })))
.pipe(gulp.dest(PATHS.dist + "/css"));
}
function watch() {
gulp.watch(PATHS.assets, copy);
gulp.watch("src/html/**/*", html.local);
gulp
.watch("src/scss/**/*.scss", sass)
.on("change", path => log("File " + colors.bold(colors.magenta(path)) + " changed."))
.on("unlink", path => log("File " + colors.bold(colors.magenta(path)) + " was removed."));
}
function getJatosStudyDescription() {
let study = {
version: "3",
data: {
uuid: uuidv4(),
title: META.title,
description: META.jatosDescription,
groupStudy: false,
dirName: META.slug,
comments: "",
jsonData: JSON.stringify(META.jatosJsonInput),
componentList: [
{
uuid: uuidv4(),
title: "Experiment",
htmlFilePath: "index.html",
reloadable: true,
active: true,
comments: "",
jsonData: "",
},
],
batchList: [
{
uuid: uuidv4(),
title: "Default",
active: true,
maxActiveMembers: null,
maxTotalMembers: null,
maxTotalWorkers: null,
allowedWorkerTypes: null,
comments: null,
jsonData: "",
},
],
},
};
return study;
}
// Create a .zip archive for JATOS import
function archive() {
let time = dateFormat(new Date(), "yyyy-mm-dd_HH-MM-ss");
let title = META.slug + "_" + time + ".zip";
let jatosStudyJsonString = JSON.stringify(getJatosStudyDescription());
return gulp
.src(PATHS.dist + "/**/*")
.pipe(
$.rename(function(file) {
file.dirname = META.slug + "/" + file.dirname;
})
)
.pipe($.file(META.slug + ".jas", jatosStudyJsonString))
.pipe($.zip(title))
.pipe(gulp.dest("packaged"));
}
exports.build = gulp.series(
clean,
gulp.parallel(
copy,
sass,
externaljs,
externalcss,
webpack.build,
gulp.series(sass, externalcss, html.local)
)
);
exports.package = gulp.series(
clean,
gulp.parallel(
copy,
sass,
externaljs,
externalcss,
webpack.build,
gulp.series(sass, externalcss, html.jatos)
),
archive
);
// Default task: Build and watch for changes
exports.default = gulp.series(exports.build, gulp.parallel(watch, webpack.watch));
This diff is collapsed.
{
"name": "webtoj",
"version": "1.0.0",
"description": "Browser-based TOJ experiments using jsPsych",
"repository": {
"type": "git",
"url": "git://github.com/bjoluc/jspsych-boilerplate.git"
},
"main": "gulpfile.babel.js",
"scripts": {
"start": "gulp --dev",
"dev": "gulp build --dev",
"build": "gulp build --production",
"package-dev": "gulp package --dev",
"package": "gulp package --production"
},
"dependencies": {
"@babel/polyfill": "7.7.0",
"core-js": "3.3.6",
"d3-color": "^1.4.0",
"delay": "4.3.0",
"jspsych": "^6.0.0",
"lodash": "^4.17.15",
"random-int": "2.0.1",
"vsync-estimate": "^0.2.0"
},
"devDependencies": {
"@babel/cli": "7.7.0",
"@babel/core": "7.7.0",
"@babel/plugin-proposal-class-properties": "7.7.0",
"@babel/plugin-transform-classes": "7.7.0",
"@babel/preset-env": "7.7.1",
"@babel/register": "7.7.0",
"eslint": "6.6.0",
"ansi-colors": "^4.1.1",
"babel-loader": "^8.0.6",
"dateformat": "^3.0.3",
"fancy-log": "^1.3.3",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^7.0.1",
"gulp-babel": "^8.0.0",
"gulp-clean-css": "^4.2.0",
"gulp-file": "^0.4.0",
"gulp-html-replace": "^1.6.2",
"gulp-if": "^3.0.0",
"gulp-inject": "^5.0.4",
"gulp-load-plugins": "^2.0.1",
"gulp-remove-empty-lines": "^0.1.0",
"gulp-rename": "^1.4.0",
"gulp-rev": "^9.0.0",
"gulp-sass": "^4.0.2",
"gulp-sourcemaps": "^2.6.5",
"gulp-uglify": "^3.0.2",
"gulp-zip": "5.0.1",
"js-yaml": "^3.13.1",
"node-sass": "^4.13.0",
"regenerator-runtime": "0.13.3",
"rimraf": "3.0.0",
"slugify": "1.3.6",
"uuid": "^3.3.3",
"vinyl-named": "^1.1.0",
"webpack": "4.41.2",
"webpack-stream": "^5.2.1",
"yargs": "^14.2.0"
},
"browserslist": [
"last 2 versions",
"ie >= 9",
"ios >= 7"
],
"author": "bjoluc",
"license": "MIT"
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- build:title -->
<!--
Babel replaces this with a title tag containing the config.yml
option META.title (you can change the title in config.yml)
-->
<!-- endbuild -->
<!-- build:jatosjs -->
<!-- If exported as a jatos study, babel will inject jatos.js here. -->
<!-- endbuild -->
<script src="js/jspsych.js"></script>
<script src="js/app.js"></script>
<!-- inject:css -->
<!-- Babel will inject css files here -->
<!-- endinject -->
</head>
<body></body>
</html>
src/images/blue.png

5.1 KiB

src/images/fixmark.png

418 B

src/images/gray.png

5.11 KiB

src/images/orange.png

5.15 KiB

src/images/quick-toj/background_0.png

4.83 KiB

src/images/quick-toj/background_1.png

7.07 KiB

src/images/quick-toj/background_10.png

7.76 KiB

src/images/quick-toj/background_11.png

8.13 KiB

src/images/quick-toj/background_12.png

8.07 KiB

src/images/quick-toj/background_13.png

8.67 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment