Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * A module to help with toggle select/deselect all.
18
 *
19
 * @module     core/checkbox-toggleall
20
 * @copyright  2019 Andrew Nicols <andrew@nicols.co.uk>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
define(['jquery', 'core/pubsub'], function($, PubSub) {
24
 
25
    /**
26
     * Whether event listeners have already been registered.
27
     *
28
     * @private
29
     * @type {boolean}
30
     */
31
    var registered = false;
32
 
33
    /**
34
     * List of custom events that this module publishes.
35
     *
36
     * @private
37
     * @type {{checkboxToggled: string}}
38
     */
39
    var events = {
40
        checkboxToggled: 'core/checkbox-toggleall:checkboxToggled',
41
    };
42
 
43
    /**
44
     * Fetches elements that are member of a given toggle group.
45
     *
46
     * @private
47
     * @param {jQuery} root The root jQuery element.
48
     * @param {string} toggleGroup The toggle group name that we're searching form.
49
     * @param {boolean} exactMatch Whether we want an exact match we just want to match toggle groups that start with the given
50
     *                             toggle group name.
51
     * @returns {jQuery} The elements matching the given toggle group.
52
     */
53
    var getToggleGroupElements = function(root, toggleGroup, exactMatch) {
54
        if (exactMatch) {
55
            return root.find('[data-action="toggle"][data-togglegroup="' + toggleGroup + '"]');
56
        } else {
57
            return root.find('[data-action="toggle"][data-togglegroup^="' + toggleGroup + '"]');
58
        }
59
    };
60
 
61
    /**
62
     * Fetches the slave checkboxes for a given toggle group.
63
     *
64
     * @private
65
     * @param {jQuery} root The root jQuery element.
66
     * @param {string} toggleGroup The toggle group name.
67
     * @returns {jQuery} The slave checkboxes belonging to the toggle group.
68
     */
69
    var getAllSlaveCheckboxes = function(root, toggleGroup) {
70
        return getToggleGroupElements(root, toggleGroup, false).filter('[data-toggle="slave"]');
71
    };
72
 
73
    /**
74
     * Fetches the master elements (checkboxes or buttons) that control the slave checkboxes in a given toggle group.
75
     *
76
     * @private
77
     * @param {jQuery} root The root jQuery element.
78
     * @param {string} toggleGroup The toggle group name.
79
     * @param {boolean} exactMatch
80
     * @returns {jQuery} The control elements belonging to the toggle group.
81
     */
82
    var getControlCheckboxes = function(root, toggleGroup, exactMatch) {
83
        return getToggleGroupElements(root, toggleGroup, exactMatch).filter('[data-toggle="master"]');
84
    };
85
 
86
    /**
87
     * Fetches the action elements that perform actions on the selected checkboxes in a given toggle group.
88
     *
89
     * @private
90
     * @param {jQuery} root The root jQuery element.
91
     * @param {string} toggleGroup The toggle group name.
92
     * @returns {jQuery} The action elements belonging to the toggle group.
93
     */
94
    var getActionElements = function(root, toggleGroup) {
95
        return getToggleGroupElements(root, toggleGroup, true).filter('[data-toggle="action"]');
96
    };
97
 
98
    /**
99
     * Toggles the slave checkboxes in a given toggle group when a master element in that toggle group is toggled.
100
     *
101
     * @private
102
     * @param {Object} e The event object.
103
     */
104
    var toggleSlavesFromMasters = function(e) {
105
        var root = e.data.root;
106
        var target = $(e.target);
107
 
108
        var toggleGroupName = target.data('togglegroup');
109
        var targetState;
110
        if (target.is(':checkbox')) {
111
            targetState = target.is(':checked');
112
        } else {
113
            targetState = target.data('checkall') === 1;
114
        }
115
 
116
        toggleSlavesToState(root, toggleGroupName, targetState);
117
    };
118
 
119
    /**
120
     * Toggles the slave checkboxes from the masters.
121
     *
122
     * @param {HTMLElement} root
123
     * @param {String} toggleGroupName
124
     */
125
    var updateSlavesFromMasterState = function(root, toggleGroupName) {
126
        // Normalise to jQuery Object.
127
        root = $(root);
128
 
129
        var target = getControlCheckboxes(root, toggleGroupName, false);
130
        var targetState;
131
        if (target.is(':checkbox')) {
132
            targetState = target.is(':checked');
133
        } else {
134
            targetState = target.data('checkall') === 1;
135
        }
136
 
137
        toggleSlavesToState(root, toggleGroupName, targetState);
138
    };
139
 
140
    /**
141
     * Toggles the master checkboxes and action elements in a given toggle group.
142
     *
143
     * @param {jQuery} root The root jQuery element.
144
     * @param {String} toggleGroupName The name of the toggle group
145
     */
146
    var toggleMastersAndActionElements = function(root, toggleGroupName) {
147
        var toggleGroupSlaves = getAllSlaveCheckboxes(root, toggleGroupName);
148
        if (toggleGroupSlaves.length > 0) {
149
            var toggleGroupCheckedSlaves = toggleGroupSlaves.filter(':checked');
150
            var targetState = toggleGroupSlaves.length === toggleGroupCheckedSlaves.length;
151
 
152
            // Make sure to toggle the exact master checkbox in the given toggle group.
153
            setMasterStates(root, toggleGroupName, targetState, true);
154
            // Enable the action elements if there's at least one checkbox checked in the given toggle group.
155
            // Disable otherwise.
156
            setActionElementStates(root, toggleGroupName, !toggleGroupCheckedSlaves.length);
157
        }
158
    };
159
 
160
    /**
161
     * Returns an array containing every toggle group level of a given toggle group.
162
     *
163
     * @param {String} toggleGroupName The name of the toggle group
164
     * @return {Array} toggleGroupLevels Array that contains every toggle group level of a given toggle group
165
     */
166
    var getToggleGroupLevels = function(toggleGroupName) {
167
        var toggleGroups = toggleGroupName.split(' ');
168
        var toggleGroupLevels = [];
169
        var toggleGroupLevel = '';
170
 
171
        toggleGroups.forEach(function(toggleGroupName) {
172
            toggleGroupLevel += ' ' + toggleGroupName;
173
            toggleGroupLevels.push(toggleGroupLevel.trim());
174
        });
175
 
176
        return toggleGroupLevels;
177
    };
178
 
179
    /**
180
     * Toggles the slave checkboxes to a specific state.
181
     *
182
     * @param {HTMLElement} root
183
     * @param {String} toggleGroupName
184
     * @param {Bool} targetState
185
     */
186
    var toggleSlavesToState = function(root, toggleGroupName, targetState) {
187
        var slaves = getAllSlaveCheckboxes(root, toggleGroupName);
188
        // Set the slave checkboxes from the masters and manually trigger the native 'change' event.
189
        slaves.prop('checked', targetState).trigger('change');
190
        // Get all checked slaves after the change of state.
191
        var checkedSlaves = slaves.filter(':checked');
192
 
193
        // Toggle the master checkbox in the given toggle group.
194
        setMasterStates(root, toggleGroupName, targetState, false);
195
        // Enable the action elements if there's at least one checkbox checked in the given toggle group. Disable otherwise.
196
        setActionElementStates(root, toggleGroupName, !checkedSlaves.length);
197
 
198
        // Get all toggle group levels and toggle accordingly all parent master checkboxes and action elements from each
199
        // level. Exclude the given toggle group (toggleGroupName) as the master checkboxes and action elements from this
200
        // level have been already toggled.
201
        var toggleGroupLevels = getToggleGroupLevels(toggleGroupName)
202
            .filter(toggleGroupLevel => toggleGroupLevel !== toggleGroupName);
203
 
204
        toggleGroupLevels.forEach(function(toggleGroupLevel) {
205
            // Toggle the master checkboxes action elements in the given toggle group level.
206
            toggleMastersAndActionElements(root, toggleGroupLevel);
207
        });
208
 
209
        PubSub.publish(events.checkboxToggled, {
210
            root: root,
211
            toggleGroupName: toggleGroupName,
212
            slaves: slaves,
213
            checkedSlaves: checkedSlaves,
214
            anyChecked: targetState,
215
        });
216
    };
217
 
218
    /**
219
     * Set the state for an entire group of checkboxes.
220
     *
221
     * @param {HTMLElement} root
222
     * @param {String} toggleGroupName
223
     * @param {Bool} targetState
224
     */
225
    var setGroupState = function(root, toggleGroupName, targetState) {
226
        // Normalise to jQuery Object.
227
        root = $(root);
228
 
229
        // Set the master and slaves.
230
        setMasterStates(root, toggleGroupName, targetState, true);
231
        toggleSlavesToState(root, toggleGroupName, targetState);
232
    };
233
 
234
    /**
235
     * Toggles the master checkboxes in a given toggle group when all or none of the slave checkboxes in the same toggle group
236
     * have been selected.
237
     *
238
     * @private
239
     * @param {Object} e The event object.
240
     */
241
    var toggleMastersFromSlaves = function(e) {
242
        var root = e.data.root;
243
        var target = $(e.target);
244
        var toggleGroupName = target.data('togglegroup');
245
        var slaves = getAllSlaveCheckboxes(root, toggleGroupName);
246
        var checkedSlaves = slaves.filter(':checked');
247
 
248
        // Get all toggle group levels for the given toggle group and toggle accordingly all master checkboxes
249
        // and action elements from each level.
250
        var toggleGroupLevels = getToggleGroupLevels(toggleGroupName);
251
        toggleGroupLevels.forEach(function(toggleGroupLevel) {
252
            // Toggle the master checkboxes action elements in the given toggle group level.
253
            toggleMastersAndActionElements(root, toggleGroupLevel);
254
        });
255
 
256
        PubSub.publish(events.checkboxToggled, {
257
            root: root,
258
            toggleGroupName: toggleGroupName,
259
            slaves: slaves,
260
            checkedSlaves: checkedSlaves,
261
            anyChecked: !!checkedSlaves.length,
262
        });
263
    };
264
 
265
    /**
266
     * Enables or disables the action elements.
267
     *
268
     * @private
269
     * @param {jQuery} root The root jQuery element.
270
     * @param {string} toggleGroupName The toggle group name of the action element(s).
271
     * @param {boolean} disableActionElements Whether to disable or to enable the action elements.
272
     */
273
    var setActionElementStates = function(root, toggleGroupName, disableActionElements) {
274
        getActionElements(root, toggleGroupName).prop('disabled', disableActionElements);
275
    };
276
 
277
    /**
278
     * Selects or deselects the master elements.
279
     *
280
     * @private
281
     * @param {jQuery} root The root jQuery element.
282
     * @param {string} toggleGroupName The toggle group name of the master element(s).
283
     * @param {boolean} targetState Whether to select (true) or deselect (false).
284
     * @param {boolean} exactMatch Whether to do an exact match for the toggle group name or not.
285
     */
286
    var setMasterStates = function(root, toggleGroupName, targetState, exactMatch) {
287
        // Set the master checkboxes value and ARIA labels..
288
        var masters = getControlCheckboxes(root, toggleGroupName, exactMatch);
289
        masters.prop('checked', targetState);
290
        masters.each(function(i, masterElement) {
291
            masterElement = $(masterElement);
292
 
293
            var targetString;
294
            if (targetState) {
295
                targetString = masterElement.data('toggle-deselectall');
296
            } else {
297
                targetString = masterElement.data('toggle-selectall');
298
            }
299
 
300
            if (masterElement.is(':checkbox')) {
301
                var masterLabel = root.find('[for="' + masterElement.attr('id') + '"]');
302
                if (masterLabel.length) {
303
                    if (masterLabel.html() !== targetString) {
304
                        masterLabel.html(targetString);
305
                    }
306
                }
307
            } else {
308
                masterElement.text(targetString);
309
                // Set the checkall data attribute.
310
                masterElement.data('checkall', targetState ? 0 : 1);
311
            }
312
        });
313
    };
314
 
315
    /**
316
     * Registers the event listeners.
317
     *
318
     * @private
319
     */
320
    var registerListeners = function() {
321
        if (!registered) {
322
            registered = true;
323
 
324
            var root = $(document.body);
325
            root.on('click', '[data-action="toggle"][data-toggle="master"]', {root: root}, toggleSlavesFromMasters);
326
            root.on('click', '[data-action="toggle"][data-toggle="slave"]', {root: root}, toggleMastersFromSlaves);
327
        }
328
    };
329
 
330
    return {
331
        init: function() {
332
            registerListeners();
333
        },
334
        events: events,
335
        setGroupState: setGroupState,
336
        updateSlavesFromMasterState: updateSlavesFromMasterState,
337
    };
338
});