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
 * This module allows to enhance the form elements MoodleQuickForm_filetypes
18
 *
19
 * @module     core_form/filetypes
20
 * @copyright  2017 David Mudrak <david@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 * @since      3.3
23
 */
24
define(['jquery', 'core/log', 'core/modal_events', 'core/modal_save_cancel', 'core/ajax',
25
        'core/templates', 'core/tree'],
26
    function($, Log, ModalEvents, ModalSaveCancel, Ajax, Templates, Tree) {
27
 
28
    "use strict";
29
 
30
    /**
31
     * Constructor of the FileTypes instances.
32
     *
33
     * @constructor
34
     * @param {String} elementId The id of the form element to enhance
35
     * @param {String} elementLabel The label of the form element used as the modal selector title
36
     * @param {String} onlyTypes Limit the list of offered types to this
37
     * @param {Bool} allowAll Allow presence of the "All file types" item
38
     */
39
    var FileTypes = function(elementId, elementLabel, onlyTypes, allowAll) {
40
 
41
        this.elementId = elementId;
42
        this.elementLabel = elementLabel;
43
        this.onlyTypes = onlyTypes;
44
        this.allowAll = allowAll;
45
 
46
        this.inputField = $('#' + elementId);
47
        this.wrapperBrowserTrigger = $('[data-filetypesbrowser="' + elementId + '"]');
48
        this.wrapperDescriptions = $('[data-filetypesdescriptions="' + elementId + '"]');
49
 
50
        if (!this.wrapperBrowserTrigger.length) {
51
            // This is a valid case. Most probably the element is frozen and
52
            // the filetypes browser should not be available.
53
            return;
54
        }
55
 
56
        if (!this.inputField.length || !this.wrapperDescriptions.length) {
57
            Log.error('core_form/filetypes: Unexpected DOM structure, unable to enhance filetypes field ' + elementId);
58
            return;
59
        }
60
 
61
        this.prepareBrowserTrigger()
62
            .then(function() {
63
                return this.prepareBrowserModal();
64
            }.bind(this))
65
 
66
            .then(function() {
67
                return this.prepareBrowserTree();
68
            }.bind(this));
69
    };
70
 
71
    /**
72
     * Create and set the browser trigger widget (this.browserTrigger).
73
     *
74
     * @method prepareBrowserTrigger
75
     * @returns {Promise}
76
     */
77
    FileTypes.prototype.prepareBrowserTrigger = function() {
78
        return Templates.render('core_form/filetypes-trigger', {})
79
            .then(function(html) {
80
                this.wrapperBrowserTrigger.html(html);
81
                this.browserTrigger = this.wrapperBrowserTrigger.find('[data-filetypeswidget="browsertrigger"]');
82
            }.bind(this));
83
    };
84
 
85
    /**
86
     * Create and set the modal for displaying the browser (this.browserModal).
87
     *
88
     * @method prepareBrowserModal
89
     * @returns {Promise}
90
     */
91
    FileTypes.prototype.prepareBrowserModal = function() {
92
        return ModalSaveCancel.create({
93
            title: this.elementLabel,
94
        })
95
        .then(function(modal) {
96
            this.browserModal = modal;
97
            return modal;
98
        }.bind(this))
99
        .then(function() {
100
            // Because we have custom conditional modal trigger, we need to
101
            // handle the focus after closing ourselves, too.
102
            this.browserModal.getRoot().on(ModalEvents.hidden, function() {
103
                this.browserTrigger.focus();
104
            }.bind(this));
105
 
106
            this.browserModal.getRoot().on(ModalEvents.save, function() {
107
                this.saveBrowserModal();
108
            }.bind(this));
109
        }.bind(this));
110
 
111
    };
112
 
113
    /**
114
     * Create and set the tree in the browser modal's body.
115
     *
116
     * @method prepareBrowserTree
117
     * @returns {Promise}
118
     */
119
    FileTypes.prototype.prepareBrowserTree = function() {
120
 
121
        this.browserTrigger.on('click', function(e) {
122
            e.preventDefault();
123
 
124
            // We want to display the browser modal only when the associated input
125
            // field is not frozen (disabled).
126
            if (this.inputField.is('[disabled]')) {
127
                return;
128
            }
129
 
130
            var bodyContent = this.loadBrowserModalBody();
131
 
132
            bodyContent.then(function() {
133
 
134
                // Turn the list of groups and extensions into the tree.
135
                this.browserTree = new Tree(this.browserModal.getBody());
136
 
137
                // Override the behaviour of the Enter and Space keys to toggle our checkbox,
138
                // rather than toggle the tree node expansion status.
139
                this.browserTree.handleKeyDown = function(item, e) {
140
                    if (e.keyCode == this.browserTree.keys.enter || e.keyCode == this.browserTree.keys.space) {
141
                        e.preventDefault();
142
                        e.stopPropagation();
143
                        this.toggleCheckbox(item.attr('data-filetypesbrowserkey'));
144
                    } else {
145
                        Tree.prototype.handleKeyDown.call(this.browserTree, item, e);
146
                    }
147
                }.bind(this);
148
 
149
                if (this.allowAll) {
150
                    // Hide all other items if "All file types" is enabled.
151
                    this.hideOrShowItemsDependingOnAllowAll(this.browserModal.getRoot()
152
                        .find('input[type="checkbox"][data-filetypesbrowserkey="*"]').first());
153
                    // And do the same whenever we click that checkbox.
154
                    this.browserModal.getRoot().on('change', 'input[type="checkbox"][data-filetypesbrowserkey="*"]', function(e) {
155
                        this.hideOrShowItemsDependingOnAllowAll($(e.currentTarget));
156
                    }.bind(this));
157
                }
158
 
159
                // Synchronize checked status if the file extension is present in multiple groups.
160
                this.browserModal.getRoot().on('change', 'input[type="checkbox"][data-filetypesbrowserkey]', function(e) {
161
                    var checkbox = $(e.currentTarget);
162
                    var key = checkbox.attr('data-filetypesbrowserkey');
163
                    this.browserModal.getRoot().find('input[type="checkbox"][data-filetypesbrowserkey="' + key + '"]')
164
                        .prop('checked', checkbox.prop('checked'));
165
                }.bind(this));
166
 
167
            }.bind(this))
168
 
169
            .then(function() {
170
                this.browserModal.show();
171
            }.bind(this));
172
 
173
            this.browserModal.setBody(bodyContent);
174
 
175
        }.bind(this));
176
 
177
        // Return a resolved promise.
178
        return $.when();
179
    };
180
 
181
    /**
182
     * Load the browser modal body contents.
183
     *
184
     * @returns {Promise}
185
     */
186
    FileTypes.prototype.loadBrowserModalBody = function() {
187
 
188
        var args = {
189
            onlytypes: this.onlyTypes.join(),
190
            allowall: this.allowAll,
191
            current: this.inputField.val()
192
        };
193
 
194
        return Ajax.call([{
195
            methodname: 'core_form_get_filetypes_browser_data',
196
            args: args
197
 
198
        }])[0].then(function(browserData) {
199
            return Templates.render('core_form/filetypes-browser', {
200
                elementid: this.elementId,
201
                groups: browserData.groups
202
            });
203
        }.bind(this));
204
    };
205
 
206
    /**
207
     * Change the checked status of the given file type (group or extension).
208
     *
209
     * @method toggleCheckbox
210
     * @param {String} key
211
     */
212
    FileTypes.prototype.toggleCheckbox = function(key) {
213
 
214
        var checkbox = this.browserModal.getRoot().find('input[type="checkbox"][data-filetypesbrowserkey="' + key + '"]').first();
215
 
216
        checkbox.prop('checked', !checkbox.prop('checked'));
217
    };
218
 
219
    /**
220
     * Update the associated input field with selected file types.
221
     *
222
     * @method saveBrowserModal
223
     */
224
    FileTypes.prototype.saveBrowserModal = function() {
225
 
226
        // Check the "All file types" first.
227
        if (this.allowAll) {
228
            var allcheckbox = this.browserModal.getRoot().find('input[type="checkbox"][data-filetypesbrowserkey="*"]');
229
            if (allcheckbox.length && allcheckbox.prop('checked')) {
230
                this.inputField.val('*');
231
                this.updateDescriptions(['*']);
232
                return;
233
            }
234
        }
235
 
236
        // Iterate over all checked boxes and populate the list.
237
        var newvalue = [];
238
 
239
        this.browserModal.getRoot().find('input[type="checkbox"]').each(/** @this represents the checkbox */ function() {
240
            var checkbox = $(this);
241
            var key = checkbox.attr('data-filetypesbrowserkey');
242
 
243
            if (checkbox.prop('checked')) {
244
                newvalue.push(key);
245
            }
246
        });
247
 
248
        // Remove duplicates (e.g. file types present in multiple groups).
249
        newvalue = newvalue.filter(function(x, i, a) {
250
            return a.indexOf(x) == i;
251
        });
252
 
253
        this.inputField.val(newvalue.join(' '));
254
        this.updateDescriptions(newvalue);
255
    };
256
 
257
    /**
258
     * Describe the selected filetypes in the form when saving the browser.
259
     *
260
     * @param {Array} keys List of keys to describe
261
     * @returns {Promise}
262
     */
263
    FileTypes.prototype.updateDescriptions = function(keys) {
264
 
265
        var descriptions = [];
266
 
267
        keys.forEach(function(key) {
268
            descriptions.push({
269
                description: this.browserModal.getRoot().find('[data-filetypesname="' + key + '"]').first().text().trim(),
270
                extensions: this.browserModal.getRoot().find('[data-filetypesextensions="' + key + '"]').first().text().trim()
271
            });
272
        }.bind(this));
273
 
274
        var templatedata = {
275
            hasdescriptions: (descriptions.length > 0),
276
            descriptions: descriptions
277
        };
278
 
279
        return Templates.render('core_form/filetypes-descriptions', templatedata)
280
            .then(function(html) {
281
                this.wrapperDescriptions.html(html);
282
            }.bind(this));
283
    };
284
 
285
    /**
286
     * If "All file types" is checked, all other browser items are made hidden, and vice versa.
287
     *
288
     * @param {jQuery} allcheckbox The "All file types" checkbox.
289
     */
290
    FileTypes.prototype.hideOrShowItemsDependingOnAllowAll = function(allcheckbox) {
291
        var others = this.browserModal.getRoot().find('[role="treeitem"][data-filetypesbrowserkey!="*"]');
292
        if (allcheckbox.prop('checked')) {
293
            others.hide();
294
        } else {
295
            others.show();
296
        }
297
    };
298
 
299
    return {
300
        init: function(elementId, elementLabel, onlyTypes, allowAll) {
301
            new FileTypes(elementId, elementLabel, onlyTypes, allowAll);
302
        }
303
    };
304
});