Files
codeocean/app/assets/javascripts/editor/editor.js.erb
Sebastian Serth 237c225732 Add support for running CodeOcean under a subpath
* Also refactor (JavaScript) routes
2021-07-06 19:33:55 +02:00

896 lines
34 KiB
Plaintext

var CodeOceanEditor = {
//ACE-Editor-Path
// ruby part adds the relative_url_root, if it is set.
ACE_FILES_PATH: '<%= "#{Rails.application.config.relative_url_root.chomp('/')}/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)
R_KEY_CODE: 82,
S_KEY_CODE: 83,
T_KEY_CODE: 84,
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) %>
// Important notice: Changing the config values requires any content-wise
// modification for this file in the development environment. Lacking to do so
// will result in the old, server-side cached serving of this file even across
// server restarts!
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();
}
},
confirmResetActiveFile: function (event) {
event.preventDefault();
let message = $('#start-over-active-file').data('message-confirm');
message = message.replace('%{filename}', CodeOceanEditor.active_file.filename.replace(/#$/, ''))
if (confirm(message)) {
this.resetCode(true); // delete only active file
}
},
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.file_role === 'teacher_defined_linter') {
return 'card bg-info text-white'
} else 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) {
const target = $(event.target).attr('href');
if (target !== "#") {
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(CodeOceanEditor.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.altKey && event.which === this.R_KEY_CODE) {
$('#run').trigger('click');
} else if (event.altKey && event.which === this.S_KEY_CODE) {
$('#assess').trigger('click');
} else if (event.altKey && event.which === this.T_KEY_CODE) {
$('#test').trigger('click');
} else {
return;
}
event.preventDefault();
},
handleCopyEvent: function (text) {
CodeOceanEditor.lastCopyText = text;
},
handlePasteEvent: function (pasteObject, event) {
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.container).data('file-id')
});
}
},
hideSpinner: function () {
$('button i.fa, button i.far, button i.fas').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 - $('#statusbar').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).parent().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();
var mode = $(element).data('mode')
session.setMode(mode);
if (mode === 'ace/mode/python') {
editor.setTheme('ace/theme/tomorrow')
}
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('keydown', 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);
if (newMode === 'ace/mode/python') {
ace.edit(editor).setTheme('ace/theme/tomorrow')
}
});
},
initializeFileTree: function () {
$('#files').jstree($('#files').data('entries'));
$('#files').on('click', 'li.jstree-leaf > a', 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));
$('#destroy-file').on('click', this.confirmDestroy.bind(this));
$('#destroy-file-collapsed').on('click', this.confirmDestroy.bind(this));
$('#download').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));
const tipButton = $('#tips-collapsed');
if (tipButton) {
tipButton.on('click', this.handleSideBarToggle.bind(this));
}
$('#sidebar').on('transitionend', this.resizeAceEditors.bind(this));
},
handleSideBarToggle: function () {
const sidebar = $('#sidebar');
sidebar.toggleClass('sidebar-col').toggleClass('sidebar-col-collapsed');
if (sidebar.hasClass('w-25') || sidebar.hasClass('restore-to-w-25')) {
sidebar.toggleClass('w-25').toggleClass('restore-to-w-25');
}
$('#sidebar-collapsed').toggleClass('d-none');
$('#sidebar-uncollapsed').toggleClass('d-none');
},
initializeRegexes: function () {
// These RegEx are run on the HTML escaped output!
this.regex_for_language.set("ace/mode/python", /File &quot;(.+?)&quot;, line (\d+)/g);
this.regex_for_language.set("ace/mode/java", /(.*\.java):(\d+):/g);
},
initializeTooltips: function () {
$('[data-tooltip]').tooltip();
},
initializeWorkspaceButtons: function () {
$('#submit').one('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));
$('#start-over-active-file').on('click', this.confirmResetActiveFile.bind(this));
$('#start-over-active-file-collapsed').on('click', this.confirmResetActiveFile.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 () {
if (this.active_frame.data() === undefined) {
return false;
}
return 'renderable' in this.active_frame.data();
},
isActiveFileRunnable: function () {
return this.isActiveFileExecutable() && ['main_file', 'user_defined_file', 'executable_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', 'teacher_defined_linter'].includes(this.active_frame.data('role'));
},
isBrowserSupported: function () {
// websockets are used for run, score and test
// Also exclude IE and IE 11
return Modernizr.websockets && window.navigator.userAgent.indexOf("MSIE") <= 0 && !navigator.userAgent.match(/Trident\/7\./);
},
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);
if (result.weight !== 0) {
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);
} else {
// Hide score row if no score could be achieved
card.find('.attribute-row.row').eq(1).addClass('d-none');
}
card.find('.row .col-sm-9').eq(2).html(result.message);
// Add error message from code to card
if (result.error_messages) {
const targetNode = card.find('.row .col-sm-9').eq(3);
let errorMessagesToShow = [];
result.error_messages.forEach(function (item) {
if (item) {
errorMessagesToShow.push(item)
}
})
// delete all current elements
targetNode.text('');
// create a new list and append each element
const ul = document.createElement("ul");
// Extract detailed linter results
if (result.file_role === 'teacher_defined_linter') {
const detailed_linter_results = result.detailed_linter_results;
const severity_groups = detailed_linter_results.reduce(function(map, obj) {
map[obj.severity] = map[obj.severity] || []
map[obj.severity].push(obj);
return map;
}, {});
for (let severity in severity_groups) {
if (!severity_groups.hasOwnProperty(severity)) {
continue;
}
const linter_results = severity_groups[severity]
const li = document.createElement("li");
const text = $.parseHTML(`<u>${severity}:</u>`);
$(li).append(text);
ul.append(li);
const sub_ul = document.createElement("ul");
sub_ul.setAttribute('class', 'error_messages_list');
for (let check_run of linter_results) {
const sub_li = document.createElement("li");
let scope = '';
if (check_run.scope) {
scope = `, ${check_run.scope}()`;
}
const context = `${check_run.file_name}: ${check_run.line}${scope}`;
const line_link = `<a href='#' data-file='${check_run.file_name}' data-line='${check_run.line}'>${context}</a>`;
const message = `${check_run.name}: ${check_run.result} (${line_link})`;
const sub_text = $.parseHTML(message);
$(sub_li).append(sub_text).on("click", "a", this.jumpToSourceLine.bind(this));
sub_ul.append(sub_li);
}
li.append(sub_ul);
}
// Just show standard results for normal test results
} else {
errorMessagesToShow.forEach(function (item) {
var li = document.createElement("li");
var text = document.createTextNode(item);
$(li).append(text);
ul.append(li);
})
}
// one or more errors?
if (errorMessagesToShow.length > 1) {
ul.setAttribute('class', 'error_messages_list');
} else {
ul.setAttribute('class', 'single_error_message');
}
targetNode.append(ul);
}
//card.find('.row .col-sm-9').eq(4).find('a').attr('href', '#output-' + index);
},
createEventHandler: function (eventType, data) {
return function (event) {
CodeOceanEditor.publishCodeOceanEvent({
category: eventType,
data: data,
exercise_id: $('#editor').data('exercise-id'),
file_id: CodeOceanEditor.active_file.id,
});
event.stopPropagation();
};
},
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');
$('#start-over-active-file').prop('disabled', this.active_frame.data('role') === 'user_defined_file' || this.active_frame.data('read-only') !== undefined);
$('#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) {
$('#output>pre').each($.proxy(function(index, element) {
element = $(element)
const text = _.escape(element.text());
element.on("click", "a", this.jumpToSourceLine.bind(this));
let matches;
let augmented_text = text;
while (matches = this.tracepositions_regex.exec(text)) {
const frame = $('div.frame[data-filename="' + matches[1] + '"]')
if (frame.length > 0) {
augmented_text = augmented_text.replace(new RegExp(matches[0], 'g'), "<a href='#' data-file='" + matches[1] + "' data-line='" + matches[2] + "'>" + matches[0] + "</a>");
}
}
element.html(augmented_text);
}, this));
}
},
resetOutputTab: function () {
this.clearOutput();
$('#flowrHint').fadeOut();
this.clearHints();
this.showOutputBar();
},
isActiveFileBinary: function () {
if (this.active_frame.data() === undefined) {
return false;
}
return 'binary' in this.active_frame.data();
},
isActiveFileExecutable: function () {
if (this.active_frame.data() === undefined) {
return false;
}
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, i.far, i.fas').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')
});
Sentry.captureException(JSON.stringify(output));
}
},
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 (error) {
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('websocket-failure'),
showPermanent: true
});
Sentry.captureException(JSON.stringify(error, ["message", "arguments", "type", "name", "data"]));
},
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));
$('#output_sidebar').on('transitionend', this.resizeAceEditors.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();
this.initializeDeadlines();
CodeOceanEditorTips.initializeEventHandlers();
window.addEventListener("turbolinks:before-render", this.unloadAutoSave.bind(this));
window.addEventListener("beforeunload", this.unloadAutoSave.bind(this));
// create autosave when the editor is opened the first time
this.autosave();
}
};