/** * @class * * @classdesc *

* specController() is called via jQueries document.ready function which appears in spectroscopy.html. It is called each time the * page is loaded or the browser is refreshed.

specController is the javascript class responsible for running the * interface for the spectroscopy JSOCLO. * *

* Category is not used in Spectroscopy so we must set the category cookie to the empty string when we initialize. * * @see {@link searcher} * @tutorial ArchitectureOverview */ function specController() { /** * load the other javascripts and the CSS */ loadJS("../js/jquery/jquery.cookie.js"); loadJS("../js/jsoclo/chemUtils.js"); loadJS("../js/jsoclo/analyzerBase.js"); loadJS("../js/jsoclo/spectroAnalyzer.js"); loadJS("../js/jsparser/smidge.js"); loadCSS("../css/spectroscopy.css"); /** the number of attempts made on this quesiton */ var attempts = 0; /** * used to give unique div ID's to jsme instances in the response div. Used by createJsmeDiv() */ var jsmeDivCount = 2; /** * The number of questions in the series. Set by getNumberOfQuestions() used by next and prev quesiton */ var questionMax = 0; /** * after 3 attempts hints are allowed this controls showing them all at once. */ var toggleHints = true; // default since hint button is hidden till needed /** * we keep track of all previous answers in submitAnswer() */ var previousAnwers = new Array(); /** * Keeps track of the the completed questions so that scoring works properly. Is reset when the category changes. The array is * stringified with Json and stored as a cookie. */ var completedQuestions = new Array(); try { //alert("hello"); $.cookie("category", "", { path : '/' }); $.cookie("fClass", config.SPEC_FCLASS, { path : '/' }); if ($.cookie("question") == null) { $.cookie("question", 1, { path : '/' }); } $.cookie("questionLock", "unlocked", { path : '/' }); $.cookie("completedQuestions", JSON.stringify(completedQuestions), { path : '/' }); $.cookie("numQuestionsTried", 0, { path : '/' }); $.cookie("score", 0, { path : '/' }); questionMax = getNumberOfQuestions(); var analyzer = new spectroAnalyzer(); // set the OCLO Functional Class and page titlE $("#title").html(config.SPEC_TITLE); $("#questionNum").html(config.SPEC_TITLE + ": " + config.SPEC_QNAME + $.cookie("question")); $("#difficultyImg").attr("src", analyzer.getDifficulty()); // handlers for the buttons. $("#massButton").click(function() { display(config.SPEC_MASS_IMG); }); $("#irButton").click(function() { display(config.SPEC_IR_IMG); }); $("#cnmrButton").click(function() { display(config.SPEC_CARBON_IMG); }); $("#hnmrButton").click(function() { display(config.SPEC_HYDROGEN_IMG); }); $("#viewAllButton").click(function() { display(config.SPEC_ALLSPEC_PAGE); }); $("#submitAnswerButton").click(function() { submitAnswer(); }); $("#nextButton").click(function() { nextQuestion(); }); $("#prevButton").click(function() { prevQuestion(); }); $("#hintButton").click(function() { showHint(); }); $("#solutionButton").click(function() { unlockQuestion(); display(config.SPEC_ANSWER_PAGE, false); }); // prevent return key on the buttons $("#solutionButton").keydown(function(e) { preventReturn(e); }); $("#hintButton").keydown(function(e) { preventReturn(e); }); $("#nextButton").keydown(function(e) { preventReturn(e); }); $("#prevButton").keydown(function(e) { preventReturn(e); }); $("#massButton").keydown(function(e) { preventReturn(e); }); $("#irButton").keydown(function(e) { preventReturn(e); }); $("#hnmrButton").keydown(function(e) { preventReturn(e); }); $("#cnmrButton").keydown(function(e) { preventReturn(e); }); $("#viewAllButton").keydown(function(e) { preventReturn(e); }); // $("#helpButton").keydown(function(e) {preventReturn(e);}); // hide the solution button $("#solutionButton").hide(); $("#hintButton").hide(); if (config.SPEC_UNLOCK_ALL_QUESTIONS) { unlockQuestion(); } } catch (e) { alert("initializeSpectroscopy: " + e); } /** * callback handler for when a new question number has been selected from the next and previous buttons */ function loadNewQuestion(num) { var qnum = num; // set the questionNumber cookie for reload. $.cookie("question", qnum, { path : '/' }); try { analyzer.loadQuestionFile(null, null, qnum); // set the OCLO Functional Class and page titlE $("#questionNum").html(config.SPEC_TITLE + ": " + config.SPEC_QNAME + $.cookie("question")); $("#difficultyImg").attr("src", analyzer.getDifficulty()); jsmeApplet.reset(); if (!config.SPEC_UNLOCK_ALL_QUESTIONS) { $("#response").val(""); $("#solutionButton").hide(); } attempts = 0; analyzer.questionScore = config.SPEC_MAX_QUESTION_SCORE; // if the question has been completed already // then unlock it. if (checkComplete()) { unlockQuestion(); } } catch (e) { // an unexpected error happened, probably loading or parsing the // question file // so we're going to reset the category and put them back on question // 1, or a complete reset back to functional groups if needed, alert(e + " Question Number: " + $.cookie("question")); $.cookie("score", 0, { path : '/' }); $.cookie("question", 1, { path : '/' }); $.cookie("numQuestionsTried", 0, { path : '/' }); // unlock the question unlockQuestion(); location.reload(); } previousAnwers = new Array(); } /** * Moves to the next question. * * End of questions behaviour is configured using CYCLE_NEXT_PREV value. * */ function nextQuestion() { if (config.SPEC_UNLOCK_ALL_QUESTIONS) { unlockQuestion(); } var locked = $.cookie("questionLock") == "locked"; if (locked) { alert(config.NO_CATEGORY_CHANGE); return; } var nextQnum = new Number($.cookie("question")) + 1; if (nextQnum.valueOf() > questionMax) { if (config.CYCLE_NEXT_PREV) { nextQnum = new Number(1); } else { alert(config.NO_NEXT_QUESTION); return; } } if (!config.SPEC_UNLOCK_ALL_QUESTIONS) { // hide the solution button $("#solutionButton").hide(); $("#hintButton").hide(); } $("#response").html(""); loadNewQuestion(nextQnum); } /** * Moves to the previous question in the category. * * Beginning of questions behaviour is configured using config CYCLE_NEXT_PREV value. * */ function prevQuestion() { if (config.SPEC_UNLOCK_ALL_QUESTIONS) { unlockQuestion(); } var locked = $.cookie("questionLock") == "locked"; if (locked) { alert(config.NO_CATEGORY_CHANGE); return; } var prevQnum = new Number($.cookie("question")) - 1; if (prevQnum.valueOf() == 0) { if (config.CYCLE_NEXT_PREV) { prevQnum = new Number(questionMax); } else { alert(config.NO_PREV_QUESTION); return; } } if (!config.SPEC_UNLOCK_ALL_QUESTIONS) { // hide the solution button $("#solutionButton").hide(); $("#hintButton").hide(); } $("#response").html(""); loadNewQuestion(prevQnum); } /** * Submits to the analyzer and updates the page attributes and values based on the analyzer responses. */ function submitAnswer() { var answer = jsmeApplet.smiles(); var feedback = ""; try { if (answer == "") { throw config.NO_ANSWER; } // If this is the first non-empty submitted // answer we disable navigation if (previousAnwers.length == 0) { // lock for page $.cookie("questionLock", "locked", { path : '/' }); } // if the question has been completed already // then unlock it. if (checkComplete()) { unlockQuestion(); } if (checkForDuplicates(answer)) { throw config.DUPLICATE_ANSWER; } attempts++; toggleHints = true; // // default a message if the analyzer produces no // feedback var tmpMsg = analyzer.getFeedback(answer); if (tmpMsg.match(/^\s*$/i)) { feedback += config.UNRECOGNIZED_ANWER; } else { feedback += tmpMsg; } if (attempts == config.MAXATTEMPTS && !analyzer.isCorrect(answer)) { unlockQuestion(); if (config.ALERT_PERSIST_MSGS) { alert(config.getPersistMessage()); } else { feedback += "" + config.getPersistMessage() + "
"; feedback += "Solution button is available
"; } attempts = 0; } if (config.ALLOW_HINTS_AT == attempts) { if (config.ALERT_HINTS_MSGS) { alert(config.HINTS_ALERT_MSG); } else { feedback += "" + config.HINTS_ALERT_MSG + "
"; } $("#hintButton").show(); } // if this is the first time the answer is correct // if (!checkComplete() && analyzer.getCorrect(answer)) { completedQuestions.push($.cookie("question")); $.cookie("completedQuestions", JSON.stringify(completedQuestions), { path : '/' }); $("#scoreDiv").html(createScoreString()); unlockQuestion(); } // create the jsmeApplet to display the answer in, // first create the div string and a unique ID. var jsme = createJsmeDiv(); // insert into the DOM var response = "Your Answer
" + jsme.div + "FeedBack:
" + feedback + "
-----------------
"; var previousResponses = $("#response").html(); $("#response").html(response + previousResponses); // create the applet inside the div and feed it the answer as a JME stirng. var myApplet = new JSApplet.JSME(jsme.id, "250px", "120px", { "options" : "depict" }); myApplet.readMolecule(jsmeApplet.jmeFile()); /* * $("#"+ jsme.id).css("margin-left", "300"); $("#"+ jsme.id).css("clear", "none"); */ } catch (e) { alert(e); } } /** * Creates the string for a div to be populated with a JSME applet. * * Returns an object with the div string and its unique id. * */ function createJsmeDiv() { var id = "JSME" + jsmeDivCount++; var response = { "div" : String, "id" : String }; response.div = '

'; response.id = id; return response; } /** * Uses the values in the cookies to create a string showing the average (as a %) over x number of questions. * * @returns {String} */ function createScoreString() { var me = "Score: " + Math.round(analyzer.getScore()) + "% on " + $.cookie("numQuestionsTried") + " question"; var numcompletedQs = numberOfCompleteQuetions(); // create the string if (numcompletedQs == 0) { return ""; } if (numcompletedQs > 1) { me += "s"; } return me; } /** * unlocks the question so that the solution can be viewed and the question or category changed. Also allows duplicate answers to be * submitted. * */ function unlockQuestion() { // $("#numberList").removeAttr("disabled"); $("#nextButton").removeAttr("disabled"); $("#prevButton").removeAttr("disabled"); $("#solutionButton").show(); $("#hintButton").show(); $.cookie("questionLock", "unlocked", { path : '/' }); previousAnwers = new Array(); } /** * Called from submitAnswer() to check if the answer was previously given. * * @param answer * The answer that was submitted. * @returns {Boolean} Can you guess what true means? */ function checkForDuplicates(answer) { if (config.SPEC_UNLOCK_ALL_QUESTIONS) { return false; } var regex = new RegExp(RegExp.escape(answer)); // alert(previousAnwers); for (var int = 0; int < previousAnwers.length; int++) { var pastAnswer = previousAnwers[int]; if (regex.test(pastAnswer) == true) { if (pastAnswer == answer) { return true; } } } previousAnwers.push(answer); return false; } /** * Alerts or displays a hint. Handler for the hint button. * Behaviour is configured via config SPEC_UNLOCK_ALL_QUESTIONS and * config ALERT_HINTS_MSGS */ function showHint() { if (config.SPEC_UNLOCK_ALL_QUESTIONS) { toggleHints = true; } if (config.ALERT_HINTS_MSGS) { if (toggleHints) { alert(analyzer.getHint()); } else { alert(config.TRY_FIRST); } } else { if (toggleHints) { $("#response").html("" + analyzer.getHint() + "
" + $("#response").html()); } else { $("#response").html("" + config.TRY_FIRST + "
" + $("#response").html()); } } toggleHints = false; // show one hint at a time. } /** * * @returns {Number} number of complete questions in the category since the category was selected. */ function numberOfCompleteQuetions() { var completedQs = JSON.parse($.cookie("completedQuestions")); return completedQs.length; } /** * checks to see if the question number in the cookie 'queston' has been completed. * * @returns {Boolean} true if the question is complete, false otherwise. */ function checkComplete() { var completedQs = JSON.parse($.cookie("completedQuestions")); var questionNum = $.cookie("question"); for (var int = 0; int < completedQs.length; int++) { var compl = completedQs[int]; if (questionNum == compl) { return true; } } return false; } /** * Displays a popup window or new tab * * @param {String} * what what is the rleative URL to display * @param {Boolean} * popup true and the window will be a popup false and it will be in a new tab. */ function display(what, popup) { try { var newWindow = null; var specString = config.getBaseUrl() + analyzer.fclass + config.STN + config.QDIR_PREFIX + $.cookie("question") + "/" + what; if (popup == true || popup == null) { newWindow = window.open(specString, "Pup", "toolbar=no,menubar=no,status=yes,scrollbars=yes,width=750,height=460"); newWindow.focus(); } else { newWindow = window.open(specString, "Ptab"); newWindow.focus(); } // alert(newWindow.document.title); } catch (e) { alert(e); } } /** * Uses Ajax (in a synchronous mode) to retrieve /json/spectroscopy.json and uses it determine the number * of questions for spectroscopy. * * If you add a question you must update /json/spectroscopy.json accordingly * * @returns the number of questions */ function getNumberOfQuestions() { var maxQuestions = 0; // make it synch $.ajaxSetup({ async : false }); try { var url = config.getBaseUrl() + config.SPEC_CONF; $.get(url, function(data, status) { maxQuestions = new Number(JSON.parse(JSON.stringify(data))).valueOf(); // alert(maxQuestions); }); } catch (e) { alert("Fatal Error: Unable to load json" + e); throw e; } return maxQuestions; } /** * * Used by initializeSpectroscopy to load javascript files it depends on. JSME and JQuery must already * be loaded (from the html page). * * @param src */ function loadJS(src) { var jsLink = $("