Source: jsoclo/analyzerBase.js


/**
 * Super Contsructor to call in inheriting classes
 * @class
 * 
 * @classdesc Base class for all inheriting analyzers.  Provides common functionality 
 * and parsing of all question files. Children use the information and searchers populated 
 * here to provide application specific feedback.   
 * 
 * This class is not intended to be directly instantiated. It's children are.
 * 
 * @example
 * function subAnalyzer(){
 * 		analyzerBase.call(this);
 * 
 * subAnalyzer specific members 
 * and methods 
 * 
 * }
 * subAnalyzer.prototype = new analyzerBase();
 * subAnalyzer.prototype.constructor = subAnalyzer; 
 * 
 * @see {@link OrgnomAnalyzer}
 * @see {@link spectroAnalyzer}
 * 
 * @tutorial NomenclatureQuestionFileFormat
 * @tutorial SpectroscopyQuestionFileFormat
 * @tutorial ArchitectureOverview
 * 
 */
function analyzerBase() {

	// public members -----------------------------

	/** The category of the question. Example: ethers for orgnom, or empty for spectroscopy. 
	 * defaults to the 'category' cookie value
	 * @type {String}  */
	this.category = $.cookie("category");
	/** The question number, from the category, being analyzed. 
	 * defaults to the 'question' cookie value
	 * @type {String} */
	this.questionNum = $.cookie("question");
	/** Functional Class of the Analyzer.  Example: orgnom or spectroscopy,<br>
	 * used, in part, to determine the location of the question files. 
	 * defaults to the 'fclass' cookie value
	 * @type {String} */
	this.fclass = $.cookie("fClass");

	// private members-----------------------------
	/** Used in private members called from the constructor  
	 * @type {analyzerBase} */
	var that = this;
	/** @type {Boolean} */
	var questionCompleted = false;
	/**The running total average score for the category
	 *  @type {Number} */
	var score = 0.0; 
	/** A constructed string using the config and fclass 
	 * @type {String} */
	var answerURL = config.getBaseUrl() + this.fclass + config.STN
			+ this.category + config.QDIR_PREFIX + this.questionNum + "/"
			+ config.ANSWER_FILE_NAME;
	/**The value from the question file for a help page link. 
	 * Can be a relative or absolute URL. 
	 * @type {String} */
	var helpURL = ""; 
	// private members to store the answer information in
	/** Used to analyze responses against the values from the question file for correct answers.  
	 * @type {searcher} */
	var correctSearcher = null;
	/**  The jme or SMILE String for the question. jme for orgnom, SMILE for spectroscopy  
	 * @type {String}  */
	var jme = "jme not set";
	/** A constructed relative URL to the difficulty img for the question.   
	 * @type {String}  */
	var difSrc = "../img/e.gif"; // as a default
	/** Used to analyze responses against the value from the question file for common wrong answers.
	 *  @type {searcher} */
	var commonSearchers = new Array(); // Searcher for common sections
	/**Used to analyze responses against the values from the question file for strings to search for in the answer. 
	 * @type {Array.<searcher>} */
	var searchers = new Array(); // Serachers for search sections

	//========Ornom itesm.  candidates to move to OrgnomAnalyzer
	/** The type found in the question file for the question.
	 *  used in orgnom
	 *  @type {String}  */
	var typeMessage = config.DEFAULT_NOM_TYPE_MSG;
	/** Used to analyze responses against the value from the question file for the 
	 * number of loci. Used in orgnom
	 * @type {searcher} */
	var lSearcher = null; // Searder for Loci sections
	/** Used to analyze responses against the value from the question file for common wrong answers.
	 *  @type {searcher} */

	/**
	 * @type {Number}
	 */
	var numberOfHints = 0; //we'll parse it, but use it as a changeable index to cycle hints 
	/**
	 * @type {Array.<String>}
	 */
	var hints = new Array(); 
	
	//============== spectro section ============
	/**
	 * Contains searchers based on the spectroscopy question file 
	 * format.  Is populated by the parse methods of this class. 
	 * 
	 * @tutorial SpectroscopyQuestionFileFormat
	 * @example 
	 * {
	 * "mw"		: 	molecularWeightSearcher
	 * "stereo"	:	searcher, 
	 * "typesC"	: 	numTypesCarbonSearcher,
	 * "typesH"	: 	numTypesHydrogenSearcher, 
	 * "numC"	:	numCarbonSearcher,
	 * "numH"	:  	numHydrogenSearcher,
	 * "charge" 	: 	chargeSearcher,
	 * "rings" 	:	ringsSearcher,
	 * "molF"	: 	molecularFormulaSearcher,
	 * "fGroups"	: 	functionalGroupsSearcher
	 * }
	 *   
	 *   @type {Object}
	 */
	var spectroSearchers = {"mw": molecularWeightSearcher,
							"stereo": searcher, 
							"typesC" : numTypesCarbonSearcher, 
							"typesH": numTypesHydrogenSearcher, 
							"numC" : numCarbonSearcher ,
							"numH" :  numHydrogenSearcher,
							"charge" : chargeSearcher,
							"rings" : ringsSearcher,
							"molF" : molecularFormulaSearcher,
							"fGroups": functionalGroupsSearcher};
	
	//some of spectro sections are optional so we need defaults 
	spectroSearchers.stereo = new searcher("/ZZZZZXY987/", "", ""); 
	spectroSearchers.fGroups = new functionalGroupsSearcher(null, "", "");
	
	
	
	// register the AJAX error callback via jquery
	$(document).ajaxError(function(event, jqxhr, settings, exception) {
		that.loadError(event, jqxhr, settings, exception);
	});

	// =========================Private methods =====================

	/**
	 * The callback handler for the AJAX call to obtain the question file. It 
	 * splits the file into sections, delimited by '@', then passes the section
	 * to a section specific parser.  
	 * <p>It does all question file parsing. 
	 * 
	 * @method
	 * @access private
	 * @tutorial QuestionFileFormatNomenclature
	 * @tutorial QuestionFileFormatSpectroscopy
	 * @param data value returned from AJAX 
	 * @param status value returned from AJAX 
	 * @throws a parse error if any of the section parsers throw an exception
	 * 
	 */
	function parseQuestionFile(data, status) {
		// Split the sections into an Array
		var Arr = data.split("@");

		try {
			for (var int = 0; int < Arr.length; int++) {
				section = Arr[int];

				// determine the type of section and
				// dispatch accordingly
				
				//used in orgnom and spectro
				if (/^correct/i.test(section)) {
					parseCorrect(section);
					continue;
				}
				//used in orgnom and spectro
				if (/^difficulty/i.test(section)) {

					parseDifficulty(section);
					continue;
				}
				//used in orgnom
				if (/^jme/i.test(section)) {
					parseJme(section);
					continue;
				}
				//used in orgnom
				if (/^loci/i.test(section)) {
					parseLoci(section);
					continue;
				}
				//used in orgnom
				if (/^type\stype/i.test(section)) {
					parseType(section);
					continue;
				}
				//used in orgnom and maybe spectro
				if (/^search/i.test(section)) {
					parseSearch(section);
					continue;
				}
				//used in orgnom and spectro
				if(/^link/i.test(section)){
					parseLink(section);
					continue;
				}
				//used in orgnom and spectro
				if (/\scommon/i.test(section)) {
					parseCommon(section);
					continue;
				}
				
				//used in spectro
				if(/^hint\shint/i.test(section)){
					parseHintNum(section);
					continue;
				}
				
				//used in spectro
				if(/^hint\d\d*\s*hint/i.test(section)){
					parseHint(section);
					continue;
				}
				
				//used in spectro
				if(/^mw\s*\d/i.test(section)){
					parseMolecularWeight(section);
					continue;
				}
				
				//used in spectro
				if(/^stereo\s/i.test(section)){
					parseStereo(section);
					continue;
				}
				
				//used in spectro
				if(/^c\s*\d/i.test(section)){
					parseNumberCarbons(section);
					continue;
				}
				//used in spectro
				if(/^h\s*\d/i.test(section)){
					parseNumberHydrogens(section);
					continue;
				}
				
				//used in spectro
				if(/^typec\s*\d/i.test(section)){
					parseNumberTypesCarbons(section);
					continue;
				}
				//used in spectro
				if(/^typeh\s*\d/i.test(section)){
					parseNumberTypesHydrogens(section);
					continue;
				}
				//used in spectro
				if(/^charge\s*\d/i.test(section)){
					parseCharge(section);
					continue;
				}
				
				//used in spectro
				if(/^rings\s*\d/i.test(section)){
					parseRings(section);
					continue;
				}
				
				//used in spectro
				if(/^mf\s/i.test(section)){
					parseMolecularFormula(section);
					continue;
				}
				
				//used in spectro
				if(/^fg\s/i.test(section)){
					parseFunctionalGroups(section);
					continue;
				}
				
				// anything else in the file is ignored
			}
		} catch (e) {
			
			throw config.AF_LOAD_ERROR + " \n\nParse Error in " + e;
		}
	}
	
	
	/**
	 * Parses the functional Group section from the question file and stores the 
	 * result in spectroSearchers.fGroups
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseFunctionalGroups(section){
		var lines = getSectionLines(section, /^fg\s*/i);
		spectroSearchers.fGroups = new functionalGroupsSearcher(lines[0].trim(), lines[1], lines[2].substr(1));
		
	}
	/**
	 * Parses the mf section from the question file and stores the 
	 * result in spectroSearchers.molF
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseMolecularFormula(section){
		var lines = getSectionLines(section, /^mf\s*/i);
		spectroSearchers.molF = new molecularFormulaSearcher(lines[0].trim(), lines[1], lines[2].substr(1));
	}
	
	/**
	 * Parses the charge section from the question file and stores the 
	 * result in spectroSearchers.charge
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseCharge(section){
		var lines = getSectionLines(section, /^charge\s*/i);
		spectroSearchers.charge = new chargeSearcher(lines[0], lines[1].substr(1));
		
	}
	
	/**
	 * Parses the ring section from the question file and stores the 
	 * result in spectroSearchers.rings
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseRings(section){
		var lines = getSectionLines(section, /^rings\s*/i);
		spectroSearchers.rings = new ringsSearcher(lines[0], lines[1].substr(1));

	}
	
	/**
	 * Parses the typeh section from the question file and stores the 
	 * result in spectroSearchers.typeH
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseNumberTypesHydrogens(section){
		
		var lines = getSectionLines(section, /^typeh\s*/i); 
		spectroSearchers.typesH = new numTypesHydrogenSearcher(lines[0], lines[1], lines[2].substr(1));
	
	}
	
	
	
	/**
	 * Parses the typec section from the question file and stores the 
	 * result in spectroSearchers.typeC
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseNumberTypesCarbons(section){
		
		var lines = getSectionLines(section, /^typec\s*/i); 
		spectroSearchers.typesC = new numTypesCarbonSearcher(lines[0], lines[1], lines[2].substr(1));

	}
	
	/**
	 * Parses the h section from the question file and stores the 
	 * result in spectroSearchers.numH
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseNumberHydrogens(section){
		
		var lines = getSectionLines(section, /^h\s*/i); 
		spectroSearchers.numH = new numHydrogenSearcher(lines[0], lines[1], lines[2].substr(1));
		
	}

	/**
	 * Parses the c section from the question file and stores the 
	 * result in spectroSearchers.numC
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseNumberCarbons(section){
		
		var lines = getSectionLines(section, /^c\s*/i); 
		spectroSearchers.numC = new numCarbonSearcher(lines[0], lines[1], lines[2].substr(1));
	
	}

	/**
	 * Parses the stereo section from the question file and stores the 
	 * result in spectroSearchers.stereo
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseStereo(section){
		
		var lines = getSectionLines(section, /^stereo\s/i); 	
		spectroSearchers.stereo = new searcher(lines[0].trim(), lines[1], "");
		//alert(typeof spectroSearchers.stereo);
	}
	
	/**
	 * Parses the mw section from the question file and stores the 
	 * result in spectroSearchers.mw
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseMolecularWeight(section){
		
		var lines = getSectionLines(section, /^mw\s*/i); 
		spectroSearchers.mw = new molecularWeightSearcher(lines[0], lines[1], lines[2].substr(1));
		
	}
	
	/**
	 * Parses the hint section from the question file and stores 
	 * the result in numberOfHints.
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseHintNum(section){
		var lines = getSectionLines(section, /^hint\s*hint\s*/i); 
		numberOfHints = lines[0];
		
		//we reset it so we can use it to 
		//feed the hints
		numberOfHints = 0; 
		
	}
	/**
	 * Parses the hintN section(s) from the question file and adds the 
	 * result to the hints array
	 * 
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * 
	 */
	function parseHint(section){
		var lines = getSectionLines(section, /^hint\d\d*\s*hint\s*/i); 
		hints.push(lines[0]);
	}
	
	
	
	/**
	 * Parses the help link section from the question file and stores the 
	 * result in this.helpURL
	 * @param section the link section from the question file.
	 * @private
	 * @method
	 * @throws 'parsLink' on error
	 * 
	 */
	function parseLink(section){
		
		try{
			var lines = getSectionLines(section, /^link\s*help\s*/i);
			helpURL = lines[0];
			
			
		}catch(e){
			throw " parseLink ";
		}
	}

	/**
	 * Parses the correct section from the question file and creates a
	 * searcher from the result
	 * @param section correct section of question file. 
	 * @private
	 * @method
	 * @throws 'parseCorrect' on error 
	 */
	function parseCorrect(section) {
		
		try {
			var lines = getSectionLines(section, /^correct\s*/i);
			correctSearcher = new searcher(lines[0].trim(), lines[1], null);
			
		} catch (e) {
			throw " parseCorrect ";
		}
		
	}

	/**
	 * Sets the img src string based on the difficulty of the question.
	 * 
	 * @param section  the difficulty section from the question file.
	 * @private
	 * @method
	 * @throws 'parseDifficulty' on error 
	 */
	function parseDifficulty(section) {

		try {
			var lines = getSectionLines(section, /^difficulty\s*difficulty\s*/i);
			difSrc = "../img/e.gif"; //default just in case

			if (lines[0][0] == "e") {
				difSrc = "../img/e2.gif";

			}
			if (lines[0][0] == "m") {
				difSrc = "../img/m2.gif";

			}

			if (lines[0][0] == "d") {
				difSrc = "../img/d2.gif";

			}
			if (lines[0][0] == "x") {
				difSrc = "../img/x2.gif";

			}
		} catch (e) {
			throw " parseDifficulty ";
		}
		

	}

	/**
	 * Sets the type message string of the question.
	 * @param section  the type section from the question file.
	 * @private
	 * @method
	 * @throws 'parseType' on error 
	 */
	function parseType(section) {

		try {
			var lines = getSectionLines(section, /^type\s*type\s*/i);
			typeMessage += "of this " + lines[0];
		} catch (e) {
			throw " parseType "; 
		}

	}
	
	
	/**
	 * Sets the SMILE  string of the question.
	 * @param section  the JME section from the question file.
	 * @private
	 * @method
	 * @throws 'parseJME' on error
	 */
	function parseJme(section) {

		try {
			var lines = getSectionLines(section, /^jme\s*jme\s*/i);
			jme = lines[0];
		} catch (e) {
			throw " parseJME "; 
		}

	}

	/**
	 * Parses the loci section from the question file and creates a
	 * {@link lociSearcher} from the result.
	 * @param section  the loci section from the question file. 
	 * @private
	 * @method
	 * @throws 'parseLoci' on error 
	 */
	function parseLoci(section) {
		try {
			var lines = getSectionLines(section, /^loci\s*/i);
			lSearcher = new lociSearcher(lines[0], lines[1].substr(1,
					lines[1].length - 1));
		} catch (e) {
			throw " parseLoci "; 
		}

	}

	/**
	 * Parses a search section from the question file, creates a
	 * {@link searcher} for it and adds it to the searchers array. 
	 * @param section  a search section from the question file. 
	 * @private
	 * @method
	 * @throws 'parseSection' on error
	 */
	function parseSearch(section) {

		try {
			var lines = getSectionLines(section, /^search\s*/i);
			var regEx = lines[0].substr(0, lines[0].length - 1); // the last char is a \r so we remove it

			var positiveMsg = "";
			var negativeMsg = "";

			if ('$' != lines[1][0]) {
				positiveMsg = lines[1];
			} else {
				negativeMsg = lines[1].substr(1); // remove the $ from the string
			}

			if (lines[2]) { // if there is a second line

				if ('$' != lines[2][0]) {
					positiveMsg = lines[2]; // overwrites the first if there were 2.
				} else {
					negativeMsg = lines[2].substr(1);
				}
			}

			// add this searcher to the analyzers array of searchers
			searchers.push(new searcher(regEx, positiveMsg, negativeMsg));
		} catch (e) {
			throw " parseSearch ";
		}

	}

	/**	  
	 * 
	 * Parses the common section from the question file and creates a
	 * {@link searcher} from the result. <p>
	 * The common section is different in that the string common does not appear
	 * at the beginning of the section  like the other section names do.
	 * Common appears last on the first line of the section. This is the only way we can tell it
	 * is a common section, but the question files seem consistent in doing this.
	 * @param section  the common section from the question file. 
	 * @private
	 * @method
	 * @throws 'parseCommon' on error 
	 */
	function parseCommon(section) {

	try {
				var lines = section.split(/\s*common\s*/i);
		var regEx = lines[0];
		var positiveMsg = "";
		var negativeMsg = "";

		lines = lines[1].split(/\n/);
		// section is only concerned with
		// either 1 or 2 lines.
		// anything else is ignored
		if ('$' != lines[0][0]) {
			positiveMsg = lines[0];
		} else {
			negativeMsg = lines[0].substr(1);
		}

		if (lines[1]) { // if there is a second line

			if ('$' != lines[1][0]) {
				positiveMsg = lines[1]; // overwrites the first if there were 2.
			} else {
				negativeMsg = lines[1].substr(1);
			}
		}

		//alert(regEx);
		var commonSearcher = new searcher(regEx, positiveMsg, negativeMsg);
		commonSearchers.push(commonSearcher); 
		

		} catch (e) {
			throw " parseCommon "; 
		}
	}
	
	

	/**	 
	 * Helper method that splits the section into lines, except for parseCommon. 
	 * Relies on each line being separate.  (splits on newlines).
	 * @private
	 * @method
	 * @param {String} section the section from the question file to split into lines. 
	 * @param regExp  the reqular expression used to split the first line from the remaining lines  
	 * @returns {Array.<String>}  each element is a line, delimited by \n in the section.
	 */
	function getSectionLines(section, regExp) {

		return section.split(regExp)[1].split(/\n/);

	}

	// =========================privileged methods =====================

	/**
	 * Global AJAX callback for failed AJAX requests.  Most likely cause of the 
	 * failure would be file not found. 
	 * @throws {String}  config.AF_LOAD_ERROR;
	 * @access protected
	 * @method
	 * @throws 'File Not Fund Error'  
	 */
	this.loadError = function(anevent, ajqxhr, thesettings, anexception) {

		throw config.AF_LOAD_ERROR + "\n\nFile Not Found Error "; 

	};

	/**
	 * AJAX (made globally Synchronous) to get the question file. we have to do
	 * synchronous because the jsmeonLoand function needs this.jme to be set and
	 * available before it(jsmeOnloand) is called.
	 * 
	 * This method should be called from the children of analyzerBase in their
	 * constructors.  After construction, children should only use the 
	 * loadQuestionFile(qClass, category,qnum) method.
	 * 
	 * @see {@link analyzerBase}
	 * @access protected
	 * @method
	 */
	this.getQuestionFile = function() {
		$.ajaxSetup({
			async : false
		});

		// alert("Loading: " + this.getAnswerURL());
		$.get(this.getAnswerURL(), function(data, status) {
			parseQuestionFile(data, status);
		});

	};

	/**
	 * Reloads the analyzer with a new questiod, based on functional class,
	 * category and qnum.  Resets the the cookies for each parameter if the parameter
	 * is not NUll.   
	 * 
	 * If this method is called with all Null parameters it will reload the current
	 * question file. 
	 * 
	 * @param {string} qClass 
	 * @param {string} category
	 * @param {string} qnum
	 * @access protected
	 * @method
	 * 
	 */
	this.loadQuestionFile = function(qClass, category, qnum) {

		// alert( category + " "+ qnum);
		try {
			// sets the member value and cookie
			if (qClass != null) {
				this.setfclass(qClass);
				$.cookie("fclass", qClass, {
					path : '/'
				});
			}
			if (category != null) {
				this.setCategory(category);
				$.cookie("category", category, {
					path : '/'
				});
			}
			if (qnum != null) {
				this.setQuestionNum(qnum);
				$.cookie("question", qnum, {
					path : '/'
				});
			}
			this.setURL(config.getBaseUrl() + this.fclass + config.STN
					+ this.category + config.QDIR_PREFIX + this.questionNum
					+ "/" + config.ANSWER_FILE_NAME);

			this.reset();
			this.getQuestionFile();

		} catch (e) {
			// rethrow the error
			throw e;
		}

	};

	/**
	 * Called by loadQuestion file. Resets the class in preperation to make 
	 * a call to getQuestionFile so that parseQuestionFile will populate the 
	 * searchers and other information properly from the new question file information. 
	 * @access protected
	 * @method
	 */
	this.reset = function() {
		searchers = new Array();
		correctSearcher = null;
		commonSearchers = new Array(); 
		spectroSearchers.stereo = new searcher("/zzzz987wrtywd/", "", ""); //needs default for spectro 
		lSearcher = null;
		typeMessage = config.DEFAULT_NOM_TYPE_MSG;
		questionCompleted = false;
		jme = null;
		helpURL = ""; 
		numberOfHints = 0; 
		hints = new Array(); 
		spectroSearchers.fGroups = new functionalGroupsSearcher(null, "", "");
		
	};

	/**
	 * Getter for the Help URL from the question file. 
	 * @returns {String} the help URL
	 */
	this.getHelpURL = function(){
		
		return helpURL;
		
	};
	/**
	 * Sets the URL for the question file location.  Use with 
	 * caution 
	 * 
	 * @param {String} value 
	 * @access protected 
	 * @method 
	 * 
	 */
	this.setURL = function(value) {
		answerURL = value;
	};

	
	/**
	 * Sets the Functional Class
	 * @param {String} value 
	 * @access protected 
	 * @method
	 */
	this.setfclass = function(value) {
		this.fclass = value;

	};

	/**
	 * Sets the Category
	 * @param {String} value 
	 * @access protected 
	 * @method 
	 */
	this.setCategory = function(value) {
		this.category = value;

	};

	/**
	 * Sets the question number (for the cateory)
	 * @param {String} value 
	 * @access protected
	 * @method
	 */
	this.setQuestionNum = function(value) {
		this.questionNum = value;

	};

	/**
	 * @returns the maximum attempts for this question. Used 
	 * to determine when to show the solution and allow another 
	 * question to be selected. 
	 * @access protected 
	 * @method 
	 */
	this.getMaxAttempts = function() {
		return config.MAXATTEMPTS;
	};

	/**
	 * Set the question score.  Used by inheriting classes. 
	 * @param {Number} value 
	 * @access protected
	 */
	this.setScore = function(value) {
		score = value;

	};

	/**
	 * @returns {String} the URL to the question File
	 * @access protected
	 */
	this.getAnswerURL = function() {
		return answerURL;
	};
	/**
	 * @returns {Number} the Question Score if the question was completed zero otherwise.
	 * @access protected
	 */
	this.getScore = function() {

		return score;
	};

	/**
	 * @returns {Boolean}
	 * @access protected
	 */
	this.isCompleted = function() {
		return questionCompleted;
	};
	
	
	/**
	 * Set the complete flag for the question. 
	 * @access protected
	 */
	this.setComplete = function() {
		questionCompleted = true;
	};

	/**
	 * Clear the complete flag for the question.
	 * @access protected
	 */
	this.clearComplete = function() {
		questionCompleted = false;
	};


	/**
	 * @returns {String} the Message from the question file for a correct answer. 
	 * @access protected
	 */
	this.getCorrectMsg = function() {
		return correctSearcher.getfoundMsg();
	};

	/**
	 * @returns {String} for the reqular expression
	 * @access protected 
	 */
	this.getCorrectAnswer = function() {

		return correctSearcher.getRegExpString();
	};

	/**
	 * checks that answer is an exact case insensitive match with any one of the
	 * acceptable answers found in the question file.
	 * 
	 * @param {String} answer a string trimmed of leading and trailing
	 *            whitespace prior to calling this method.
	 * 
	 * @returns {Boolean} true if it is an exact match. 
	 * @access protected 
	 * 
	 */
	this.isCorrect = function(answer) {
		
		
		if (correctSearcher.test(answer)) {
			// the answer is found by the regex (ie. the regex is at least
			// a subset of the answer)
			// now test for an exact match with one of the regex values
			answerUpper = answer.toUpperCase();
			var correctAnswers = correctSearcher.getRegExpString().split("|");
			
			
			for (var int = 0; int < correctAnswers.length; int++) {
				var toTest = correctAnswers[int].toUpperCase();
				if (answerUpper == toTest) {
					return true;
				}

			}
		}
		// if we get here then the given answer
		// had something else in it.
		return false;
	};

	/**
	 * @returns {String} representing the relative path to the image to use
	 *          for the difficulty img on the question page. The path is built
	 *          based on the difficulty value in the question file.
	 * @access protected
	 */
	this.getDifficulty = function() {
		return difSrc;
	};

	/**
	 * @returns {String} the type message from the question file. 
	 * @access protected
	 */
	this.getType = function() {
		return typeMessage;
	};

	/**
	 * @returns the JME string found in the question file.
	 * access protected
	 */
	this.getJme = function() {
		return jme;
	};

	/**
	 * @returns {lociSearcher} the loci searcher for the question
	 * @access protected 
	 */
	this.getLociSearcher = function() {
		return lSearcher;
	};


	/**
	 * This method should be implemented by children of this class to provide
	 * meaningful feedback.
	 * 
	 * It is provided as a default to facilitate design for new presentations
	 * using analyzers.
	 * @param {string} answer
	 * @throws String
	 * @virtual
	 * @access protected 
	 */
	this.getFeedback = function(answer) {

		throw "analyzerBase.getFeedBack must be overriden by inheriting classes"
				+ " in order to provide meaningful feedback";

	};
	
	this.getHint = function(){
		if(numberOfHints >= hints.length){
			numberOfHints = 0; 
		}
		return hints[numberOfHints++];
		
	};

	/**
	 * This method should be implemented by children of this class to provide
	 * meaningful syntax validation
	 * 
	 * It is provided as a default to facilitate design for new presentations
	 * using analyzers.
	 * @param {string} answer
	 * @throws String
	 * @virtual
	 * @access protected 
	 */
	this.checkSyntax = function(answer) {

		throw "analyzerBase.checkSyntax must be overriden by inheriting classes"
				+ " in order to provide syntax validation";

	};

	/**
	 * @returns {searcher} The first common searcher in the array of common searchers or 
	 * null if no common searchers are present. 
	 * @access protected 
	 */
	this.getCommonSearcher = function() {

	    	if(commonSearchers.length > 0){
	    	    return commonSearchers[0]; 
	    	}
		return null;
	};
	
	/**
	 * @returns [{searcher}] The array of  common searchers or  
	 * null if no common searchers are present. 
	 * @access protected 
	 */
	this.getCommonSearchers = function() {

	    	if(commonSearchers.length > 0){
	    	    return commonSearchers;
	    	}
		return null;
	};
	
	

	/**
	 * @returns {Array.<searcher>} the array of Searchers other than common, correct and loci. 
	 * @access protected 
	 */
	this.getSearchers = function() {

		return searchers;
	};
	
	this.getMolecularFormulaSearcher = function(){
		return spectroSearchers.molF; 
	}; 

	this.getMolecularWeightSearcher = function(){
		return spectroSearchers.mw; 
	}; 
	
	this.getStereoSearcher = function(){
		
		return spectroSearchers.stereo; 
	};
	
	this.getFunctionalGroupsSearcher = function(){
		
		return spectroSearchers.fGroups; 
	};
	
	this.getTypesCarbonSearcher = function(){
		return spectroSearchers.typesC; 
	}; 
	
	this.getTypesHydrogenSearcher = function(){
		return spectroSearchers.typesH; 
	}; 
	
	this.getNumCargonSearcher = function(){
		return spectroSearchers.numC; 
	}; 
	
	this.getNumHydrogenSearcher = function(){
		return spectroSearchers.numH; 
	}; 
	
	this.getChargeSearcher = function(){
		return spectroSearchers.charge; 
	}; 
	
	this.getRingsSearcher = function(){
		return spectroSearchers.rings; 
	}; 
	
}