2 * Show/hide admin settings based on other settings selected
4 * @copyright 2018 Davo Smith, Synergy Learning
5 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7 define(['jquery'], function($) {
10 // -------------------------------------------------
11 // Support functions, used by dependency functions.
12 // -------------------------------------------------
15 * Check to see if the given element is the hidden element that makes sure checkbox
16 * elements always submit a value.
20 function isCheckboxHiddenElement($el) {
21 return ($el.is('input[type=hidden]') && $el.siblings('input[type=checkbox][name="' + $el.attr('name') + '"]').length);
25 * Check to see if this is a radio button with the wrong value (i.e. a radio button from
26 * the group we are interested in, but not the specific one we wanted).
28 * @param {string} value
31 function isWrongRadioButton($el, value) {
32 return ($el.is('input[type=radio]') && $el.attr('value') !== value);
36 * Is this element relevant when we're looking for checked / not checked status?
38 * @param {string} value
41 function isCheckedRelevant($el, value) {
42 return (!isCheckboxHiddenElement($el) && !isWrongRadioButton($el, value));
46 * Is this an unchecked radio button? (If it is, we want to skip it, as
47 * we're only interested in the value of the radio button that is checked)
51 function isUncheckedRadioButton($el) {
52 return ($el.is('input[type=radio]') && !$el.prop('checked'));
56 * Is this an unchecked checkbox?
60 function isUncheckedCheckbox($el) {
61 return ($el.is('input[type=checkbox]') && !$el.prop('checked'));
65 * Is this a multi-select select element?
69 function isMultiSelect($el) {
70 return ($el.is('select') && $el.prop('multiple'));
74 * Does the multi-select exactly match the list of values provided?
76 * @param {array} values
79 function multiSelectMatches($el, values) {
80 var selected = $el.val() || [];
82 // No values - nothing to match against.
85 if (selected.length !== values.length) {
86 // Different number of expected and actual values - cannot possibly be a match.
89 for (var i in selected) {
90 if (selected.hasOwnProperty(i)) {
91 if (values.indexOf(selected[i]) === -1) {
92 return false; // Found a non-matching value - give up immediately.
96 // Didn't find a non-matching value, so we have a match.
100 // -------------------------------
101 // Specific dependency functions.
102 // -------------------------------
105 notchecked: function($dependon, value) {
107 value = String(value);
108 $dependon.each(function(idx, el) {
110 if (isCheckedRelevant($el, value)) {
111 hide = hide || !$el.prop('checked');
117 checked: function($dependon, value) {
119 value = String(value);
120 $dependon.each(function(idx, el) {
122 if (isCheckedRelevant($el, value)) {
123 hide = hide || $el.prop('checked');
129 noitemselected: function($dependon) {
131 $dependon.each(function(idx, el) {
133 hide = hide || ($el.prop('selectedIndex') === -1);
138 eq: function($dependon, value) {
140 var hiddenVal = false;
141 value = String(value);
142 $dependon.each(function(idx, el) {
144 if (isUncheckedRadioButton($el)) {
145 // For radio buttons, we're only interested in the one that is checked.
148 if (isCheckboxHiddenElement($el)) {
149 // This is the hidden input that is part of the checkbox setting.
150 // We will use this value, if the associated checkbox is unchecked.
151 hiddenVal = ($el.val() === value);
154 if (isUncheckedCheckbox($el)) {
155 // Checkbox is not checked - hide depends on the 'unchecked' value stored in
156 // the associated hidden element, which we have already found, above.
157 hide = hide || hiddenVal;
160 if (isMultiSelect($el)) {
161 // Expect a list of values to match, separated by '|' - all of them must
162 // match the values selected.
163 var values = value.split('|');
164 hide = multiSelectMatches($el, values);
167 // All other element types - just compare the value directly.
168 hide = hide || ($el.val() === value);
173 'in': function($dependon, value) {
175 var hiddenVal = false;
176 var values = value.split('|');
177 $dependon.each(function(idx, el) {
179 if (isUncheckedRadioButton($el)) {
180 // For radio buttons, we're only interested in the one that is checked.
183 if (isCheckboxHiddenElement($el)) {
184 // This is the hidden input that is part of the checkbox setting.
185 // We will use this value, if the associated checkbox is unchecked.
186 hiddenVal = (values.indexOf($el.val()) > -1);
189 if (isUncheckedCheckbox($el)) {
190 // Checkbox is not checked - hide depends on the 'unchecked' value stored in
191 // the associated hidden element, which we have already found, above.
192 hide = hide || hiddenVal;
195 if (isMultiSelect($el)) {
196 // For multiselect, we check to see if the list of values provided matches the list selected.
197 hide = multiSelectMatches($el, values);
200 // All other element types - check to see if the value is in the list.
201 hide = hide || (values.indexOf($el.val()) > -1);
206 defaultCondition: function($dependon, value) { // Not equal.
208 var hiddenVal = false;
209 value = String(value);
210 $dependon.each(function(idx, el) {
212 if (isUncheckedRadioButton($el)) {
213 // For radio buttons, we're only interested in the one that is checked.
216 if (isCheckboxHiddenElement($el)) {
217 // This is the hidden input that is part of the checkbox setting.
218 // We will use this value, if the associated checkbox is unchecked.
219 hiddenVal = ($el.val() !== value);
222 if (isUncheckedCheckbox($el)) {
223 // Checkbox is not checked - hide depends on the 'unchecked' value stored in
224 // the associated hidden element, which we have already found, above.
225 hide = hide || hiddenVal;
228 if (isMultiSelect($el)) {
229 // Expect a list of values to match, separated by '|' - all of them must
230 // match the values selected to *not* hide the element.
231 var values = value.split('|');
232 hide = !multiSelectMatches($el, values);
235 // All other element types - just compare the value directly.
236 hide = hide || ($el.val() !== value);
243 * Find the element with the given name
244 * @param {String} name
245 * @returns {*|jQuery|HTMLElement}
247 function getElementsByName(name) {
248 // For the array elements, we use [name^="something["] to find the elements that their name begins with 'something['/
249 // This is to find both name = 'something[]' and name='something[index]'.
250 return $('[name="' + name + '"],[name^="' + name + '["]');
254 * Check to see whether a particular condition is met
255 * @param {*|jQuery|HTMLElement} $dependon
256 * @param {String} condition
257 * @param {mixed} value
260 function checkDependency($dependon, condition, value) {
261 if (typeof depFns[condition] === "function") {
262 return depFns[condition]($dependon, value);
264 return depFns.defaultCondition($dependon, value);
268 * Show / hide the elements that depend on some elements.
270 function updateDependencies() {
271 // Process all dependency conditions.
273 $.each(dependencies, function(dependonname) {
274 var dependon = getElementsByName(dependonname);
275 $.each(dependencies[dependonname], function(condition, values) {
276 $.each(values, function(value, elements) {
277 var hide = checkDependency(dependon, condition, value);
278 $.each(elements, function(idx, elToHide) {
279 if (toHide.hasOwnProperty(elToHide)) {
280 toHide[elToHide] = toHide[elToHide] || hide;
282 toHide[elToHide] = hide;
289 // Update the hidden status of all relevant elements.
290 $.each(toHide, function(elToHide, hide) {
291 getElementsByName(elToHide).each(function(idx, el) {
292 var $parent = $(el).closest('.form-item');
293 if ($parent.length) {
305 * Initialise the event handlers.
307 function initHandlers() {
308 $.each(dependencies, function(depname) {
309 var $el = getElementsByName(depname);
311 $el.on('change', updateDependencies);
314 updateDependencies();
318 * Hide the 'this setting may be hidden' messages.
320 function hideDependencyInfo() {
321 $('.form-dependenton').hide();
325 init: function(opts) {
326 dependencies = opts.dependencies;
328 hideDependencyInfo();