Commit 95f149b1 authored by Ben Lafreniere's avatar Ben Lafreniere
Browse files

Initial commit with Ben's code

parent 898df90e
# Generic stuff
*~
*.lock
*.DS_Store
*.swp
*.out
# Node project specific:
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
node_modules
# Debug log from npm
npm-debug.log
# For the log directory
data/*.db
# For the generated app.js
public/app.js
public/.app.js
public/learning.js
public/.learning.js
public/hometrial.js
public/.hometrial.js
var Trial = require('./Trial.js'),
StudyLogger = require('study-logger'),
items = require('./Items.js'),
moment = require('moment'),
Strings = require('./Strings.js');
function BlindTrial(experiment, options) {
Trial.call(this, experiment, options);
this.type = "BlindTrial";
this.trialid = options.trialid;
this.blockid = options.blockid;
this.stimulus = options.stimulus;
}
BlindTrial.prototype = Object.create(Trial.prototype);
BlindTrial.prototype.start = function() {
this.experiment.fastTapMenu.openable = false;
this.experiment.stimulus.setByItem(items.getItemByName(this.stimulus));
this.experiment.instructions.setText(Strings['side-instructions-blind']);
this.startTimestamp = +moment();
this.handleItemClick = function(data) {
StudyLogger.logEvent({type: 'event.endtrial',
trialtype: this.type,
trialid: this.trialid,
blockid: this.blockid,
stimulus: this.stimulus,
success: data.item.name === this.stimulus,
duration: +moment() - this.startTimestamp,
clickitem: data.item.name,
clickrow: data.row,
clickcol: data.col,
clickx: data.menuX,
clicky: data.menuY});
this.experiment.fastTapMenu.removeItemClickedCB(this.handleItemClick);
this.completed = true;
}.bind(this);
this.experiment.fastTapMenu.addItemClickedCB(this.handleItemClick);
StudyLogger.logEvent({type: 'event.starttrial',
trialtype: this.type,
trialid: this.trialid,
blockid: this.blockid,
stimulus: this.stimulus});
};
BlindTrial.prototype.update = function(delta) {
};
module.exports = BlindTrial;
\ No newline at end of file
var PIXI = require('pixi.js');
var DIALOG_COLOR = 0xffffff;
function ConfidenceRating() {
this.view = new PIXI.DisplayObjectContainer();
this.width = 1260;
this.height = 810;
this.dialogBox = new PIXI.Graphics();
this.view.addChild(this.dialogBox);
this.message = new PIXI.Text("", {font:"28pt Arial", fill:"#000000", align:"center"});
this.view.addChild(this.message);
this._responseHandlers = [];
this.buttons = [];
this.buttons.push( new Button("1\nNot at all confident", 1) );
this.buttons.push( new Button("2\n", 2) );
this.buttons.push( new Button("3\n", 3) );
this.buttons.push( new Button("4\n", 4) );
this.buttons.push( new Button("5\nVery confident", 5) );
this.buttonGroup = new PIXI.DisplayObjectContainer();
var acc = 0;
var _this = this;
this.buttons.forEach(function(button) {
_this.buttonGroup.addChild(button.view);
button.view.position.y = 0;
button.view.position.x = acc;
acc += button.width + 10;
button.view.click = function(data) {
data.response = button.id;
data.responseText = button.text;
// Notify listeners
for (var i = 0; i < _this._responseHandlers.length; i++) {
_this._responseHandlers[i](data);
}
};
});
this.view.addChild(this.buttonGroup);
this.dialogBox.interactive = true;
this.dialogBox.click = function() {
// do nothing (prevents clicks from passing through to the fast tap menu)
};
this.view.visible = false;
this.updateLayout();
}
ConfidenceRating.prototype.open = function() {
this.view.visible = true;
};
ConfidenceRating.prototype.close = function() {
this.view.visible = false;
};
ConfidenceRating.prototype.setMessage = function(s) {
this.message.setText(s);
this.updateLayout();
};
ConfidenceRating.prototype.addResponseCB = function(f) {
this._responseHandlers.push(f);
};
ConfidenceRating.prototype.removeResponseCB = function(f) {
for (var i = 0; i < this._responseHandlers.length; i++) {
if (this._responseHandlers[i] === f) {
this._responseHandlers.splice(i,1);
return true;
}
}
return false;
};
ConfidenceRating.prototype.updateLayout = function() {
this.dialogBox.clear();
this.dialogBox.beginFill(DIALOG_COLOR);
this.dialogBox.drawRect(0,0,this.width,this.height);
this.buttonGroup.position.x = this.width/2 - this.buttonGroup.width/2;
this.buttonGroup.position.y = this.height - 250;
for (var i = 0; i < this.buttons.length; i++) {
this.buttons[i].updateLayout();
}
this.message.position.x = this.width/2 - this.message.width/2;
this.message.position.y = this.height/2 - this.message.height/2;
};
function Button(labelText, id) {
this.enabled = true;
this.hovered = false;
this.text = labelText || " ";
this.id = id || -1;
this.width = 200;
this.height = 80;
this.view = new PIXI.DisplayObjectContainer();
this.bg = new PIXI.Graphics();
this.view.addChild(this.bg);
this.label = new PIXI.Text(this.text, {font:"16pt Arial", fill:"#ffffff", align:"center"});
this.view.addChild(this.label);
this.view.interactive = true;
this.view.mouseover = function(data) {
this.hovered = true;
this.updateLayout();
}.bind(this);
this.view.mouseout = function(data) {
this.hovered = false;
this.updateLayout();
}.bind(this);
this.updateLayout();
}
Button.prototype.setLabel = function(labelText) {
this.text = labelText;
this.label.setText(this.text);
this.updateLayout();
};
Button.prototype.updateLayout = function() {
this.label.position.x = Math.floor(this.width / 2 - this.label.width / 2);
this.label.position.y = Math.floor(this.height / 2 - this.label.height / 2);
this.bg.clear();
if (this.enabled) {
if (this.hovered) {
this.bg.beginFill(0x0066cc);
} else {
this.bg.beginFill(0x0066ff);
}
} else {
this.bg.beginFill(0xd0d0d0);
}
this.bg.drawRect(0,0,this.width,this.height);
};
module.exports = ConfidenceRating;
\ No newline at end of file
var Trial = require('./Trial.js'),
StudyLogger = require('study-logger'),
moment = require('moment');
function ConfidenceRatingTrial(experiment, options) {
Trial.call(this, experiment, options);
this.type = "ConfidenceRatingTrial";
this.message = options.message || "No question specified";
}
ConfidenceRatingTrial.prototype = Object.create(Trial.prototype);
ConfidenceRatingTrial.prototype.start = function() {
this.experiment.fastTapMenu.openable = false;
this.startTimestamp = +moment();
this.experiment.confidenceDialog.setMessage(this.message);
this.experiment.confidenceDialog.open();
this.handleResponse = function(data) {
var clickPos = data.getLocalPosition(this.experiment.view);
StudyLogger.logEvent({type: 'event.closeinstructions',
message: this.message,
response: data.response,
responseText: data.responseText
});
this.experiment.confidenceDialog.close();
this.completed = true;
this.experiment.confidenceDialog.removeResponseCB(this.handleResponse);
}.bind(this);
this.experiment.confidenceDialog.addResponseCB(this.handleResponse);
StudyLogger.logEvent({type: 'event.showinstructions',
message: this.message});
};
ConfidenceRatingTrial.prototype.update = function(delta) {
};
module.exports = ConfidenceRatingTrial;
\ No newline at end of file
var Trial = require('./Trial.js'),
StudyLogger = require('study-logger'),
items = require('./Items.js'),
moment = require('moment');
function DoneTrial(experiment, options) {
Trial.call(this, experiment, options);
this.type = "DoneTrial";
this.message = options.message;
}
DoneTrial.prototype = Object.create(Trial.prototype);
DoneTrial.prototype.start = function() {
this.experiment.fastTapMenu.openable = false;
this.startTimestamp = +moment();
this.experiment.modalDialog.setMessage(this.message);
this.experiment.modalDialog.setButtonVisible(false);
this.experiment.modalDialog.setButtonEnabled(false);
this.experiment.modalDialog.open();
StudyLogger.logEvent({type: 'event.showinstructions',
message: this.message});
StudyLogger.logEvent({type: 'event.experimentcomplete'});
};
DoneTrial.prototype.update = function(delta) {
};
module.exports = DoneTrial;
\ No newline at end of file
var _ = require('lodash'),
PIXI = require('pixi.js'),
Trial = require('./Trial.js'),
FastTapMenu = require('./FastTapMenu.js'),
FastTapMenuItem = require('./FastTapMenuItem.js'),
StudyLogger = require('study-logger'),
util = require('util'),
items = require('./Items.js'),
Stimulus = require('./Stimulus.js'),
Instructions = require('./Instructions.js'),
ModalDialog = require('./ModalDialog.js'),
ConfidenceRating = require('./ConfidenceRating.js'),
store = require('store'),
moment = require('moment'),
KeyboardManager = require('./KeyboardManager.js');
var menuItems = [];
items.allItems.forEach(function(item) {
menuItems.push( new FastTapMenuItem(item.name, item.icon) );
});
var fastTapMenu = new FastTapMenu({
rows: 8,
cols: 8,
width: 800,
height: 800,
menuItems: menuItems
});
fastTapMenu.position.x = 5;
fastTapMenu.position.y = 5;
var stimulus = new Stimulus();
stimulus.view.position.x = 855;
stimulus.view.position.y = 355;
var instructions = new Instructions();
instructions.view.position.x = 855;
instructions.view.position.y = 355;
var modalDialog = new ModalDialog();
modalDialog.view.position.x = 0;
modalDialog.view.position.y = 0;
var confidenceDialog = new ConfidenceRating();
confidenceDialog.view.position.x = 0;
confidenceDialog.view.position.y = 0;
function Experiment(options) {
this.user = options.user;
this.auth = options.auth;
this.exp = options.exp;
this.seed = options.seed;
// For local storage keys
this.storageDomain = util.format("ca.usask.%s.%s.%s", this.exp, this.user, this.seed);
this.fastTapMenu = fastTapMenu;
this.stimulus = stimulus;
this.instructions = instructions;
this.modalDialog = modalDialog;
this.confidenceDialog = confidenceDialog;
this.trials = this.generateTrials(this.seed);
this.started = false;
this.curTrial = 0;
this.kbManager = new KeyboardManager();
this.kbManager.addStateChangeHandler(function(keyDown) {
if (keyDown == true) {
this.fastTapMenu.openMenu();
} else {
this.fastTapMenu.closeMenu();
}
}.bind(this));
this.view = new PIXI.DisplayObjectContainer();
this.view.addChild( fastTapMenu );
this.view.addChild( stimulus.view );
this.view.addChild( instructions.view );
this.view.addChild( modalDialog.view );
this.view.addChild( confidenceDialog.view );
fastTapMenu.addItemClickedCB(function(data) {
StudyLogger.logEvent({type: 'event.menuclick',
menuX: data.menuX,
menuY: data.menuY,
globalX: data.globalX,
globalY: data.globalY,
row: data.row,
col: data.col,
item: data.item.name});
});
// Load previous state (if it exists)
if (options.reset === "true") {
this.started = false;
this.curTrial = 0;
this.saveState();
} else {
this.resumeState();
}
// For debugging:
if (!this.started) {
StudyLogger.logEvent({type: 'event.startexperiment'});
this.started = true;
} else {
StudyLogger.logEvent({type: 'event.resumeexperiment',
trialnum: this.curTrial});
}
this.setTrial(this.curTrial);
}
Experiment.prototype.generateTrials = function(seed) {
// Abstract, specified in subclasses
};
Experiment.prototype.getTrial = function() {
if (this.curTrial >= this.trials.length) {
return null;
}
return this.trials[this.curTrial];
};
Experiment.prototype.nextTrial = function() {
this.setTrial(this.curTrial+1);
};
Experiment.prototype.setTrial = function(to) {
this.curTrial = to;
this.saveState();
this.startTrial();
};
Experiment.prototype.saveState = function() {
store.set(this.storageDomain + ".started", this.started);
store.set(this.storageDomain + ".curTrial", this.curTrial);
};
Experiment.prototype.resumeState = function() {
this.started = store.get(this.storageDomain + '.started') || false;
this.curTrial = store.get(this.storageDomain + ".curTrial") || 0;
};
Experiment.prototype.startTrial = function() {
var trial = this.getTrial();
if (trial === null) {
this.saveState();
return;
}
this.fastTapMenu.closeMenu();
trial.start();
};
Experiment.prototype.update = function(delta) {
if (this.getTrial() && this.getTrial().completed === true) {
this.nextTrial();
}
var trial = this.getTrial();
if (trial) {
trial.update(delta);
}
// KB input
this.kbManager.tick();
this.fastTapMenu.update(delta);
this.stimulus.update(delta);
};
Experiment.prototype.render = function() {
this.fastTapMenu.draw();
this.stimulus.render();
};
module.exports = Experiment;
var PIXI = require('pixi.js'),
StudyLogger = require('study-logger'),
int2clr = require('./int2clr.js');
var BG_COLOR = 0xc8c8c8;
var GRID_COLOR = 0x646464;
var FLASH_DURATION = 1500;
var landmarks = [{r: 2, c: 2},
{r: 2, c: 6},
{r: 6, c: 2},
{r: 6, c: 6},
{r: 4, c: 4},
{r: 0, c: 4},
{r: 8, c: 4},
{r: 4, c: 0},
{r: 4, c: 8}];
function FastTapMenu(options) {
PIXI.DisplayObjectContainer.call(this);
this.menuOpen = options.menuOpen || false;
this.flashable = options.flashable || true;
this.openable = options.openable || false;
this.rows = options.rows;
this.cols = options.cols;
this.w = options.width;
this.h = options.height;
this.menuItems = options.menuItems;
this.itemGrid = new Array(this.rows);
this.flashTime = new Array(this.rows);
var r, c;
for (r = 0; r < this.rows; r++) {
this.itemGrid[r] = new Array(this.cols);
this.flashTime[r] = new Array(this.cols);
}
this._itemClickHandlers = [];
this.bg = new PIXI.Graphics();
this.addChild(this.bg);
var i = 0,
colWidth = this.w / this.cols,
rowHeight = this.h / this.rows;
for (r = 0; r < this.rows; r++) {
for (c = 0; c < this.cols; c++) {
i = r*this.cols + c;
if (i < this.menuItems.length) {
this.menuItems[i].position.x = c*colWidth;
this.menuItems[i].position.y = r*rowHeight;
this.menuItems[i].w = colWidth;
this.menuItems[i].h = rowHeight;
this.menuItems[i].updateLayout();
this.addChild(this.menuItems[i]); // add to the display tree
this.itemGrid[r][c] = this.menuItems[i];
this.flashTime[r][c] = 0;
}
}
}
this.grid = new PIXI.Graphics();
this.addChild(this.grid);
this.interactive = true;
this.click = function(data) {
var clickPos = data.getLocalPosition(this);
var r = Math.floor(clickPos.y / rowHeight);
var c = Math.floor(clickPos.x / colWidth);
var item = this.menuItems[r*this.cols + c];
// Call the callbacks registered with item clicks
for (var i = 0; i < this._itemClickHandlers.length; i++) {
this._itemClickHandlers[i]({'menuX': clickPos.x,
'menuY': clickPos.y,
'globalX': data.global.x,
'globalY': data.global.y,
'row': r,
'col': c,
'item': item});
}
};
this.updateLayout();
}
FastTapMenu.constructor = FastTapMenu;