Fixed closing mobile menu after option selection
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / WoltLabSuite / Core / Ui / Mobile.js
1 /**
2 * Modifies the interface to provide a better usability for mobile devices.
3 *
4 * @author Alexander Ebert
5 * @copyright 2001-2016 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7 * @module WoltLabSuite/Core/Ui/Mobile
8 */
9 define(
10 [ 'Core', 'Environment', 'EventHandler', 'Language', 'List', 'Dom/ChangeListener', 'Dom/Traverse', 'Ui/CloseOverlay', 'Ui/Screen', './Page/Menu/Main', './Page/Menu/User'],
11 function(Core, Environment, EventHandler, Language, List, DomChangeListener, DomTraverse, UiCloseOverlay, UiScreen, UiPageMenuMain, UiPageMenuUser)
12 {
13 "use strict";
14
15 var _buttonGroupNavigations = elByClass('buttonGroupNavigation');
16 var _enabled = false;
17 var _knownMessages = new List();
18 var _main = null;
19 var _messages = elByClass('message');
20 var _options = {};
21 var _pageMenuMain = null;
22 var _pageMenuUser = null;
23 var _messageGroups = null;
24
25 /**
26 * @exports WoltLabSuite/Core/Ui/Mobile
27 */
28 return {
29 /**
30 * Initializes the mobile UI.
31 *
32 * @param {Object=} options initialization options
33 */
34 setup: function(options) {
35 _options = Core.extend({
36 enableMobileMenu: true
37 }, options);
38
39 _main = elById('main');
40
41 if (Environment.touch()) {
42 document.documentElement.classList.add('touch');
43 }
44
45 if (Environment.platform() !== 'desktop') {
46 document.documentElement.classList.add('mobile');
47 }
48
49 var messageGroupList = elBySel('.messageGroupList');
50 if (messageGroupList) _messageGroups = elByClass('messageGroup', messageGroupList);
51
52 UiScreen.on('screen-md-down', {
53 match: this.enable.bind(this),
54 unmatch: this.disable.bind(this),
55 setup: this._init.bind(this)
56 });
57 },
58
59 /**
60 * Enables the mobile UI.
61 */
62 enable: function() {
63 _enabled = true;
64
65 if (_options.enableMobileMenu) {
66 _pageMenuMain.enable();
67 _pageMenuUser.enable();
68 }
69
70 if (_messageGroups) this.rebuildShadow(_messageGroups, '.messageGroupLink');
71 },
72
73 /**
74 * Disables the mobile UI.
75 */
76 disable: function() {
77 _enabled = false;
78
79 if (_options.enableMobileMenu) {
80 _pageMenuMain.disable();
81 _pageMenuUser.disable();
82 }
83
84 if (_messageGroups) this.removeShadow(_messageGroups);
85 },
86
87 _init: function() {
88 _enabled = true;
89
90 this._initSearchBar();
91 this._initButtonGroupNavigation();
92 this._initMessages();
93 this._initMobileMenu();
94
95 UiCloseOverlay.add('WoltLabSuite/Core/Ui/Mobile', this._closeAllMenus.bind(this));
96 DomChangeListener.add('WoltLabSuite/Core/Ui/Mobile', (function() {
97 this._initButtonGroupNavigation();
98 this._initMessages();
99 }).bind(this));
100
101 if (_messageGroups) this.rebuildShadow(_messageGroups, '.messageGroupLink');
102 },
103
104 _initSearchBar: function() {
105 var _searchBar = elById('pageHeaderSearch');
106 var _searchInput = elById('pageHeaderSearchInput');
107
108 EventHandler.add('com.woltlab.wcf.MainMenuMobile', 'more', function(data) {
109 if (data.identifier === 'com.woltlab.wcf.search') {
110 _searchBar.style.setProperty('top', elById('pageHeader').offsetHeight + 'px', '');
111 _searchBar.classList.add('open');
112 _searchInput.focus();
113
114 data.handler.close(true);
115 }
116 });
117
118 _main.addEventListener(WCF_CLICK_EVENT, function() { _searchBar.classList.remove('open'); });
119 },
120
121 _initButtonGroupNavigation: function() {
122 for (var i = 0, length = _buttonGroupNavigations.length; i < length; i++) {
123 var navigation = _buttonGroupNavigations[i];
124
125 if (navigation.classList.contains('jsMobileButtonGroupNavigation')) continue;
126 else navigation.classList.add('jsMobileButtonGroupNavigation');
127
128 navigation.parentNode.classList.add('hasMobileNavigation');
129
130 var button = elCreate('a');
131 button.className = 'dropdownLabel';
132
133 var span = elCreate('span');
134 span.className = 'icon icon24 fa-ellipsis-v';
135 button.appendChild(span);
136
137 var list = elBySel('.buttonList', navigation);
138 list.addEventListener(WCF_CLICK_EVENT, function(event) {
139 event.stopPropagation();
140
141 navigation.classList.remove('open');
142 });
143
144 (function(navigation, button) {
145 button.addEventListener(WCF_CLICK_EVENT, function(event) {
146 event.preventDefault();
147 event.stopPropagation();
148
149 navigation.classList.toggle('open');
150 });
151 })(navigation, button);
152
153 navigation.insertBefore(button, navigation.firstChild);
154 }
155 },
156
157 _initMessages: function() {
158 Array.prototype.forEach.call(_messages, function(message) {
159 if (_knownMessages.has(message)) {
160 return;
161 }
162
163 var navigation = elBySel('.jsMobileNavigation', message);
164 var quickOptions = elBySel('.messageQuickOptions', message);
165
166 if (quickOptions) {
167 quickOptions.addEventListener(WCF_CLICK_EVENT, function (event) {
168 if (_enabled) {
169 event.preventDefault();
170 event.stopPropagation();
171
172 navigation.classList.toggle('open');
173 }
174 });
175 }
176 if (navigation) {
177 navigation.addEventListener(WCF_CLICK_EVENT, function(event) {
178 event.stopPropagation();
179
180 // mimic dropdown behavior
181 window.setTimeout(function () {
182 navigation.classList.remove('open');
183 }, 10);
184 });
185 }
186
187 _knownMessages.add(message);
188 });
189 },
190
191 _initMobileMenu: function() {
192 if (_options.enableMobileMenu) {
193 _pageMenuMain = new UiPageMenuMain();
194 _pageMenuUser = new UiPageMenuUser();
195 }
196
197 elBySelAll('.boxMenu', null, function(boxMenu) {
198 boxMenu.addEventListener(WCF_CLICK_EVENT, function(event) {
199 event.stopPropagation();
200
201 if (event.target === boxMenu) {
202 event.preventDefault();
203
204 boxMenu.classList.add('open');
205 }
206 });
207 });
208 },
209
210 _closeAllMenus: function() {
211 elBySelAll('.jsMobileButtonGroupNavigation.open, .jsMobileNavigation.open, .boxMenu.open', null, function (menu) {
212 menu.classList.remove('open');
213 });
214 },
215
216 rebuildShadow: function(elements, linkSelector) {
217 var element, parent, shadow;
218 for (var i = 0, length = elements.length; i < length; i++) {
219 element = elements[i];
220 parent = element.parentNode;
221
222 shadow = DomTraverse.childByClass(parent, 'mobileLinkShadow');
223 if (shadow === null) {
224 if (elBySel(linkSelector, element).href) {
225 shadow = elCreate('a');
226 shadow.className = 'mobileLinkShadow';
227 shadow.href = elBySel(linkSelector, element).href;
228
229 parent.appendChild(shadow);
230 parent.classList.add('mobileLinkShadowContainer');
231 }
232 }
233 }
234 },
235
236 removeShadow: function(elements) {
237 var element, parent, shadow;
238 for (var i = 0, length = elements.length; i < length; i++) {
239 element = elements[i];
240 parent = element.parentNode;
241
242 if (parent.classList.contains('mobileLinkShadowContainer')) {
243 shadow = DomTraverse.childByClass(parent, 'mobileLinkShadow');
244 if (shadow !== null) {
245 elRemove(shadow);
246 }
247
248 parent.classList.remove('mobileLinkShadowContainer');
249 }
250 }
251 }
252 };
253 });