1.
+-
+
-
+You can take the mystery box alone, or ++ +
+
-
+You can take both boxes. ++ +
+
diff --git a/course/.mapping.json b/course/.mapping.json index 51bcaa9..1db5706 100755 --- a/course/.mapping.json +++ b/course/.mapping.json @@ -1 +1 @@ -{"source/main.ptx": ["frontmatter", "course-documents", "ch-notes", "quizzes", "exams", "recitations", "review"], "source/syllabus/syllabus.ptx": ["syllabus"], "source/notes/1-13.ptx": ["notes-01-13"], "source/notes/1-15.ptx": ["notes-01-15"], "source/notes/1-20.ptx": ["notes-01-20"], "source/notes/1-22.ptx": ["notes-01-22"], "source/notes/1-27.ptx": ["notes-01-27"], "source/notes/1-29.ptx": ["notes-01-29"], "source/notes/2-3.ptx": ["notes-02-03"], "source/notes/2-5.ptx": ["notes-02-05"], "source/notes/2-10.ptx": ["notes-02-10"], "source/notes/2-19.ptx": ["notes-02-19"], "source/notes/2-24.ptx": ["notes-02-24"], "source/quizzes/quiz-01.ptx": ["quiz-01"], "source/quizzes/quiz-02.ptx": ["quiz-02"], "source/quizzes/quiz-03.ptx": ["quiz-03"], "source/exams/exam-01.ptx": ["exam-01"], "source/recitations/calculus-review.ptx": ["recitation-calculus-review"], "source/review/Exam-1-Review.ptx": ["Exam-1-Review"]} \ No newline at end of file +{"source/main.ptx": ["frontmatter", "course-documents", "ch-notes", "quizzes", "exams", "recitations", "review", "fun"], "source/syllabus/syllabus.ptx": ["syllabus"], "source/notes/1-13.ptx": ["notes-01-13"], "source/notes/1-15.ptx": ["notes-01-15"], "source/notes/1-20.ptx": ["notes-01-20"], "source/notes/1-22.ptx": ["notes-01-22"], "source/notes/1-27.ptx": ["notes-01-27"], "source/notes/1-29.ptx": ["notes-01-29"], "source/notes/2-3.ptx": ["notes-02-03"], "source/notes/2-5.ptx": ["notes-02-05"], "source/notes/2-10.ptx": ["notes-02-10"], "source/notes/2-19.ptx": ["notes-02-19"], "source/notes/2-24.ptx": ["notes-02-24"], "source/quizzes/quiz-01.ptx": ["quiz-01"], "source/quizzes/quiz-02.ptx": ["quiz-02"], "source/quizzes/quiz-03.ptx": ["quiz-03"], "source/exams/exam-01.ptx": ["exam-01"], "source/recitations/calculus-review.ptx": ["recitation-calculus-review"], "source/review/Exam-1-Review.ptx": ["Exam-1-Review"], "source/fun/boxes.ptx": ["boxes"]} \ No newline at end of file diff --git a/course/Exam-1-Review.html b/course/Exam-1-Review.html index 4423b4b..7f13f55 100755 --- a/course/Exam-1-Review.html +++ b/course/Exam-1-Review.html @@ -101,15 +101,15 @@ eBookConfig.allow_pairs = false; eBookConfig.enableScratchAC = false; eBookConfig.build_info = ""; eBookConfig.python3 = null; -eBookConfig.runestone_version = '7.11.18'; +eBookConfig.runestone_version = '7.11.19'; eBookConfig.jobehost = ''; eBookConfig.proxyuri_runs = ''; eBookConfig.proxyuri_files = ''; eBookConfig.enable_chatcodes = false; - - + + @@ -135,7 +135,7 @@ eBookConfig.enable_chatcodes = false; -PrevUpNext +PrevUpNext
element that will be replaced by new HTML\n this.useRunestoneServices =\n opts.useRunestoneServices || eBookConfig.useRunestoneServices;\n this.origElem = orig;\n this.divid = orig.id;\n this.question = this.origElem.innerHTML;\n this.optional = false;\n this.attachURL = opts.attachURL;\n if (this.origElem.hasAttribute(\"data-optional\")) {\n this.optional = true;\n }\n if (this.origElem.hasAttribute(\"data-attachment\")) {\n this.attachment = true;\n }\n this.placeholder =\n this.origElem.getAttribute(\"data-placeholder\") ||\n \"Write your answer here\";\n this.renderHTML();\n this.caption = \"shortanswer\";\n this.addCaption(\"runestone\");\n this.checkServer(\"shortanswer\", true);\n if (typeof Prism !== \"undefined\") {\n Prism.highlightAllUnder(this.containerDiv);\n }\n if (this.attachment) {\n this.getAttachmentName();\n }\n }\n }\n\n renderHTML() {\n this.containerDiv = document.createElement(\"div\");\n this.containerDiv.id = this.divid;\n this.containerDiv.classList.add(...(this.origElem.getAttribute(\"class\") || \"\").split(\" \").filter(Boolean));\n this.newForm = document.createElement(\"form\");\n this.newForm.id = this.divid + \"_journal\";\n this.newForm.name = this.newForm.id;\n this.newForm.action = \"\";\n this.containerDiv.appendChild(this.newForm);\n this.fieldSet = document.createElement(\"fieldset\");\n this.newForm.appendChild(this.fieldSet);\n this.firstLegendDiv = document.createElement(\"div\");\n // move contents to new div, keeping any event listeners\n while (this.origElem.firstChild) {\n this.firstLegendDiv.appendChild(this.origElem.firstChild);\n }\n this.firstLegendDiv.classList.add(\"journal-question\");\n this.firstLegendDiv.classList.add(\"exercise-statement\");\n this.fieldSet.appendChild(this.firstLegendDiv);\n this.jInputDiv = document.createElement(\"div\");\n this.jInputDiv.id = this.divid + \"_journal_input\";\n this.fieldSet.appendChild(this.jInputDiv);\n this.jOptionsDiv = document.createElement(\"div\");\n this.jOptionsDiv.classList.add(\"journal-options\");\n this.jInputDiv.appendChild(this.jOptionsDiv);\n this.jTextArea = document.createElement(\"textarea\");\n this.jTextArea.id = this.divid + \"_solution\";\n this.jTextArea.setAttribute(\"aria-label\", \"textarea\");\n this.jTextArea.placeholder = this.placeholder;\n this.jTextArea.style.display = \"inline\";\n this.jTextArea.style.width = \"530px\";\n this.jTextArea.classList.add(\"form-control\");\n this.jTextArea.rows = 4;\n this.jTextArea.cols = 50;\n this.jOptionsDiv.appendChild(this.jTextArea);\n\n // fires when we loose focus on the textarea after making a change\n // mark it as answered for peer/timed purposes\n this.jTextArea.onchange = function () {\n this.isAnswered = true;\n }.bind(this);\n\n // the answer has not been saved yet. Update as soon as user types in\n // the box, not just when they loose focus.\n this.jTextArea.addEventListener(\"keydown\", () => {\n if (this.isTimed) {\n // no need for danger status... nothing for user to do here\n this.feedbackDiv.innerHTML = \"Your answer is automatically saved.\";\n } else {\n this.feedbackDiv.innerHTML = \"Your answer has not been saved yet!\";\n this.feedbackDiv.classList.remove(\"alert-success\");\n this.feedbackDiv.classList.add(\"alert\", \"alert-danger\");\n }\n });\n\n this.jTextArea.addEventListener(\"input\", () => {\n this.jTextArea.style.height = \"auto\";\n this.jTextArea.style.height = `${this.jTextArea.scrollHeight}px`;\n this.renderMath(this.jTextArea.value);\n });\n this.buttonDiv = document.createElement(\"div\");\n this.fieldSet.appendChild(this.buttonDiv);\n this.submitButton = document.createElement(\"button\");\n this.submitButton.classList.add(\"btn\", \"btn-success\");\n this.submitButton.type = \"button\";\n this.submitButton.textContent = \"Save\";\n this.submitButton.onclick = function () {\n this.checkCurrentAnswer();\n this.logCurrentAnswer();\n this.renderFeedback();\n }.bind(this);\n this.buttonDiv.appendChild(this.submitButton);\n\n this.otherOptionsDiv = document.createElement(\"div\");\n this.otherOptionsDiv.style.paddingLeft = \"20px\";\n this.otherOptionsDiv.classList.add(\"journal-options\");\n this.fieldSet.appendChild(this.otherOptionsDiv);\n // add a feedback div to give user feedback\n this.feedbackDiv = document.createElement(\"div\");\n this.feedbackDiv.style.width = \"530px\";\n this.feedbackDiv.style.fontStyle = \"italic\";\n this.feedbackDiv.id = this.divid + \"_feedback\";\n this.feedbackDiv.classList.add(\"shortanswer__feedback\");\n this.feedbackDiv.style.display = \"none\";\n this.fieldSet.appendChild(this.feedbackDiv);\n if (this.attachment) {\n let attachDiv = document.createElement(\"div\")\n this.attachDiv = attachDiv;\n if (this.graderactive) {\n // If in grading mode make a button to create a popup with the image\n let viewButton = document.createElement(\"button\")\n viewButton.type = \"button\"\n viewButton.innerHTML = \"View Attachment\"\n viewButton.onclick = this.viewFile.bind(this);\n attachDiv.appendChild(viewButton);\n } else {\n // Otherwise make a button for the student to select a file to upload.\n this.fileUpload = document.createElement(\"input\")\n this.fileUpload.type = \"file\";\n this.fileUpload.id = `${this.divid}_fileme`;\n attachDiv.appendChild(this.fileUpload);\n }\n this.containerDiv.appendChild(attachDiv);\n }\n this.origElem.replaceWith(this.containerDiv);\n // This is a stopgap measure for when MathJax is not loaded at all. There is another\n // more difficult case that when MathJax is loaded asynchronously we will get here\n // before MathJax is loaded. In that case we will need to implement something\n // like `the solution described here
\");\n this.renderedAnswer.innerHTML = value;\n\n this.rederedAnswerDiv.style.display = \"block\";\n this.queueMathJax(this.renderedAnswer);\n\n } else {\n if (this.renderedAnswer) {\n this.rederedAnswerDiv.style.display = \"none\";\n }\n }\n }\n\n async checkCurrentAnswer() {\n let value = document.getElementById(this.divid + \"_solution\").value;\n this.renderMath(value);\n this.setLocalStorage({\n answer: value,\n timestamp: new Date(),\n });\n }\n\n async logCurrentAnswer(sid) {\n let value = document.getElementById(this.divid + \"_solution\").value;\n this.renderMath(value);\n this.setLocalStorage({\n answer: value,\n timestamp: new Date(),\n });\n let data = {\n event: \"shortanswer\",\n act: value,\n answer: value,\n div_id: this.divid,\n };\n if (typeof sid !== \"undefined\") {\n data.sid = sid;\n }\n await this.logBookEvent(data);\n if (this.attachment) {\n await this.uploadFile();\n }\n }\n\n renderFeedback() {\n this.feedbackDiv.innerHTML = \"Your answer has been saved.\";\n this.feedbackDiv.classList.remove(\"alert-danger\");\n this.feedbackDiv.classList.add(\"alert\", \"alert-success\");\n this.feedbackDiv.style.display = \"block\";\n }\n setLocalStorage(data) {\n if (!this.graderactive) {\n let key = this.localStorageKey();\n localStorage.setItem(key, JSON.stringify(data));\n }\n }\n checkLocalStorage() {\n // Repopulates the short answer text\n // which was stored into local storage.\n var answer = \"\";\n if (this.graderactive) {\n return;\n }\n var len = localStorage.length;\n if (len > 0) {\n var ex = localStorage.getItem(this.localStorageKey());\n if (ex !== null) {\n try {\n var storedData = JSON.parse(ex);\n answer = storedData.answer;\n } catch (err) {\n // error while parsing; likely due to bad value stored in storage\n console.log(err.message);\n localStorage.removeItem(this.localStorageKey());\n return;\n }\n let solution = document.getElementById(this.divid + \"_solution\");\n if (solution) {\n solution.value = answer;\n }\n this.renderMath(answer);\n }\n }\n }\n restoreAnswers(data) {\n // Restore answers from storage retrieval done in RunestoneBase\n // sometimes data.answer can be null\n if (!data.answer) {\n data.answer = \"\";\n }\n this.answer = data.answer;\n this.jTextArea.value = this.answer;\n this.renderMath(this.answer);\n\n let p = document.createElement(\"p\");\n p.classList.add(\"shortanswer__timestamp\");\n this.jInputDiv.appendChild(p);\n var tsString = \"\";\n if (data.timestamp) {\n tsString = new Date(data.timestamp).toLocaleString();\n } else {\n tsString = \"\";\n }\n p.textContent = tsString;\n this.jTextArea.style.height = \"auto\";\n this.jTextArea.style.height = `${this.jTextArea.scrollHeight}px`;\n\n if (data.last_answer) {\n this.current_answer = \"ontime\";\n let toggle_answer_button = document.createElement(\"button\");\n toggle_answer_button.type = \"button\";\n toggle_answer_button.textContent = \"Show Late Answer\";\n toggle_answer_button.classList.add(\"btn\", \"btn-warning\");\n toggle_answer_button.style.marginLeft = \"5px\";\n\n toggle_answer_button.addEventListener(\n \"click\",\n function () {\n var display_timestamp, button_text;\n if (this.current_answer === \"ontime\") {\n this.jTextArea.value = data.last_answer;\n this.answer = data.last_answer;\n display_timestamp = new Date(\n data.last_timestamp\n ).toLocaleString();\n button_text = \"Show on-Time Answer\";\n this.current_answer = \"late\";\n } else {\n this.jTextArea.value = data.answer;\n this.answer = data.answer;\n display_timestamp = tsString;\n button_text = \"Show Late Answer\";\n this.current_answer = \"ontime\";\n }\n this.renderMath(this.answer);\n p.textContent = `Submitted: ${display_timestamp}`;\n toggle_answer_button.textContent = button_text;\n }.bind(this)\n );\n\n this.buttonDiv.appendChild(toggle_answer_button);\n }\n let feedbackStr = \"\";\n if (typeof data.score !== \"undefined\") {\n feedbackStr = `Score: ${data.score}`;\n }\n if (data.comment) {\n feedbackStr += ` -- ${data.comment}`;\n }\n if (feedbackStr !== \"\") {\n this.feedbackDiv.innerHTML = feedbackStr;\n this.feedbackDiv.style.display = \"block\";\n this.feedbackDiv.classList.add(\"alert\", \"alert-success\");\n }\n }\n\n disableInteraction() {\n this.jTextArea.disabled = true;\n }\n\n async getAttachmentName() {\n // Get the attachment name from the /ns/assessment/has_attachment endpoint\n let requestUrl = `/ns/assessment/has_attachment/${this.divid}`;\n if (this.sid) {\n requestUrl += `?sid=${this.sid}`;\n }\n const response = await fetch(requestUrl);\n if (!response.ok) {\n console.error(\"Error fetching attachment name:\", response.statusText);\n return null;\n }\n const obj = await response.json();\n if (obj.detail.hasAttachment) {\n // Return the S3 key for the attachment\n let filename = obj.detail.hasAttachment;\n // filename is everthing after the last slash\n filename = filename.substring(filename.lastIndexOf(\"/\") + 1);\n let fmess = document.createElement(\"span\");\n fmess.innerHTML = `Attachment: ${filename}`;\n this.attachDiv.appendChild(fmess);\n }\n }\n\n async uploadFile() {\n const files = this.fileUpload.files\n // get the suffix from the file name\n const fileName = files[0].name;\n const suffix = fileName.split('.').pop();\n // if the suffix is not in the list of allowed suffixes, return\n if (!['jpg', 'jpeg', 'png', 'gif', 'pdf'].includes(suffix)) {\n alert(\"File type not allowed. Please upload a jpg, jpeg, png, gif, or pdf file.\");\n return;\n }\n // if the file size is greater than 5MB, return\n if (files[0].size > 5 * 1024 * 1024) {\n alert(\"File size exceeds 5MB limit. Please upload a smaller file.\");\n return;\n }\n const data = new FormData()\n if (this.fileUpload.files.length > 0) {\n data.append('file', files[0])\n fetch(`/ns/logger/upload/${this.divid}`, {\n method: 'POST',\n body: data\n })\n .then(response => response.json())\n .then(data => {\n console.log(data)\n })\n .catch(error => {\n console.error(error)\n })\n }\n }\n\n viewFile() {\n // Get the URL from the S3 API -- saved when we display in grader mode\n if (this.attachURL) {\n //window.open(this.attachURL, \"_blank\");\n //\n //switch to \n const image_window = window.open(\"\", \"_blank\")\n if (this.attachURL.indexOf('.pdf?') !== -1) {\n const embed = image_window.document.createElement(\"object\");\n embed.setAttribute(\"data\", this.attachURL);\n embed.setAttribute(\"type\", \"application/pdf\");\n embed.setAttribute(\"width\", \"800px\");\n embed.setAttribute(\"height\", \"1040px\");\n image_window.document.body.appendChild(embed);\n let alt = image_window.document.createElement(\"a\");\n alt.setAttribute(\"href\", this.attachURL);\n alt.innerText = \"Download PDF\";\n embed.appendChild(alt);\n }\n else {\n const img = image_window.document.createElement(\"img\");\n img.setAttribute(\"src\", this.attachURL);\n img.setAttribute(\"style\", \"width:100%; height:auto;\");\n image_window.document.body.appendChild(img);\n }\n } else {\n alert(\"No attachment for this student.\")\n }\n }\n}\n\n/*=================================\n== Find the custom HTML tags and ==\n== execute our code on them ==\n=================================*/\ndocument.addEventListener(\"runestone:login-complete\", function () {\n document.querySelectorAll(\"[data-component=shortanswer]\").forEach(function (el) {\n // If this element exists within a timed component, don't render it here\n if (!el.closest(\"[data-component=timedAssessment]\")) {\n try {\n window.componentMap[el.id] = new ShortAnswer({\n orig: el,\n useRunestoneServices: eBookConfig.useRunestoneServices,\n });\n } catch (err) {\n console.log(`Error rendering ShortAnswer Problem ${el.id}\n Details: ${err}`);\n }\n }\n });\n});\n","import ShortAnswer from \"./shortanswer.js\";\n\nexport default class TimedShortAnswer extends ShortAnswer {\n constructor(opts) {\n super(opts);\n this.renderTimedIcon(this.containerDiv);\n this.isTimed = true;\n this.hideButtons();\n }\n hideButtons() {\n if (this.submitButton) {\n this.submitButton.style.display = \"none\";\n }\n }\n renderTimedIcon(component) {\n // renders the clock icon on timed components. The component parameter\n // is the element that the icon should be appended to.\n var timeIconDiv = document.createElement(\"div\");\n var timeIcon = document.createElement(\"img\");\n\n timeIcon.src = \"../_static/clock.png\";\n timeIcon.style.width = \"15px\";\n timeIcon.style.height = \"15px\";\n\n timeIconDiv.className = \"timeTip\";\n timeIconDiv.title = \"\";\n timeIconDiv.appendChild(timeIcon);\n\n // If firstChild is null, insertBefore acts like appendChild\n component.insertBefore(timeIconDiv, component.firstChild);\n }\n checkCorrectTimed() {\n return \"I\"; // we ignore this in the grading\n }\n hideFeedback() {\n if (this.feedbackDiv) {\n this.feedbackDiv.style.display = \"none\";\n }\n }\n}\n\nif (typeof window.component_factory === \"undefined\") {\n window.component_factory = {};\n}\n\nwindow.component_factory.shortanswer = function (opts) {\n if (opts.timed) {\n return new TimedShortAnswer(opts);\n }\n return new ShortAnswer(opts);\n};\n"],"names":["ShortAnswer","constructor","opts","super","orig","this","useRunestoneServices","eBookConfig","origElem","divid","id","question","innerHTML","optional","attachURL","hasAttribute","attachment","placeholder","getAttribute","renderHTML","caption","addCaption","checkServer","Prism","highlightAllUnder","containerDiv","getAttachmentName","document","createElement","classList","add","split","filter","Boolean","newForm","name","action","appendChild","fieldSet","firstLegendDiv","firstChild","jInputDiv","jOptionsDiv","jTextArea","setAttribute","style","display","width","rows","cols","onchange","isAnswered","bind","addEventListener","isTimed","feedbackDiv","remove","height","scrollHeight","renderMath","value","buttonDiv","submitButton","type","textContent","onclick","checkCurrentAnswer","logCurrentAnswer","renderFeedback","otherOptionsDiv","paddingLeft","fontStyle","attachDiv","graderactive","viewButton","viewFile","fileUpload","replaceWith","MathJax","queueMathJax","renderedAnswer","rederedAnswerDiv","renderedAnswerLabel","replace","getElementById","setLocalStorage","answer","timestamp","Date","sid","data","event","act","div_id","logBookEvent","uploadFile","key","localStorageKey","localStorage","setItem","JSON","stringify","checkLocalStorage","length","ex","getItem","parse","err","console","log","message","removeItem","solution","restoreAnswers","p","tsString","toLocaleString","last_answer","current_answer","toggle_answer_button","marginLeft","display_timestamp","button_text","last_timestamp","feedbackStr","score","comment","disableInteraction","disabled","requestUrl","response","fetch","ok","error","statusText","obj","json","detail","hasAttachment","filename","substring","lastIndexOf","fmess","files","suffix","pop","includes","alert","size","FormData","append","method","body","then","catch","image_window","window","open","indexOf","embed","alt","innerText","img","querySelectorAll","forEach","el","closest","componentMap","TimedShortAnswer","renderTimedIcon","hideButtons","component","timeIconDiv","timeIcon","src","className","title","insertBefore","checkCorrectTimed","hideFeedback","component_factory","shortanswer","timed"],"sourceRoot":""}
\ No newline at end of file
diff --git a/course/_static/819.66dbba90006846bf.js.map.gz b/course/_static/819.66dbba90006846bf.js.map.gz
new file mode 100755
index 0000000..031b3e3
Binary files /dev/null and b/course/_static/819.66dbba90006846bf.js.map.gz differ
diff --git a/course/_static/_runestone-services.xml b/course/_static/_runestone-services.xml
index 8586f2d..695bc06 100755
--- a/course/_static/_runestone-services.xml
+++ b/course/_static/_runestone-services.xml
@@ -1,14 +1,14 @@
\n
\n\nThe HTML for a footnote referenced multiple times is:\n\n.. code-block:: HTML\n :number-lines:\n\n \n\nHowever, there's no (current) CSS selector that will add a ``content`` to the multiply-referenced footnote without adding an additional ``content`` to the typical footnote: we want to select ``dt.label > span.brackets > text-only content`` or ``dt.label > span.brackets > :not(:has a)`` (see the unsupported `:has selector Print preview
+Additional printing options
+Worksheet Boxes
+
+1.
+
+
+(a)
+(b)
+
+