MDL-55851 core: fixed AJAX error when user not set up
[moodle.git] / lib / amd / src / custom_interaction_events.js
CommitLineData
e845b96b
RW
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 provides a wrapper to encapsulate a lot of the common combinations of
18 * user interaction we use in Moodle.
19 *
20 * @module core/custom_interaction_events
21 * @class custom_interaction_events
22 * @package core
23 * @copyright 2016 Ryan Wyllie <ryan@moodle.com>
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 * @since 3.2
26 */
27define(['jquery', 'core/key_codes'], function($, keyCodes) {
28 // The list of events provided by this module. Namespaced to avoid clashes.
29 var events = {
30 activate: 'cie:activate',
31 keyboardActivate: 'cie:keyboardactivate',
32 escape: 'cie:escape',
33 down: 'cie:down',
34 up: 'cie:up',
35 home: 'cie:home',
36 end: 'cie:end',
37 next: 'cie:next',
38 previous: 'cie:previous',
39 asterix: 'cie:asterix',
40 scrollTop: 'cie:scrollTop',
41 scrollBottom: 'cie:scrollBottom',
42 ctrlPageUp: 'cie:ctrlPageUp',
43 ctrlPageDown: 'cie:ctrlPageDown',
44 enter: 'cie:enter',
45 };
46
47 /**
48 * Check if the caller has asked for the given event type to be
49 * registered.
50 *
51 * @method shouldAddEvent
52 * @private
53 * @param {string} eventType name of the event (see events above)
54 * @param {array} include the list of events to be added
2bcef559 55 * @return {bool} true if the event should be added, false otherwise.
e845b96b
RW
56 */
57 var shouldAddEvent = function(eventType, include) {
58 include = include || [];
59
60 if (include.length && include.indexOf(eventType) !== -1) {
61 return true;
62 }
63
64 return false;
65 };
66
67 /**
68 * Check if any of the modifier keys have been pressed on the event.
69 *
70 * @method isModifierPressed
71 * @private
72 * @param {event} e jQuery event
2bcef559 73 * @return {bool} true if shift, meta (command on Mac), alt or ctrl are pressed
e845b96b
RW
74 */
75 var isModifierPressed = function(e) {
76 return (e.shiftKey || e.metaKey || e.altKey || e.ctrlKey);
77 };
78
79 /**
80 * Register a keyboard event that ignores modifier keys.
81 *
82 * @method addKeyboardEvent
83 * @private
2bcef559 84 * @param {object} element A jQuery object of the element to bind events to
e845b96b
RW
85 * @param {string} event The custom interaction event name
86 * @param {int} keyCode The key code.
87 */
88 var addKeyboardEvent = function(element, event, keyCode) {
89 element.off('keydown.' + event).on('keydown.' + event, function(e) {
90 if (!isModifierPressed(e)) {
91 if (e.keyCode == keyCode) {
92 $(e.target).trigger(event, [{originalEvent: e}]);
93 }
94 }
95 });
96 };
97
98 /**
99 * Trigger the activate event on the given element if it is clicked or the enter
100 * or space key are pressed without a modifier key.
101 *
102 * @method addActivateListener
103 * @private
2bcef559 104 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
105 */
106 var addActivateListener = function(element) {
107 element.off('click.cie.activate').on('click.cie.activate', function(e) {
108 $(e.target).trigger(events.activate, [{originalEvent: e}]);
109 });
110 element.off('keydown.cie.activate').on('keydown.cie.activate', function(e) {
111 if (!isModifierPressed(e)) {
112 if (e.keyCode == keyCodes.enter || e.keyCode == keyCodes.space) {
113 $(e.target).trigger(events.activate, [{originalEvent: e}]);
114 }
115 }
116 });
117 };
118
119 /**
120 * Trigger the keyboard activate event on the given element if the enter
121 * or space key are pressed without a modifier key.
122 *
123 * @method addKeyboardActivateListener
124 * @private
2bcef559 125 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
126 */
127 var addKeyboardActivateListener = function(element) {
128 element.off('keydown.cie.keyboardactivate').on('keydown.cie.keyboardactivate', function(e) {
129 if (!isModifierPressed(e)) {
130 if (e.keyCode == keyCodes.enter || e.keyCode == keyCodes.space) {
131 $(e.target).trigger(events.keyboardActivate, [{originalEvent: e}]);
132 }
133 }
134 });
135 };
136
137 /**
138 * Trigger the escape event on the given element if the escape key is pressed
139 * without a modifier key.
140 *
141 * @method addEscapeListener
142 * @private
2bcef559 143 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
144 */
145 var addEscapeListener = function(element) {
146 addKeyboardEvent(element, events.escape, keyCodes.escape);
147 };
148
149 /**
150 * Trigger the down event on the given element if the down arrow key is pressed
151 * without a modifier key.
152 *
153 * @method addDownListener
154 * @private
2bcef559 155 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
156 */
157 var addDownListener = function(element) {
158 addKeyboardEvent(element, events.down, keyCodes.arrowDown);
159 };
160
161 /**
162 * Trigger the up event on the given element if the up arrow key is pressed
163 * without a modifier key.
164 *
165 * @method addUpListener
166 * @private
2bcef559 167 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
168 */
169 var addUpListener = function(element) {
170 addKeyboardEvent(element, events.up, keyCodes.arrowUp);
171 };
172
173 /**
174 * Trigger the home event on the given element if the home key is pressed
175 * without a modifier key.
176 *
177 * @method addHomeListener
178 * @private
2bcef559 179 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
180 */
181 var addHomeListener = function(element) {
182 addKeyboardEvent(element, events.home, keyCodes.home);
183 };
184
185 /**
186 * Trigger the end event on the given element if the end key is pressed
187 * without a modifier key.
188 *
189 * @method addEndListener
190 * @private
2bcef559 191 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
192 */
193 var addEndListener = function(element) {
194 addKeyboardEvent(element, events.end, keyCodes.end);
195 };
196
197 /**
198 * Trigger the next event on the given element if the right arrow key is pressed
199 * without a modifier key in LTR mode or left arrow key in RTL mode.
200 *
201 * @method addNextListener
202 * @private
2bcef559 203 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
204 */
205 var addNextListener = function(element) {
206 // Left and right are flipped in RTL mode.
207 var keyCode = $('html').attr('dir') == "rtl" ? keyCodes.arrowLeft : keyCodes.arrowRight;
208
209 addKeyboardEvent(element, events.next, keyCode);
210 };
211
212 /**
213 * Trigger the previous event on the given element if the left arrow key is pressed
214 * without a modifier key in LTR mode or right arrow key in RTL mode.
215 *
216 * @method addPreviousListener
217 * @private
2bcef559 218 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
219 */
220 var addPreviousListener = function(element) {
221 // Left and right are flipped in RTL mode.
222 var keyCode = $('html').attr('dir') == "rtl" ? keyCodes.arrowRight : keyCodes.arrowLeft;
223
224 addKeyboardEvent(element, events.previous, keyCode);
225 };
226
227 /**
228 * Trigger the asterix event on the given element if the asterix key is pressed
229 * without a modifier key.
230 *
231 * @method addAsterixListener
232 * @private
2bcef559 233 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
234 */
235 var addAsterixListener = function(element) {
236 addKeyboardEvent(element, events.asterix, keyCodes.asterix);
237 };
238
239
240 /**
241 * Trigger the scrollTop event on the given element if the user scrolls to
242 * the top of the given element.
243 *
244 * @method addScrollTopListener
245 * @private
2bcef559 246 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
247 */
248 var addScrollTopListener = function(element) {
249 element.off('scroll.cie.scrollTop').on('scroll.cie.scrollTop', function() {
250 var scrollTop = element.scrollTop();
251 if (scrollTop === 0) {
252 element.trigger(events.scrollTop);
253 }
254 });
255 };
256
257 /**
258 * Trigger the scrollBottom event on the given element if the user scrolls to
259 * the bottom of the given element.
260 *
261 * @method addScrollBottomListener
262 * @private
2bcef559 263 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
264 */
265 var addScrollBottomListener = function(element) {
266 element.off('scroll.cie.scrollBottom').on('scroll.cie.scrollBottom', function() {
267 var scrollTop = element.scrollTop();
268 var innerHeight = element.innerHeight();
269 var scrollHeight = element[0].scrollHeight;
270
271 if (scrollTop + innerHeight >= scrollHeight) {
272 element.trigger(events.scrollBottom);
273 }
274 });
275 };
276
277 /**
278 * Trigger the ctrlPageUp event on the given element if the user presses the
279 * control and page up key.
280 *
281 * @method addCtrlPageUpListener
282 * @private
2bcef559 283 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
284 */
285 var addCtrlPageUpListener = function(element) {
286 element.off('keydown.cie.ctrlpageup').on('keydown.cie.ctrlpageup', function(e) {
287 if (e.ctrlKey) {
288 if (e.keyCode == keyCodes.pageUp) {
289 $(e.target).trigger(events.ctrlPageUp, [{originalEvent: e}]);
290 }
291 }
292 });
293 };
294
295 /**
296 * Trigger the ctrlPageDown event on the given element if the user presses the
297 * control and page down key.
298 *
299 * @method addCtrlPageDownListener
300 * @private
2bcef559 301 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
302 */
303 var addCtrlPageDownListener = function(element) {
304 element.off('keydown.cie.ctrlpagedown').on('keydown.cie.ctrlpagedown', function(e) {
305 if (e.ctrlKey) {
306 if (e.keyCode == keyCodes.pageDown) {
307 $(e.target).trigger(events.ctrlPageDown, [{originalEvent: e}]);
308 }
309 }
310 });
311 };
312
313 /**
314 * Trigger the enter event on the given element if the enter key is pressed
315 * without a modifier key.
316 *
317 * @method addEnterListener
318 * @private
2bcef559 319 * @param {object} element jQuery object to add event listeners to
e845b96b
RW
320 */
321 var addEnterListener = function(element) {
322 addKeyboardEvent(element, events.enter, keyCodes.enter);
323 };
324
325 /**
326 * Get the list of events and their handlers.
327 *
328 * @method getHandlers
329 * @private
2bcef559 330 * @return {object} object key of event names and value of handler functions
e845b96b
RW
331 */
332 var getHandlers = function() {
333 var handlers = {};
334
335 handlers[events.activate] = addActivateListener;
336 handlers[events.keyboardActivate] = addKeyboardActivateListener;
337 handlers[events.escape] = addEscapeListener;
338 handlers[events.down] = addDownListener;
339 handlers[events.up] = addUpListener;
340 handlers[events.home] = addHomeListener;
341 handlers[events.end] = addEndListener;
342 handlers[events.next] = addNextListener;
343 handlers[events.previous] = addPreviousListener;
344 handlers[events.asterix] = addAsterixListener;
345 handlers[events.scrollTop] = addScrollTopListener;
346 handlers[events.scrollBottom] = addScrollBottomListener;
347 handlers[events.ctrlPageUp] = addCtrlPageUpListener;
348 handlers[events.ctrlPageDown] = addCtrlPageDownListener;
349 handlers[events.enter] = addEnterListener;
350
351 return handlers;
352 };
353
354 /**
355 * Add all of the listeners on the given element for the requested events.
356 *
357 * @method define
358 * @public
2bcef559 359 * @param {object} element the DOM element to register event listeners on
e845b96b
RW
360 * @param {array} include the array of events to be triggered
361 */
362 var define = function(element, include) {
363 element = $(element);
364 include = include || [];
365
366 if (!element.length || !include.length) {
367 return;
368 }
369
370 $.each(getHandlers(), function(eventType, handler) {
371 if (shouldAddEvent(eventType, include)) {
372 handler(element);
373 }
374 });
375 };
376
377 return /** @module core/custom_interaction_events */ {
378 define: define,
379 events: events,
380 };
381});