diff --git a/runestone/activecode/js/activecode.js b/runestone/activecode/js/activecode.js index 53e69bebf..3b1eeeaee 100755 --- a/runestone/activecode/js/activecode.js +++ b/runestone/activecode/js/activecode.js @@ -229,7 +229,7 @@ ActiveCode.prototype.addHistoryScrubber = function (pos_last) { $(scrubberDiv).css("margin-right","10px"); $(scrubberDiv).width("180px"); scrubber = document.createElement("div"); - var slideit = function() { + this.slideit = function() { this.editor.setValue(this.history[$(scrubber).slider("value")]); var curVal = this.timestamps[$(scrubber).slider("value")]; //this.scrubberTime.innerHTML = curVal; @@ -239,12 +239,12 @@ ActiveCode.prototype.addHistoryScrubber = function (pos_last) { setTimeout(function () { $(scrubber).find(".sltooltip").fadeOut() }, 4000); - }.bind(this); + }; $(scrubber).slider({ max: this.history.length-1, value: this.history.length-1, - slide: slideit, - change: slideit + slide: this.slideit.bind(this), + change: this.slideit.bind(this) }); scrubberDiv.appendChild(scrubber); diff --git a/runestone/activecode/js/timed_activecode.js b/runestone/activecode/js/timed_activecode.js index 3fa2ae16b..e7b391e7b 100644 --- a/runestone/activecode/js/timed_activecode.js +++ b/runestone/activecode/js/timed_activecode.js @@ -61,4 +61,12 @@ TimedActiveCode.prototype.reinitializeListeners = function () { // re-attach the run button listener $(this.runButton).click(this.runProg.bind(this)); $(this.histButton).click(this.addHistoryScrubber.bind(this)); + if (this.historyScrubber !== null) { + $(this.historyScrubber).slider({ + max: this.history.length-1, + value: this.history.length-1, + slide: this.slideit.bind(this), + change: this.slideit.bind(this) + }); + } }; diff --git a/runestone/assess/js/fitb.js b/runestone/assess/js/fitb.js index 7055d11de..cefe8a8bd 100644 --- a/runestone/assess/js/fitb.js +++ b/runestone/assess/js/fitb.js @@ -50,7 +50,7 @@ FITB.prototype.init = function (opts) { } this.populateFeedbackArray(); this.createFITBElement(); - this.checkServer(); + this.checkServer("fillb"); }; /*==================================== @@ -199,35 +199,11 @@ FITB.prototype.renderFITBFeedbackDiv = function () { === Checking/loading from storage === ===================================*/ -FITB.prototype.checkServer = function () { - // Check if the server has stored answer - if (this.useRunestoneServices) { - var data = {}; - data.div_id = this.divid; - data.course = eBookConfig.course; - data.event = "fillb"; - jQuery.getJSON(eBookConfig.ajaxURL + "getAssessResults", data, this.repopulateFromStorage.bind(this)).error(this.checkLocalStorage.bind(this)); - } else { - this.checkLocalStorage(); - } - -}; - -FITB.prototype.repopulateFromStorage = function (data, status, whatever) { - // decide whether to use the server's answer (if there is one) or to load from storage - if (data !== null) { - if (this.shouldUseServer(data)) { - var arr = data.answer.split(","); - for (var i = 0; i < this.blankArray.length; i++) { - $(this.blankArray[i]).attr("value", arr[i]); - } - this.setLocalStorage(data.correct); // We don't want to set this.correct here because that would interfere with timed grading functionality - } else { - this.checkLocalStorage(); - } - this.enableCompareButton(); - } else { - this.checkLocalStorage(); +FITB.prototype.restoreAnswers = function (data) { + // Restore answers from storage retrieval done in RunestoneBase + var arr = data.answer.split(","); + for (var i = 0; i < this.blankArray.length; i++) { + $(this.blankArray[i]).attr("value", arr[i]); } }; @@ -238,47 +214,27 @@ FITB.prototype.checkLocalStorage = function () { var ex = localStorage.getItem(eBookConfig.email + ":" + this.divid + "-given"); if (ex !== null) { var storedData = JSON.parse(ex); - var arr = storedData.givenArr; + var arr = storedData.answer; for (var i = 0; i < this.blankArray.length; i++) { $(this.blankArray[i]).attr("value", arr[i]); } if (this.useRunestoneServices) { - var answerInfo = "answer:" + storedData.givenArr+ ":" + (storedData.correct ? "correct" : "no"); - this.logBookEvent({"event": "fillb", "act": answerInfo, "div_id": this.divid}); + this.logBookEvent({"event": "fillb", "act": "submitFITB", "answer": storedData.answer.join(","), "correct": storedData.correct, "div_id": this.divid}); this.enableCompareButton(); } } } }; -FITB.prototype.shouldUseServer = function (data) { - // returns true if server data is more recent than local storage or if server storage is correct - if (data.correct == "T" || localStorage.length === 0) - return true; - var ex = localStorage.getItem(eBookConfig.email + ":" + this.divid + "-given"); - if (ex === null) - return true; - var storedData = JSON.parse(ex); - if (data.answer == storedData.givenArr) - return true; - var storageDate = new Date(storedData.timestamp); - var serverDate = new Date(data.timestamp); - if (serverDate < storageDate) - return false; - return true; -}; - -FITB.prototype.enableCompareButton = function () { - this.compareButton.disabled = false; -}; - -FITB.prototype.setLocalStorage = function (correct) { +FITB.prototype.setLocalStorage = function (data) { // logs answer to local storage this.given_arr = []; for (var i = 0; i < this.blankArray.length; i++) this.given_arr.push(this.blankArray[i].value); + var now = new Date(); - var storageObject = {"givenArr": this.given_arr, "correct": correct, "timestamp": now}; + var correct = data.correct; + var storageObject = {"answer": this.given_arr, "correct": correct, "timestamp": now}; localStorage.setItem(eBookConfig.email + ":" + this.divid + "-given", JSON.stringify(storageObject)); }; @@ -294,8 +250,7 @@ FITB.prototype.startEvaluation = function (logFlag) { this.evaluateAnswers(); this.renderFITBFeedback(); if (logFlag) { // Sometimes we don't want to log the answer--for example, when timed exam questions are re-loaded - var answerInfo = "answer:" + this.given_arr + ":" + (this.correct ? "correct" : "no"); - this.logBookEvent({"event": "fillb", "act": answerInfo, "div_id": this.divid}); + this.logBookEvent({"event": "fillb", "act": "submitFITB", "answer": this.given_arr.join(","), "correct": (this.correct ? "T" : "F"), "div_id": this.divid}); } if (this.useRunestoneServices) { this.enableCompareButton(); @@ -328,7 +283,7 @@ FITB.prototype.evaluateAnswers = function () { } else { this.correct = false; } - this.setLocalStorage(this.correct); + this.setLocalStorage({"correct": (this.correct ? "T" : "F")}); }; FITB.prototype.isCompletelyBlank = function () { @@ -385,6 +340,10 @@ FITB.prototype.renderFITBFeedback = function () { === Functions for compare button === ==================================*/ +FITB.prototype.enableCompareButton = function () { + this.compareButton.disabled = false; +}; + FITB.prototype.compareFITBAnswers = function () { var data = {}; data.div_id = this.divid; diff --git a/runestone/assess/js/mchoice.js b/runestone/assess/js/mchoice.js index 18834cbea..52b07b0f0 100644 --- a/runestone/assess/js/mchoice.js +++ b/runestone/assess/js/mchoice.js @@ -57,7 +57,7 @@ MultipleChoice.prototype.init = function (opts) { this.findFeedbacks(); this.createCorrectList(); this.createMCForm(); - this.checkServer(); + this.checkServer("mChoice"); }; /*==================================== @@ -277,67 +277,17 @@ MultipleChoice.prototype.randomizeAnswers = function () { === Checking/loading from storage === ===================================*/ -MultipleChoice.prototype.checkServer = function () { - // Check if the server has stored answer - if (this.useRunestoneServices) { - var data = {}; - data.div_id = this.divid; - data.course = eBookConfig.course; - data.event = "mChoice"; - jQuery.getJSON(eBookConfig.ajaxURL + "getAssessResults", data, this.repopulateFromStorage.bind(this)).error(this.checkLocalStorage.bind(this)); - } else { - this.checkLocalStorage(); // just go right to local storage - } - -}; - -MultipleChoice.prototype.repopulateFromStorage = function (data, status, whatever) { - if (data !== null) { - if (this.shouldUseServer(data)) { - var answers; - if (this.multipleanswers) { - answers = data.answer.split(","); - } else { - answers = [data.answer.charCodeAt(0) - 97]; // Get index for lowercase letter - } - for (var a = 0; a < answers.length; a++) { - var index = answers[a]; - for (var b = 0; b < this.optionArray.length; b++) { - if (this.optionArray[b].input.value == index) { - $(this.optionArray[b].input).attr("checked", "true"); - } - } +MultipleChoice.prototype.restoreAnswers = function (data) { + // Restore answers from storage retrieval done in RunestoneBase + var answers = data.answer.split(","); + for (var a = 0; a < answers.length; a++) { + var index = answers[a]; + for (var b = 0; b < this.optionArray.length; b++) { + if (this.optionArray[b].input.value == index) { + $(this.optionArray[b].input).attr("checked", "true"); } - this.setLocalStorage(); - } else { - this.checkLocalStorage(); } - this.enableMCComparison(); - } else { - this.checkLocalStorage(); - } -}; - -MultipleChoice.prototype.shouldUseServer = function (data) { - // returns true if server data is more recent than local storage or if server storage is correct - if (data.correct == "T" || localStorage.length === 0) - return true; - var ex = localStorage.getItem(eBookConfig.email + ":" + this.divid); - if (ex === null) - return true; - var storedData = JSON.parse(ex); - if (this.multipleanswers) { - if (data.answer == storedData.answer) - return true; - } else { - if ((data.answer.charCodeAt(0) - 97).toString() == storedData.answer[0]) - return true; } - var storageDate = new Date(storedData.timestamp); - var serverDate = new Date(data.timestamp); - if (serverDate < storageDate) - return false; - return true; }; MultipleChoice.prototype.checkLocalStorage = function () { @@ -345,10 +295,10 @@ MultipleChoice.prototype.checkLocalStorage = function () { // which were stored into local storage. var len = localStorage.length; if (len > 0) { - var ex = localStorage.getItem(eBookConfig.email + ":" + this.divid); + var ex = localStorage.getItem(eBookConfig.email + ":" + this.divid + "-given"); if (ex !== null) { var storedData = JSON.parse(ex); - var answers = storedData.answer; + var answers = storedData.answer.split(","); for (var a = 0; a < answers.length; a++) { var index = answers[a]; for (var b = 0; b < this.optionArray.length; b++) { @@ -361,20 +311,19 @@ MultipleChoice.prototype.checkLocalStorage = function () { this.enableMCComparison(); this.getSubmittedOpts(); // to populate givenlog for logging if (this.multipleanswers) { - this.logMCMAsubmission(); + this.logMCMAsubmission(storedData); } else { - this.logMCMFsubmission(); + this.logMCMFsubmission(storedData); } } } } }; -MultipleChoice.prototype.setLocalStorage = function () { - this.getSubmittedOpts(); // make sure this.givenarray is populateDisplayFeed +MultipleChoice.prototype.setLocalStorage = function (data) { var timeStamp = new Date(); - var storageObj = {"answer": this.givenArray, "timestamp": timeStamp}; - localStorage.setItem(eBookConfig.email + ":" + this.divid, JSON.stringify(storageObj)); + var storageObj = {"answer": data.answer, "timestamp": timeStamp, "correct": data.correct}; + localStorage.setItem(eBookConfig.email + ":" + this.divid + "-given", JSON.stringify(storageObj)); }; /*=============================== @@ -383,10 +332,12 @@ MultipleChoice.prototype.setLocalStorage = function () { MultipleChoice.prototype.processMCMASubmission = function (logFlag) { // Called when the submit button is clicked - this.setLocalStorage(); + this.getSubmittedOpts(); // make sure this.givenArray is populated this.scoreMCMASubmission(); + this.setLocalStorage({"correct": (this.correct ? "T" : "F"), "answer": this.givenArray.join(",")}); if (logFlag) { - this.logMCMAsubmission(); + var answer = this.givenArray.join(","); + this.logMCMAsubmission({"answer": answer, "correct": this.correct}); } this.renderMCMAFeedBack(); if (this.useRunestoneServices) { @@ -428,13 +379,15 @@ MultipleChoice.prototype.scoreMCMASubmission = function () { correctIndex++; } } + this.correct = (this.correctCount == this.correctList.length); }; -MultipleChoice.prototype.logMCMAsubmission = function () { - var answerInfo = "answer:" + this.givenlog.substring(0, this.givenlog.length - 1) + ":" + (this.correctCount == this.correctList.length ? "correct" : "no"); - this.logBookEvent({"event": "mChoice", "act": answerInfo, "div_id": this.divid}); +MultipleChoice.prototype.logMCMAsubmission = function (data) { + var answer = data.answer; + var correct = data.correct; + this.logBookEvent({"event": "mChoice", "act": "submitMC", "answer": answer, "correct": correct, "div_id": this.divid}); }; @@ -461,8 +414,9 @@ MultipleChoice.prototype.renderMCMAFeedBack = function () { MultipleChoice.prototype.processMCMFSubmission = function (logFlag) { // Called when the submit button is clicked - this.setLocalStorage(); + this.getSubmittedOpts(); // make sure this.givenArray is populated this.scoreMCMFSubmission(); + this.setLocalStorage({"correct": (this.correct ? "T" : "F"), "answer": this.givenArray.join(",")}); if (logFlag) { this.logMCMFsubmission(); } @@ -483,8 +437,9 @@ MultipleChoice.prototype.scoreMCMFSubmission = function () { MultipleChoice.prototype.logMCMFsubmission = function () { - var answerInfo = "answer:" + this.givenArray[0] + ":" + (this.givenArray[0] == this.correctIndexList[0] ? "correct" : "no"); - this.logBookEvent({"event": "mChoice", "act": answerInfo, "div_id": this.divid}); + var answer = this.givenArray[0]; + var correct = (this.givenArray[0] == this.correctIndexList[0] ? "T" : "F"); + this.logBookEvent({"event": "mChoice", "act": "submitMC", "answer": answer, "correct": correct, "div_id": this.divid}); }; MultipleChoice.prototype.renderMCMFFeedback = function (correct, feedbackText) { diff --git a/runestone/assess/js/timed.js b/runestone/assess/js/timed.js index 41b576ce8..d82360933 100644 --- a/runestone/assess/js/timed.js +++ b/runestone/assess/js/timed.js @@ -65,6 +65,7 @@ Timed.prototype.init = function (opts) { this.incorrectStr = ""; this.skippedStr = ""; this.skipped = 0; + this.hasRenderedFirstQuestion = false; this.currentQuestionIndex = 0; // Which question is currently displaying on the page this.renderedQuestionArray = []; // list of all problems @@ -90,7 +91,6 @@ Timed.prototype.renderTimedAssess = function () { this.renderControlButtons(); this.assessDiv.appendChild(this.timedDiv); // This can't be appended in renderContainer because then it renders above the timer and control buttons. this.createRenderedQuestionArray(); - this.renderTimedQuestion(); this.renderNavControls(); this.renderSubmitButton(); this.renderFeedbackContainer(); @@ -141,6 +141,7 @@ Timed.prototype.renderControlButtons = function () { }); this.startBtn.textContent = "Start"; this.startBtn.addEventListener("click", function () { + this.renderTimedQuestion(); this.startAssessment(); }.bind(this), false); $(this.pauseBtn).attr({ @@ -340,11 +341,15 @@ Timed.prototype.randomizeRQA = function () { Timed.prototype.renderTimedQuestion = function () { $(this.switchDiv).replaceWith(this.renderedQuestionArray[this.currentQuestionIndex].containerDiv); this.switchDiv = this.renderedQuestionArray[this.currentQuestionIndex].containerDiv; - // If the timed component has listeners, those need to be reinitialized + // If the timed component has listeners, those might need to be reinitialized // This flag will only be set in the elements that need it--it will be undefined in the others and thus evaluate to false if (this.renderedQuestionArray[this.currentQuestionIndex].needsReinitialization) { - this.renderedQuestionArray[this.currentQuestionIndex].reinitializeListeners(); + // if this is the first time we're rendering the first question, nothing should be reinitialized + if (this.currentQuestionIndex !== 0 || this.hasRenderedFirstQuestion) { + this.renderedQuestionArray[this.currentQuestionIndex].reinitializeListeners(); + } } + this.hasRenderedFirstQuestion = true; }; @@ -378,8 +383,8 @@ Timed.prototype.startAssessment = function () { this.increment(); this.logBookEvent({"event": "timedExam", "act": "start", "div_id": this.divid}); var timeStamp = new Date(); - var storageObj = {"answerData": [0,0,this.renderedQuestionArray.length,0], "timestamp": timeStamp}; - localStorage.setItem(eBookConfig.email + ":" + this.divid, JSON.stringify(storageObj)); + var storageObj = {"answer": [0,0,this.renderedQuestionArray.length,0], "timestamp": timeStamp}; + localStorage.setItem(eBookConfig.email + ":" + this.divid + "-given", JSON.stringify(storageObj)); } } else { this.handlePrevAssessment(); @@ -511,7 +516,7 @@ Timed.prototype.tookTimedExam = function () { "color": "white" }); - this.checkServer(); + this.checkServer("timedExam"); }; @@ -594,77 +599,47 @@ Timed.prototype.storeScore = function () { var storage_arr = []; storage_arr.push(this.score, this.correctStr, this.incorrect, this.incorrectStr, this.skipped, this.skippedStr, this.timeTaken); var timeStamp = new Date(); - var storageObj = JSON.stringify({"answerData": storage_arr, "timestamp": timeStamp}); - localStorage.setItem(eBookConfig.email + ":" + this.divid, storageObj); + var storageObj = JSON.stringify({"answer": storage_arr, "timestamp": timeStamp}); + localStorage.setItem(eBookConfig.email + ":" + this.divid + "-given", storageObj); }; Timed.prototype.logScore = function () { this.logBookEvent({"event": "timedExam", "act": "finish", "div_id": this.divid, "correct": this.score, "incorrect": this.incorrect, "skipped": this.skipped, "time": this.timeTaken}); }; -Timed.prototype.checkServer = function () { - if (this.useRunestoneServices) { - var data = {}; - data.div_id = this.divid; - data.course = eBookConfig.course; - data.event = "timedExam"; - jQuery.getJSON(eBookConfig.ajaxURL + "getAssessResults", data, this.repopulateFromStorage.bind(this)).error(this.useLocalStorage.bind(this)); - } else { - this.repopulateFromStorage(null, null, null); - } -}; - -Timed.prototype.useLocalStorage = function () { - this.repopulateFromStorage(null, null, null); -}; - -Timed.prototype.repopulateFromStorage = function (data, status, whatever) { - if (data !== null) { - this.taken = 1; - if (this.shouldUseServer(data)) { - this.restoreFromStorage(data); - } else { - this.checkLocalStorage(); - } - } else { - this.checkLocalStorage(); - } - - if (this.taken) { - this.handlePrevAssessment(); - } -}; - Timed.prototype.shouldUseServer = function (data) { - // returns true if server data is more recent than local storage or if server storage is correct + // We override the RunestoneBase version because there is no "correct" attribute, and there are 2 possible localStorage schemas + // --we also want to default to local storage because it contains more information if (localStorage.length === 0) return true; - var storageObj = localStorage.getItem(eBookConfig.email + ":" + this.divid); + var storageObj = localStorage.getItem(eBookConfig.email + ":" + this.divid + "-given"); if (storageObj === null) return true; - var storedData = JSON.parse(storageObj).answerData; + var storedData = JSON.parse(storageObj).answer; if (storedData.length == 4) { if (data.correct == storedData[0] && data.incorrect == storedData[1] && data.skipped == storedData[2] && data.timeTaken == storedData[3]) return true; } else if (storedData.length == 7) { - if (data.correct == storedData[0] && data.incorrect == storedData[2] && data.skipped == storedData[4] && data.timeTaken == storedData[6]) + if (data.correct == storedData[0] && data.incorrect == storedData[2] && data.skipped == storedData[4] && data.timeTaken == storedData[6]) { + this.logScore(); return false; // In this case, because local storage has more info, we want to use that if it's consistent + } } var storageDate = new Date(JSON.parse(storageObj[1]).timestamp); var serverDate = new Date(data.timestamp); - if (serverDate < storageDate) + if (serverDate < storageDate) { + this.logScore(); return false; + } return true; }; Timed.prototype.checkLocalStorage = function () { var len = localStorage.length; if (len > 0) { - if (localStorage.getItem(eBookConfig.email + ":" + this.divid) !== null) { + if (localStorage.getItem(eBookConfig.email + ":" + this.divid + "-given") !== null) { this.taken = 1; - this.restoreFromStorage(""); - if (this.useRunestoneServices) - this.logScore(); + this.restoreAnswers(""); } else { this.taken = 0; } @@ -673,13 +648,17 @@ Timed.prototype.checkLocalStorage = function () { } }; -Timed.prototype.restoreFromStorage = function (data) { +Timed.prototype.restoreAnswers = function (data) { + this.taken = 1; + if (this.taken) { + this.handlePrevAssessment(); + } var tmpArr; if (data === "") { - tmpArr = JSON.parse(localStorage.getItem(eBookConfig.email + ":" + this.divid)).answerData; + tmpArr = JSON.parse(localStorage.getItem(eBookConfig.email + ":" + this.divid + "-given")).answer; } else { tmpArr = [parseInt(data.correct), parseInt(data.incorrect), parseInt(data.skipped), parseInt(data.timeTaken)]; - this.setLocalStorageFromServer(tmpArr); + this.setLocalStorage(data); } if (tmpArr.length == 4) { @@ -704,12 +683,16 @@ Timed.prototype.restoreFromStorage = function (data) { this.skipped = this.renderedQuestionArray.length; this.timeTaken = 0; } + this.renderTimedQuestion(); this.displayScore(); this.showTime(); }; -Timed.prototype.setLocalStorageFromServer = function (serverArr) { - // If the server array is more recent +Timed.prototype.setLocalStorage = function (data) { + var timeStamp = new Date(); + var answer = [parseInt(data.correct), parseInt(data.incorrect), parseInt(data.skipped), parseInt(data.timeTaken)]; + var storageObj = {"answer": answer, "timestamp": timeStamp}; + localStorage.setItem(eBookConfig.email + ":" + this.divid + "-given", JSON.stringify(storageObj)); }; Timed.prototype.displayScore = function () { diff --git a/runestone/clickableArea/js/clickable.js b/runestone/clickableArea/js/clickable.js index 7fab84367..885065d85 100644 --- a/runestone/clickableArea/js/clickable.js +++ b/runestone/clickableArea/js/clickable.js @@ -99,7 +99,7 @@ ClickableArea.prototype.renderNewElements = function () { this.newDiv.innerHTML = newContent; this.containerDiv.appendChild(this.newDiv); - this.checkServer(); + this.checkServer("clickableArea"); this.createButtons(); this.createFeedbackDiv(); @@ -128,38 +128,31 @@ ClickableArea.prototype.createFeedbackDiv = function () { }; /*=================================== -=== Checking/loading from storage === +=== Checking/restoring from storage === ===================================*/ -ClickableArea.prototype.checkServer = function () { - // Check if the server has stored answer - if (this.useRunestoneServices) { - var data = {}; - data.div_id = this.divid; - data.course = eBookConfig.course; - data.event = "clickableArea"; - jQuery.getJSON(eBookConfig.ajaxURL + "getAssessResults", data, this.repopulateFromStorage.bind(this)).error(this.checkLocalStorage.bind(this)); - } else { - this.checkLocalStorage(); // just go right to local storage +ClickableArea.prototype.restoreAnswers = function (data) { + // Restore answers from storage retrieval done in RunestoneBase or from local storage + if (data.answer !== undefined) { // if we got data from the server + this.hasStoredAnswers = true; + this.clickedIndexArray = data.answer.split(";"); } -}; -ClickableArea.prototype.repopulateFromStorage = function (data, status, whatever) { - // decide whether to use the server's answer (if there is one) or to load from storage - if (data !== null) { - this.hasStoredAnswers = true; - if (this.shouldUseServer(data)) { - this.clickedIndexArray = data.answer.split(";"); - this.setLocalStorage(true, data.correct); - this.finishRestoringAnswers(); + if (this.ccArray === undefined) { + this.modifyClickables(this.newDiv.childNodes); + } else { // For use with Sphinx-rendered HTML + this.ccCounter = 0; + this.ccIndex = 0; + this.ciIndex = 0; + if (!this.isTable) { + this.modifyViaCC(this.newDiv.children); } else { - this.checkLocalStorage(); + this.modifyTableViaCC(this.newDiv.children); } - } else { - this.checkLocalStorage(); } }; + ClickableArea.prototype.checkLocalStorage = function () { // Gets previous answer data from local storage if it exists this.hasStoredAnswers = false; @@ -178,50 +171,18 @@ ClickableArea.prototype.checkLocalStorage = function () { this.givenIndexArray.push(i); } } - this.logBookEvent({"event": "clickableArea", "act": this.clickedIndexArray.join(";"), "div_id": this.divid, "correct": (storageObj.correct ? "T" : "F")}); + this.logBookEvent({"event": "clickableArea", "act": this.clickedIndexArray.join(";"), "div_id": this.divid, "correct": storageObj.correct}); } } } - this.finishRestoringAnswers(); + this.restoreAnswers({}); // pass empty object }; -ClickableArea.prototype.finishRestoringAnswers = function () { - // this code is used multipe times, so i made it into a function - if (this.ccArray === undefined) { - this.modifyClickables(this.newDiv.childNodes); - } else { // For use with Sphinx-rendered HTML - this.ccCounter = 0; - this.ccIndex = 0; - this.ciIndex = 0; - if (!this.isTable) { - this.modifyViaCC(this.newDiv.children); - } else { - this.modifyTableViaCC(this.newDiv.children); - } - } -}; - -ClickableArea.prototype.shouldUseServer = function (data) { - // returns true if server data is more recent than local storage or if server storage is correct - if (data.correct == "T" || localStorage.length === 0) - return true; - var ex = localStorage.getItem(eBookConfig.email + ":" + this.divid + "-given"); - if (ex === null) - return true; - var storedData = JSON.parse(ex); - if (data.answer == storedData.answer) - return true; - var storageDate = new Date(storedData.timestamp); - var serverDate = new Date(data.timestamp); - if (serverDate < storageDate) - return false; - return true; -}; -ClickableArea.prototype.setLocalStorage = function (fromServer, correct) { +ClickableArea.prototype.setLocalStorage = function (data) { // Array of the indices of clicked elements is passed to local storage var answer; - if (fromServer) { + if (data.answer !== undefined) { // If we got data from the server, we can just use that answer = this.clickedIndexArray.join(";"); } else { this.givenIndexArray = []; @@ -235,6 +196,7 @@ ClickableArea.prototype.setLocalStorage = function (fromServer, correct) { var timeStamp = new Date(); + var correct = data.correct; var storageObject = {"answer": answer, "correct": correct, "timestamp": timeStamp}; localStorage.setItem(eBookConfig.email + ":" + this.divid + "-given", JSON.stringify(storageObject)); }; @@ -381,7 +343,7 @@ ClickableArea.prototype.clickableEval = function (logFlag) { $(this.incorrectArray[i]).removeClass("clickable-incorrect"); } } - this.setLocalStorage(false, this.correct); + this.setLocalStorage({"correct": (this.correct ? "T" : "F")}); if (logFlag) { // Sometimes we don't want to log the answer; for example, on reload of timed exam questions this.logBookEvent({"event": "clickableArea", "act": this.givenIndexArray.join(";"), "div_id": this.divid, "correct": (this.correct ? "T" : "F")}); } diff --git a/runestone/codelens/js/jquery.ba-bbq.min.js b/runestone/codelens/js/jquery.ba-bbq.min.js index bcbf24834..a8eca8905 100644 --- a/runestone/codelens/js/jquery.ba-bbq.min.js +++ b/runestone/codelens/js/jquery.ba-bbq.min.js @@ -1,18 +1,18 @@ -/* - * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010 +/*! + * jQuery BBQ: Back Button & Query Library - v1.3pre - 8/26/2010 * http://benalman.com/projects/jquery-bbq-plugin/ - * + * * Copyright (c) 2010 "Cowboy" Ben Alman * Dual licensed under the MIT and GPL licenses. * http://benalman.com/about/license/ */ -(function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this); \ No newline at end of file +(function($,e,b){var c="hashchange",h=document,f,g=$.event.special,i=h.documentMode,d="on"+c in e&&(i===b||i>7);function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}$.fn[c]=function(j){return j?this.bind(c,j):this.trigger(c)};$.fn[c].delay=50;g[c]=$.extend(g[c],{setup:function(){if(d){return false}$(f.start)},teardown:function(){if(d){return false}$(f.stop)}});f=(function(){var j={},p,m=a(),k=function(q){return q},l=k,o=k;j.start=function(){p||n()};j.stop=function(){p&&clearTimeout(p);p=b};function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if(q!==m){location.href=location.href.replace(/#.*/,"")+q}}p=setTimeout(n,$.fn[c].delay)}return j})()})(jQuery,this); \ No newline at end of file diff --git a/runestone/common/css/runestone-custom-sphinx-bootstrap.css b/runestone/common/css/runestone-custom-sphinx-bootstrap.css index eaba59316..4bcd2d755 100644 --- a/runestone/common/css/runestone-custom-sphinx-bootstrap.css +++ b/runestone/common/css/runestone-custom-sphinx-bootstrap.css @@ -673,3 +673,17 @@ border-color: #000000; border-radius: 4px; } +ul.dropdown-menu.globaltoc { + max-height: 700px; + overflow: auto; +} + +span.caption-text { + display: none; +} + +.globaltoc span.caption-text { + display: inline-block; + font-weight: bold; + font-size: large; +} \ No newline at end of file diff --git a/runestone/common/js/runestonebase.js b/runestone/common/js/runestonebase.js index 0b10e93f8..b30035ecb 100644 --- a/runestone/common/js/runestonebase.js +++ b/runestone/common/js/runestonebase.js @@ -21,3 +21,44 @@ RunestoneBase.prototype.logRunEvent = function (eventInfo) { console.log("running " + eventInfo); }; +/* Checking/loading from storage */ + +RunestoneBase.prototype.checkServer = function (eventInfo) { + // Check if the server has stored answer + if (this.useRunestoneServices) { + var data = {}; + data.div_id = this.divid; + data.course = eBookConfig.course; + data.event = eventInfo; + jQuery.getJSON(eBookConfig.ajaxURL + "getAssessResults", data, this.repopulateFromStorage.bind(this)).error(this.checkLocalStorage.bind(this)); + } else { + this.checkLocalStorage(); // just go right to local storage + } +}; + +RunestoneBase.prototype.repopulateFromStorage = function (data, status, whatever) { + // decide whether to use the server's answer (if there is one) or to load from storage + if (data !== null && this.shouldUseServer(data)) { + this.restoreAnswers(data); + this.setLocalStorage(data); + } else { + this.checkLocalStorage(); + } +}; + +RunestoneBase.prototype.shouldUseServer = function (data) { + // returns true if server data is more recent than local storage or if server storage is correct + if (data.correct == "T" || localStorage.length === 0) + return true; + var ex = localStorage.getItem(eBookConfig.email + ":" + this.divid + "-given"); + if (ex === null) + return true; + var storedData = JSON.parse(ex); + if (data.answer == storedData.answer) + return true; + var storageDate = new Date(storedData.timestamp); + var serverDate = new Date(data.timestamp); + if (serverDate < storageDate) + return false; + return true; +}; diff --git a/runestone/dragndrop/js/dragndrop.js b/runestone/dragndrop/js/dragndrop.js index 80d0fef19..089fad300 100644 --- a/runestone/dragndrop/js/dragndrop.js +++ b/runestone/dragndrop/js/dragndrop.js @@ -96,7 +96,7 @@ DragNDrop.prototype.createNewElements = function () { this.dragDropWrapDiv.appendChild(this.dropZoneDiv); this.createButtons(); - this.checkServer(); + this.checkServer("dragNdrop"); }; DragNDrop.prototype.finishSettingUp = function () { @@ -334,7 +334,7 @@ DragNDrop.prototype.dragEval = function (logFlag) { } } this.correctNum = this.dragNum - this.incorrectNum - this.unansweredNum; - this.setLocalStorage(false, this.correct); + this.setLocalStorage({"correct": (this.correct ? "T" : "F")}); this.renderFeedback(); if (logFlag) // Sometimes we don't want to log the answers--for example, on re-load of a timed exam this.logBookEvent({"event": "dragNdrop", "act": "submitDND", "answer": this.pregnantIndexArray.join(";"), "minHeight": this.minheight, "div_id": this.divid, "correct": this.correct}); @@ -351,43 +351,22 @@ DragNDrop.prototype.renderFeedback = function () { } }; /*=================================== -=== Checking/loading from storage === +=== Checking/restoring from storage === ===================================*/ -DragNDrop.prototype.checkServer = function () { - if (this.useRunestoneServices) { - var data = {}; - data.div_id = this.divid; - data.course = eBookConfig.course; - data.event = "dragNdrop"; - jQuery.getJSON(eBookConfig.ajaxURL + "getAssessResults", data, this.repopulateFromStorage.bind(this)).error(this.checkLocalStorage.bind(this)); - } else { - this.checkLocalStorage(); - } -}; - -DragNDrop.prototype.repopulateFromStorage = function (data, status, whatever) { - if (data !== null) { - if (this.shouldUseServer(data)) { - this.hasStoredDropzones = true; - this.minheight = data.minHeight; - this.pregnantIndexArray = data.answer.split(";"); - this.setLocalStorage(true, data.correct); - this.finishSettingUp(); - } else { - this.checkLocalStorage(); - } - } else { - this.checkLocalStorage(); - } - +DragNDrop.prototype.restoreAnswers = function (data) { + // Restore answers from storage retrieval done in RunestoneBase + this.hasStoredDropzones = true; + this.minheight = data.minHeight; + this.pregnantIndexArray = data.answer.split(";"); + this.finishSettingUp(); }; DragNDrop.prototype.checkLocalStorage = function () { this.hasStoredDropzones = false; var len = localStorage.length; if (len > 0) { - var ex = localStorage.getItem(eBookConfig.email + ":" + this.divid + "-dragInfo"); + var ex = localStorage.getItem(eBookConfig.email + ":" + this.divid + "-given"); if (ex !== null) { this.hasStoredDropzones = true; var storedObj = JSON.parse(ex); @@ -402,26 +381,8 @@ DragNDrop.prototype.checkLocalStorage = function () { this.finishSettingUp(); }; -DragNDrop.prototype.shouldUseServer = function (data) { - // returns true if server data is more recent than local storage or if server storage is correct - if (data.correct == "T" || localStorage.length === 0) - return true; - var ex = localStorage.getItem(eBookConfig.email + ":" + this.divid + "-dragInfo"); - var x = 0; - if (ex === null) - return true; - var storedData = JSON.parse(ex); - if (data.answer == storedData.answer) - return true; - var storageDate = new Date(storedData.timestamp); - var serverDate = new Date(data.timestamp); - if (serverDate < storageDate) - return false; - return true; -}; - -DragNDrop.prototype.setLocalStorage = function (fromServer, correct) { - if (!fromServer) { // If we loaded from the server, then pregnantIndexArray is already defined +DragNDrop.prototype.setLocalStorage = function (data) { + if (data.answer === undefined) { // If we didn't load from the server, we must generate the data this.pregnantIndexArray = []; for (var i = 0; i < this.dragPairArray.length; i++) { if (!this.hasNoDragChild(this.dragPairArray[i][1])) { @@ -437,8 +398,9 @@ DragNDrop.prototype.setLocalStorage = function (fromServer, correct) { } var timeStamp = new Date(); + var correct = data.correct; var storageObj = {"answer": this.pregnantIndexArray.join(";"), "minHeight": this.minheight, "timestamp": timeStamp, "correct": correct}; - localStorage.setItem(eBookConfig.email + ":" + this.divid + "-dragInfo", JSON.stringify(storageObj)); + localStorage.setItem(eBookConfig.email + ":" + this.divid + "-given", JSON.stringify(storageObj)); }; /*================================= == Find the custom HTML tags and ==