Disables shadow links for screen-md
[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 UiScreen.on('screen-sm-down', {
59 match: this.enableShadow.bind(this),
60 unmatch: this.disableShadow.bind(this),
61 setup: this.enableShadow.bind(this)
62 });
63 },
64
65 /**
66 * Enables the mobile UI.
67 */
68 enable: function() {
69 _enabled = true;
70
71 if (_options.enableMobileMenu) {
72 _pageMenuMain.enable();
73 _pageMenuUser.enable();
74 }
75 },
76
77 /**
78 * Enables shadow links for larger click areas on messages.
79 */
80 enableShadow: function () {
81 if (_messageGroups) this.rebuildShadow(_messageGroups, '.messageGroupLink');
82 },
83
84 /**
85 * Disables the mobile UI.
86 */
87 disable: function() {
88 _enabled = false;
89
90 if (_options.enableMobileMenu) {
91 _pageMenuMain.disable();
92 _pageMenuUser.disable();
93 }
94 },
95
96 /**
97 * Disables shadow links.
98 */
99 disableShadow: function () {
100 if (_messageGroups) this.removeShadow(_messageGroups);
101 },
102
103 _init: function() {
104 _enabled = true;
105
106 this._initSearchBar();
107 this._initButtonGroupNavigation();
108 this._initMessages();
109 this._initMobileMenu();
110
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();
115 }).bind(this));
116 },
117
118 _initSearchBar: function() {
119 var _searchBar = elById('pageHeaderSearch');
120 var _searchInput = elById('pageHeaderSearchInput');
121
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();
127
128 data.handler.close(true);
129 }
130 });
131
132 _main.addEventListener(WCF_CLICK_EVENT, function() { _searchBar.classList.remove('open'); });
133 },
134
135 _initButtonGroupNavigation: function() {
136 for (var i = 0, length = _buttonGroupNavigations.length; i < length; i++) {
137 var navigation = _buttonGroupNavigations[i];
138
139 if (navigation.classList.contains('jsMobileButtonGroupNavigation')) continue;
140 else navigation.classList.add('jsMobileButtonGroupNavigation');
141
142 navigation.parentNode.classList.add('hasMobileNavigation');
143
144 var button = elCreate('a');
145 button.className = 'dropdownLabel';
146
147 var span = elCreate('span');
148 span.className = 'icon icon24 fa-ellipsis-v';
149 button.appendChild(span);
150
151 (function(navigation, button) {
152 button.addEventListener(WCF_CLICK_EVENT, function(event) {
153 event.preventDefault();
154 event.stopPropagation();
155
156 navigation.classList.toggle('open');
157 });
158
159 var list = elBySel('.buttonList', navigation);
160 list.addEventListener(WCF_CLICK_EVENT, function(event) {
161 event.stopPropagation();
162
163 navigation.classList.remove('open');
164 });
165 })(navigation, button);
166
167 navigation.insertBefore(button, navigation.firstChild);
168 }
169 },
170
171 _initMessages: function() {
172 Array.prototype.forEach.call(_messages, function(message) {
173 if (_knownMessages.has(message)) {
174 return;
175 }
176
177 var navigation = elBySel('.jsMobileNavigation', message);
178 var quickOptions = elBySel('.messageQuickOptions', message);
179
180 if (quickOptions) {
181 quickOptions.addEventListener(WCF_CLICK_EVENT, function (event) {
182 if (_enabled) {
183 event.preventDefault();
184 event.stopPropagation();
185
186 navigation.classList.toggle('open');
187 }
188 });
189 }
190 if (navigation) {
191 navigation.addEventListener(WCF_CLICK_EVENT, function(event) {
192 event.stopPropagation();
193
194 // mimic dropdown behavior
195 window.setTimeout(function () {
196 navigation.classList.remove('open');
197 }, 10);
198 });
199 }
200
201 _knownMessages.add(message);
202 });
203 },
204
205 _initMobileMenu: function() {
206 if (_options.enableMobileMenu) {
207 _pageMenuMain = new UiPageMenuMain();
208 _pageMenuUser = new UiPageMenuUser();
209 }
210
211 elBySelAll('.boxMenu', null, function(boxMenu) {
212 boxMenu.addEventListener(WCF_CLICK_EVENT, function(event) {
213 event.stopPropagation();
214
215 if (event.target === boxMenu) {
216 event.preventDefault();
217
218 boxMenu.classList.add('open');
219 }
220 });
221 });
222 },
223
224 _closeAllMenus: function() {
225 elBySelAll('.jsMobileButtonGroupNavigation.open, .jsMobileNavigation.open, .boxMenu.open', null, function (menu) {
226 menu.classList.remove('open');
227 });
228 },
229
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;
235
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;
242
243 parent.appendChild(shadow);
244 parent.classList.add('mobileLinkShadowContainer');
245 }
246 }
247 }
248 },
249
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;
255
256 if (parent.classList.contains('mobileLinkShadowContainer')) {
257 shadow = DomTraverse.childByClass(parent, 'mobileLinkShadow');
258 if (shadow !== null) {
259 elRemove(shadow);
260 }
261
262 parent.classList.remove('mobileLinkShadowContainer');
263 }
264 }
265 }
266 };
267 });