1 |
efrain |
1 |
document.addEventListener("DOMContentLoaded", function() {
|
|
|
2 |
var container = document.getElementById("gradable-container");
|
|
|
3 |
var questions = [];
|
|
|
4 |
var questionElements = container.querySelectorAll('.h5p-iv-open-ended-reporting-container');
|
|
|
5 |
var inputs = [];
|
|
|
6 |
var maxScores = {};
|
|
|
7 |
var index = 0;
|
|
|
8 |
var currentQuestion = questionElements[0];
|
|
|
9 |
|
|
|
10 |
// Translatable labels
|
|
|
11 |
var IVOpenEndedQuestionTitle;
|
|
|
12 |
var scoreLabel;
|
|
|
13 |
var scoreDelimiter;
|
|
|
14 |
var questionsRemainingLabel;
|
|
|
15 |
var submitButtonLabel;
|
|
|
16 |
|
|
|
17 |
// Render each of the question containers
|
|
|
18 |
for (var i = 0; i < questionElements.length; i++) {
|
|
|
19 |
IVOpenEndedQuestionTitle = questionElements[i].getAttribute('data-report-iv-open-ended-question-title');
|
|
|
20 |
scoreLabel = questionElements[i].getAttribute('data-report-score-label');
|
|
|
21 |
scoreDelimiter = questionElements[i].getAttribute('data-report-score-delimiter');
|
|
|
22 |
questionsRemainingLabel = questionElements[i].getAttribute('data-report-questions-remaining-label');
|
|
|
23 |
submitButtonLabel = questionElements[i].getAttribute('data-report-submit-button-label');
|
|
|
24 |
|
|
|
25 |
// Add the title to an existing div in the the header
|
|
|
26 |
addTitle(i);
|
|
|
27 |
|
|
|
28 |
// Add other elements to the header
|
|
|
29 |
var header = document.getElementById('h5p-iv-open-ended-reporting-header-' + i);
|
|
|
30 |
|
|
|
31 |
var gradeInputWrapper = document.createElement('div');
|
|
|
32 |
gradeInputWrapper.classList.add('h5p-iv-open-ended-reporting-grade-input-wrapper');
|
|
|
33 |
|
|
|
34 |
var inputDiv = createInputDiv(questionElements[i], i);
|
|
|
35 |
gradeInputWrapper.append(inputDiv);
|
|
|
36 |
|
|
|
37 |
var submitButtonWrapper = createSubmitButtonWrapper(i);
|
|
|
38 |
gradeInputWrapper.append(submitButtonWrapper);
|
|
|
39 |
|
|
|
40 |
header.append(gradeInputWrapper);
|
|
|
41 |
|
|
|
42 |
// Add the header to the question container
|
|
|
43 |
questionElements[i].prepend(header);
|
|
|
44 |
|
|
|
45 |
// Keep track of the elements created and general data for later use
|
|
|
46 |
questions[i] = {};
|
|
|
47 |
questions[i] = {
|
|
|
48 |
'element': questionElements[i],
|
|
|
49 |
'inputDiv': inputDiv,
|
|
|
50 |
'submitButton': submitButtonWrapper,
|
|
|
51 |
'gradebookContainer': questionElements[i].querySelectorAll('.h5p-iv-open-ended-reporting-scores')[0]
|
|
|
52 |
};
|
|
|
53 |
}
|
|
|
54 |
|
|
|
55 |
updateMainGradeBookContainer();
|
|
|
56 |
|
|
|
57 |
/**
|
|
|
58 |
* Add a title
|
|
|
59 |
*
|
|
|
60 |
* @param {number} index
|
|
|
61 |
* @return {null}
|
|
|
62 |
*/
|
|
|
63 |
function addTitle(index) {
|
|
|
64 |
var titleCounter = document.createElement('div');
|
|
|
65 |
titleCounter.classList.add('h5p-iv-open-ended-title-counter');
|
|
|
66 |
titleCounter.innerHTML = IVOpenEndedQuestionTitle + ' ' + '<span>' + (index + 1) + ' ' + scoreDelimiter + ' ' + questionElements.length + '</span>';
|
|
|
67 |
|
|
|
68 |
var titleWrapper = document.getElementById('h5p-iv-open-ended-reporting-title-wrapper-' + index);
|
|
|
69 |
titleWrapper.prepend(titleCounter);
|
|
|
70 |
}
|
|
|
71 |
|
|
|
72 |
/**
|
|
|
73 |
* Create submit button wrapper
|
|
|
74 |
* @param {number} index
|
|
|
75 |
* @return {HTMLElement}
|
|
|
76 |
*/
|
|
|
77 |
function createSubmitButtonWrapper(index) {
|
|
|
78 |
var submitButtonWrapper = document.createElement('div');
|
|
|
79 |
submitButtonWrapper.classList.add('h5p-iv-open-ended-reporting-submit-button-wrapper');
|
|
|
80 |
|
|
|
81 |
var submitButton = document.createElement('button');
|
|
|
82 |
submitButton.classList.add('h5p-iv-open-ended-reporting-submit-button');
|
|
|
83 |
submitButton.id = 'h5p-iv-open-ended-reporting-submit-button-' + index;
|
|
|
84 |
submitButton.innerHTML = submitButtonLabel;
|
|
|
85 |
|
|
|
86 |
submitButtonWrapper.append(submitButton);
|
|
|
87 |
return submitButtonWrapper;
|
|
|
88 |
}
|
|
|
89 |
|
|
|
90 |
/**
|
|
|
91 |
* Create input wrapper
|
|
|
92 |
* @param {HTMLElement} wrapper
|
|
|
93 |
* @param {number} index
|
|
|
94 |
* @return {HTMLElement}
|
|
|
95 |
*/
|
|
|
96 |
function createInputDiv(wrapper, index) {
|
|
|
97 |
var inputDiv = document.createElement('div');
|
|
|
98 |
inputDiv.classList.add('h5p-iv-open-ended-input-div');
|
|
|
99 |
|
|
|
100 |
var scoreText = document.createElement('span');
|
|
|
101 |
scoreText.innerHTML = scoreLabel + ': ';
|
|
|
102 |
inputDiv.append(scoreText);
|
|
|
103 |
|
|
|
104 |
var input = document.createElement('input');
|
|
|
105 |
input.setAttribute('type', 'number');
|
|
|
106 |
input.id = 'h5p-grade-input-' + index;
|
|
|
107 |
input.subcontentID = wrapper.getAttribute('data-report-id');
|
|
|
108 |
input.scaleFactor = wrapper.getAttribute('data-report-scale');
|
|
|
109 |
input.maxScore = wrapper.getAttribute('data-report-max');
|
|
|
110 |
inputDiv.append(input);
|
|
|
111 |
|
|
|
112 |
var maxScoreText = document.createElement('span');
|
|
|
113 |
maxScoreText.id = 'h5p-max-score-text-' + index;
|
|
|
114 |
inputDiv.append(maxScoreText);
|
|
|
115 |
|
|
|
116 |
inputs.push(input);
|
|
|
117 |
return inputDiv;
|
|
|
118 |
}
|
|
|
119 |
|
|
|
120 |
// Add logic to the inputs
|
|
|
121 |
inputs.forEach(function(input, index) {
|
|
|
122 |
// Populate the inputs with existing questionElements
|
|
|
123 |
populateInputDiv(input.subcontentID, index);
|
|
|
124 |
|
|
|
125 |
input.addEventListener('focus', function() {
|
|
|
126 |
var submitButton = document.getElementById('h5p-iv-open-ended-reporting-submit-button-' + index);
|
|
|
127 |
submitButton.disabled = false;
|
|
|
128 |
});
|
|
|
129 |
|
|
|
130 |
// Validate on blur
|
|
|
131 |
input.addEventListener('blur', function() {
|
|
|
132 |
if (this.value == '' || parseInt(this.value) < 0) {
|
|
|
133 |
this.value = 0;
|
|
|
134 |
}
|
|
|
135 |
|
|
|
136 |
if (parseInt(this.value) > parseInt(maxScores[index])) {
|
|
|
137 |
this.value = maxScores[index];
|
|
|
138 |
}
|
|
|
139 |
});
|
|
|
140 |
|
|
|
141 |
// Add logic for the corresponding submit button
|
|
|
142 |
var submitButton = document.getElementById('h5p-iv-open-ended-reporting-submit-button-' + index);
|
|
|
143 |
submitButton.addEventListener('click', function() {
|
|
|
144 |
|
|
|
145 |
// Validate on submit again since blur doesn't always work
|
|
|
146 |
if (this.value == '' || parseInt(this.value) < 0) {
|
|
|
147 |
this.value = 0;
|
|
|
148 |
}
|
|
|
149 |
|
|
|
150 |
if (parseInt(this.value) > parseInt(maxScores[index])) {
|
|
|
151 |
this.value = maxScores[index];
|
|
|
152 |
}
|
|
|
153 |
|
|
|
154 |
H5P.jQuery.post(data_for_page.setSubContentEndpoint, {
|
|
|
155 |
subcontent_id: input.subcontentID,
|
|
|
156 |
score: input.value,
|
|
|
157 |
maxScore: input.maxScore
|
|
|
158 |
}, function(response) {
|
|
|
159 |
renderAfterSubmit(input, index, response.data.totalUngraded)
|
|
|
160 |
});
|
|
|
161 |
});
|
|
|
162 |
});
|
|
|
163 |
|
|
|
164 |
/**
|
|
|
165 |
* rerenders elements with new data
|
|
|
166 |
* @param {HTMLElement} input
|
|
|
167 |
* @param {number} index
|
|
|
168 |
* @param {number} totalUngraded
|
|
|
169 |
*/
|
|
|
170 |
function renderAfterSubmit(input, index, totalUngraded) {
|
|
|
171 |
hideInputs(index);
|
|
|
172 |
|
|
|
173 |
// Update the gradebook score for this question and for the main content type
|
|
|
174 |
updateGradeBookContainer(index, input.value, input.scaleFactor);
|
|
|
175 |
updateMainGradeBookContainer();
|
|
|
176 |
updateQuestionCounter(totalUngraded);
|
|
|
177 |
}
|
|
|
178 |
|
|
|
179 |
/**
|
|
|
180 |
* hide inputs
|
|
|
181 |
*
|
|
|
182 |
* @param {number} index
|
|
|
183 |
*/
|
|
|
184 |
function hideInputs(index) {
|
|
|
185 |
questions[index].submitButton.classList.add('h5p-iv-open-ended-reporting-hidden');
|
|
|
186 |
questions[index].inputDiv.classList.add('h5p-iv-open-ended-reporting-hidden');
|
|
|
187 |
}
|
|
|
188 |
|
|
|
189 |
|
|
|
190 |
/**
|
|
|
191 |
* Updates the gradebook scores for a particular question
|
|
|
192 |
*
|
|
|
193 |
* @param {number} index
|
|
|
194 |
* @param {number} inputValue
|
|
|
195 |
* @param {float} scaleFactor
|
|
|
196 |
*/
|
|
|
197 |
function updateGradeBookContainer(index, inputValue, scaleFactor) {
|
|
|
198 |
var scoreContainer = document.getElementById('h5p-iv-open-ended-reporting-score-' + index);
|
|
|
199 |
scoreContainer.classList.remove('h5p-iv-open-ended-reporting-hidden');
|
|
|
200 |
|
|
|
201 |
var scaledScoreElement = scoreContainer.querySelectorAll('.h5p-reporting-scaled-score')[0];
|
|
|
202 |
scaledScoreElement.innerHTML = Math.round((scaleFactor * inputValue) * 100 + Number.EPSILON) / 100;
|
|
|
203 |
|
|
|
204 |
var rawScoreElement = scoreContainer.querySelectorAll('.h5p-reporting-raw-score')[0];
|
|
|
205 |
rawScoreElement.innerHTML = inputValue;
|
|
|
206 |
}
|
|
|
207 |
|
|
|
208 |
|
|
|
209 |
/**
|
|
|
210 |
* Updates the values of the main gradebook container for the containing content type
|
|
|
211 |
*/
|
|
|
212 |
function updateMainGradeBookContainer() {
|
|
|
213 |
// Only look within the same report
|
|
|
214 |
var thisReport = findAncestor(container, '.h5p-reporting-main-container');
|
|
|
215 |
var thisReportView = findAncestor(container, '.h5p-report-view');
|
|
|
216 |
var rawScores = thisReport.querySelectorAll('.h5p-reporting-raw-score');
|
|
|
217 |
rawScores = Array.prototype.slice.call(rawScores).map(function(rawScoreElement) {
|
|
|
218 |
return parseInt(rawScoreElement.innerHTML);
|
|
|
219 |
});
|
|
|
220 |
var rawScore = rawScores.reduce(function(a,b) {
|
|
|
221 |
return a + b;
|
|
|
222 |
});
|
|
|
223 |
var mainRawScoreElement = thisReport.querySelectorAll('.h5p-reporting-main-score-raw-score')[0];
|
|
|
224 |
mainRawScoreElement.innerHTML = rawScore;
|
|
|
225 |
|
|
|
226 |
// Update the gradebook score
|
|
|
227 |
var scaledScores = thisReportView.querySelectorAll('.h5p-reporting-scaled-score');
|
|
|
228 |
scaledScores = Array.prototype.slice.call(scaledScores).map(function(scaledScoreElement) {
|
|
|
229 |
return parseFloat(scaledScoreElement.innerHTML);
|
|
|
230 |
});
|
|
|
231 |
|
|
|
232 |
var scaledScore = scaledScores.reduce(function(a,b) {
|
|
|
233 |
return a + b;
|
|
|
234 |
});
|
|
|
235 |
|
|
|
236 |
var scaledScoreElement = thisReport.querySelectorAll('.h5p-reporting-main-score-scaled-score')[0];
|
|
|
237 |
scaledScoreElement.innerHTML = Number((scaledScore).toFixed(2));
|
|
|
238 |
}
|
|
|
239 |
|
|
|
240 |
|
|
|
241 |
/**
|
|
|
242 |
* Renders the input div
|
|
|
243 |
*
|
|
|
244 |
* @param {number} subcontentID
|
|
|
245 |
* @param {number} index
|
|
|
246 |
*/
|
|
|
247 |
function populateInputDiv(subcontentID, index) {
|
|
|
248 |
H5P.jQuery.get(data_for_page.getSubContentEndpoint, {subcontent_id : subcontentID}, function(response) {
|
|
|
249 |
var loadedInput = document.getElementById('h5p-grade-input-' + index);
|
|
|
250 |
loadedInput.value = response.data.score;
|
|
|
251 |
|
|
|
252 |
var loadedMaxScore = document.getElementById('h5p-max-score-text-' + index);
|
|
|
253 |
loadedMaxScore.innerHTML = scoreDelimiter + ' ' + response.data.maxScore;
|
|
|
254 |
maxScores[index] = response.data.maxScore;
|
|
|
255 |
|
|
|
256 |
// Disable buttons if the content type hasn't been graded yet
|
|
|
257 |
if (response.data.score === null) {
|
|
|
258 |
var submitButton = document.getElementById('h5p-iv-open-ended-reporting-submit-button-' + index);
|
|
|
259 |
submitButton.disabled = true;
|
|
|
260 |
}
|
|
|
261 |
else {
|
|
|
262 |
// Hide input div and show grade container if it already has been graded
|
|
|
263 |
hideInputs(index);
|
|
|
264 |
updateGradeBookContainer(index, loadedInput.value, loadedInput.scaleFactor);
|
|
|
265 |
}
|
|
|
266 |
|
|
|
267 |
updateQuestionCounter(response.data.totalUngraded);
|
|
|
268 |
});
|
|
|
269 |
}
|
|
|
270 |
|
|
|
271 |
|
|
|
272 |
/**
|
|
|
273 |
* Updates the remanining question counter
|
|
|
274 |
*
|
|
|
275 |
* @param {number} totalUngraded
|
|
|
276 |
*/
|
|
|
277 |
function updateQuestionCounter(totalUngraded) {
|
|
|
278 |
if (totalUngraded > 0) {
|
|
|
279 |
container.querySelectorAll('.h5p-iv-open-ended-reporting-question-counter').forEach(function(questionCounter) {
|
|
|
280 |
questionCounter.innerHTML = '<span>' + totalUngraded + ' ' + questionsRemainingLabel + '</span>';
|
|
|
281 |
});
|
|
|
282 |
} else {
|
|
|
283 |
container.querySelectorAll('.h5p-iv-open-ended-reporting-question-counter').forEach(function(questionCounter) {
|
|
|
284 |
questionCounter.innerHTML = '<span>All questions have been graded</span>';
|
|
|
285 |
questionCounter.classList.add('reporting-completed');
|
|
|
286 |
});
|
|
|
287 |
}
|
|
|
288 |
}
|
|
|
289 |
|
|
|
290 |
// Initialize buttons
|
|
|
291 |
container.querySelectorAll('.h5p-iv-open-ended-previous').forEach(function(button, index) {
|
|
|
292 |
button.addEventListener("click", showPreviousQuestion);
|
|
|
293 |
|
|
|
294 |
// Disable the first previous button
|
|
|
295 |
if (index === 0) {
|
|
|
296 |
button.disabled = true;
|
|
|
297 |
}
|
|
|
298 |
});
|
|
|
299 |
|
|
|
300 |
container.querySelectorAll('.h5p-iv-open-ended-next').forEach(function(button, index) {
|
|
|
301 |
button.addEventListener("click", showNextQuestion);
|
|
|
302 |
|
|
|
303 |
// Disable the last next button
|
|
|
304 |
if (index === container.querySelectorAll('.h5p-iv-open-ended-next').length - 1) {
|
|
|
305 |
button.disabled = true;
|
|
|
306 |
}
|
|
|
307 |
});
|
|
|
308 |
|
|
|
309 |
// Add logic to the 'Change grade' button to show the input again
|
|
|
310 |
container.querySelectorAll('.h5p-iv-open-ended-reporting-change-grade').forEach(function(button) {
|
|
|
311 |
var index = button.getAttribute('data-report-id');
|
|
|
312 |
button.addEventListener('click', function() {
|
|
|
313 |
questions[index].inputDiv.classList.remove('h5p-iv-open-ended-reporting-hidden');
|
|
|
314 |
questions[index].submitButton.classList.remove('h5p-iv-open-ended-reporting-hidden');
|
|
|
315 |
questions[index].gradebookContainer.classList.add('h5p-iv-open-ended-reporting-hidden');
|
|
|
316 |
});
|
|
|
317 |
});
|
|
|
318 |
|
|
|
319 |
/**
|
|
|
320 |
* showPreviousQuestion
|
|
|
321 |
*/
|
|
|
322 |
function showPreviousQuestion() {
|
|
|
323 |
// If we are on the first question don't do anything
|
|
|
324 |
if (index === 0) {
|
|
|
325 |
return;
|
|
|
326 |
}
|
|
|
327 |
currentQuestion.classList.add('h5p-iv-open-ended-reporting-hidden');
|
|
|
328 |
|
|
|
329 |
index -= 1;
|
|
|
330 |
currentQuestion = questionElements[index];
|
|
|
331 |
currentQuestion.classList.remove('h5p-iv-open-ended-reporting-hidden');
|
|
|
332 |
}
|
|
|
333 |
|
|
|
334 |
/**
|
|
|
335 |
* showNextQuestion
|
|
|
336 |
*/
|
|
|
337 |
function showNextQuestion() {
|
|
|
338 |
if (index == questionElements.length - 1) {
|
|
|
339 |
return;
|
|
|
340 |
}
|
|
|
341 |
currentQuestion.classList.add('h5p-iv-open-ended-reporting-hidden');
|
|
|
342 |
|
|
|
343 |
index += 1;
|
|
|
344 |
currentQuestion = questionElements[index];
|
|
|
345 |
currentQuestion.classList.remove('h5p-iv-open-ended-reporting-hidden');
|
|
|
346 |
}
|
|
|
347 |
|
|
|
348 |
/**
|
|
|
349 |
* Finds an ancestor that matches a selector
|
|
|
350 |
*
|
|
|
351 |
* @param {type} el
|
|
|
352 |
* @param {type} sel
|
|
|
353 |
* @return {HTMLElement} element to be returned
|
|
|
354 |
*/
|
|
|
355 |
function findAncestor (el, sel) {
|
|
|
356 |
while ((el = el.parentElement) && !((el.matches || el.matchesSelector).call(el,sel)));
|
|
|
357 |
return el;
|
|
|
358 |
}
|
|
|
359 |
});
|