Commit | Line | Data |
---|---|---|
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 | 9 | define( |
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 | }); |