
This commit also fixes an issue with the flash messages being positioned too high and displayed for too long
741 lines
25 KiB
Plaintext
741 lines
25 KiB
Plaintext
var CodeOceanEditor = {
|
|
//ACE-Editor-Path
|
|
// ruby part adds the relative_url_root, if it is set.
|
|
ACE_FILES_PATH: '<%= (defined? Rails.application.config.relative_url_root) && Rails.application.config.relative_url_root != nil && Rails.application.config.relative_url_root != "" ? Rails.application.config.relative_url_root : "" %>' + '/assets/ace/',
|
|
THEME: 'ace/theme/textmate',
|
|
|
|
//Color-Encoding for Percentages in Progress Bars (For submissions)
|
|
ADEQUATE_PERCENTAGE: 50,
|
|
SUCCESSFULL_PERCENTAGE: 90,
|
|
|
|
//Key-Codes (for Hotkeys)
|
|
ALT_R_KEY_CODE: 174,
|
|
ALT_S_KEY_CODE: 8218,
|
|
ALT_T_KEY_CODE: 8224,
|
|
ENTER_KEY_CODE: 13,
|
|
|
|
//Request-For-Comments-Configuration
|
|
REQUEST_FOR_COMMENTS_DELAY: 3 * 60 * 1000,
|
|
REQUEST_TOOLTIP_TIME: 5000,
|
|
|
|
editors: [],
|
|
editor_for_file: new Map(),
|
|
regex_for_language: new Map(),
|
|
tracepositions_regex: undefined,
|
|
|
|
active_file: undefined,
|
|
active_frame: undefined,
|
|
running: false,
|
|
|
|
lastCopyText: null,
|
|
|
|
<% self.class.include Rails.application.routes.url_helpers %>
|
|
<% @config ||= CodeOcean::Config.new(:code_ocean).read(erb: false) %>
|
|
sendEvents: <%= @config['codeocean_events'] ? @config['codeocean_events']['enabled'] : false %>,
|
|
eventURL: "<%= @config['codeocean_events'] ? events_path : '' %>",
|
|
fileTypeURL: "<%= file_types_path %>",
|
|
|
|
|
|
configureEditors: function () {
|
|
_.each(['modePath', 'themePath', 'workerPath'], function (attribute) {
|
|
ace.config.set(attribute, this.ACE_FILES_PATH);
|
|
}.bind(this));
|
|
},
|
|
|
|
confirmDestroy: function (event) {
|
|
event.preventDefault();
|
|
if (confirm($(event.target).data('message-confirm'))) {
|
|
this.destroyFile();
|
|
}
|
|
},
|
|
|
|
confirmReset: function (event) {
|
|
event.preventDefault();
|
|
if (confirm($('#start-over').data('message-confirm'))) {
|
|
this.resetCode();
|
|
}
|
|
},
|
|
|
|
fileActionsAvailable: function () {
|
|
return this.isActiveFileRenderable() || this.isActiveFileRunnable() || this.isActiveFileStoppable() || this.isActiveFileTestable();
|
|
},
|
|
|
|
findOrCreateOutputElement: function (index) {
|
|
if ($('#output-' + index).isPresent()) {
|
|
return $('#output-' + index);
|
|
} else {
|
|
var element = $('<pre class="mt-2">').attr('id', 'output-' + index);
|
|
$('#output').append(element);
|
|
return element;
|
|
}
|
|
},
|
|
|
|
findOrCreateRenderElement: function (index) {
|
|
if ($('#render-' + index).isPresent()) {
|
|
return $('#render-' + index);
|
|
} else {
|
|
var element = $('<div>').attr('id', 'render-' + index);
|
|
$('#render').append(element);
|
|
return element;
|
|
}
|
|
},
|
|
|
|
getCardClass: function (result) {
|
|
if (result.stderr && !result.score) {
|
|
return 'card bg-danger text-white';
|
|
} else if (result.score < 1) {
|
|
return 'card bg-warning text-white';
|
|
} else {
|
|
return 'card bg-success text-white';
|
|
}
|
|
},
|
|
|
|
showOutput: function(event) {
|
|
event.preventDefault();
|
|
this.showOutputBar();
|
|
$('body').scrollTo($(event.target).attr('href'));
|
|
},
|
|
|
|
renderProgressBar: function(score, maximum_score) {
|
|
var percentage = score / maximum_score * 100;
|
|
var progress_bar = $('#score .progress-bar');
|
|
progress_bar.removeClass().addClass(this.getProgressBarClass(percentage));
|
|
progress_bar.attr({
|
|
'aria-valuemax': maximum_score,
|
|
'aria-valuemin': 0,
|
|
'aria-valuenow': score
|
|
});
|
|
progress_bar.css('width', percentage + '%');
|
|
},
|
|
|
|
// The event ready.jstree is fired too early and thus doesn't work.
|
|
selectFileInJsTree: function(filetree, file_id) {
|
|
if (!filetree.is(':visible'))
|
|
// The left sidebar is not shown and thus the filetree is not rendered.
|
|
return;
|
|
|
|
if (!filetree.hasClass('jstree-loading')) {
|
|
filetree.jstree("deselect_all");
|
|
filetree.jstree().select_node(file_id);
|
|
} else {
|
|
setTimeout(this.selectFileInJsTree.bind(null, filetree, file_id), 250);
|
|
}
|
|
},
|
|
|
|
showFirstFile: function() {
|
|
var frame = $('.frame[data-role="main_file"]').isPresent() ? $('.frame[data-role="main_file"]') : $('.frame').first();
|
|
var file_id = frame.find('.editor').data('file-id');
|
|
this.setActiveFile(frame.data('filename'), file_id);
|
|
var filetree = $('#files');
|
|
this.selectFileInJsTree(filetree, file_id);
|
|
this.showFrame(frame);
|
|
this.toggleButtonStates();
|
|
},
|
|
|
|
showFrame: function(frame) {
|
|
this.active_frame = frame;
|
|
$('.frame').hide();
|
|
frame.show();
|
|
},
|
|
|
|
getProgressBarClass: function (percentage) {
|
|
if (percentage < this.ADEQUATE_PERCENTAGE) {
|
|
return 'progress-bar progress-bar-striped bg-danger';
|
|
} else if (percentage < this.SUCCESSFULL_PERCENTAGE) {
|
|
return 'progress-bar progress-bar-striped bg-warning';
|
|
} else {
|
|
return 'progress-bar progress-bar-striped bg-success';
|
|
}
|
|
},
|
|
|
|
handleKeyPress: function (event) {
|
|
if (event.which === this.ALT_R_KEY_CODE) {
|
|
$('#run').trigger('click');
|
|
} else if (event.which === this.ALT_S_KEY_CODE) {
|
|
$('#assess').trigger('click');
|
|
} else if (event.which === this.ALT_T_KEY_CODE) {
|
|
$('#test').trigger('click');
|
|
} else {
|
|
return;
|
|
}
|
|
event.preventDefault();
|
|
},
|
|
|
|
handleCopyEvent: function (text) {
|
|
this.lastCopyText = text;
|
|
},
|
|
|
|
handlePasteEvent: function (pasteObject) {
|
|
var same = (CodeOceanEditor.lastCopyText === pasteObject.text);
|
|
|
|
// if the text is not copied from within the editor (from any file), send an event to the backend
|
|
if (!same) {
|
|
CodeOceanEditor.publishCodeOceanEvent({
|
|
category: 'editor_paste',
|
|
data: pasteObject.text,
|
|
exercise_id: $('#editor').data('exercise-id'),
|
|
file_id: $(event.target).data('file-id')
|
|
});
|
|
}
|
|
},
|
|
|
|
hideSpinner: function () {
|
|
$('button i.fa').show();
|
|
$('button i.fa-spin').hide();
|
|
},
|
|
|
|
|
|
resizeAceEditors: function (){
|
|
$('.editor').each(function (index, element) {
|
|
this.resizeParentOfAceEditor(element);
|
|
}.bind(this));
|
|
window.dispatchEvent(new Event('resize'));
|
|
},
|
|
|
|
resizeParentOfAceEditor: function (element){
|
|
// calculate needed size: window height - position of top of ACE editor - height of autosave label below editor - 5 for bar margins
|
|
var windowHeight = window.innerHeight - $(element).offset().top - $('#autosave-label').height() - 5;
|
|
$(element).parent().height(windowHeight);
|
|
},
|
|
|
|
initializeEditors: function () {
|
|
this.editors = [];
|
|
$('.editor').each(function (index, element) {
|
|
|
|
// Resize frame on load
|
|
this.resizeParentOfAceEditor(element);
|
|
|
|
// Resize frame on window size change
|
|
$(window).resize(function(){
|
|
this.resizeParentOfAceEditor(element);
|
|
}.bind(this));
|
|
|
|
var editor = ace.edit(element);
|
|
|
|
if (this.qa_api) {
|
|
editor.getSession().on("change", function (deltaObject) {
|
|
this.qa_api.executeCommand('syncEditor', [this.active_file, deltaObject]);
|
|
}.bind(this));
|
|
}
|
|
|
|
var document = editor.getSession().getDocument();
|
|
// insert pre-existing code into editor. we have to use insertLines, otherwise the deltas are not properly added
|
|
var file_id = $(element).data('file-id');
|
|
var content = $('.editor-content[data-file-id=' + file_id + ']');
|
|
this.setActiveFile($(element).parent().data('filename'), file_id);
|
|
|
|
document.insertLines(0, content.text().split(/\n/));
|
|
// remove last (empty) that is there by default line
|
|
document.removeLines(document.getLength() - 1, document.getLength() - 1);
|
|
editor.setReadOnly($(element).data('read-only') !== undefined);
|
|
if (editor.getReadOnly()) {
|
|
editor.setHighlightActiveLine(false);
|
|
editor.setHighlightGutterLine(false);
|
|
editor.renderer.$cursorLayer.element.style.opacity = 0;
|
|
}
|
|
editor.setShowPrintMargin(false);
|
|
editor.setTheme(this.THEME);
|
|
|
|
|
|
|
|
// set options for autocompletion
|
|
if($(element).data('allow-auto-completion')){
|
|
editor.setOptions({
|
|
enableBasicAutocompletion: true,
|
|
enableSnippets: false,
|
|
enableLiveAutocompletion: true
|
|
});
|
|
}
|
|
|
|
|
|
editor.commands.bindKey("ctrl+alt+0", null);
|
|
this.editors.push(editor);
|
|
this.editor_for_file.set($(element).parent().data('filename'), editor);
|
|
var session = editor.getSession();
|
|
session.setMode($(element).data('mode'));
|
|
session.setTabSize($(element).data('indent-size'));
|
|
session.setUseSoftTabs(true);
|
|
session.setUseWrapMode(true);
|
|
|
|
// set regex for parsing error traces based on the mode of the main file.
|
|
if ($(element).parent().data('role') == "main_file") {
|
|
this.tracepositions_regex = this.regex_for_language.get($(element).data('mode'));
|
|
}
|
|
|
|
var file_id = $(element).data('id');
|
|
|
|
/*
|
|
* Register event handlers
|
|
*/
|
|
|
|
// editor itself
|
|
editor.on("paste", this.handlePasteEvent.bind(element));
|
|
editor.on("copy", this.handleCopyEvent.bind(element));
|
|
|
|
// listener for autosave
|
|
session.on("change", function (deltaObject) {
|
|
this.resetSaveTimer();
|
|
}.bind(this));
|
|
}.bind(this));
|
|
},
|
|
|
|
initializeEventHandlers: function () {
|
|
$(document).on('click', '#results a', this.showOutput.bind(this));
|
|
$(document).on('keypress', this.handleKeyPress.bind(this));
|
|
this.initializeFileTreeButtons();
|
|
this.initializeWorkspaceButtons();
|
|
this.initializeRequestForComments()
|
|
},
|
|
|
|
updateEditorModeToFileTypeID: function (editor, fileTypeID) {
|
|
var newMode = 'ace/mode/text'
|
|
|
|
$.ajax(this.fileTypeURL + '/' + fileTypeID, {
|
|
dataType: 'json'
|
|
}).done(function (data) {
|
|
if (data['editor_mode'] !== null) {
|
|
newMode = data['editor_mode'];
|
|
}
|
|
}).fail(_.noop)
|
|
.always(function () {
|
|
ace.edit(editor).session.setMode(newMode);
|
|
});
|
|
},
|
|
|
|
initializeFileTree: function () {
|
|
$('#files').jstree($('#files').data('entries'));
|
|
$('#files').on('click', 'li.jstree-leaf', function (event) {
|
|
this.setActiveFile(
|
|
$(event.target).parent().text(),
|
|
parseInt($(event.target).parent().attr('id'))
|
|
);
|
|
var frame = $('[data-file-id="' + this.active_file.id + '"]').parent();
|
|
this.showFrame(frame);
|
|
this.toggleButtonStates();
|
|
}.bind(this));
|
|
},
|
|
|
|
initializeFileTreeButtons: function () {
|
|
$('#create-file').on('click', this.showFileDialog.bind(this));
|
|
$('#create-file-collapsed').on('click', this.showFileDialog.bind(this));
|
|
$('#destroy-file').on('click', this.confirmDestroy.bind(this));
|
|
$('#destroy-file-collapsed').on('click', this.confirmDestroy.bind(this));
|
|
$('#download').on('click', this.downloadCode.bind(this));
|
|
$('#download-collapsed').on('click', this.downloadCode.bind(this));
|
|
$('#request-for-comments').on('click', this.requestComments.bind(this));
|
|
},
|
|
|
|
initializeSideBarCollapse: function() {
|
|
$('#sidebar-collapse-collapsed').on('click',this.handleSideBarToggle.bind(this));
|
|
$('#sidebar-collapse').on('click',this.handleSideBarToggle.bind(this))
|
|
},
|
|
|
|
handleSideBarToggle: function() {
|
|
$('#sidebar').toggleClass('sidebar-col').toggleClass('sidebar-col-collapsed');
|
|
$('#sidebar-collapsed').toggleClass('d-none');
|
|
$('#sidebar-uncollapsed').toggleClass('d-none');
|
|
},
|
|
|
|
initializeRegexes: function () {
|
|
this.regex_for_language.set("ace/mode/python", /File "(.+?)", line (\d+)/g);
|
|
this.regex_for_language.set("ace/mode/java", /(.*\.java):(\d+):/g);
|
|
},
|
|
|
|
initializeTooltips: function () {
|
|
$('[data-tooltip]').tooltip();
|
|
},
|
|
|
|
|
|
initializeWorkspaceButtons: function () {
|
|
$('#submit').on('click', this.submitCode.bind(this));
|
|
$('#assess').on('click', this.scoreCode.bind(this));
|
|
$('#dropdown-render, #render').on('click', this.renderCode.bind(this));
|
|
$('#dropdown-run, #run').on('click', this.runCode.bind(this));
|
|
$('#dropdown-stop, #stop').on('click', this.stopCode.bind(this));
|
|
$('#dropdown-test, #test').on('click', this.testCode.bind(this));
|
|
$('#save').on('click', this.saveCode.bind(this));
|
|
$('#start-over').on('click', this.confirmReset.bind(this));
|
|
$('#start-over-collapsed').on('click', this.confirmReset.bind(this));
|
|
},
|
|
|
|
initializeRequestForComments: function () {
|
|
var button = $('#requestComments');
|
|
button.prop('disabled', true);
|
|
button.on('click', function () {
|
|
$('#rfc_intervention_text').hide()
|
|
$('#comment-modal').modal('show');
|
|
});
|
|
|
|
$('#askForCommentsButton').on('click', this.requestComments.bind(this));
|
|
$('#closeAskForCommentsButton').on('click', function(){
|
|
$('#comment-modal').modal('hide');
|
|
});
|
|
|
|
setTimeout(function () {
|
|
button.prop('disabled', false);
|
|
button.tooltip('show');
|
|
setTimeout(function() {
|
|
button.tooltip('hide');
|
|
}, this.REQUEST_TOOLTIP_TIME);
|
|
}.bind(this), this.REQUEST_FOR_COMMENTS_DELAY);
|
|
},
|
|
|
|
isActiveFileRenderable: function () {
|
|
return 'renderable' in this.active_frame.data();
|
|
},
|
|
|
|
isActiveFileRunnable: function () {
|
|
return this.isActiveFileExecutable() && ['main_file', 'user_defined_file'].includes(this.active_frame.data('role'));
|
|
},
|
|
|
|
isActiveFileStoppable: function () {
|
|
return this.isActiveFileRunnable() && this.running;
|
|
},
|
|
|
|
isActiveFileSubmission: function () {
|
|
return ['Submission'].includes(this.active_frame.data('contextType'));
|
|
},
|
|
|
|
isActiveFileTestable: function () {
|
|
return this.isActiveFileExecutable() && ['teacher_defined_test', 'user_defined_test'].includes(this.active_frame.data('role'));
|
|
},
|
|
|
|
isBrowserSupported: function () {
|
|
// websockets are used for run, score and test
|
|
return Modernizr.websockets;
|
|
},
|
|
|
|
populateCard: function (card, result, index) {
|
|
card.addClass(this.getCardClass(result));
|
|
card.find('.card-title .filename').text(result.filename);
|
|
card.find('.card-title .number').text(index + 1);
|
|
card.find('.row .col-sm-9').eq(0).find('.number').eq(0).text(result.passed);
|
|
card.find('.row .col-sm-9').eq(0).find('.number').eq(1).text(result.count);
|
|
card.find('.row .col-sm-9').eq(1).find('.number').eq(0).text(parseFloat((result.score * result.weight).toFixed(2)));
|
|
card.find('.row .col-sm-9').eq(1).find('.number').eq(1).text(result.weight);
|
|
card.find('.row .col-sm-9').eq(2).html(result.message);
|
|
if (result.error_messages) card.find('.row .col-sm-9').eq(3).text(result.error_messages.join(' '));
|
|
//card.find('.row .col-sm-9').eq(4).find('a').attr('href', '#output-' + index);
|
|
},
|
|
|
|
publishCodeOceanEvent: function (payload) {
|
|
if(this.sendEvents){
|
|
$.ajax(this.eventURL, {
|
|
type: 'POST',
|
|
cache: false,
|
|
dataType: 'JSON',
|
|
data: {
|
|
event: payload
|
|
},
|
|
success: _.noop,
|
|
error: _.noop
|
|
});
|
|
}
|
|
},
|
|
|
|
sendError: function (message, submission_id) {
|
|
this.showSpinner($('#render'));
|
|
var jqxhr = this.ajax({
|
|
data: {
|
|
error: {
|
|
message: message,
|
|
submission_id: submission_id
|
|
}
|
|
},
|
|
url: $('#editor').data('errors-url')
|
|
});
|
|
jqxhr.always(this.hideSpinner);
|
|
},
|
|
|
|
toggleButtonStates: function () {
|
|
$('#destroy-file').prop('disabled', this.active_frame.data('role') !== 'user_defined_file');
|
|
$('#dummy').toggle(!this.fileActionsAvailable());
|
|
$('#render').toggle(this.isActiveFileRenderable());
|
|
$('#run').toggle(this.isActiveFileRunnable() && !this.running);
|
|
$('#stop').toggle(this.isActiveFileStoppable());
|
|
$('#test').toggle(this.isActiveFileTestable());
|
|
},
|
|
|
|
jumpToSourceLine: function (event) {
|
|
var file = $(event.target).data('file');
|
|
var line = $(event.target).data('line');
|
|
|
|
// set active file, only needed for codepilot, so skipped for now
|
|
|
|
var frame = $('div.frame[data-filename="' + file + '"]');
|
|
this.showFrame(frame);
|
|
|
|
var editor = this.editor_for_file.get(file);
|
|
editor.gotoLine(line, 0);
|
|
event.preventDefault();
|
|
},
|
|
|
|
augmentStacktraceInOutput: function () {
|
|
if (this.tracepositions_regex) {
|
|
var element = $('#output>pre');
|
|
var text = element.text();
|
|
element.on("click", "a", this.jumpToSourceLine.bind(this));
|
|
|
|
var matches;
|
|
|
|
while (matches = this.tracepositions_regex.exec(text)) {
|
|
var frame = $('div.frame[data-filename="' + matches[1] + '"]')
|
|
|
|
if (frame.length > 0) {
|
|
element.html(text.replace(matches[0], "<a href='#' data-file='" + matches[1] + "' data-line='" + matches[2] + "'>" + matches[0] + "</a>"));
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
resetOutputTab: function () {
|
|
this.clearOutput();
|
|
$('#flowrHint').fadeOut();
|
|
this.clearHints();
|
|
this.showOutputBar();
|
|
},
|
|
|
|
isActiveFileBinary: function () {
|
|
return 'binary' in this.active_frame.data();
|
|
},
|
|
|
|
isActiveFileExecutable: function () {
|
|
return 'executable' in this.active_frame.data();
|
|
},
|
|
|
|
setActiveFile: function (filename, fileId) {
|
|
this.active_file = {
|
|
filename: filename,
|
|
id: fileId
|
|
};
|
|
},
|
|
|
|
showSpinner: function(initiator) {
|
|
$(initiator).find('i.fa').hide();
|
|
$(initiator).find('i.fa-spin').show();
|
|
},
|
|
|
|
showStatus: function(output) {
|
|
if (output.status === 'timeout') {
|
|
this.showTimeoutMessage();
|
|
} else if (output.status === 'container_depleted') {
|
|
this.showContainerDepletedMessage();
|
|
} else if (output.stderr) {
|
|
$.flash.danger({
|
|
icon: ['fa', 'fa-bug'],
|
|
text: $('#run').data('message-failure')
|
|
});
|
|
}
|
|
},
|
|
|
|
clearHints: function() {
|
|
var container = $('#error-hints');
|
|
container.find('ul.body > li.hint').remove();
|
|
container.fadeOut();
|
|
},
|
|
|
|
showHint: function(message) {
|
|
var template = function(description, hint) {
|
|
return '\
|
|
<li class="hint">\
|
|
<div class="description">\
|
|
' + description + '\
|
|
</div>\
|
|
<div class="hint">\
|
|
' + hint + '\
|
|
</div>\
|
|
</li>\
|
|
'
|
|
};
|
|
var container = $('#error-hints');
|
|
container.find('ul.body').append(template(message.description, message.hint));
|
|
container.fadeIn();
|
|
},
|
|
|
|
showContainerDepletedMessage: function() {
|
|
$.flash.danger({
|
|
icon: ['fa', 'fa-clock-o'],
|
|
text: $('#editor').data('message-depleted')
|
|
});
|
|
},
|
|
|
|
showTimeoutMessage: function() {
|
|
$.flash.info({
|
|
icon: ['fa', 'fa-clock-o'],
|
|
text: $('#editor').data('message-timeout')
|
|
});
|
|
},
|
|
|
|
showWebsocketError: function() {
|
|
if (window.navigator.userAgent.indexOf('Edge') > -1 || window.navigator.userAgent.indexOf('Trident') > -1) {
|
|
// Mute errors in Microsoft Edge and Internet Explorer
|
|
return;
|
|
}
|
|
$.flash.danger({
|
|
text: $('#flash').data('message-failure')
|
|
});
|
|
},
|
|
|
|
showFileDialog: function(event) {
|
|
event.preventDefault();
|
|
this.createSubmission('#create-file', null, function(response) {
|
|
$('#code_ocean_file_context_id').val(response.id);
|
|
$('#modal-file').modal('show');
|
|
}.bind(this));
|
|
},
|
|
|
|
initializeOutputBarToggle: function() {
|
|
$('#toggle-sidebar-output').on('click',this.hideOutputBar.bind(this));
|
|
$('#toggle-sidebar-output-collapsed').on('click',this.showOutputBar.bind(this));
|
|
},
|
|
|
|
showOutputBar: function() {
|
|
$('#output_sidebar_collapsed').addClass('d-none');
|
|
$('#output_sidebar_uncollapsed').removeClass('d-none');
|
|
$('#output_sidebar').removeClass('output-col-collapsed').addClass('output-col');
|
|
},
|
|
|
|
hideOutputBar: function() {
|
|
$('#output_sidebar_collapsed').removeClass('d-none');
|
|
$('#output_sidebar_uncollapsed').addClass('d-none');
|
|
$('#output_sidebar').removeClass('output-col').addClass('output-col-collapsed');
|
|
},
|
|
|
|
initializeSideBarTooltips: function() {
|
|
$('[data-toggle="tooltip"]').tooltip()
|
|
},
|
|
|
|
initializeDescriptionToggle: function() {
|
|
$('#exercise-headline').on('click', this.toggleDescriptionCard.bind(this));
|
|
$('a#toggle').on('click', this.toggleDescriptionCard.bind(this));
|
|
},
|
|
|
|
toggleDescriptionCard: function() {
|
|
$('#description-card').toggleClass('description-card-collapsed').toggleClass('description-card');
|
|
$('#description-symbol').toggleClass('fa-chevron-down').toggleClass('fa-chevron-right');
|
|
var toggle = $('a#toggle');
|
|
toggle.text(toggle.text() == toggle.data('hide') ? toggle.data('show') : toggle.data('hide'));
|
|
this.resizeAceEditors();
|
|
event.preventDefault();
|
|
},
|
|
|
|
/**
|
|
* interventions
|
|
* */
|
|
initializeInterventionTimer: function() {
|
|
|
|
if ($('#editor').data('rfc-interventions') || $('#editor').data('break-interventions')) { // split in break or rfc intervention
|
|
window.onblur = function() { window.blurred = true; };
|
|
window.onfocus = function() { window.blurred = false; };
|
|
|
|
var delta = 100; // time in ms to wait for window event before time gets stopped
|
|
var tid;
|
|
$.ajax({
|
|
data: {
|
|
exercise_id: $('#editor').data('exercise-id'),
|
|
user_id: $('#editor').data('user-id')
|
|
},
|
|
dataType: 'json',
|
|
method: 'GET',
|
|
// get working times for this exercise
|
|
url: $('#editor').data('working-times-url'),
|
|
success: function (data) {
|
|
var percentile75 = data['working_time_75_percentile'];
|
|
var accumulatedWorkTimeUser = data['working_time_accumulated'];
|
|
|
|
var minTimeIntervention = 10 * 60 * 1000;
|
|
|
|
if ((accumulatedWorkTimeUser - percentile75) > 0) {
|
|
// working time is already over 75 percentile
|
|
var timeUntilIntervention = minTimeIntervention;
|
|
} else {
|
|
// working time is less than 75 percentile
|
|
// ensure we give user at least minTimeIntervention before we bother the user
|
|
var timeUntilIntervention = Math.max(percentile75 - accumulatedWorkTimeUser, minTimeIntervention);
|
|
}
|
|
|
|
tid = setInterval(function() {
|
|
if ( window.blurred ) { return; }
|
|
timeUntilIntervention -= delta;
|
|
if ( timeUntilIntervention <= 0 ) {
|
|
clearInterval(tid);
|
|
// timeUntilIntervention passed
|
|
if ($('#editor').data('break-interventions')) {
|
|
$('#break-intervention-modal').modal('show');
|
|
$.ajax({
|
|
data: {
|
|
intervention_type: 'BreakIntervention'
|
|
},
|
|
dataType: 'json',
|
|
type: 'POST',
|
|
url: $('#editor').data('intervention-save-url')
|
|
});
|
|
} else if ($('#editor').data('rfc-interventions')){
|
|
var button = $('#requestComments');
|
|
// only show intervention if user did not requested for a comment already
|
|
if (!button.prop('disabled')) {
|
|
$('#rfc_intervention_text').show();
|
|
$('#comment-modal').modal('show');
|
|
$.ajax({
|
|
data: {
|
|
intervention_type: 'QuestionIntervention'
|
|
},
|
|
dataType: 'json',
|
|
type: 'POST',
|
|
url: $('#editor').data('intervention-save-url')
|
|
});
|
|
};
|
|
}
|
|
}
|
|
}, delta);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
initializeSearchButton: function(){
|
|
$('#btn-search-col').button().click(function(){
|
|
var search = $('#search-input-text').val();
|
|
var course_token = $('#editor').data('course_token')
|
|
var save_search_url = $('#editor').data('search-save-url')
|
|
window.open("https://open.hpi.de/courses/" + course_token + "/pinboard?query=" + search, '_blank');
|
|
// save search
|
|
$.ajax({
|
|
data: {
|
|
search_text: search
|
|
},
|
|
dataType: 'json',
|
|
type: 'POST',
|
|
url: save_search_url});
|
|
})
|
|
|
|
$('#sidebar-search-collapsed').on('click',this.handleSideBarToggle.bind(this));
|
|
},
|
|
|
|
|
|
initializeEverything: function() {
|
|
this.initializeRegexes();
|
|
this.initializeCodePilot();
|
|
$('.score, #development-environment').show();
|
|
this.configureEditors();
|
|
this.initializeEditors();
|
|
this.initializeEventHandlers();
|
|
this.initializeFileTree();
|
|
this.initializeSideBarCollapse();
|
|
this.initializeOutputBarToggle();
|
|
this.initializeDescriptionToggle();
|
|
this.initializeSideBarTooltips();
|
|
this.initializeTooltips();
|
|
this.initializeInterventionTimer();
|
|
this.initializeSearchButton();
|
|
this.initPrompt();
|
|
this.renderScore();
|
|
this.showFirstFile();
|
|
this.resizeAceEditors();
|
|
|
|
$(window).on("beforeunload", this.unloadAutoSave.bind(this));
|
|
// create autosave when the editor is opened the first time
|
|
this.autosave();
|
|
}
|
|
};
|