Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('moodle-editor_atto-menu', function (Y, NAME) {
2
 
3
// This file is part of Moodle - http://moodle.org/
4
//
5
// Moodle is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
//
10
// Moodle is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17
 
18
/**
19
 * A Menu for the Atto editor.
20
 *
21
 * @module     moodle-editor_atto-menu
22
 * @submodule  menu-base
23
 * @package    editor_atto
24
 * @copyright  2013 Damyon Wiese
25
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26
 */
27
 
28
var LOGNAME = 'moodle-editor_atto-menu';
29
var MENUDIALOGUE = '' +
30
        '<div class="open {{config.buttonClass}} atto_menu" ' +
31
            'style="min-width:{{config.innerOverlayWidth}};">' +
32
            '<ul class="dropdown-menu" role="menu" id="{{config.buttonId}}_menu" aria-labelledby="{{config.buttonId}}">' +
33
                '{{#each config.items}}' +
34
                    '<li role="none" class="atto_menuentry">' +
35
                        '<a href="#" role="menuitem" data-index="{{@index}}" {{#each data}}data-{{@key}}="{{this}}"{{/each}}>' +
36
                            '{{{text}}}' +
37
                        '</a>' +
38
                    '</li>' +
39
                '{{/each}}' +
40
            '</ul>' +
41
        '</div>';
42
 
43
/**
44
 * A Menu for the Atto editor used in Moodle.
45
 *
46
 * This is a drop down list of buttons triggered (and aligned to) a
47
 * location.
48
 *
49
 * @namespace M.editor_atto
50
 * @class Menu
51
 * @main
52
 * @constructor
53
 * @extends M.core.dialogue
54
 */
55
var Menu = function() {
56
    Menu.superclass.constructor.apply(this, arguments);
57
};
58
 
59
Y.extend(Menu, M.core.dialogue, {
60
 
61
    /**
62
     * A list of the menu handlers which have been attached here.
63
     *
64
     * @property _menuHandlers
65
     * @type Array
66
     * @private
67
     */
68
    _menuHandlers: null,
69
 
70
    /**
71
     * The menu button that controls this menu.
72
     */
73
    _menuButton: null,
74
 
75
    initializer: function(config) {
76
        var headerText,
77
            bb;
78
 
79
        this._menuHandlers = [];
80
 
81
        this._menuButton = document.getElementById(config.buttonId);
82
 
83
        // Create the actual button.
84
        var template = Y.Handlebars.compile(MENUDIALOGUE),
85
            menu = Y.Node.create(template({
86
                config: config
87
            }));
88
        this.set('bodyContent', menu);
89
 
90
        bb = this.get('boundingBox');
91
        bb.addClass('editor_atto_controlmenu');
92
        bb.addClass('editor_atto_menu');
93
 
94
        // Get the dialogue container for this menu.
95
        var content = bb.one('.moodle-dialogue-wrap');
96
        content.removeClass('moodle-dialogue-wrap')
97
            .addClass('moodle-dialogue-content');
98
        // Remove the dialog role attribute.
99
        content.removeAttribute('role');
100
        // Remove aria-labelledby in the container. The aria-labelledby attribute is properly set in the menu's template.
101
        content.removeAttribute('aria-labelledby');
102
 
103
        // Render heading if necessary.
104
        headerText = this.get('headerText').trim();
105
        if (headerText) {
106
            var heading = Y.Node.create('<h3/>')
107
                .addClass('accesshide')
108
                .setHTML(headerText);
109
            this.get('bodyContent').prepend(heading);
110
        }
111
 
112
        // Hide the header and footer node entirely.
113
        this.headerNode.hide();
114
        this.footerNode.hide();
115
 
116
        this._setupHandlers();
117
    },
118
 
119
    /**
120
     * Setup the Event handlers.
121
     *
122
     * @method _setupHandlers
123
     * @private
124
     */
125
    _setupHandlers: function() {
126
        var contentBox = this.get('contentBox');
127
        // Handle menu item selection.
128
        this._menuHandlers.push(
129
            // Select the menu item on space, and enter.
130
            contentBox.delegate('key', this._chooseMenuItem, '32, enter', '.atto_menuentry', this),
131
 
132
            // Move up and down the menu on up/down.
133
            contentBox.delegate('key', this._handleKeyboardEvent, 'down:38,40', '.dropdown-menu', this),
134
 
135
            // Hide the menu when clicking outside of it.
136
            contentBox.on('focusoutside', this.hide, this),
137
 
138
            // Hide the menu on left/right, and escape keys.
139
            contentBox.delegate('key', this.hide, 'down:37,39,esc', '.dropdown-menu', this)
140
        );
141
    },
142
 
143
    /**
144
     * Simulate other types of menu selection.
145
     *
146
     * @method _chooseMenuItem
147
     * @param {EventFacade} e
148
     */
149
    _chooseMenuItem: function(e) {
150
        e.target.simulate('click');
151
        e.preventDefault();
152
    },
153
 
154
    /**
155
     * Hide a menu, removing all of the event handlers which trigger the hide.
156
     *
157
     * @method hide
158
     * @param {EventFacade} e
159
     */
160
    hide: function(e) {
161
        if (this.get('preventHideMenu') === true) {
162
            return;
163
        }
164
 
165
        // We must prevent the default action (left/right/escape) because
166
        // there are other listeners on the toolbar which will focus on the
167
        // editor.
168
        if (e) {
169
            e.preventDefault();
170
        }
171
 
172
        // Remove menu button's aria-expanded attribute when this menu is hidden.
173
        if (this._menuButton) {
174
            this._menuButton.removeAttribute('aria-expanded');
175
        }
176
 
177
        return Menu.superclass.hide.call(this, arguments);
178
    },
179
 
180
    /**
181
     * Implement arrow-key navigation for the items in a toolbar menu.
182
     *
183
     * @method _handleKeyboardEvent
184
     * @param {EventFacade} e The keyboard event.
185
     * @static
186
     */
187
    _handleKeyboardEvent: function(e) {
188
        // Prevent the default browser behaviour.
189
        e.preventDefault();
190
 
191
        // Get a list of all buttons in the menu.
192
        var buttons = e.currentTarget.all('a[role="menuitem"]');
193
 
194
        // On cursor moves we loops through the buttons.
195
        var found = false,
196
            index = 0,
197
            direction = 1,
198
            checkCount = 0,
199
            current = e.target.ancestor('a[role="menuitem"]', true),
200
            next;
201
 
202
        // Determine which button is currently selected.
203
        while (!found && index < buttons.size()) {
204
            if (buttons.item(index) === current) {
205
                found = true;
206
            } else {
207
                index++;
208
            }
209
        }
210
 
211
        if (!found) {
212
            return;
213
        }
214
 
215
        if (e.keyCode === 38) {
216
            // Moving up so reverse the direction.
217
            direction = -1;
218
        }
219
 
220
        // Try to find the next
221
        do {
222
            index += direction;
223
            if (index < 0) {
224
                index = buttons.size() - 1;
225
            } else if (index >= buttons.size()) {
226
                // Handle wrapping.
227
                index = 0;
228
            }
229
            next = buttons.item(index);
230
 
231
            // Add a counter to ensure we don't get stuck in a loop if there's only one visible menu item.
232
            checkCount++;
233
            // Loop while:
234
            // * we are not in a loop and have not already checked every button; and
235
            // * we are on a different button; and
236
            // * the next menu item is not hidden.
237
        } while (checkCount < buttons.size() && next !== current && next.hasAttribute('hidden'));
238
 
239
        if (next) {
240
            next.focus();
241
        }
242
 
243
        e.preventDefault();
244
        e.stopImmediatePropagation();
245
    }
246
}, {
247
    NAME: "menu",
248
    ATTRS: {
249
        /**
250
         * The header for the drop down (only accessible to screen readers).
251
         *
252
         * @attribute headerText
253
         * @type String
254
         * @default ''
255
         */
256
        headerText: {
257
            value: ''
258
        }
259
 
260
    }
261
});
262
 
263
Y.Base.modifyAttrs(Menu, {
264
    /**
265
     * The width for this menu.
266
     *
267
     * @attribute width
268
     * @default 'auto'
269
     */
270
    width: {
271
        value: 'auto'
272
    },
273
 
274
    /**
275
     * When to hide this menu.
276
     *
277
     * By default, this attribute consists of:
278
     * <ul>
279
     * <li>an object which will cause the menu to hide when the user clicks outside of the menu</li>
280
     * </ul>
281
     *
282
     * @attribute hideOn
283
     */
284
    hideOn: {
285
        value: [
286
            {
287
                eventName: 'clickoutside'
288
            }
289
        ]
290
    },
291
 
292
    /**
293
     * The default list of extra classes for this menu.
294
     *
295
     * @attribute extraClasses
296
     * @type Array
297
     * @default editor_atto_menu
298
     */
299
    extraClasses: {
300
        value: [
301
            'editor_atto_menu'
302
        ]
303
    },
304
 
305
    /**
306
     * Override the responsive nature of the core dialogues.
307
     *
308
     * @attribute responsive
309
     * @type boolean
310
     * @default false
311
     */
312
    responsive: {
313
        value: false
314
    },
315
 
316
    /**
317
     * The default visibility of the menu.
318
     *
319
     * @attribute visible
320
     * @type boolean
321
     * @default false
322
     */
323
    visible: {
324
        value: false
325
    },
326
 
327
    /**
328
     * Whether to centre the menu.
329
     *
330
     * @attribute center
331
     * @type boolean
332
     * @default false
333
     */
334
    center: {
335
        value: false
336
    },
337
 
338
    /**
339
     * Hide the close button.
340
     * @attribute closeButton
341
     * @type boolean
342
     * @default false
343
     */
344
    closeButton: {
345
        value: false
346
    }
347
});
348
 
349
Y.namespace('M.editor_atto').Menu = Menu;
350
 
351
 
352
}, '@VERSION@', {"requires": ["moodle-core-notification-dialogue", "node", "event", "event-custom"]});