window.codio = (function () {
var callbacks = {}
var domReady = false
var pendingEvents = []
function parseSearchParams() {
var query = window.location.search.substring(1)
var vars = query.split('&')
var res = {}
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=')
res[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1])
}
return res
}
var codioId = parseSearchParams().id
var codioOrigin = '*'
// Send a message to the parent window.
function send(event, args) {
window.parent.postMessage(
JSON.stringify({
action: event,
args: args,
id: codioId
}),
codioOrigin
)
}
function runInBox(command, callback) {
var id = Math.random().toString(10)
callbacks[id] = callback
send('runInBox', {
id: id,
cmd: command
})
}
function renderCodeOutputCompareResult(
elem,
assessmentSource,
result,
showAsTeacher
) {
try {
if (result.state && result.state === 'progress') {
elem.textContent = 'Please wait while we check your answer...'
return
}
var parsed = JSON.parse(result.output)
if (parsed.sequence) {
var expectedOutputs = []
if (showAsTeacher && assessmentSource.sequence) {
expectedOutputs = assessmentSource.sequence
} else if (assessmentSource && assessmentSource.expectedOutputs) {
expectedOutputs = assessmentSource.expectedOutputs
} else if (result.expectedOutputs && result.expectedOutputs) {
expectedOutputs = result.expectedOutputs
}
var content = parsed.sequence
.map(function (item, pos) {
var state =
'failed'
var reason = ''
var feedback = ''
if (item && item.passed) {
state =
'passed'
} else if (item.stdout || item.stderr) {
var preContent = ['Output: ', item.stdout, item.stderr]
if (expectedOutputs && expectedOutputs[pos]) {
preContent.push('
Expected: ')
preContent.push(expectedOutputs[pos].output)
}
reason = '
' + preContent.join('') + '
'
}
if (item && !item.passed) {
var sourceSequence =
assessmentSource.sequence && assessmentSource.sequence[pos]
var sourceFeedback =
sourceSequence &&
sourceSequence.showFeedback &&
sourceSequence.feedback
var resultFeedbackObject =
!assessmentSource.sequence &&
assessmentSource.feedbacks &&
assessmentSource.feedbacks[pos]
var feedbackToShow =
sourceFeedback ||
(resultFeedbackObject.show && resultFeedbackObject.feedback) ||
''
if (feedbackToShow) {
var content = ['Feedback: ', feedbackToShow].join('')
feedback = '' + content + '
'
}
}
return [
'',
'Check ',
pos + 1,
' ',
state,
reason,
feedback,
'
'
].join('')
})
.join('')
elem.innerHTML = content
}
} catch (e) {
elem.textContent = result.output
}
}
function onMessage(event) {
var message = event.data
try {
message = JSON.parse(message)
} catch (e) {
return
}
switch (message.action) {
case 'callback':
var callback = callbacks[message.args.id]
if (typeof callback === 'function') {
callback(message.args.error, message.args.result)
}
delete callbacks[message.args.id]
break
case 'setContent':
if (!message.args || !message.args.buttons) {
return
}
var buttons = message.args.buttons
for (var key in buttons) {
if (!Object.prototype.hasOwnProperty.call(buttons, key)) {
continue
}
var button = message.args.buttons[key]
if (button.type !== 'assessment') {
continue
}
var assessment = button.data
if (
assessment.type !== 'code-output-compare' &&
assessment.type !== 'test'
) {
continue
}
var taskId = assessment.taskId
var result = assessment.result
if (result === null) {
continue
}
var lastResult = result.result
var selector = '[data-codio-assessment-output="' + taskId + '"]'
var assessmentElements = document.querySelectorAll(selector)
for (var i = 0; i < assessmentElements.length; ++i) {
var assessmentElem = assessmentElements[i]
try {
assessmentElem.dataset.codioAssessmentState = lastResult.state
assessmentElem.dataset.codioAssessmentExitCode = lastResult.code
assessmentElem.dataset.codioAssessmentTimestamp =
lastResult.timestamp
assessmentElem.dataset.codioAssessmentPoints = result.points
} catch (e) {}
if (!lastResult) {
assessmentElem.textContent = ''
continue
}
switch (assessment.type) {
case 'test':
var output = lastResult.output
if (output[0] === '<') {
// looks like html content
assessmentElem.innerHTML = output
} else {
assessmentElem.textContent = output
}
break
case 'code-output-compare':
renderCodeOutputCompareResult(
assessmentElem,
assessment.source,
lastResult,
message.args.showAsTeacher
)
break
}
}
}
break
}
}
//onMessage
window.addEventListener(
'message',
function () {
if (!domReady) {
pendingEvents.push(arguments)
} else {
onMessage.apply(this, arguments)
}
},
false
)
function onDocumentLoad() {
domReady = true
send('load')
for (var i = 0; i < pendingEvents.length; i++) {
onMessage.apply(this, pendingEvents[i])
}
pendingEvents = []
}
if (document && document.readyState === 'complete') {
onDocumentLoad()
} else {
window.addEventListener(
'load',
function () {
onDocumentLoad()
},
false
)
}
return {
open: function (type, path, panelNumber) {
send('openTab', {
type: type,
content: path,
panelNumber: panelNumber
})
},
run: runInBox,
runAndShow: function (command, outputElementId) {
runInBox(command, function (error, result) {
if (result && result.stdout) {
var elem = document.getElementById(outputElementId)
if (elem) {
elem.textContent = result.stdout
}
}
})
},
assessments: {
reset: function (assessmentId) {
send('codioAssessmentReset', {
id: assessmentId
})
},
modify: function (assessmentId, status) {
send('codioAssessmentModify', {
id: assessmentId,
status: status
})
},
check: function (assessmentId) {
send('checkAssessment', {
id: assessmentId
})
},
send: function (assessmentId, answer, points, callback) {
var id = Math.random().toString(10)
callbacks[id] = callback
send('sendAssessment', {
id: assessmentId,
callbackId: id,
answer: answer,
points: points
})
},
get: function (assessmentId, callback) {
var id = Math.random().toString(10)
callbacks[id] = callback
send('getAssessment', {
id: assessmentId,
callbackId: id
})
}
},
goToSectionTitled: function (sectionTitle) {
send('goToSectionTitled', {
sectionTitle: sectionTitle
})
},
goToNextSection: function () {
send('goToNextSection')
},
goTo: function (sectionId, optionalAnchor) {
send('goToSection', {
sectionId: sectionId,
optionalAnchor: optionalAnchor
})
},
goToPreviousSection: function () {
send('goToPreviousSection')
},
resetCurrentFiles: function () {
send('resetCurrentFiles')
},
switchUnit: function (unitId) {
send('switchUnit', { id: unitId })
},
closeAllTabs: function () {
send('closeAllTabs')
},
closeTab: function (path) {
send('closeTab', { path: path })
},
BUTTON_STATE: {
SUCCESS: 'pass',
FAILURE: 'fail',
PROGRESS: 'progress',
INVALID: 'invalid'
},
setButtonValue: function (id, state, value) {
send('buttonState', {
id: id,
state: state,
value: value
})
}
}
})()