aboutsummaryrefslogtreecommitdiff
path: root/resources/assets/javascripts/bootstrap
diff options
context:
space:
mode:
authorPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
committerPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
commit4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch)
tree5c07151ae61276d334e88f6309c30d439a85c12e /resources/assets/javascripts/bootstrap
parentda0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff)
parent97a188592c679890a25c37ab78463add76a52ff7 (diff)
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'resources/assets/javascripts/bootstrap')
-rw-r--r--resources/assets/javascripts/bootstrap/application.js1
-rw-r--r--resources/assets/javascripts/bootstrap/article.js1
-rw-r--r--resources/assets/javascripts/bootstrap/clipboard.js13
-rw-r--r--resources/assets/javascripts/bootstrap/consultations.js4
-rw-r--r--resources/assets/javascripts/bootstrap/contentbox.js1
-rw-r--r--resources/assets/javascripts/bootstrap/copyable_links.js24
-rw-r--r--resources/assets/javascripts/bootstrap/forms.js10
-rw-r--r--resources/assets/javascripts/bootstrap/global_search.js42
-rw-r--r--resources/assets/javascripts/bootstrap/resources.js67
-rw-r--r--resources/assets/javascripts/bootstrap/responsive-navigation.js2
-rw-r--r--resources/assets/javascripts/bootstrap/search.js2
-rw-r--r--resources/assets/javascripts/bootstrap/studip_helper_attributes.js9
-rw-r--r--resources/assets/javascripts/bootstrap/system-notifications.js11
-rw-r--r--resources/assets/javascripts/bootstrap/tooltip.js67
-rw-r--r--resources/assets/javascripts/bootstrap/treeview.js3
-rw-r--r--resources/assets/javascripts/bootstrap/vue.js12
-rw-r--r--resources/assets/javascripts/bootstrap/wysiwyg.js7
17 files changed, 125 insertions, 151 deletions
diff --git a/resources/assets/javascripts/bootstrap/application.js b/resources/assets/javascripts/bootstrap/application.js
index a9f53df..4ad248f 100644
--- a/resources/assets/javascripts/bootstrap/application.js
+++ b/resources/assets/javascripts/bootstrap/application.js
@@ -353,6 +353,7 @@ jQuery(document).on('click', 'a[data-behaviour~="ajax-toggle"]', function (event
(function ($) {
$(document).on('click', 'form[name=course-details] fieldset legend', function () {
$('#open_variable').attr('value', $(this).parent('fieldset').data('open'));
+ $(this).parent('fieldset').attr('aria-expanded', $(this).parent('fieldset').attr('aria-expanded') == 'true' ? 'false' : 'true');
});
}(jQuery));
diff --git a/resources/assets/javascripts/bootstrap/article.js b/resources/assets/javascripts/bootstrap/article.js
index fbfc131..04316f5 100644
--- a/resources/assets/javascripts/bootstrap/article.js
+++ b/resources/assets/javascripts/bootstrap/article.js
@@ -13,6 +13,7 @@
// Open the contentbox
article.toggleClass('open').removeClass('new');
+ article.attr('aria-expanded', article.attr('aria-expanded') === 'true' ? 'false' : 'true');
});
// Open closed article contents when location hash matches
diff --git a/resources/assets/javascripts/bootstrap/clipboard.js b/resources/assets/javascripts/bootstrap/clipboard.js
index e525b35..a64a605 100644
--- a/resources/assets/javascripts/bootstrap/clipboard.js
+++ b/resources/assets/javascripts/bootstrap/clipboard.js
@@ -25,7 +25,9 @@ STUDIP.domReady(function () {
jQuery(document).on('click', '.clipboard-remove-button', function (event) {
event.preventDefault();
- STUDIP.Dialog.confirm($(this).data('confirm-message'), function() {
+
+ const message = $(this).data('confirm-message');
+ STUDIP.Dialog.confirm(message).done(() => {
STUDIP.Clipboard.handleRemoveClick(event.target);
});
});
@@ -62,10 +64,11 @@ STUDIP.domReady(function () {
});
});
- jQuery(document).on('submit', '.clipboard-widget .new-clipboard-form', function (event) {
- event.preventDefault();
- STUDIP.Clipboard.handleAddForm(event);
- });
+ jQuery(document).on(
+ 'submit',
+ '.clipboard-widget .new-clipboard-form',
+ STUDIP.Clipboard.handleAddForm
+ );
jQuery(document).on('click', '.clipboard-add-item-button', function (event) {
event.preventDefault();
diff --git a/resources/assets/javascripts/bootstrap/consultations.js b/resources/assets/javascripts/bootstrap/consultations.js
index ef79d9c..51ffa85 100644
--- a/resources/assets/javascripts/bootstrap/consultations.js
+++ b/resources/assets/javascripts/bootstrap/consultations.js
@@ -10,9 +10,9 @@ $(document).on('click', '.consultation-delete-check:not(.ignore)', event => {
}
let requests = ids.map(id => {
- return STUDIP.jsonapi.GET(`consultation-slots/${id}/bookings`).then(result => result.data.length);
+ return STUDIP.jsonapi.withPromises().get(`consultation-slots/${id}/bookings`).then(response => response.data.length);
});
- $.when(...requests).done((...results) => {
+ Promise.all(requests).then((...results) => {
if (results.some(result => result > 0)) {
$(event.target).addClass('ignore').click().removeClass('ignore');
} else {
diff --git a/resources/assets/javascripts/bootstrap/contentbox.js b/resources/assets/javascripts/bootstrap/contentbox.js
index 42c5df1..3f05331 100644
--- a/resources/assets/javascripts/bootstrap/contentbox.js
+++ b/resources/assets/javascripts/bootstrap/contentbox.js
@@ -13,5 +13,6 @@ $(document).on('click', 'section.contentbox article header h1 a', function(e) {
// Open the contentbox
article.toggleClass('open').removeClass('new');
+ article.attr('aria-expanded', article.attr('aria-expanded') === 'true' ? 'false' : 'true');
}
});
diff --git a/resources/assets/javascripts/bootstrap/copyable_links.js b/resources/assets/javascripts/bootstrap/copyable_links.js
index 521eae4..b4f6fc6 100644
--- a/resources/assets/javascripts/bootstrap/copyable_links.js
+++ b/resources/assets/javascripts/bootstrap/copyable_links.js
@@ -16,24 +16,8 @@ $(document).on('click', 'a.copyable-link', function (event) {
document.execCommand('Copy');
dummy.remove();
- // Show visual hint using a deferred (this way we don't need to
- // duplicate the functionality in the done() handler)
- (new Promise((resolve, reject) => {
- let confirmation = $('<div class="copyable-link-confirmation copyable-link-success">');
- confirmation.text($gettext('Link wurde kopiert'));
- confirmation.insertBefore('#content');
-
- // Resolve deferred when animation has ended or after 2 seconds as a
- // fail safe
- let timeout = setTimeout(() => {
- $(this).parent().off('animationend');
- resolve(confirmation);
- }, 1500);
- $(this).parent().one('animationend', () => {
- clearTimeout(timeout);
- resolve(confirmation);
- });
- })).then((confirmation, parent) => {
- confirmation.remove();
- });
+ STUDIP.eventBus.emit(
+ 'push-system-notification',
+ { type: 'success', message: $gettext('Link wurde kopiert') }
+ );
});
diff --git a/resources/assets/javascripts/bootstrap/forms.js b/resources/assets/javascripts/bootstrap/forms.js
index c34a0c1..7643612 100644
--- a/resources/assets/javascripts/bootstrap/forms.js
+++ b/resources/assets/javascripts/bootstrap/forms.js
@@ -1,4 +1,5 @@
import { $gettext, $gettextInterpolate } from '../lib/gettext';
+import Report from '../lib/report.js';
// Allow fieldsets to collapse
$(document).on(
@@ -291,9 +292,12 @@ STUDIP.ready(function () {
url: v.STUDIPFORM_AUTOSAVEURL,
data: params,
type: 'post',
- success() {
- if (v.STUDIPFORM_REDIRECTURL) {
- window.location.href = v.STUDIPFORM_REDIRECTURL
+ success(output) {
+ if (output === 'STUDIPFORM_STORE_SUCCESS' && v.STUDIPFORM_REDIRECTURL) {
+ //The form has been stored successfully:
+ window.location.href = v.STUDIPFORM_REDIRECTURL;
+ } else if (output !== 'STUDIPFORM_STORE_SUCCESS') {
+ Report.error($gettext('Es ist ein Fehler aufgetreten'), output);
}
}
});
diff --git a/resources/assets/javascripts/bootstrap/global_search.js b/resources/assets/javascripts/bootstrap/global_search.js
index 4d0738e..0e179b7 100644
--- a/resources/assets/javascripts/bootstrap/global_search.js
+++ b/resources/assets/javascripts/bootstrap/global_search.js
@@ -27,15 +27,47 @@ STUDIP.domReady(() => {
// Enlarge search input on focus and show hints.
$('#globalsearch-input').on('focus', function() {
STUDIP.GlobalSearch.toggleSearchBar(true, false);
- });
-
- // Start search on Enter
- $('#globalsearch-input').on('keypress', function(e) {
- if (e.which === 13) {
+ }).on('keypress', (e) => {
+ // Start search on Enter
+ if (e.key === 'Enter') {
STUDIP.GlobalSearch.doSearch();
return false;
}
});
+ $('#globalsearch-searchbar').on('keydown', function(e) {
+ if (!['ArrowDown', 'ArrowUp'].includes(e.key)) {
+ return;
+ }
+
+ e.preventDefault();
+
+ // Get all possible items
+ const items = $('#globalsearch-list [role=listitem]:visible');
+
+ // Find focussed element
+ const focussed = items.filter(':focus');
+
+ // Get index of focussed element in all items
+ let index = focussed.length > 0 ? items.index(focussed[0]) : null;
+
+ // Move focussed element up or down in items
+ if (e.key === 'ArrowDown') {
+ index = (index ?? -1) + 1;
+ } else {
+ index = (index ?? items.length) - 1;
+ }
+
+ // Clamp index to sane boundaries
+ if (index < 0) {
+ index = 0;
+ } else if (index > items.length - 1) {
+ index = items.length - 1;
+ }
+
+ // Focus new element by index
+ items.get(index).focus();
+ });
+
// Close search on click on page.
$('#navigation-level-1, #current-page-structure, #main-footer').on('click', function() {
diff --git a/resources/assets/javascripts/bootstrap/resources.js b/resources/assets/javascripts/bootstrap/resources.js
index 8c89b7f..7eb6a68 100644
--- a/resources/assets/javascripts/bootstrap/resources.js
+++ b/resources/assets/javascripts/bootstrap/resources.js
@@ -416,7 +416,7 @@ STUDIP.ready(function () {
$("#BookingEndDateInput").prop('defaultValue', $(this).val());
$("#BookingEndDateInput").val($(this).val()).trigger('change');
}
- updateRepeatEndSemesterByTimestamp(Math.floor(d / 1000));
+ updateRepeatEndSemesterByTimestamp(d);
} else if ($(this).attr('id') == 'BookingEndDateInput') {
$("#end_date-weekdays span").addClass('invisible');
$("#end_date-weekdays #" + day_numer).removeClass('invisible');
@@ -545,38 +545,41 @@ STUDIP.ready(function () {
}
);
- function updateRepeatEndSemesterByTimestamp(timestamp, api_url = 'api.php/semesters') {
- var semester = null;
- jQuery.ajax(
- STUDIP.URLHelper.getURL(api_url),
- {
- method: 'get',
- dataType: 'json',
- success: function (data) {
- if (data) {
- Object.values(data.collection).forEach(item => {
- if (timestamp >= item.begin && timestamp < item.end) {
- semester = item;
- }
- });
- if (semester) {
- $("#semester_course_name").text(semester.title);
- $(".semester-time-option").prop('disabled', false);
- } else {
- if (data.pagination && data.pagination.links.next != api_url) {
- semester = updateRepeatEndSemesterByTimestamp(timestamp, data.pagination.links.next);
- } else {
- $("#semester_course_name").text('außerhalb definierter Zeiten');
- $(".semester-time-option").prop('checked', false);
- $(".semester-time-option").prop('disabled', true);
- $(".manual-time-option").prop('checked', true);
- $(".manual-time-option").trigger('change');
- }
- }
- }
- }
+ function updateRepeatEndSemesterByTimestamp(timestamp) {
+ (new Promise((resolve, reject) => {
+ const cache = STUDIP.Cache.getInstance('jsonapi');
+ if (cache.has('semesters')) {
+ resolve(cache.get('semesters'));
+ } else {
+ STUDIP.jsonapi.GET('semesters', { data: { page: { limit: 100000 }}})
+ .done(({data}) => {
+ cache.set('semesters', data);
+ resolve(data)
+ })
+ .fail(() => {
+ reject(new Error('Could not load semesters'));
+ });
+ }
+ })).then(semesters => {
+ const semester = semesters.find(({attributes}) => {
+ return new Date(attributes.start) <= timestamp
+ && timestamp <= new Date(attributes.end);
+ });
+
+ if (semester) {
+ $('#semester_course_name').text(semester.attributes.title);
+ $('.semester-time-option').prop('disabled', false);
+ } else {
+ $('#semester_course_name').text('außerhalb definierter Zeiten');
+ $('.semester-time-option').prop({
+ checked: false,
+ disabled: true
+ });
+ $('.manual-time-option')
+ .prop('checked', true)
+ .trigger('change');
}
- );
+ });
}
function updateViewURL(defaultView) {
diff --git a/resources/assets/javascripts/bootstrap/responsive-navigation.js b/resources/assets/javascripts/bootstrap/responsive-navigation.js
index aa81107..ad39d2b 100644
--- a/resources/assets/javascripts/bootstrap/responsive-navigation.js
+++ b/resources/assets/javascripts/bootstrap/responsive-navigation.js
@@ -1,6 +1,6 @@
import ResponsiveNavigation from '../../../vue/components/responsive/ResponsiveNavigation.vue';
-STUDIP.ready(() => {
+STUDIP.domReady(() => {
STUDIP.Vue.load().then(({ createApp }) => {
createApp({
el: '#responsive-menu',
diff --git a/resources/assets/javascripts/bootstrap/search.js b/resources/assets/javascripts/bootstrap/search.js
index 6a0559b..7d4fb76 100644
--- a/resources/assets/javascripts/bootstrap/search.js
+++ b/resources/assets/javascripts/bootstrap/search.js
@@ -2,8 +2,6 @@ STUDIP.domReady(() => {
var cache = STUDIP.Search.getCache();
// initially hide all filters except for the semester filter
$('#reset-search').hide();
- STUDIP.Search.hideAllFilters();
- $('div#semester_filter').show();
STUDIP.Search.setActiveCategory('show_all_categories');
STUDIP.Search.showActiveFilters(STUDIP.Search.getFilter());
diff --git a/resources/assets/javascripts/bootstrap/studip_helper_attributes.js b/resources/assets/javascripts/bootstrap/studip_helper_attributes.js
index c106de3..aea1823 100644
--- a/resources/assets/javascripts/bootstrap/studip_helper_attributes.js
+++ b/resources/assets/javascripts/bootstrap/studip_helper_attributes.js
@@ -276,6 +276,15 @@ $(document).on('click keydown', '[data-toggles]', function (event) {
$(target).toggle();
}
+ const controls = $(event.currentTarget).attr('aria-controls');
+ if (controls) {
+ // Find elements which control the expanded status of the same element.
+ const elements = $('[aria-controls="' + controls + '"]');
+ const expanded = $(event.currentTarget).attr('aria-expanded') === 'true';
+ // Set the aria-expanded status accordingly.
+ elements.attr('aria-expanded', !expanded);
+ }
+
event.preventDefault();
}
});
diff --git a/resources/assets/javascripts/bootstrap/system-notifications.js b/resources/assets/javascripts/bootstrap/system-notifications.js
new file mode 100644
index 0000000..7a85fcd
--- /dev/null
+++ b/resources/assets/javascripts/bootstrap/system-notifications.js
@@ -0,0 +1,11 @@
+import SystemNotificationManager from '../../../vue/components/SystemNotificationManager.vue';
+
+STUDIP.domReady(() => {
+ document.getElementById('system-notifications')?.classList.add('vueified');
+ STUDIP.Vue.load().then(({ createApp }) => {
+ createApp({
+ el: '#system-notifications',
+ components: { SystemNotificationManager }
+ });
+ });
+});
diff --git a/resources/assets/javascripts/bootstrap/tooltip.js b/resources/assets/javascripts/bootstrap/tooltip.js
deleted file mode 100644
index c84042b..0000000
--- a/resources/assets/javascripts/bootstrap/tooltip.js
+++ /dev/null
@@ -1,67 +0,0 @@
-// Attach global hover handler for tooltips.
-// Applies to all elements having a "data-tooltip" attribute.
-// Tooltip may be provided in the data-attribute itself or by
-// defining a title attribute. The latter is prefered due to
-// the obvious accessibility issues.
-
-var timeout = null;
-
-STUDIP.Tooltip.threshold = 6;
-
-$(document).on('mouseenter mouseleave focusin focusout', '[data-tooltip],.tooltip:has(.tooltip-content)', function(event) {
- let data = $(this).data();
-
- const visible = event.type === 'mouseenter' || event.type === 'focusin';
- const offset = $(this).offset();
- const x = offset.left + $(this).outerWidth(true) / 2;
- const y = offset.top;
- const delay = data.tooltipDelay ?? 300;
-
- let content;
- let tooltip;
-
- if (!data.tooltipObject) {
- // If tooltip has not yet been created (first hover), obtain it's
- // contents and create the actual tooltip object.
- if (!data.tooltip || !$.isPlainObject(data.tooltip)) {
- content = $('<div/>').text(data.tooltip || $(this).attr('title')).html();
- } else if (data.tooltip.html !== undefined) {
- content = data.tooltip.html;
- } else if (data.tooltip.text !== undefined) {
- content = data.tooltip.text;
- } else {
- throw "Invalid content for tooltip via data";
- }
- if (!content) {
- content = $(this).find('.tooltip-content').remove().html();
- }
- $(this).attr('title', null);
- $(this).attr('data-tooltip', content);
-
- tooltip = new STUDIP.Tooltip(x, y, content);
-
- data.tooltipObject = tooltip;
- $(this).attr('aria-describedby', tooltip.id);
-
- $(this).on('remove', function() {
- tooltip.remove();
- });
- } else if (visible) {
- // If tooltip has already been created, update it's position.
- // This is neccessary if the surrounding content is scrollable AND has
- // been scrolled. Otherwise the tooltip would appear at it's previous
- // and now wrong location.
- data.tooltipObject.position(x, y);
- }
-
- if (visible) {
- $('.studip-tooltip').not(data.tooltipObject).hide();
- data.tooltipObject.show();
- } else {
- timeout = setTimeout(() => data.tooltipObject.hide(), delay);
- }
-}).on('mouseenter focusin', '.studip-tooltip', () => {
- clearTimeout(timeout);
-}).on('mouseleave focusout', '.studip-tooltip', function() {
- $(this).hide();
-});
diff --git a/resources/assets/javascripts/bootstrap/treeview.js b/resources/assets/javascripts/bootstrap/treeview.js
index 998a70e..d132775 100644
--- a/resources/assets/javascripts/bootstrap/treeview.js
+++ b/resources/assets/javascripts/bootstrap/treeview.js
@@ -1,7 +1,8 @@
import StudipTree from '../../../vue/components/tree/StudipTree.vue'
STUDIP.ready(() => {
- document.querySelectorAll('[data-studip-tree]').forEach(element => {
+ document.querySelectorAll('[data-studip-tree]:not(.vueified)').forEach(element => {
+ element.classList.add('vueified');
STUDIP.Vue.load().then(({ createApp }) => {
createApp({
el: element,
diff --git a/resources/assets/javascripts/bootstrap/vue.js b/resources/assets/javascripts/bootstrap/vue.js
index b8c938d..c6816a2 100644
--- a/resources/assets/javascripts/bootstrap/vue.js
+++ b/resources/assets/javascripts/bootstrap/vue.js
@@ -28,27 +28,17 @@ STUDIP.ready(() => {
});
STUDIP.Vue.load().then(async ({createApp, store}) => {
- let vm;
if (config.store) {
const storeConfig = await import(`../../../vue/store/${config.store}.js`);
- console.log('store', storeConfig.default);
store.registerModule(config.id, storeConfig.default, {root: true});
Object.keys(data).forEach(command => {
store.commit(`${config.id}/${command}`, data[command]);
});
- vm = createApp({components});
- } else {
- vm = createApp({data, components});
}
- // import myCoursesStore from '../stores/MyCoursesStore.js';
- //
- // myCoursesStore.namespaced = true;
- //
- // store.registerModule('my-courses', myCoursesStore);
- vm.$mount(this);
+ createApp({components, data}).$mount(this);
});
$(this).attr('data-vue-app-created', '');
diff --git a/resources/assets/javascripts/bootstrap/wysiwyg.js b/resources/assets/javascripts/bootstrap/wysiwyg.js
index fb158bb..9e18cf8 100644
--- a/resources/assets/javascripts/bootstrap/wysiwyg.js
+++ b/resources/assets/javascripts/bootstrap/wysiwyg.js
@@ -6,8 +6,11 @@ STUDIP.domReady(() => {
$(document).on('focus blur', '.studip-dialog .ck-editor__editable_inline', function(event) {
let height = this.clientHeight;
let editor = this.ckeditorInstance;
- editor.editing.view.change(writer => {
- writer.setStyle('height', height + 'px', editor.editing.view.document.getRoot());
+ // this is needed on Chrome, see https://gitlab.studip.de/studip/studip/-/issues/3510
+ setTimeout(() => {
+ editor.editing.view.change(writer => {
+ writer.setStyle('height', height + 'px', editor.editing.view.document.getRoot());
+ });
});
});