themes/default/scripts/theme.js   A
last analyzed

Complexity

Total Complexity 40
Complexity/F 2

Size

Lines of Code 236
Function Count 20

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 40
eloc 124
mnd 20
bc 20
fnc 20
dl 0
loc 236
rs 9.2
bpm 1
cpm 2
noi 7
c 0
b 0
f 0

4 Functions

Rating   Name   Duplication   Size   Complexity  
A theme.js ➔ onFirstTouch 0 4 1
A theme.js ➔ useClickMenu 0 9 2
F theme.js ➔ elk_addButton 0 42 32
A theme.js ➔ stickyMenu 0 19 5

How to fix   Complexity   

Complexity

Complex classes like themes/default/scripts/theme.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*!
2
 * @package   ElkArte Forum
3
 * @copyright ElkArte Forum contributors
4
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
5
 *
6
 * @version 2.0 dev
7
 */
8
9
/**
10
 * This file contains javascript associated with the current theme
11
 */
12
13
// Normal JS document ready event
14
document.addEventListener('DOMContentLoaded', function() {
15
16
	// If they touch the screen, then we switch to click menus
17
	window.addEventListener('touchstart', onFirstTouch, false);
18
19
	// Or if they specifically only want click menus
20
	if (use_click_menu)
0 ignored issues
show
Best Practice introduced by
If you intend to check if the variable use_click_menu is declared in the current environment, consider using typeof use_click_menu === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
21
	{
22
		useClickMenu();
23
	}
24
25
	// Fix code blocks so they are as compact as possible
26
	if (typeof elk_codefix === 'function')
0 ignored issues
show
Bug introduced by
The variable elk_codefix seems to be never declared. If this is a global, consider adding a /** global: elk_codefix */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
27
	{
28
		elk_codefix();
29
	}
30
31
	if (typeof elk_quotefix === 'function')
0 ignored issues
show
Bug introduced by
The variable elk_quotefix seems to be never declared. If this is a global, consider adding a /** global: elk_quotefix */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
32
	{
33
		elk_quotefix();
34
	}
35
36
	// If you want a sticky menu on scroll, add an appropriate .sticky css class to your theme
37
	stickyMenu();
38
39
	// Smooth scroll to top.
40
	document.getElementById('gotop').addEventListener('click', function(e) {
41
		e.preventDefault();
42
		window.scrollTo({top: 0, behavior: 'smooth'});
43
	});
44
45
	// Smooth scroll to bottom.
46
	document.getElementById('gobottom').addEventListener('click', function(e) {
47
		e.preventDefault();
48
49
		// Don't scroll all the way down to the footer, just the content bottom
50
		let link = document.querySelector('#footer_section'),
51
			linkY = link.offsetHeight,
52
			heightDiff = link.getBoundingClientRect().top + linkY - window.innerHeight;
53
54
		window.scrollBy({top: heightDiff, behavior: 'smooth'});
55
	});
56
57
	// Find all nested linked images and turn off the border
58
	let elements = document.querySelectorAll('a.bbc_link img.bbc_img');
59
	for (let i = 0; i < elements.length; i++)
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
60
	{
61
		let parentElement = elements[i].parentNode;
62
		parentElement.style.border = '0';
63
	}
64
65
	// Expand the moderation hamburger icon/button view for mobile devices
66
	let hamburger = document.querySelector('.hamburger_30');
67
	if (hamburger)
68
	{
69
		hamburger.addEventListener('click', function(e) {
70
			let id = this.getAttribute('data-id');
71
			e.preventDefault();
72
			document.getElementById(id).classList.add('visible');
73
			this.classList.add('visible');
74
		});
75
	}
76
77
	// Collapsible fieldsets, pure candy
78
	document.querySelector('body').addEventListener('click', function(event) {
79
		if (event.target.matches('legend'))
80
		{
81
			let siblings = elkGetSiblings(event.target);
82
			siblings.forEach(sib => sib.slideToggle());
83
			event.target.parentNode.classList.toggle('collapsed');
84
		}
85
	});
86
87
	// For any legends with data-collapsed="true", start them collapsed
88
	document.querySelectorAll('legend').forEach(function(el) {
89
		if (el.getAttribute('data-collapsed') !== null)
90
		{
91
			el.click();
92
		}
93
	});
94
95
	// Spoiler
96
	document.querySelectorAll('.spoilerheader').forEach(element => {
97
		element.addEventListener('click', function() {
98
			element.nextElementSibling.children[0].slideToggle(250);
99
		});
100
	});
101
});
102
103
// Jquery document ready
104
$(function() {
105
	// Enable the ... page expansion
106
	$('.expand_pages').expand_pages();
107
108
	// Attachment thumbnail expand on click, you can turn off this namespaced click
109
	// event with $('[data-lightboximage]').off('click.elk_lb');
110
	$('[data-lightboximage]').on('click.elk_lb', function(e) {
111
		e.preventDefault();
112
		expandThumbLB($(this).data('lightboximage'), $(this).data('lightboxmessage'));
113
	});
114
115
	// BBC [img] element toggle for height and width styles of an image.
116
	$('img').each(function() {
117
		// Not a resized image? Skip it.
118
		if ($(this).hasClass('bbc_img resized') === false)
119
		{
120
			return true;
121
		}
122
123
		$(this).css({'cursor': 'pointer'});
124
125
		// Note to addon authors, if you want to enable your own click events to bbc images
126
		// you can turn off this namespaced click event with $("img").off("click.elk_bbc")
127
		$(this).on('click.elk_bbc', function() {
128
			var $this = $(this);
129
130
			// No saved data, then lets set it to auto
131
			if ($.isEmptyObject($this.data('bbc_img')))
132
			{
133
				$this.data('bbc_img', {
134
					width: $this.css('width'),
135
					height: $this.css('height'),
136
					'max-width': $this.css('max-width'),
137
					'max-height': $this.css('max-height')
138
				});
139
				$this.css({'width': $this.css('width') === 'auto' ? null : 'auto'});
140
				$this.css({'height': $this.css('height') === 'auto' ? null : 'auto'});
141
142
				// Override default css to allow the image to expand fully, add a div to expand in
143
				$this.css({'max-height': 'none'});
144
				$this.css({'max-width': '100%'});
145
				$this.wrap('<div style="overflow:auto;display:inline-block;"></div>');
146
			}
147
			else
148
			{
149
				// Was clicked and saved, so set it back
150
				$this.css({'width': $this.data('bbc_img').width});
151
				$this.css({'height': $this.data('bbc_img').height});
152
				$this.css({'max-width': $this.data('bbc_img')['max-width']});
153
				$this.css({'max-height': $this.data('bbc_img')['max-height']});
154
155
				// Remove the data
156
				$this.removeData('bbc_img');
157
158
				// Remove the div we added to allow the image to overflow expand in
159
				$this.unwrap();
160
				$this.css({'max-width': '100%'});
161
			}
162
		});
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
163
	});
164
});
165
166
/**
167
 * Adds a button to the quick topic moderation after a checkbox is selected
168
 *
169
 * @param {string} sButtonStripId
170
 * @param {boolean} bUseImage
171
 * @param {object} oOptions
172
 */
