2 * Modifies the interface to provide a better usability for mobile devices.
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
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
)
15 var _buttonGroupNavigations
= elByClass('buttonGroupNavigation');
17 var _knownMessages
= new List();
19 var _messages
= elByClass('message');
21 var _pageMenuMain
= null;
22 var _pageMenuUser
= null;
23 var _messageGroups
= null;
26 * @exports WoltLabSuite/Core/Ui/Mobile
30 * Initializes the mobile UI.
32 * @param {Object=} options initialization options
34 setup: function(options
) {
35 _options
= Core
.extend({
36 enableMobileMenu
: true
39 _main
= elById('main');
41 if (Environment
.touch()) {
42 document
.documentElement
.classList
.add('touch');
45 if (Environment
.platform() !== 'desktop') {
46 document
.documentElement
.classList
.add('mobile');
49 var messageGroupList
= elBySel('.messageGroupList');
50 if (messageGroupList
) _messageGroups
= elByClass('messageGroup', messageGroupList
);
52 UiScreen
.on('screen-md-down', {
53 match
: this.enable
.bind(this),
54 unmatch
: this.disable
.bind(this),
55 setup
: this._init
.bind(this)
58 UiScreen
.on('screen-sm-down', {
59 match
: this.enableShadow
.bind(this),
60 unmatch
: this.disableShadow
.bind(this),
61 setup
: this.enableShadow
.bind(this)
66 * Enables the mobile UI.
71 if (_options
.enableMobileMenu
) {
72 _pageMenuMain
.enable();
73 _pageMenuUser
.enable();
78 * Enables shadow links for larger click areas on messages.
80 enableShadow: function () {
81 if (_messageGroups
) this.rebuildShadow(_messageGroups
, '.messageGroupLink');
85 * Disables the mobile UI.
90 if (_options
.enableMobileMenu
) {
91 _pageMenuMain
.disable();
92 _pageMenuUser
.disable();
97 * Disables shadow links.
99 disableShadow: function () {
100 if (_messageGroups
) this.removeShadow(_messageGroups
);
106 this._initSearchBar();
107 this._initButtonGroupNavigation();
108 this._initMessages();
109 this._initMobileMenu();
111 UiCloseOverlay
.add('WoltLabSuite/Core/Ui/Mobile', this._closeAllMenus
.bind(this));
112 DomChangeListener
.add('WoltLabSuite/Core/Ui/Mobile', (function() {
113 this._initButtonGroupNavigation();
114 this._initMessages();
118 _initSearchBar: function() {
119 var _searchBar
= elById('pageHeaderSearch');
120 var _searchInput
= elById('pageHeaderSearchInput');
122 EventHandler
.add('com.woltlab.wcf.MainMenuMobile', 'more', function(data
) {
123 if (data
.identifier
=== 'com.woltlab.wcf.search') {
124 _searchBar
.style
.setProperty('top', elById('pageHeader').offsetHeight
+ 'px', '');
125 _searchBar
.classList
.add('open');
126 _searchInput
.focus();
128 data
.handler
.close(true);
132 _main
.addEventListener(WCF_CLICK_EVENT
, function() { _searchBar
.classList
.remove('open'); });
135 _initButtonGroupNavigation: function() {
136 for (var i
= 0, length
= _buttonGroupNavigations
.length
; i
< length
; i
++) {
137 var navigation
= _buttonGroupNavigations
[i
];
139 if (navigation
.classList
.contains('jsMobileButtonGroupNavigation')) continue;
140 else navigation
.classList
.add('jsMobileButtonGroupNavigation');
142 navigation
.parentNode
.classList
.add('hasMobileNavigation');
144 var button
= elCreate('a');
145 button
.className
= 'dropdownLabel';
147 var span
= elCreate('span');
148 span
.className
= 'icon icon24 fa-ellipsis-v';
149 button
.appendChild(span
);
151 (function(navigation
, button
) {
152 button
.addEventListener(WCF_CLICK_EVENT
, function(event
) {
153 event
.preventDefault();
154 event
.stopPropagation();
156 navigation
.classList
.toggle('open');
159 var list
= elBySel('.buttonList', navigation
);
160 list
.addEventListener(WCF_CLICK_EVENT
, function(event
) {
161 event
.stopPropagation();
163 navigation
.classList
.remove('open');
165 })(navigation
, button
);
167 navigation
.insertBefore(button
, navigation
.firstChild
);
171 _initMessages: function() {
172 Array
.prototype.forEach
.call(_messages
, function(message
) {
173 if (_knownMessages
.has(message
)) {
177 var navigation
= elBySel('.jsMobileNavigation', message
);
178 var quickOptions
= elBySel('.messageQuickOptions', message
);
181 quickOptions
.addEventListener(WCF_CLICK_EVENT
, function (event
) {
183 event
.preventDefault();
184 event
.stopPropagation();
186 navigation
.classList
.toggle('open');
191 navigation
.addEventListener(WCF_CLICK_EVENT
, function(event
) {
192 event
.stopPropagation();
194 // mimic dropdown behavior
195 window
.setTimeout(function () {
196 navigation
.classList
.remove('open');
201 _knownMessages
.add(message
);
205 _initMobileMenu: function() {
206 if (_options
.enableMobileMenu
) {
207 _pageMenuMain
= new UiPageMenuMain();
208 _pageMenuUser
= new UiPageMenuUser();
211 elBySelAll('.boxMenu', null, function(boxMenu
) {
212 boxMenu
.addEventListener(WCF_CLICK_EVENT
, function(event
) {
213 event
.stopPropagation();
215 if (event
.target
=== boxMenu
) {
216 event
.preventDefault();
218 boxMenu
.classList
.add('open');
224 _closeAllMenus: function() {
225 elBySelAll('.jsMobileButtonGroupNavigation.open, .jsMobileNavigation.open, .boxMenu.open', null, function (menu
) {
226 menu
.classList
.remove('open');
230 rebuildShadow: function(elements
, linkSelector
) {
231 var element
, parent
, shadow
;
232 for (var i
= 0, length
= elements
.length
; i
< length
; i
++) {
233 element
= elements
[i
];
234 parent
= element
.parentNode
;
236 shadow
= DomTraverse
.childByClass(parent
, 'mobileLinkShadow');
237 if (shadow
=== null) {
238 if (elBySel(linkSelector
, element
).href
) {
239 shadow
= elCreate('a');
240 shadow
.className
= 'mobileLinkShadow';
241 shadow
.href
= elBySel(linkSelector
, element
).href
;
243 parent
.appendChild(shadow
);
244 parent
.classList
.add('mobileLinkShadowContainer');
250 removeShadow: function(elements
) {
251 var element
, parent
, shadow
;
252 for (var i
= 0, length
= elements
.length
; i
< length
; i
++) {
253 element
= elements
[i
];
254 parent
= element
.parentNode
;
256 if (parent
.classList
.contains('mobileLinkShadowContainer')) {
257 shadow
= DomTraverse
.childByClass(parent
, 'mobileLinkShadow');
258 if (shadow
!== null) {
262 parent
.classList
.remove('mobileLinkShadowContainer');