var Stimulus = require("./Stimulus").Stimulus,
Q = require("q");
/**
* @class The Response is a minimal customization of a Stimulus which allows the user to add additional information
* which can be used for experiments.
*
* @name Response
* @extends Stimulus
* @constructs
*/
var Response = function Response(options) {
if (!this._fieldDBtype) {
this._fieldDBtype = "Response";
}
this.debug("Constructing Response ", options);
Stimulus.apply(this, arguments);
};
Response.prototype = Object.create(Stimulus.prototype, /** @lends Response.prototype */ {
constructor: {
value: Response
},
jsonType: {
get: function() {
return this.fieldDBtype;
}
},
collection: {
get: function() {
return this.fieldDBtype;
}
},
// responses: {
// value: null,
// configurable: true
// },
pauseAudioWhenConfirmingResponse: {
value: null,
configurable: true
},
addResponse: {
value: function(responseEvent, stimulusId) {
if (!responseEvent) {
throw new Error("Cannot add response without the x y information found in the touch/click responseEvent");
}
var reactionTimeEnd = Date.now();
var audioDuration = this.application.audioPlayer.getDuration(this.audioFile) || 0;
if (audioDuration) {
audioDuration = audioDuration * 1000;
} else {
this.warn("The audio has no duration.. This is strange.");
}
if (this.pauseAudioWhenConfirmingResponse) {
this.pauseAudio();
}
var self = this;
var continueToNextStimulus = Q.defer();
if (this.confirmResponseChoiceMessage) {
this.contextualizer.currentLocale = this.application.interfaceLocale;
var confirmChoicePrompt = this.contextualizer.localize(this.confirmResponseChoiceMessage);
var options = {
iconSrc: self.ownerComponent.iconSrc,
message: confirmChoicePrompt
};
this.confirm(options.message).then(function() {
continueToNextStimulus.resolve();
}, function() {
continueToNextStimulus.reject(new Error("The x prevented the cancel?"));
}).fail(function(error) {
console.error(error.stack, self);
});
} else {
continueToNextStimulus.resolve();
}
continueToNextStimulus.promise.then(function() {
// self.ownerComponent.templateObjects.reinforcement.next();
self.stopAudio();
self.ownerComponent.nextStimulus();
}, function(reason) {
self.warn("Not continuing to next stimulus", reason);
if (this.pauseAudioWhenConfirmingResponse) {
self.playAudio();
}
}).fail(function(error) {
console.error(error.stack, self);
});
var choice = "";
if (stimulusId) {
choice = this[stimulusId].substring(this[stimulusId].lastIndexOf("/") + 1).replace(/\..+$/, "").replace(/\d+_/, "");
if (choice === this.target.orthographic) {
choice = this.target;
} else {
this.distractors.map(function(distractor) {
if (choice === distractor.orthographic) {
choice = distractor;
}
});
}
}
var response = {
"reactionTimeAudioOffset": reactionTimeEnd - this.reactionTimeStart - audioDuration,
"reactionTimeAudioOnset": reactionTimeEnd - this.reactionTimeStart,
"x": responseEvent.x,
"y": responseEvent.y,
"pageX": responseEvent.pageX,
"pageY": responseEvent.pageY,
// "prime": {
// phonemic: this.prime.phonemic,
// orthographic: this.prime.orthographic,
// imageFile: this.prime.imageFile
// },
"choice": choice,
// "target": this.target,
"score": this.scoreResponse(this.target, choice)
};
this.responses.push(response);
self.warn("Recorded response", JSON.stringify(response));
}
},
addOralResponse: {
value: function(choice, dontAutoAdvance) {
var reactionTimeEnd = Date.now();
var audioDuration = this.application.audioPlayer.getDuration(this.audioFile) || 0;
if (audioDuration) {
audioDuration = audioDuration * 1000;
} else {
this.warn("The audio has no duration.. This is strange.");
}
if (this.pauseAudioWhenConfirmingResponse) {
this.pauseAudio();
}
var self = this;
var continueToNextStimulus = Q.defer();
if (this.confirmResponseChoiceMessage) {
this.contextualizer.currentLocale = this.application.interfaceLocale;
var confirmChoicePrompt = this.contextualizer.localize(this.confirmResponseChoiceMessage);
var options = {
iconSrc: self.ownerComponent.iconSrc,
message: confirmChoicePrompt
};
this.confirm(options.message).then(function() {
continueToNextStimulus.resolve();
}, function() {
continueToNextStimulus.reject(new Error("The x prevented the cancel?"));
}).fail(function(error) {
console.error(error.stack, self);
});
} else {
if (!dontAutoAdvance) {
continueToNextStimulus.resolve();
}
}
continueToNextStimulus.promise.then(function() {
// self.ownerComponent.templateObjects.reinforcement.next();
self.stopAudio();
self.ownerComponent.nextStimulus();
}, function(reason) {
self.warn("Not continuing to next stimulus", reason);
if (this.pauseAudioWhenConfirmingResponse) {
self.playAudio();
}
}).fail(function(error) {
console.error(error.stack, self);
});
var response = {
"reactionTimeAudioOffset": reactionTimeEnd - this.reactionTimeStart - audioDuration,
"reactionTimeAudioOnset": reactionTimeEnd - this.reactionTimeStart,
"x": 0,
"y": 0,
"pageX": 0,
"pageY": 0,
"choice": choice,
"score": choice.score
};
this.responses = this.responses || [];
this.responses.push(response);
self.warn("Recorded response", JSON.stringify(response));
}
},
scoreResponse: {
value: function(expectedResponse, actualResponse) {
if (!actualResponse.orthographic) {
return "error";
}
if (actualResponse.orthographic === expectedResponse.orthographic) {
return 1;
} else {
return 0;
}
}
},
addNonResponse: {
value: function(responseEvent) {
if (!responseEvent) {
throw "Cannot add response without the x y information found in the touch/click responseEvent";
}
var reactionTimeEnd = Date.now();
var response = {
"reactionTimeAudioOffset": reactionTimeEnd - this.reactionTimeStart,
"reactionTimeAudioOnset": reactionTimeEnd - this.reactionTimeStart,
"x": responseEvent.x,
"y": responseEvent.y,
"pageX": responseEvent.pageX,
"pageY": responseEvent.pageY,
"chosenVisualStimulus": "none",
"responseScore": -1
};
this.responses = this.responses || [];
this.nonResponses.push(response);
this.warn("Recorded non-response, the user is confused or not playing the game.", JSON.stringify(response));
}
},
/**
* TODO try using a media controller later montage/ui/controller/media-controller
* @type {Object}
*/
playAudio: {
value: function(delay) {
this.application.audioPlayer.play(this.audioFile, delay);
}
},
pauseAudio: {
value: function() {
this.application.audioPlayer.pause(this.audioFile);
}
},
stopAudio: {
value: function() {
this.application.audioPlayer.stop(this.audioFile);
}
},
load: {
value: function(details) {
for (var d in details) {
if (details.hasOwnProperty(d)) {
this[d] = details[d];
}
}
if (this.responses === null) {
this.responses = [];
}
if (this.nonResponses === null) {
this.nonResponses = [];
}
this.nonResponses = [];
this.experimenterId = this.application.experiment.experimenter.id;
this.participantId = this.application.experiment.participant.id;
// Not playing audio by default, child must call it.
// this.playAudio(2000);
}
}
});
exports.Response = Response;