173
function elk_addButton (sButtonStripId, bUseImage, oOptions)
174
{
175
	let oButtonStrip = document.getElementById(sButtonStripId),
176
		aItems = oButtonStrip.getElementsByTagName('span');
177
178
	// Remove the 'last' class from the last item.
179
	if (aItems.length > 0)
180
	{
181
		let oLastSpan = aItems[aItems.length - 1];
182
		oLastSpan.className = oLastSpan.className.replace(/\s*last/, 'position_holder');
183
	}
184
185
	// Add the button.
186
	let oButtonStripList = oButtonStrip.getElementsByTagName('ul')[0],
187
		oNewButton = document.createElement('li'),
188
		oRole = document.createAttribute('role');
189
190
	oRole.value = 'menuitem';
191
	oNewButton.setAttributeNode(oRole);
192
193
	if ('sId' in oOptions)
194
	{
195
		oNewButton.id = oOptions.sId;
196
	}
197
198
	oNewButton.innerHTML = '' +
199
		'<a class="linklevel1" href="' + oOptions.sUrl + '" ' + ('sCustom' in oOptions ? oOptions.sCustom : '') + '>' +
200
		('sImage' in oOptions && bUseImage ? '<i class="icon ' + oOptions.sImage + '"></i>' : '') +
201
		'   <span class="last"' + ('sId' in oOptions ? ' id="' + oOptions.sId + '_text"' : '') + '>' +
202
		oOptions.sText +
203
		'   </span>' +
204
		'</a>';
205
206
	if (oOptions.aEvents)
207
	{
208
		oOptions.aEvents.forEach(function(e) {
209
			oNewButton.addEventListener(e[0], e[1]);
210
		});
211
	}
212
213
	oButtonStripList.appendChild(oNewButton);
214
}
215
216
function onFirstTouch ()
217
{
218
	useClickMenu();
219
}
220
221
function useClickMenu ()
222
{
223
	// Click Menu drop downs
224
	let menus = ['#main_menu', '#sort_by', 'ul.poster', 'ul.quickbuttons', 'ul.admin_menu', 'ul.sidebar_menu'];
225
226
	menus.forEach((area) => new elkMenu(area));
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like elkMenu should be capitalized.
Loading history...
Bug introduced by
The variable elkMenu seems to be never declared. If this is a global, consider adding a /** global: elkMenu */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
227
228
	window.removeEventListener('touchstart', onFirstTouch, false);
229
}
230
231
function stickyMenu ()
232
{
233
	let menu = document.getElementById('menu_nav');
234
235
	if (menu)
236
	{
237
		let offset = menu.getBoundingClientRect().y;
238
		window.onscroll = function() {
239
			if (window.scrollY > offset - 5)
240
			{
241
				menu.classList.add('sticky');
242
			}
243
			else if (window.scrollY < offset - 20)
244
			{
245
				menu.classList.remove('sticky');
246
			}
247
		};
248
	}
249
}
250