Skip to content
Snippets Groups Projects
vips.js 17.8 KiB
Newer Older
// 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');
        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);
        }
    }
}