// generic Vips JS functions function vips_url(url, param_object) { return STUDIP.URLHelper.getURL(VIPS_BASE_URL + '/' + url, param_object); } function vips_error_dialog(title, content) { STUDIP.Dialog.show(content, { title: title, size: 'fit', wikilink: false, dialogClass: 'studip-confirmation' }); } $(function() { if ($('#exam_timer').length) { var exam_timer = $('#exam_timer'); var user_end_time = parseInt(exam_timer.data('time')) + Math.floor(Date.now() / 1000); var timer_id = setInterval(function() { var remaining_time = user_end_time - Math.floor(Date.now() / 1000); // update timer exam_timer.children('.time').text(Math.round(remaining_time / 60)); if (remaining_time < 180 && !exam_timer.hasClass('alert')) { exam_timer.addClass('alert'); } if (remaining_time < 0) { if (document.jsfrm) { clearInterval(timer_id); document.jsfrm.removeAttribute('data-secure'); document.jsfrm.forced.value = 1; document.jsfrm.submit(); } else { location.reload(); } } }, 1000); exam_timer.draggable(); } if ($('#list').length) { var assignment = $('#list').data('assignment'); $('#list').sortable({ axis: 'y', containment: 'parent', handle: '.vips_drag', helper: function(event, element) { element.children().width(function(index, width) { return width; }); return element; }, tolerance: 'pointer', update: function(event, ui) { $.post( vips_url('sheets/move_exercise', { assignment_id: assignment }), $('#list').sortable('serialize') ); } }); $('#list > tr').keydown(function(event) { if (event.key === 'ArrowUp' && event.target === this) { $(this).prev().before(this); } else if (event.key === 'ArrowDown' && event.target === this) { $(this).next().after(this); } else { return; } $(this).focus(); $('#list').sortable('option').update(); event.preventDefault(); }); } if (typeof CKEDITOR !== 'undefined') { CKEDITOR.on('instanceReady', function(event) { event.editor.on('focus', function(event) { vipsCharacterPickerTarget = $(event.editor.container.$); vipsCharacterPickerTarget.data('ckeditor', event.editor); }); }); } $(document).on('focus', '.ck-content', function(event) { vipsCharacterPickerTarget = $(this); vipsCharacterPickerTarget.data('ckeditor', this.ckeditorInstance); }); $(document).on('focus', '.character_input', function(event) { vipsCharacterPickerTarget = $(this); }); $(document).on('click', '.open_character_picker', function(event) { openCharacterPicker($(this).data('language')); event.preventDefault(); }); $(document).on('click', '.add_ip_range', function(event) { var input = $(this).closest('fieldset').find('input[name=ip_range]'); input.val(input.val() + ' ' + $(this).data('value')); event.preventDefault(); }); $(document).on('input', '.validate_ip_range', function(event) { var ip_ranges = $(this).val().split(/[ ,]+/); var message = ''; for (var ip_range of ip_ranges) { if (ip_range.length > 0 && ip_range.charAt(0) !== '#' && !ip_range.match(/^[\d.]+(\/\d+|-[\d.]+)?$/) && !ip_range.match(/^[\da-fA-F:]+(\/\d+|-[\da-fA-F:]+)?$/)) { message = 'Der IP-Zugriffsbereich ist ungültig.'; } } this.setCustomValidity(message); }); $(document).on('click', '.vips_file_upload', function(event) { $(this).closest('form').find('.file_upload').click(); event.preventDefault(); }); $(document).on('change', '.file_upload.attach', function(event) { var button = $(this).closest('form').find('.vips_file_upload'); if (this.files && this.files.length > 1) { button.text(button.data('label').replace('%d', this.files.length)); button.next('.file_upload_hint').show(); } else if (this.files) { button.text(this.files[0].name); button.next('.file_upload_hint').show(); } }); $(document).on('change', '.file_upload.inline', function(event) { var textarea = $(this).closest('form').find('.download'); var reader = new FileReader(); if (this.files && this.files.length > 0) { reader.onload = function(event) { textarea.val(reader.result); }; reader.onerror = function(event) { vips_error_dialog('Fehler beim Hochladen', reader.error.message); } reader.readAsText(this.files[0]); } event.preventDefault(); }); $(document).on('click', '.vips_file_download', function(event) { var text = $(this).closest('form').find('.download').val(); var link = $(this).closest('form').find('a[download]'); var blob = new Blob([text], {type: 'text/plain; charset=UTF-8'}); link.attr('href', URL.createObjectURL(blob)); link[0].click(); event.preventDefault(); }); $(document).on('click', '.textarea_toggle', function(event) { var toggle = $(this).closest('.size_toggle'); var items = toggle.find('.character_input'); var name = items[0].name; items[0].name = items[1].name; items[1].name = name; var value = items[0].value; items[0].value = items[1].value; items[1].value = value; toggle.toggleClass('size_large').toggleClass('size_small'); event.preventDefault(); }); $(document).on('change', '.tb_layout', function(event) { var toggle = $(this).closest('fieldset').find('.size_toggle'); toggle.find('.small_input').toggleClass('monospace', $(this).val() === 'code'); if ($(this).val() === '' && toggle.hasClass('size_large') || $(this).val() === 'code' && toggle.hasClass('size_large') || $(this).val() === 'markup' && toggle.hasClass('size_small')) { toggle.find('.textarea_toggle').click(); } }); $(document).on('click', '.choice_list .add_dynamic_row', function(event) { $(this).closest('fieldset').find('.choice_select').each(function() { var template = $(this).children('.template').last(); var clone = template.clone(true).removeClass('template'); var index = template.data('index'); template.data('index', index + 1); clone.insertBefore(template); clone.find('input[data-value]').each(function(i) { $(this).attr('value', index); $(this).removeAttr('data-value'); }); }); }); $(document).on('change', '.choice_list input', function(event) { var index = $(this).closest('.dynamic_row').data('index'); var items = $(this).closest('fieldset').find('.choice_select'); items.children().filter(function(i) { return $(this).data('index') === index; }).children('span').text($(this).val()); }); $(document).on('click', '.choice_list .delete_dynamic_row', function(event) { var index = $(this).closest('.dynamic_row').data('index'); var items = $(this).closest('fieldset').find('.choice_select'); items.children().filter(function(i) { return $(this).data('index') === index; }).remove(); }); $('.dynamic_list').each(function() { $(this).children('.dynamic_row').each(function(i) { $(this).data('index', i); }); }); $(document).on('click', '.add_dynamic_row', function(event) { var container = $(this).closest('.dynamic_list'); var template = container.children('.template').last(); var clone = template.clone(true).removeClass('template'); var index = template.data('index'); template.data('index', index + 1); clone.insertBefore(template); clone.find('input[data-name], select[data-name], textarea[data-name]').each(function(i) { if ($(this).data('name').indexOf(':') === 0) { $(this).data('name', $(this).data('name').substr(1) + '[' + index + ']'); } else { $(this).attr('name', $(this).data('name') + '[' + index + ']'); $(this).removeAttr('data-name'); } }); clone.find('input[data-value], select[data-value], textarea[data-value]').each(function(i) { if ($(this).data('value').indexOf(':') === 0) { $(this).data('value', $(this).data('value').substr(1)); } else { $(this).attr('value', index); $(this).removeAttr('data-value'); } }); clone.find('.add_dynamic_row:visible').click(); event.preventDefault(); }); $(document).on('click', '.delete_dynamic_row', function(event) { $(this).closest('.dynamic_row').remove(); event.preventDefault(); }); $(document).on('click', '.solution-toggle', function(event) { if ($(this).closest('.solution').length) { $(this).closest('.solution').toggleClass('solution-closed'); } else { if ($('.arrow_all').first().css('display') != 'none') { $('.arrow_all').toggle(); $('.solution').removeClass('solution-closed'); } else { $('.arrow_all').toggle(); $('.solution').addClass('solution-closed'); } } $(document.body).trigger('sticky_kit:recalc'); event.preventDefault(); }); $(document).on('click', '.edit_solution', function(event) { var tabs = $(this).closest('.vips_tabs'); tabs.removeClass('edit-hidden'); tabs.find('.wysiwyg').attr('name', 'commented_solution'); tabs.tabs('option', 'active', 0); event.preventDefault(); }); // add select2 to modal dialog including selects with optgroups $(document).on('dialog-open', function(event, parameters) { $('.vips_nested_select').select2({ minimumResultsForSearch: 12, dropdownParent: $(parameters.dialog).closest('.ui-dialog, body'), matcher: function(params, data) { const originalMatcher = $.fn.select2.defaults.defaults.matcher; const result = originalMatcher(params, data); if (result && result.children && data.children && data.children.length) { if (data.children.length !== result.children.length && data.text.toLowerCase().includes(params.term.toLowerCase())) { result.children = data.children; } } return result; } }); }); $('.assignment_type').change(function(event) { $('#assignment').attr('class', $(this).val()); if ($(this).val() === 'exam') { $('#exam_length input').attr('disabled', null); } else { $('#exam_length input').attr('disabled', 'disabled'); } if ($(this).val() === 'selftest') { $('#end_date input').attr('required', null); $('#end_date span').removeClass('required'); } else { $('#end_date input').attr('required', 'required'); $('#end_date span').addClass('required'); } }); $('.rh_select_type').change(function(event) { $(this).parent().next('table').toggleClass('rh_single'); }); vips_post_render(document); }); function vips_post_render(element) { $(element).find('.rh_list').sortable({ item: '> .rh_item', tolerance: 'pointer', connectWith: '.rh_list', update: function(event, ui) { if (ui.sender) { ui.item.find('input').val($(this).data('group')); } }, over: function(event, ui) { $(this).addClass('hover'); }, out: function(event, ui) { $(this).removeClass('hover'); }, receive: function(event, ui) { var sortable = $(this).not('.multiple'); var container = sortable.closest('.rh_table').find('.answer_container'); // default answer container can have more items if (sortable.children().length > 1 && !sortable.is(container)) { sortable.find('.rh_item').each(function(i) { if (!ui.item.is(this)) { $(this).find('input').val(-1); $(this).detach().appendTo(container) .css('opacity', 0).animate({opacity: 1}); } }); } }, }); $(element).find('.rh_item').keydown(function(event) { var sortable = $(this).parent(); var container = sortable.closest('.rh_table').find('.answer_container'); var target = $(); if (sortable.is('.mc_list')) { if (event.key === 'ArrowUp') { $(this).prev().before(this); $(this).focus(); event.preventDefault(); } else if (event.key === 'ArrowDown') { $(this).next().after(this); $(this).focus(); event.preventDefault(); } } else if (sortable.is(container)) { if (event.key === 'ArrowLeft') { target = sortable.parent().find('.rh_list').first(); } } else { if (event.key === 'ArrowRight') { target = container; } else if (event.key === 'ArrowUp') { target = sortable.parent().prev().find('.rh_list').first(); } else if (event.key === 'ArrowDown') { target = sortable.parent().next().find('.rh_list').first(); } } if (target.length) { $(this).find('input').val(target.data('group')); $(this).appendTo(target).focus(); event.preventDefault(); } }); $(element).find('.cloze_item').draggable({ revert: 'invalid' }); $(element).find('.cloze_drop').droppable({ accept: '.cloze_item', tolerance: 'pointer', classes: { 'ui-droppable-hover': 'hover' }, drop: function(event, ui) { var container = $(this).closest('fieldset').find('.cloze_items'); if (!$(this).is(container)) { $(this).find('.cloze_item').detach().appendTo(container) .css('opacity', 0).animate({opacity: 1}) } ui.draggable.closest('.cloze_drop').find('input').val(''); ui.draggable.detach().css({top: 0, left: 0}).appendTo(this); $(this).find('input').val(ui.draggable.text()); } }); $(element).find('.vips_tabs').each(function() { $(this).tabs({ active: $(this).hasClass('edit-hidden') ? 1 : 0 }); }) } // JS character picker var vipsCharacterPicker = null; var vipsCharacterPickerTarget = null; function openCharacterPicker(language) { if (vipsCharacterPicker == null) { vipsCharacterPicker = $('<div id="character_picker"></div>').appendTo('body'); // fill character picker via ajax and make it a jQuery dialog vipsCharacterPicker.load(vips_url('sheets/character_picker'), function() { if (language) { vipsCharacterPicker.find('select').val(language).trigger('change'); } }).dialog({ autoOpen: false, title: 'Zeichenwähler', width: 600 }); } if (!vipsCharacterPickerTarget || !vipsCharacterPickerTarget.is(':visible')) { vipsCharacterPickerTarget = $('.character_input').filter(':visible').first(); } vipsCharacterPickerTarget.focus(); vipsCharacterPicker.dialog('open'); } // insert the chosen character into the target function insertAtCaret(button) { var target = vipsCharacterPickerTarget[0]; var editor = vipsCharacterPickerTarget.data('ckeditor'); var character = $.trim($(button).text()).replace(/\u25cc/g, ''); if (target) { if (editor) { // ckeditor if (editor.model) { editor.model.change(function(writer) { var selection = editor.model.document.selection; var position = selection.getFirstPosition(); var attributes = selection.getAttributes(); writer.remove(selection.getFirstRange()); writer.insertText(character, attributes, position); editor.focus(); }); } else { editor.insertText(character); } } else { var caret_start = Math.min(target.selectionStart, target.selectionEnd); var caret_end = Math.max(target.selectionStart, target.selectionEnd); var content_before = target.value.substring(0, caret_start); var content_after = target.value.substring(caret_end); var new_content = content_before + character + content_after; var new_caret_pos = caret_start + character.length; target.value = new_content; target.focus(); target.setSelectionRange(new_caret_pos, new_caret_pos); } } }