/**
* Contsructor for OrgnomAnalyzer
* @class
*
* @classdesc Inherits from analyzerBase and provides Nomenclature specific
* feedback, scoring and syntax checking. <p>
*
* @extends analyzerBase
*
* @tutorial NomenclatureQuestionFileFormat
*
* @see {@link searcher}
*
* @tutorial ArchitectureOverview
*/
function OrgnomAnalyzer(){
//========================= Constructor =====================
//super constructor
analyzerBase.call(this);
var that = this;
// public instance variables
/**
* The question score for this question only
*/
this.questionScore = config.ORGOM_MAX_QUESTION_SCORE;
//Load the question display and feedback information
this.getQuestionFile();
//=========================privileged methods =====================
/**
* Overrides {@link analyzerBase} checkSyntax
* to provide syntax validation for Organic Nomenclature
* answers. The syntax validation is not complete, but rather,
* focuses on more common syntax errors.
*
* @returns a string of feedback based on the syntax of the answer
*
* This method is called in getFeedback(String).
*
* This method replaces the functionality from tutor.java checkSyntax. It produces the
* same result as that method given the same input.
*
*/
this.checkSyntax = function(answer){
var response = "";
var regex = null;
var substr = "";
var match = null;
var openBrackets = [];
var closedBrackets = [];
if(/\d[^\-][a-z]/i.test(answer)){
response += "Syntax error: numbers must be seperated from letters by hyphens.\n";
}
if(/[a-z][^\sa-z][a-z]/i.test(answer)){
response += "Syntax error: letters must be seperated from each other only by spaces.\n";
}
if(/[RSEZ][^\),]/.test(answer)){
response += "Syntax error: stereogenic centers should be in brackets and seperated by commas.\n";
}
if(/[A-DF-MO-QT-Y]/.test(answer)){
response += "Syntax error: make sure you only use uppercase letters to define stereogenic centers and the locant N.\n";
}
//deal with numbers found inside of []
regex = /\[([^\]]*\d[^\[]*)\]/g;
match = regex.exec(answer);
while (match != null){
substr = match[1];
//there is at least 1 digit in substr at this point
if(/\d[^\.|\d]\d/i.test(substr)){
response += "Syntax error: numbers in square brackets must be seperated from eachother by periods.\n";
break; //avoid duplicates of the same error message
}
//next match, if there is one.
match = regex.exec(answer);
}
//deal with numbers not in []
//find the index(es) of open square brackets
regex = /\[/g;
match = regex.exec(answer);
while (match != null){
openBrackets.push(match.index);
match = regex.exec(answer);
}
//find the index(es) of closed square brackets
regex = /\]/g;
match = regex.exec(answer);
while (match != null){
closedBrackets.push(match.index);
match = regex.exec(answer);
}
//before the first [ value
var eob = 0;
if(openBrackets.length > 0){
eob = openBrackets[0];
}
// after the last ] value
var boe = 0;
if(closedBrackets.length >0){
boe = closedBrackets[closedBrackets.length -1];
}
//mmb will be true if the number of brackets is mismatched.
var mmb = openBrackets.length != closedBrackets.length;
//find all numbers not separated by commas
regex = /(\d\s*[^,|\d]\s*\d)/g;
match = regex.exec(answer);
while (match != null){
//before first [, after last ], mismatched and no square brackets in answer.
if(match.index < eob || match.index > boe || mmb){
response += "Syntax error: numbers not in square brackets must be seperated from eachother by commas.\n";
//alert("error on : " + match[1]);
break;
}
//check in between ] and [
var inSquare = false;
//iterate over the equally sized arrays
for(var i = 0; i < openBrackets.length ; i++){
if(match.index > openBrackets[i] && match.index < closedBrackets[i]){
inSquare = true;
break;
}
}
if(!inSquare){ //then it's an error.
response += "Syntax error: numbers not in square brackets must be seperated from eachother by commas.\n";
break;
}
//next match, if there is one.
match = regex.exec(answer);
}
return response;
};
/**
* Overrides {@link analyzerBase getFeedback}
* to provide feedback for Organic Nomenclature
* answers.
* @param {String} answer
* @returns a string of feedback based on the answer content.
*
* This method should be called from the presentation layer.
*
*/
this.getFeedback = function(answer){
var response = "";
if(this.isCorrect(answer)){
return response + this.getCorrectMsg();
}
//if its not correct then...
response += this.checkSyntax(answer);
var commonSearcher = this.getCommonSearcher();
if(commonSearcher != null){
var tmp = commonSearcher.search(answer);
if(tmp != ""){response += tmp +"\n";}
}
var searchers = this.getSearchers();
for (var int = 0; int < searchers.length; int++) {
var element = searchers[int];
var tmp = element.search(answer);
if(tmp != ""){response += tmp + "\n";}
}
//finally check the number of loci
var lse = this.getLociSearcher();
if( lse != null){
response += lse.search(answer) + "\n";
}
return response;
};
}
//inherit from the analyzerParent
OrgnomAnalyzer.prototype = new analyzerBase();
OrgnomAnalyzer.prototype.constructor = OrgnomAnalyzer;
//=========================Public methods =====================
/**
*
* getCorrect() deals with all scoring. getCorrect must be
* called for any scoring to be be dealt with.
*
* Side affect: will calculate the score on the first
* and only the first correct answer.
*/
OrgnomAnalyzer.prototype.getCorrect = function (answer){
if(!this.isCompleted() && this.isCorrect(answer) ){
this.calcScore();
this.setComplete();
return true;
}else if(this.questionScore > 0){
this.questionScore--;
}
return false;
};
/**
* Gives the question a zero and calculates the new average
* for the category.
*/
OrgnomAnalyzer.prototype.forfeitQuestion = function (){
this.questionScore = 0;
this.setComplete();
this.calcScore();
};
/**
*
* This method should be called by either OrgnomAnalyzer.getCorrect(),
* or by OrgnomAnalyzer.forfeitQuestion() only.
*
* It uses the cookie values to calculate the average score for
* the section. It keeps, in cookies, the total score and the number
* of questions tried and simply calculates the average.
*
* It sets the resultant average by calling analyzerBase.setScore.
*
*/
OrgnomAnalyzer.prototype.calcScore = function(){
var theQScore = this.questionScore/config.ORGOM_MAX_QUESTION_SCORE*100;
var theRunningScore = new Number($.cookie("score"));
var theNumberOfQuestions = new Number($.cookie("numQuestionsTried")) + 1; //number of questions
var theNewRunningScore = theRunningScore + theQScore;
$.cookie("score",theNewRunningScore, {path: '/'});
$.cookie("numQuestionsTried",theNumberOfQuestions, {path: '/'});
this.setScore(theNewRunningScore/theNumberOfQuestions); // The new score
};