Added sidebar toggle for screen-xs
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / WoltLabSuite / Core / Ui / Mobile.js
CommitLineData
4bbf6ff1
AE
1/**
2 * Modifies the interface to provide a better usability for mobile devices.
3 *
4 * @author Alexander Ebert
50d96bd8 5 * @copyright 2001-2017 WoltLab GmbH
4bbf6ff1 6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
58d7e8f8 7 * @module WoltLabSuite/Core/Ui/Mobile
4bbf6ff1 8 */
981a52f5 9define(
06cf7304
MW
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)
981a52f5 12{
565853e8
TD
13 "use strict";
14
9ae45102 15 var _buttonGroupNavigations = elByClass('buttonGroupNavigation');
4bbf6ff1 16 var _enabled = false;
9ae45102 17 var _knownMessages = new List();
4bbf6ff1 18 var _main = null;
9ae45102 19 var _messages = elByClass('message');
cbbb348c 20 var _options = {};
431e4cb4
AE
21 var _pageMenuMain = null;
22 var _pageMenuUser = null;
06cf7304 23 var _messageGroups = null;
c5ea84f0
AE
24 var _sidebars = [];
25 var _sidebarXsEnabled = false;
4bbf6ff1
AE
26
27 /**
58d7e8f8 28 * @exports WoltLabSuite/Core/Ui/Mobile
4bbf6ff1 29 */
431e4cb4 30 return {
4bbf6ff1 31 /**
cbbb348c
AE
32 * Initializes the mobile UI.
33 *
34 * @param {Object=} options initialization options
4bbf6ff1 35 */
cbbb348c
AE
36 setup: function(options) {
37 _options = Core.extend({
38 enableMobileMenu: true
39 }, options);
40
d0023381 41 _main = elById('main');
4bbf6ff1 42
c5ea84f0
AE
43 elBySelAll('.sidebar', undefined, function (sidebar) {
44 _sidebars.push(sidebar);
45 });
46
bd969ed4 47 if (Environment.touch()) {
4bbf6ff1
AE
48 document.documentElement.classList.add('touch');
49 }
50
bd969ed4
AE
51 if (Environment.platform() !== 'desktop') {
52 document.documentElement.classList.add('mobile');
53 }
54
06cf7304
MW
55 var messageGroupList = elBySel('.messageGroupList');
56 if (messageGroupList) _messageGroups = elByClass('messageGroup', messageGroupList);
57
6971725f
AE
58 UiScreen.on('screen-md-down', {
59 match: this.enable.bind(this),
60 unmatch: this.disable.bind(this),
431e4cb4 61 setup: this._init.bind(this)
4bbf6ff1 62 });
7ac7d09e
AE
63
64 UiScreen.on('screen-sm-down', {
65 match: this.enableShadow.bind(this),
66 unmatch: this.disableShadow.bind(this),
67 setup: this.enableShadow.bind(this)
68 });
c5ea84f0
AE
69
70 UiScreen.on('screen-xs', {
71 match: this._enableSidebarXS.bind(this),
72 unmatch: this._disableSidebarXS.bind(this),
73 setup: this._setupSidebarXS.bind(this)
74 });
4bbf6ff1
AE
75 },
76
77 /**
78 * Enables the mobile UI.
79 */
80 enable: function() {
81 _enabled = true;
82
cbbb348c
AE
83 if (_options.enableMobileMenu) {
84 _pageMenuMain.enable();
85 _pageMenuUser.enable();
86 }
7ac7d09e
AE
87 },
88
89 /**
90 * Enables shadow links for larger click areas on messages.
91 */
92 enableShadow: function () {
06cf7304 93 if (_messageGroups) this.rebuildShadow(_messageGroups, '.messageGroupLink');
4bbf6ff1
AE
94 },
95
96 /**
97 * Disables the mobile UI.
98 */
99 disable: function() {
100 _enabled = false;
101
cbbb348c
AE
102 if (_options.enableMobileMenu) {
103 _pageMenuMain.disable();
104 _pageMenuUser.disable();
105 }
7ac7d09e
AE
106 },
107
108 /**
109 * Disables shadow links.
110 */
111 disableShadow: function () {
06cf7304 112 if (_messageGroups) this.removeShadow(_messageGroups);
4bbf6ff1
AE
113 },
114
115 _init: function() {
9ae45102
AE
116 _enabled = true;
117
231008a5 118 this._initSearchBar();
4bbf6ff1 119 this._initButtonGroupNavigation();
9ae45102 120 this._initMessages();
431e4cb4 121 this._initMobileMenu();
4bbf6ff1 122
58d7e8f8 123 UiCloseOverlay.add('WoltLabSuite/Core/Ui/Mobile', this._closeAllMenus.bind(this));
8720ec09
MW
124 DomChangeListener.add('WoltLabSuite/Core/Ui/Mobile', (function() {
125 this._initButtonGroupNavigation();
126 this._initMessages();
127 }).bind(this));
4bbf6ff1
AE
128 },
129
4bbf6ff1 130 _initSearchBar: function() {
231008a5
AE
131 var _searchBar = elById('pageHeaderSearch');
132 var _searchInput = elById('pageHeaderSearchInput');
4bbf6ff1 133
374fb74a
AE
134 var scrollTop = null;
135
231008a5
AE
136 EventHandler.add('com.woltlab.wcf.MainMenuMobile', 'more', function(data) {
137 if (data.identifier === 'com.woltlab.wcf.search') {
374fb74a
AE
138 data.handler.close(true);
139
140 if (Environment.platform() === 'ios') {
141 scrollTop = document.body.scrollTop;
142 UiScreen.scrollDisable();
143 }
144
231008a5
AE
145 _searchBar.style.setProperty('top', elById('pageHeader').offsetHeight + 'px', '');
146 _searchBar.classList.add('open');
147 _searchInput.focus();
4bbf6ff1 148
374fb74a
AE
149 if (Environment.platform() === 'ios') {
150 document.body.scrollTop = 0;
151 }
4bbf6ff1 152 }
4bbf6ff1
AE
153 });
154
374fb74a 155 _main.addEventListener(WCF_CLICK_EVENT, function() {
339d8264 156 if (_searchBar) _searchBar.classList.remove('open');
374fb74a 157
565b59ca 158 if (Environment.platform() === 'ios' && scrollTop !== null) {
374fb74a
AE
159 UiScreen.scrollEnable();
160 document.body.scrollTop = scrollTop;
565b59ca
AE
161
162 scrollTop = null;
374fb74a
AE
163 }
164 });
4bbf6ff1
AE
165 },
166
167 _initButtonGroupNavigation: function() {
168 for (var i = 0, length = _buttonGroupNavigations.length; i < length; i++) {
169 var navigation = _buttonGroupNavigations[i];
170
171 if (navigation.classList.contains('jsMobileButtonGroupNavigation')) continue;
172 else navigation.classList.add('jsMobileButtonGroupNavigation');
173
b583b2f3
AE
174 var list = elBySel('.buttonList', navigation);
175 if (list.childElementCount === 0) {
176 // ignore objects without options
177 continue;
178 }
179
f5ba6d2c
AE
180 navigation.parentNode.classList.add('hasMobileNavigation');
181
d0023381 182 var button = elCreate('a');
f5ba6d2c 183 button.className = 'dropdownLabel';
4bbf6ff1 184
d0023381 185 var span = elCreate('span');
f5ba6d2c 186 span.className = 'icon icon24 fa-ellipsis-v';
4bbf6ff1
AE
187 button.appendChild(span);
188
b583b2f3 189 (function(navigation, button, list) {
f5ba6d2c
AE
190 button.addEventListener(WCF_CLICK_EVENT, function(event) {
191 event.preventDefault();
192 event.stopPropagation();
4bbf6ff1 193
f5ba6d2c 194 navigation.classList.toggle('open');
431e4cb4 195 });
95652251 196
95652251
AE
197 list.addEventListener(WCF_CLICK_EVENT, function(event) {
198 event.stopPropagation();
199
200 navigation.classList.remove('open');
201 });
b583b2f3 202 })(navigation, button, list);
4bbf6ff1
AE
203
204 navigation.insertBefore(button, navigation.firstChild);
205 }
206 },
207
9ae45102
AE
208 _initMessages: function() {
209 Array.prototype.forEach.call(_messages, function(message) {
210 if (_knownMessages.has(message)) {
211 return;
212 }
213
214 var navigation = elBySel('.jsMobileNavigation', message);
51267670 215 if (navigation) {
9ae45102
AE
216 navigation.addEventListener(WCF_CLICK_EVENT, function(event) {
217 event.stopPropagation();
ab57bfdd
AE
218
219 // mimic dropdown behavior
220 window.setTimeout(function () {
221 navigation.classList.remove('open');
222 }, 10);
9ae45102 223 });
9c38465c
AE
224
225 var quickOptions = elBySel('.messageQuickOptions', message);
9b42b443
AE
226 if (quickOptions && navigation.childElementCount) {
227 quickOptions.classList.add('active');
228 quickOptions.addEventListener(WCF_CLICK_EVENT, function (event) {
41b57040 229 if (_enabled && event.target.nodeName !== 'LABEL') {
9b42b443
AE
230 event.preventDefault();
231 event.stopPropagation();
232
233 navigation.classList.toggle('open');
234 }
235 });
9c38465c 236 }
9ae45102
AE
237 }
238
239 _knownMessages.add(message);
240 });
241 },
242
431e4cb4 243 _initMobileMenu: function() {
cbbb348c
AE
244 if (_options.enableMobileMenu) {
245 _pageMenuMain = new UiPageMenuMain();
246 _pageMenuUser = new UiPageMenuUser();
247 }
d9a5604a 248
9384e330 249 elBySelAll('.boxMenu:not(.forceOpen)', null, function(boxMenu) {
d9a5604a
AE
250 boxMenu.addEventListener(WCF_CLICK_EVENT, function(event) {
251 event.stopPropagation();
252
253 if (event.target === boxMenu) {
254 event.preventDefault();
255
256 boxMenu.classList.add('open');
257 }
258 });
259 });
431e4cb4
AE
260 },
261
4bbf6ff1 262 _closeAllMenus: function() {
9ae45102 263 elBySelAll('.jsMobileButtonGroupNavigation.open, .jsMobileNavigation.open, .boxMenu.open', null, function (menu) {
d9a5604a
AE
264 menu.classList.remove('open');
265 });
06cf7304
MW
266 },
267
268 rebuildShadow: function(elements, linkSelector) {
269 var element, parent, shadow;
270 for (var i = 0, length = elements.length; i < length; i++) {
271 element = elements[i];
272 parent = element.parentNode;
273
274 shadow = DomTraverse.childByClass(parent, 'mobileLinkShadow');
275 if (shadow === null) {
d0f167f3
MW
276 if (elBySel(linkSelector, element).href) {
277 shadow = elCreate('a');
278 shadow.className = 'mobileLinkShadow';
279 shadow.href = elBySel(linkSelector, element).href;
280
281 parent.appendChild(shadow);
282 parent.classList.add('mobileLinkShadowContainer');
283 }
06cf7304
MW
284 }
285 }
286 },
287
288 removeShadow: function(elements) {
289 var element, parent, shadow;
290 for (var i = 0, length = elements.length; i < length; i++) {
291 element = elements[i];
292 parent = element.parentNode;
293
294 if (parent.classList.contains('mobileLinkShadowContainer')) {
295 shadow = DomTraverse.childByClass(parent, 'mobileLinkShadow');
296 if (shadow !== null) {
297 elRemove(shadow);
298 }
299
300 parent.classList.remove('mobileLinkShadowContainer');
301 }
302 }
c5ea84f0
AE
303 },
304
305 _enableSidebarXS: function() {
306 _sidebarXsEnabled = true;
307 },
308
309 _disableSidebarXS: function() {
310 _sidebarXsEnabled = false;
311
312 _sidebars.forEach(function (sidebar) {
313 sidebar.classList.remove('open');
314 });
315 },
316
317 _setupSidebarXS: function() {
318 _sidebars.forEach(function (sidebar) {
319 sidebar.addEventListener('mousedown', function(event) {
320 if (_sidebarXsEnabled && event.target === sidebar) {
321 event.preventDefault();
322
323 sidebar.classList.toggle('open');
324 }
325 });
326 });
327
328 _sidebarXsEnabled = true;
4bbf6ff1
AE
329 }
330 };
4bbf6ff1 331});