Merge branch 'MDL-62449-master' of git://github.com/sarjona/moodle
[moodle.git] / theme / boost / amd / src / drawer.js
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/>.
16 /**
17  * Contain the logic for a drawer.
18  *
19  * @package    theme_boost
20  * @copyright  2016 Damyon Wiese
21  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  */
23 define(['jquery', 'core/custom_interaction_events', 'core/log'],
24      function($, CustomEvents, Log) {
26     var SELECTORS = {
27         TOGGLE_REGION: '[data-region="drawer-toggle"]',
28         TOGGLE_ACTION: '[data-action="toggle-drawer"]',
29         TOGGLE_TARGET: 'aria-controls',
30         TOGGLE_SIDE: 'left',
31         BODY: 'body',
32         SECTION: '.list-group-item[href*="#section-"]'
33     };
35     var small = $(document).width() < 768;
37     /**
38      * Constructor for the Drawer.
39      *
40      * @param {object} root The root jQuery element for the modal
41      */
42     var Drawer = function() {
44         if (!$(SELECTORS.TOGGLE_REGION).length) {
45             Log.debug('Page is missing a drawer region');
46         }
47         if (!$(SELECTORS.TOGGLE_ACTION).length) {
48             Log.debug('Page is missing a drawer toggle link');
49         }
50         $(SELECTORS.TOGGLE_REGION).each(function(index, ele) {
51             var trigger = $(ele).find(SELECTORS.TOGGLE_ACTION);
52             var drawerid = trigger.attr('aria-controls');
53             var drawer = $(document.getElementById(drawerid));
54             var hidden = trigger.attr('aria-expanded') == 'false';
55             var side = trigger.attr('data-side');
56             var body = $(SELECTORS.BODY);
57             var preference = trigger.attr('data-preference');
58             if (small) {
59                 M.util.set_user_preference(preference, 'false');
60             }
62             drawer.on('mousewheel DOMMouseScroll', this.preventPageScroll);
64             if (!hidden) {
65                 body.addClass('drawer-open-' + side);
66                 trigger.attr('aria-expanded', 'true');
67             } else {
68                 trigger.attr('aria-expanded', 'false');
69             }
70         }.bind(this));
72         this.registerEventListeners();
73         if (small) {
74             this.closeAll();
75         }
76     };
78     Drawer.prototype.closeAll = function() {
79         $(SELECTORS.TOGGLE_REGION).each(function(index, ele) {
80             var trigger = $(ele).find(SELECTORS.TOGGLE_ACTION);
81             var side = trigger.attr('data-side');
82             var body = $(SELECTORS.BODY);
83             var drawerid = trigger.attr('aria-controls');
84             var drawer = $(document.getElementById(drawerid));
85             var preference = trigger.attr('data-preference');
87             trigger.attr('aria-expanded', 'false');
88             body.removeClass('drawer-open-' + side);
89             drawer.attr('aria-hidden', 'true');
90             drawer.addClass('closed');
91             if (!small) {
92                 M.util.set_user_preference(preference, 'false');
93             }
94         });
95     };
97     /**
98      * Open / close the blocks drawer.
99      *
100      * @method toggleDrawer
101      * @param {Event} e
102      */
103     Drawer.prototype.toggleDrawer = function(e) {
104         var trigger = $(e.target).closest('[data-action=toggle-drawer]');
105         var drawerid = trigger.attr('aria-controls');
106         var drawer = $(document.getElementById(drawerid));
107         var body = $(SELECTORS.BODY);
108         var side = trigger.attr('data-side');
109         var preference = trigger.attr('data-preference');
110         if (small) {
111             M.util.set_user_preference(preference, 'false');
112         }
114         body.addClass('drawer-ease');
115         var open = trigger.attr('aria-expanded') == 'true';
116         if (!open) {
117             // Open.
118             trigger.attr('aria-expanded', 'true');
119             drawer.attr('aria-hidden', 'false');
120             drawer.focus();
121             body.addClass('drawer-open-' + side);
122             drawer.removeClass('closed');
123             if (!small) {
124                 M.util.set_user_preference(preference, 'true');
125             }
126         } else {
127             // Close.
128             body.removeClass('drawer-open-' + side);
129             trigger.attr('aria-expanded', 'false');
130             drawer.attr('aria-hidden', 'true');
131             drawer.addClass('closed');
132             if (!small) {
133                 M.util.set_user_preference(preference, 'false');
134             }
135         }
136     };
138     /**
139      * Prevent the page from scrolling when the drawer is at max scroll.
140      *
141      * @method preventPageScroll
142      * @param  {Event} e
143      */
144     Drawer.prototype.preventPageScroll = function(e) {
145         var delta = e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.originalEvent.detail,
146             bottomOverflow = (this.scrollTop + $(this).outerHeight() - this.scrollHeight) >= 0,
147             topOverflow = this.scrollTop <= 0;
149         if ((delta < 0 && bottomOverflow) || (delta > 0 && topOverflow)) {
150             e.preventDefault();
151         }
152     };
154     /**
155      * Set up all of the event handling for the modal.
156      *
157      * @method registerEventListeners
158      */
159     Drawer.prototype.registerEventListeners = function() {
161         $(SELECTORS.TOGGLE_ACTION).each(function(index, element) {
162             CustomEvents.define($(element), [CustomEvents.events.activate]);
163             $(element).on(CustomEvents.events.activate, function(e, data) {
164                 this.toggleDrawer(data.originalEvent);
165                 data.originalEvent.preventDefault();
166             }.bind(this));
167         }.bind(this));
169         $(SELECTORS.SECTION).click(function() {
170             if (small) {
171                 this.closeAll();
172             }
173         }.bind(this));
174     };
176     return {
177         'init': function() {
178             return new Drawer();
179         }
180     };
181 });