Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { $gettext } from '../lib/gettext.js';
/**
* This file provides a set of global handlers.
*/
var proxy_elements_selector = ':checkbox[data-proxyfor], :radio[data-proxyfor]';
var proxied_elements_selector = ':checkbox[data-proxiedby], :radio[data-proxiedby]';
function connectProxyAndProxied(event) {
$(proxy_elements_selector).each(function () {
const proxy = $(this);
const proxyId = proxy.uniqueId().attr('id');
const proxied = proxy.data('proxyfor');
// The following seems like a hack but works perfectly fine.
$(proxied).each(function () {
const proxiedBy = ($(this).attr('data-proxiedby') || '').split(',').filter(a => a.length > 0);
proxiedBy.push(`#${proxyId}`);
$(this)
.attr('data-proxiedby', proxiedBy.join(','))
.data('proxiedby', `#${proxyId}`);
});
}).trigger('update.proxy');
}
// Use a checkbox as a proxy for a set of other checkboxes. Define
// proxied elements by a css selector in attribute "data-proxyfor".
$(document).on('change', proxy_elements_selector, function (event, force) {
// Detect if event was triggered natively (triggered events have no
// originalEvent)
if (event.originalEvent !== undefined || !!force) {
const proxied = $(this).data('proxyfor');
$(proxied)
.filter(':not(:disabled)')
.prop('checked', this.checked)
.prop('indeterminate', false)
.each((index, element) => {
const proxiedBy = $(element).attr('data-proxiedby');
$(proxiedBy)
.filter((idx, item) => item !== this)
.trigger('update.proxy');
})
.filter('[data-proxyfor]')
.trigger('change', [true]);
}
}).on('update.proxy', proxy_elements_selector, function () {
const proxied = $(this).data('proxyfor');
const $proxied = $(proxied).filter(':not(:disabled)');
const $checked = $proxied.filter(':checked');
const $indeterminate = $proxied.filter(function () {
return $(this).prop('indeterminate');
});
$(this).prop('checked', $proxied.length > 0 && $proxied.length === $checked.length);
$(this).prop(
'indeterminate',
($checked.length > 0 && $checked.length < $proxied.length) || $indeterminate.length > 0
);
$(this).trigger('change');
}).on('change', proxied_elements_selector, function () {
//In case of radio buttons in a group that are deselected,
//we must trigger the update.proxy event for each radio
//button in the group, if the proxy is another element
//than the proxy for "this" element.
if ($(this).is(':radio')) {
var proxy = $(this).data('proxiedby');
var name = $(this).attr('name');
var radio_button_group = $(`:radio[name="${name}"]`);
$(radio_button_group).each(function () {
var button_proxy = $(this).data('proxiedby');
if (button_proxy != proxy) {
$(button_proxy).trigger('update.proxy');
}
});
} else {
const proxy = $(this).attr('data-proxiedby');
$(proxy).trigger('update.proxy');
}
});
STUDIP.ready(connectProxyAndProxied);
$(document).on('refresh-handlers', connectProxyAndProxied);
// Use a checkbox or radiobox as a toggle switch for the disabled attribute of
// another set of elements. Define set of elements to disable/enable if item is
// neither :checked nor :indeterminate by a css selector in attribute
// "data-activates" / "deactivates".
$(document).on('change', '[data-activates],[data-deactivates]', function() {
if (!$(this).is(':checkbox,:radio')) {
return;
}
['activates', 'deactivates'].forEach((type) => {
var selector = $(this).data(type);
if (selector === undefined || $(this).prop('disabled')) {
return;
}
var state = $(this).prop('checked') || $(this).prop('indeterminate') || false;
$(selector).each(function() {
Jan-Hendrik Willms
committed
var condition = $(this).data(`${type}Condition`),
toggle = state && (!condition || $(condition).length > 0);
$(this)
.attr('disabled', type === 'activates' ? !toggle : toggle)
.trigger('update.proxy');
});
});
});
STUDIP.ready((event) => {
$('[data-activates],[data-deactivates]', event.target).trigger('change');
});
// Use a select as a toggle switch for the disabled attribute of another
// element. Define element to disable if select has a value different from
// an empty string by a css selector in attribute "data-activates".
$(document).on('change update.proxy', 'select[data-activates]', function() {
Jan-Hendrik Willms
committed
var activates = $(this).data('activates'),
disabled = $(this).is(':disabled') || $(this).val().length === 0;
$(activates).attr('disabled', disabled);
});
STUDIP.ready((event) => {
$('select[data-activates]', event.target).trigger('change');
});
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//
$(document).on('change', '[data-hides],[data-shows]', function () {
if (!$(this).is(':checkbox,:radio')) {
return;
}
['hides', 'shows'].forEach((type) => {
var selector = $(this).data(type);
if (selector === undefined || $(this).prop('disabled')) {
return;
}
var state = $(this).prop('checked') || $(this).prop('indeterminate') || false;
$(selector).each(function() {
var condition = $(this).data(`${type}Condition`),
toggle = state && (!condition || $(condition).length > 0);
$(this)
.toggle(type === 'shows' ? toggle : !toggle)
.trigger('update.proxy');
});
});
});
STUDIP.ready(event => {
$('[data-hides],[data-shows]', event.target).trigger('change');
});
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Enable the user to set the checked state on a subset of related
// checkboxes by clicking the first checkbox of the subset and then
// clicking the last checkbox of the subset while holding down the shift
// key, thus toggling all the checkboxes in between.
// This only works if the first and last checkbox of the subset are set
// to the same state.
var last_element = null;
$(document).on('click', '[data-shiftcheck] :checkbox', function(event) {
if (!event.originalEvent || last_element === event.target) {
return;
}
if (last_element !== null && event.shiftKey) {
var $this = $(event.target),
$form = $this.closest('form'),
name = $this.attr('name'),
state = $this.prop('checked'),
$last = $(last_element),
children,
idx0,
idx1,
tmp;
if ($form.is($last.closest('form')) && name === $last.attr('name') && state === $last.prop('checked')) {
children = $form.find(':checkbox[name="' + name + '"]:not(:disabled)');
idx0 = children.index(event.target);
idx1 = children.index(last_element);
if (idx0 > idx1) {
tmp = idx0;
idx0 = idx1;
idx1 = tmp;
}
children.slice(idx0, idx1).prop('checked', state);
}
}
last_element = event.target;
});
// Lets the user confirm a specific action (submit or click event).
function confirmation_handler(event) {
if (!event.isDefaultPrevented()) {
event.stopPropagation();
event.preventDefault();
var element = $(event.currentTarget).closest('[data-confirm]'),
question =
element.data().confirm ||
element.attr('title') ||
element.find('[title]:first').attr('title') ||
$gettext('Wollen Sie die Aktion wirklich ausführen?');
STUDIP.Dialog.confirm(question).done(function() {
var content = element.data().confirm;
// We need to trigger the native event because for
// some reason, jQuery's .trigger() won't always
// work. Thus the data-confirm attribute will be removed
// so that the original event can be executed
element
.removeAttr('data-confirm')
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// Reapply the data-confirm attribute
window.setTimeout(function() {
element.attr('data-confirm', content);
}, 0);
});
}
}
$(document).on(
'click',
'a[data-confirm],input[data-confirm],button[data-confirm],img[data-confirm]',
confirmation_handler
);
$(document).on('submit', 'form[data-confirm]', confirmation_handler);
// Ensures an element has the same value as another element.
$(document).on('change', 'input[data-must-equal]', function() {
var value = $(this).val(),
rel = $(this).data().mustEqual,
other = $(rel).val(),
labels = $.map([this, rel], function(element) {
var label = $(element)
.closest('label')
.text();
label = label || $('label[for="' + $(element).attr('id') + '"]').text();
return $.trim(label.split(':')[0]);
}),
error_message = $gettext('Die beiden Werte "$1" und "$2" stimmen nicht überein. '),
matches = error_message.match(/\$\d/g);
$.each(matches, function(i) {
error_message = error_message.replace(this, labels[i]);
});
if (value !== other) {
this.setCustomValidity(error_message);
} else {
this.setCustomValidity('');
}
});
//Generalisation: The enter-accessible class allows an element to be accessible via keyboard
//by triggering the click event when the enter key is pressed.
$(document).on('keydown', '.enter-accessible', function(event) {
if (event.code == 'Enter') {
//The enter key has been pressed.
$(this).trigger('click');
}
});