Completed
Pull Request — release-2.1 (#4471)
by Scott
08:27
created

script.js ➔ ... ➔ setTimeout   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 4
rs 10
1
var smf_formSubmitted = false;
2
var lastKeepAliveCheck = new Date().getTime();
3
var smf_editorArray = new Array();
4
5
// Some very basic browser detection - from Mozilla's sniffer page.
6
var ua = navigator.userAgent.toLowerCase();
7
8
var is_opera = ua.indexOf('opera') != -1;
9
var is_ff = (ua.indexOf('firefox') != -1 || ua.indexOf('iceweasel') != -1 || ua.indexOf('icecat') != -1 || ua.indexOf('shiretoko') != -1 || ua.indexOf('minefield') != -1) && !is_opera;
10
var is_gecko = ua.indexOf('gecko') != -1 && !is_opera;
11
12
var is_chrome = ua.indexOf('chrome') != -1;
13
var is_safari = ua.indexOf('applewebkit') != -1 && !is_chrome;
14
var is_webkit = ua.indexOf('applewebkit') != -1;
15
16
var is_ie = ua.indexOf('msie') != -1 && !is_opera;
17
// Stupid Microsoft...
18
var is_ie11 = ua.indexOf('trident') != -1 && ua.indexOf('gecko') != -1;
19
var is_iphone = ua.indexOf('iphone') != -1 || ua.indexOf('ipod') != -1;
20
var is_android = ua.indexOf('android') != -1;
21
22
var ajax_indicator_ele = null;
23
24
// Some older versions of Mozilla don't have this, for some reason.
25
if (!('forms' in document))
26
	document.forms = document.getElementsByTagName('form');
27
28
// Versions of ie < 9 do not have this built in
29
if (!('getElementsByClassName' in document))
30
{
31
	document.getElementsByClassName = function(className)
32
	{
33
		return $('".' + className + '"');
34
	}
35
}
36
37
// Get a response from the server.
38
function getServerResponse(sUrl, funcCallback, sType, sDataType)
39
{
40
	var oCaller = this;
41
	var oMyDoc = $.ajax({
42
		type: sType,
43
		url: sUrl,
44
		cache: false,
45
		dataType: sDataType,
46
		success: function(response) {
47
			if (typeof(funcCallback) != 'undefined')
48
			{
49
				funcCallback.call(oCaller, response);
50
			}
51
		},
52
	});
53
54
	return oMyDoc;
55
}
56
57
// Load an XML document.
58
function getXMLDocument(sUrl, funcCallback)
59
{
60
	var oCaller = this;
61
	var oMyDoc = $.ajax({
62
		type: 'GET',
63
		url: sUrl,
64
		cache: false,
65
		dataType: 'xml',
66
		success: function(responseXML) {
67
			if (typeof(funcCallback) != 'undefined')
68
			{
69
				funcCallback.call(oCaller, responseXML);
70
			}
71
		},
72
	});
73
74
	return oMyDoc;
75
}
76
77
// Send a post form to the server.
78
function sendXMLDocument(sUrl, sContent, funcCallback)
79
{
80
	var oCaller = this;
81
	var oSendDoc = $.ajax({
0 ignored issues
show
Unused Code introduced by
The variable oSendDoc seems to be never used. Consider removing it.
Loading history...
82
		type: 'POST',
83
		url: sUrl,
84
		data: sContent,
85
		beforeSend: function(xhr) {
86
			xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
87
		},
88
		dataType: 'xml',
89
		success: function(responseXML) {
90
			if (typeof(funcCallback) != 'undefined')
91
			{
92
				funcCallback.call(oCaller, responseXML);
93
			}
94
		},
95
	});
96
97
	return true;
98
}
99
100
// A property we'll be needing for php_to8bit.
101
String.prototype.oCharsetConversion = {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
102
	from: '',
103
	to: ''
104
};
105
106
// Convert a string to an 8 bit representation (like in PHP).
107
String.prototype.php_to8bit = function ()
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
108
{
109
	if (smf_charset == 'UTF-8')
110
	{
111
		var n, sReturn = '';
112
113
		for (var i = 0, iTextLen = this.length; i < iTextLen; i++)
114
		{
115
			n = this.charCodeAt(i);
116
			if (n < 128)
117
				sReturn += String.fromCharCode(n);
118
			else if (n < 2048)
119
				sReturn += String.fromCharCode(192 | n >> 6) + String.fromCharCode(128 | n & 63);
120
			else if (n < 65536)
121
				sReturn += String.fromCharCode(224 | n >> 12) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63);
122
			else
123
				sReturn += String.fromCharCode(240 | n >> 18) + String.fromCharCode(128 | n >> 12 & 63) + String.fromCharCode(128 | n >> 6 & 63) + String.fromCharCode(128 | n & 63);
124
		}
125
126
		return sReturn;
127
	}
128
129
	else if (this.oCharsetConversion.from.length == 0)
130
	{
131
		switch (smf_charset)
132
		{
133
			case 'ISO-8859-1':
134
				this.oCharsetConversion = {
135
					from: '\xa0-\xff',
136
					to: '\xa0-\xff'
137
				};
138
			break;
139
140
			case 'ISO-8859-2':
141
				this.oCharsetConversion = {
142
					from: '\xa0\u0104\u02d8\u0141\xa4\u013d\u015a\xa7\xa8\u0160\u015e\u0164\u0179\xad\u017d\u017b\xb0\u0105\u02db\u0142\xb4\u013e\u015b\u02c7\xb8\u0161\u015f\u0165\u017a\u02dd\u017e\u017c\u0154\xc1\xc2\u0102\xc4\u0139\u0106\xc7\u010c\xc9\u0118\xcb\u011a\xcd\xce\u010e\u0110\u0143\u0147\xd3\xd4\u0150\xd6\xd7\u0158\u016e\xda\u0170\xdc\xdd\u0162\xdf\u0155\xe1\xe2\u0103\xe4\u013a\u0107\xe7\u010d\xe9\u0119\xeb\u011b\xed\xee\u010f\u0111\u0144\u0148\xf3\xf4\u0151\xf6\xf7\u0159\u016f\xfa\u0171\xfc\xfd\u0163\u02d9',
143
					to: '\xa0-\xff'
144
				};
145
			break;
146
147
			case 'ISO-8859-5':
148
				this.oCharsetConversion = {
149
					from: '\xa0\u0401-\u040c\xad\u040e-\u044f\u2116\u0451-\u045c\xa7\u045e\u045f',
150
					to: '\xa0-\xff'
151
				};
152
			break;
153
154
			case 'ISO-8859-9':
155
				this.oCharsetConversion = {
156
					from: '\xa0-\xcf\u011e\xd1-\xdc\u0130\u015e\xdf-\xef\u011f\xf1-\xfc\u0131\u015f\xff',
157
					to: '\xa0-\xff'
158
				};
159
			break;
160
161
			case 'ISO-8859-15':
162
				this.oCharsetConversion = {
163
					from: '\xa0-\xa3\u20ac\xa5\u0160\xa7\u0161\xa9-\xb3\u017d\xb5-\xb7\u017e\xb9-\xbb\u0152\u0153\u0178\xbf-\xff',
164
					to: '\xa0-\xff'
165
				};
166
			break;
167
168
			case 'tis-620':
169
				this.oCharsetConversion = {
170
					from: '\u20ac\u2026\u2018\u2019\u201c\u201d\u2022\u2013\u2014\xa0\u0e01-\u0e3a\u0e3f-\u0e5b',
171
					to: '\x80\x85\x91-\x97\xa0-\xda\xdf-\xfb'
172
				};
173
			break;
174
175
			case 'windows-1251':
176
				this.oCharsetConversion = {
177
					from: '\u0402\u0403\u201a\u0453\u201e\u2026\u2020\u2021\u20ac\u2030\u0409\u2039\u040a\u040c\u040b\u040f\u0452\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u0459\u203a\u045a\u045c\u045b\u045f\xa0\u040e\u045e\u0408\xa4\u0490\xa6\xa7\u0401\xa9\u0404\xab-\xae\u0407\xb0\xb1\u0406\u0456\u0491\xb5-\xb7\u0451\u2116\u0454\xbb\u0458\u0405\u0455\u0457\u0410-\u044f',
178
					to: '\x80-\x97\x99-\xff'
179
				};
180
			break;
181
182
			case 'windows-1253':
183
				this.oCharsetConversion = {
184
					from: '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u2122\u203a\xa0\u0385\u0386\xa3-\xa9\xab-\xae\u2015\xb0-\xb3\u0384\xb5-\xb7\u0388-\u038a\xbb\u038c\xbd\u038e-\u03a1\u03a3-\u03ce',
185
					to: '\x80\x82-\x87\x89\x8b\x91-\x97\x99\x9b\xa0-\xa9\xab-\xd1\xd3-\xfe'
186
				};
187
			break;
188
189
			case 'windows-1255':
190
				this.oCharsetConversion = {
191
					from: '\u20ac\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u2039\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\u203a\xa0-\xa3\u20aa\xa5-\xa9\xd7\xab-\xb9\xf7\xbb-\xbf\u05b0-\u05b9\u05bb-\u05c3\u05f0-\u05f4\u05d0-\u05ea\u200e\u200f',
192
					to: '\x80\x82-\x89\x8b\x91-\x99\x9b\xa0-\xc9\xcb-\xd8\xe0-\xfa\xfd\xfe'
193
				};
194
			break;
195
196
			case 'windows-1256':
197
				this.oCharsetConversion = {
198
					from: '\u20ac\u067e\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0679\u2039\u0152\u0686\u0698\u0688\u06af\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u06a9\u2122\u0691\u203a\u0153\u200c\u200d\u06ba\xa0\u060c\xa2-\xa9\u06be\xab-\xb9\u061b\xbb-\xbe\u061f\u06c1\u0621-\u0636\xd7\u0637-\u063a\u0640-\u0643\xe0\u0644\xe2\u0645-\u0648\xe7-\xeb\u0649\u064a\xee\xef\u064b-\u064e\xf4\u064f\u0650\xf7\u0651\xf9\u0652\xfb\xfc\u200e\u200f\u06d2',
199
					to: '\x80-\xff'
200
				};
201
			break;
202
203
			default:
204
				this.oCharsetConversion = {
205
					from: '',
206
					to: ''
207
				};
208
			break;
209
		}
210
		var funcExpandString = function (sSearch) {
211
			var sInsert = '';
212
			for (var i = sSearch.charCodeAt(0), n = sSearch.charCodeAt(2); i <= n; i++)
213
				sInsert += String.fromCharCode(i);
214
			return sInsert;
215
		};
216
		this.oCharsetConversion.from = this.oCharsetConversion.from.replace(/.\-./g, funcExpandString);
217
		this.oCharsetConversion.to = this.oCharsetConversion.to.replace(/.\-./g, funcExpandString);
218
	}
219
220
	var sReturn = '', iOffsetFrom = 0;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable sReturn already seems to be declared on line 111. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
221
	for (var i = 0, n = this.length; i < n; i++)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable n already seems to be declared on line 111. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 113. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
222
	{
223
		iOffsetFrom = this.oCharsetConversion.from.indexOf(this.charAt(i));
224
		sReturn += iOffsetFrom > -1 ? this.oCharsetConversion.to.charAt(iOffsetFrom) : (this.charCodeAt(i) > 127 ? '&#' + this.charCodeAt(i) + ';' : this.charAt(i));
225
	}
226
227
	return sReturn
228
}
229
230
// Character-level replacement function.
231
String.prototype.php_strtr = function (sFrom, sTo)
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
232
{
233
	return this.replace(new RegExp('[' + sFrom + ']', 'g'), function (sMatch) {
234
		return sTo.charAt(sFrom.indexOf(sMatch));
235
	});
236
}
237
238
// Simulate PHP's strtolower (in SOME cases PHP uses ISO-8859-1 case folding).
239
String.prototype.php_strtolower = function ()
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
240
{
241
	return typeof(smf_iso_case_folding) == 'boolean' && smf_iso_case_folding == true ? this.php_strtr(
242
		'ABCDEFGHIJKLMNOPQRSTUVWXYZ\x8a\x8c\x8e\x9f\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde',
243
		'abcdefghijklmnopqrstuvwxyz\x9a\x9c\x9e\xff\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe'
244
	) : this.php_strtr('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
245
}
246
247
String.prototype.php_urlencode = function()
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
248
{
249
	return escape(this).replace(/\+/g, '%2b').replace('*', '%2a').replace('/', '%2f').replace('@', '%40');
250
}
251
252
String.prototype.php_htmlspecialchars = function()
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
253
{
254
	return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
255
}
256
257
String.prototype.php_unhtmlspecialchars = function()
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
258
{
259
	return this.replace(/&quot;/g, '"').replace(/&gt;/g, '>').replace(/&lt;/g, '<').replace(/&amp;/g, '&');
260
}
261
262
String.prototype.php_addslashes = function()
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
263
{
264
	return this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'');
265
}
266
267
String.prototype._replaceEntities = function(sInput, sDummy, sNum)
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
268
{
269
	return String.fromCharCode(parseInt(sNum));
270
}
271
272
String.prototype.removeEntities = function()
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
273
{
274
	return this.replace(/&(amp;)?#(\d+);/g, this._replaceEntities);
275
}
276
277
String.prototype.easyReplace = function (oReplacements)
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type String. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
278
{
279
	var sResult = this;
280
	for (var sSearch in oReplacements)
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
281
		sResult = sResult.replace(new RegExp('%' + sSearch + '%', 'g'), oReplacements[sSearch]);
282
283
	return sResult;
284
}
285
286
// Open a new window
287
function reqWin(desktopURL, alternateWidth, alternateHeight, noScrollbars)
288
{
289
	if ((alternateWidth && self.screen.availWidth * 0.8 < alternateWidth) || (alternateHeight && self.screen.availHeight * 0.8 < alternateHeight))
290
	{
291
		noScrollbars = false;
292
		alternateWidth = Math.min(alternateWidth, self.screen.availWidth * 0.8);
293
		alternateHeight = Math.min(alternateHeight, self.screen.availHeight * 0.8);
294
	}
295
	else
296
		noScrollbars = typeof(noScrollbars) == 'boolean' && noScrollbars == true;
297
298
	window.open(desktopURL, 'requested_popup', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=' + (noScrollbars ? 'no' : 'yes') + ',width=' + (alternateWidth ? alternateWidth : 480) + ',height=' + (alternateHeight ? alternateHeight : 220) + ',resizable=no');
299
300
	// Return false so the click won't follow the link ;).
301
	return false;
302
}
303
304
// Open a overlay div
305
function reqOverlayDiv(desktopURL, sHeader, sIcon)
306
{
307
	// Set up our div details
308
	var sAjax_indicator = '<div class="centertext"><img src="' + smf_images_url + '/loading_sm.gif"></div>';
309
	var sIcon = smf_images_url + '/' + (typeof(sIcon) == 'string' ? sIcon : 'helptopics.png');
310
	var sHeader = typeof(sHeader) == 'string' ? sHeader : help_popup_heading_text;
311
312
	// Create the div that we are going to load
313
	var oContainer = new smc_Popup({heading: sHeader, content: sAjax_indicator, icon: sIcon});
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like smc_Popup should be capitalized.
Loading history...
314
	var oPopup_body = $('#' + oContainer.popup_id).find('.popup_content');
315
316
	// Load the help page content (we just want the text to show)
317
	$.ajax({
318
		url: desktopURL,
319
		type: "GET",
320
		dataType: "html",
321
		beforeSend: function () {
322
		},
323
		success: function (data, textStatus, xhr) {
0 ignored issues
show
Unused Code introduced by
The parameter xhr is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter textStatus is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
324
			var help_content = $('<div id="temp_help">').html(data).find('a[href$="self.close();"]').hide().prev('br').hide().parent().html();
325
			oPopup_body.html(help_content);
326
		},
327
		error: function (xhr, textStatus, errorThrown) {
0 ignored issues
show
Unused Code introduced by
The parameter errorThrown is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
328
			oPopup_body.html(textStatus);
329
		}
330
	});
331
	return false;
332
}
333
334
// Create the popup menus for the top level/user menu area.
335
function smc_PopupMenu(oOptions)
336
{
337
	this.opt = (typeof oOptions == 'object') ? oOptions : {};
338
	this.opt.menus = {};
339
}
340
341
smc_PopupMenu.prototype.add = function (sItem, sUrl)
342
{
343
	var $menu = $('#' + sItem + '_menu'), $item = $('#' + sItem + '_menu_top');
344
	if ($item.length == 0)
345
		return;
346
347
	this.opt.menus[sItem] = {open: false, loaded: false, sUrl: sUrl, itemObj: $item, menuObj: $menu };
348
349
	$item.click({obj: this}, function (e) {
350
		e.preventDefault();
351
352
		e.data.obj.toggle(sItem);
353
	});
354
}
355
356
smc_PopupMenu.prototype.toggle = function (sItem)
357
{
358
	if (!!this.opt.menus[sItem].open)
359
		this.close(sItem);
360
	else
361
		this.open(sItem);
362
}
363
364
smc_PopupMenu.prototype.open = function (sItem)
365
{
366
	this.closeAll();
367
368
	if (!this.opt.menus[sItem].loaded)
369
	{
370
		this.opt.menus[sItem].menuObj.html('<div class="loading">' + (typeof(ajax_notification_text) != null ? ajax_notification_text : '') + '</div>');
371
		this.opt.menus[sItem].menuObj.load(this.opt.menus[sItem].sUrl, function() {
372
			if ($(this).hasClass('scrollable'))
373
				$(this).customScrollbar({
374
					skin: "default-skin",
375
					hScroll: false,
376
					updateOnWindowResize: true
377
				});
378
		});
379
		this.opt.menus[sItem].loaded = true;
380
	}
381
382
	this.opt.menus[sItem].menuObj.addClass('visible');
383
	this.opt.menus[sItem].itemObj.addClass('open');
384
	this.opt.menus[sItem].open = true;
385
386
	// Now set up closing the menu if we click off.
387
	$(document).on('click.menu', {obj: this}, function(e) {
388
		if ($(e.target).closest(e.data.obj.opt.menus[sItem].menuObj.parent()).length)
389
			return;
390
		e.data.obj.closeAll();
391
		$(document).off('click.menu');
392
	});
393
}
394
395
smc_PopupMenu.prototype.close = function (sItem)
396
{
397
	this.opt.menus[sItem].menuObj.removeClass('visible');
398
	this.opt.menus[sItem].itemObj.removeClass('open');
399
	this.opt.menus[sItem].open = false;
400
	$(document).off('click.menu');
401
}
402
403
smc_PopupMenu.prototype.closeAll = function ()
404
{
405
	for (var prop in this.opt.menus)
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
406
		if (!!this.opt.menus[prop].open)
407
			this.close(prop);
408
}
409
410
// *** smc_Popup class.
411
function smc_Popup(oOptions)
412
{
413
	this.opt = oOptions;
414
	this.popup_id = this.opt.custom_id ? this.opt.custom_id : 'smf_popup';
415
	this.show();
416
}
417
418
smc_Popup.prototype.show = function ()
419
{
420
	popup_class = 'popup_window ' + (this.opt.custom_class ? this.opt.custom_class : 'description');
0 ignored issues
show
Bug introduced by
The variable popup_class seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.popup_class.
Loading history...
421
	if (this.opt.icon_class)
422
		icon = '<span class="' + this.opt.icon_class + '"></span> ';
0 ignored issues
show
Bug introduced by
The variable icon seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.icon.
Loading history...
423
	else
424
		icon = this.opt.icon ? '<img src="' + this.opt.icon + '" class="icon" alt=""> ' : '';
425
426
	// Create the div that will be shown
427
	$('body').append('<div id="' + this.popup_id + '" class="popup_container"><div class="' + popup_class + '"><div class="catbg popup_heading"><a href="javascript:void(0);" class="generic_icons hide_popup"></a>' + icon + this.opt.heading + '</div><div class="popup_content">' + this.opt.content + '</div></div></div>');
428
429
	// Show it
430
	this.popup_body = $('#' + this.popup_id).children('.popup_window');
431
	this.popup_body.parent().fadeIn(300);
432
433
	// Trigger hide on escape or mouse click
434
	var popup_instance = this;
435
	$(document).mouseup(function (e) {
436
		if ($('#' + popup_instance.popup_id).has(e.target).length === 0)
437
			popup_instance.hide();
438
	}).keyup(function(e){
439
		if (e.keyCode == 27)
440
			popup_instance.hide();
441
	});
442
	$('#' + this.popup_id).find('.hide_popup').click(function (){ return popup_instance.hide(); });
443
444
	return false;
445
}
446
447
smc_Popup.prototype.hide = function ()
448
{
449
	$('#' + this.popup_id).fadeOut(300, function(){ $(this).remove(); });
450
451
	return false;
452
}
453
454
// Remember the current position.
455
function storeCaret(oTextHandle)
456
{
457
	// Only bother if it will be useful.
458
	if ('createTextRange' in oTextHandle)
459
		oTextHandle.caretPos = document.selection.createRange().duplicate();
460
}
461
462
// Replaces the currently selected text with the passed text.
463
function replaceText(text, oTextHandle)
464
{
465
	// Attempt to create a text range (IE).
466
	if ('caretPos' in oTextHandle && 'createTextRange' in oTextHandle)
467
	{
468
		var caretPos = oTextHandle.caretPos;
469
470
		caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text;
471
		caretPos.select();
472
	}
473
	// Mozilla text range replace.
474
	else if ('selectionStart' in oTextHandle)
475
	{
476
		var begin = oTextHandle.value.substr(0, oTextHandle.selectionStart);
477
		var end = oTextHandle.value.substr(oTextHandle.selectionEnd);
478
		var scrollPos = oTextHandle.scrollTop;
479
480
		oTextHandle.value = begin + text + end;
481
482
		if (oTextHandle.setSelectionRange)
483
		{
484
			oTextHandle.focus();
485
			var goForward = is_opera ? text.match(/\n/g).length : 0;
486
			oTextHandle.setSelectionRange(begin.length + text.length + goForward, begin.length + text.length + goForward);
487
		}
488
		oTextHandle.scrollTop = scrollPos;
489
	}
490
	// Just put it on the end.
491
	else
492
	{
493
		oTextHandle.value += text;
494
		oTextHandle.focus(oTextHandle.value.length - 1);
495
	}
496
}
497
498
// Surrounds the selected text with text1 and text2.
499
function surroundText(text1, text2, oTextHandle)
500
{
501
	// Can a text range be created?
502
	if ('caretPos' in oTextHandle && 'createTextRange' in oTextHandle)
503
	{
504
		var caretPos = oTextHandle.caretPos, temp_length = caretPos.text.length;
505
506
		caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text1 + caretPos.text + text2 + ' ' : text1 + caretPos.text + text2;
507
508
		if (temp_length == 0)
509
		{
510
			caretPos.moveStart('character', -text2.length);
511
			caretPos.moveEnd('character', -text2.length);
512
			caretPos.select();
513
		}
514
		else
515
			oTextHandle.focus(caretPos);
516
	}
517
	// Mozilla text range wrap.
518
	else if ('selectionStart' in oTextHandle)
519
	{
520
		var begin = oTextHandle.value.substr(0, oTextHandle.selectionStart);
521
		var selection = oTextHandle.value.substr(oTextHandle.selectionStart, oTextHandle.selectionEnd - oTextHandle.selectionStart);
522
		var end = oTextHandle.value.substr(oTextHandle.selectionEnd);
523
		var newCursorPos = oTextHandle.selectionStart;
524
		var scrollPos = oTextHandle.scrollTop;
525
526
		oTextHandle.value = begin + text1 + selection + text2 + end;
527
528
		if (oTextHandle.setSelectionRange)
529
		{
530
			var goForward = is_opera ? text1.match(/\n/g).length : 0, goForwardAll = is_opera ? (text1 + text2).match(/\n/g).length : 0;
531
			if (selection.length == 0)
532
				oTextHandle.setSelectionRange(newCursorPos + text1.length + goForward, newCursorPos + text1.length + goForward);
533
			else
534
				oTextHandle.setSelectionRange(newCursorPos, newCursorPos + text1.length + selection.length + text2.length + goForwardAll);
535
			oTextHandle.focus();
536
		}
537
		oTextHandle.scrollTop = scrollPos;
538
	}
539
	// Just put them on the end, then.
540
	else
541
	{
542
		oTextHandle.value += text1 + text2;
543
		oTextHandle.focus(oTextHandle.value.length - 1);
544
	}
545
}
546
547
// Checks if the passed input's value is nothing.
548
function isEmptyText(theField)
549
{
550
	// Copy the value so changes can be made..
551
	if (typeof(theField) == 'string')
552
		var theValue = theField;
553
	else
554
		var theValue = theField.value;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable theValue already seems to be declared on line 552. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
555
556
	// Strip whitespace off the left side.
557
	while (theValue.length > 0 && (theValue.charAt(0) == ' ' || theValue.charAt(0) == '\t'))
558
		theValue = theValue.substring(1, theValue.length);
559
	// Strip whitespace off the right side.
560
	while (theValue.length > 0 && (theValue.charAt(theValue.length - 1) == ' ' || theValue.charAt(theValue.length - 1) == '\t'))
561
		theValue = theValue.substring(0, theValue.length - 1);
562
563
	if (theValue == '')
564
		return true;
565
	else
566
		return false;
567
}
568
569
// Only allow form submission ONCE.
570
function submitonce(theform)
0 ignored issues
show
Unused Code introduced by
The parameter theform is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
571
{
572
	smf_formSubmitted = true;
573
574
	// If there are any editors warn them submit is coming!
575
	for (var i = 0; i < smf_editorArray.length; i++)
576
		smf_editorArray[i].doSubmit();
577
}
578
function submitThisOnce(oControl)
579
{
580
	// oControl might also be a form.
581
	var oForm = 'form' in oControl ? oControl.form : oControl;
582
583
	var aTextareas = oForm.getElementsByTagName('textarea');
584
	for (var i = 0, n = aTextareas.length; i < n; i++)
585
		aTextareas[i].readOnly = true;
586
587
	return !smf_formSubmitted;
588
}
589
590
// Deprecated, as innerHTML is supported everywhere.
591
function setInnerHTML(oElement, sToValue)
592
{
593
	oElement.innerHTML = sToValue;
594
}
595
596
function getInnerHTML(oElement)
597
{
598
	return oElement.innerHTML;
599
}
600
601
// Set the "outer" HTML of an element.
602
function setOuterHTML(oElement, sToValue)
603
{
604
	if ('outerHTML' in oElement)
605
		oElement.outerHTML = sToValue;
606
	else
607
	{
608
		var range = document.createRange();
609
		range.setStartBefore(oElement);
610
		oElement.parentNode.replaceChild(range.createContextualFragment(sToValue), oElement);
611
	}
612
}
613
614
// Checks for variable in theArray.
615
function in_array(variable, theArray)
616
{
617
	for (var i in theArray)
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
618
		if (theArray[i] == variable)
619
			return true;
620
621
	return false;
622
}
623
624
// Checks for variable in theArray.
625
function array_search(variable, theArray)
626
{
627
	for (var i in theArray)
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
628
		if (theArray[i] == variable)
629
			return i;
630
631
	return null;
632
}
633
634
// Find a specific radio button in its group and select it.
635
function selectRadioByName(oRadioGroup, sName)
636
{
637
	if (!('length' in oRadioGroup))
638
		return oRadioGroup.checked = true;
639
640
	for (var i = 0, n = oRadioGroup.length; i < n; i++)
641
		if (oRadioGroup[i].value == sName)
642
			return oRadioGroup[i].checked = true;
643
644
	return false;
645
}
646
647
function selectAllRadio(oInvertCheckbox, oForm, sMask, sValue, bIgnoreDisabled)
648
{
649
	for (var i = 0; i < oForm.length; i++)
650
		if (oForm[i].name != undefined && oForm[i].name.substr(0, sMask.length) == sMask && oForm[i].value == sValue && (!oForm[i].disabled || (typeof(bIgnoreDisabled) == 'boolean' && bIgnoreDisabled)))
651
			oForm[i].checked = true;
652
}
653
654
// Invert all checkboxes at once by clicking a single checkbox.
655
function invertAll(oInvertCheckbox, oForm, sMask, bIgnoreDisabled)
656
{
657
	for (var i = 0; i < oForm.length; i++)
658
	{
659
		if (!('name' in oForm[i]) || (typeof(sMask) == 'string' && oForm[i].name.substr(0, sMask.length) != sMask && oForm[i].id.substr(0, sMask.length) != sMask))
660
			continue;
661
662
		if (!oForm[i].disabled || (typeof(bIgnoreDisabled) == 'boolean' && bIgnoreDisabled))
663
			oForm[i].checked = oInvertCheckbox.checked;
664
	}
665
}
666
667
// Keep the session alive - always!
668
var lastKeepAliveCheck = new Date().getTime();
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable lastKeepAliveCheck already seems to be declared on line 2. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
669
function smf_sessionKeepAlive()
670
{
671
	var curTime = new Date().getTime();
672
673
	// Prevent a Firefox bug from hammering the server.
674
	if (smf_scripturl && curTime - lastKeepAliveCheck > 900000)
0 ignored issues
show
Best Practice introduced by
If you intend to check if the variable smf_scripturl is declared in the current environment, consider using typeof smf_scripturl === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
675
	{
676
		var tempImage = new Image();
677
		tempImage.src = smf_prepareScriptUrl(smf_scripturl) + 'action=keepalive;time=' + curTime;
678
		lastKeepAliveCheck = curTime;
679
	}
680
681
	window.setTimeout('smf_sessionKeepAlive();', 1200000);
0 ignored issues
show
introduced by
Using setTimeout with a string argument to run code is slow and may pose a security risk. Consider using a function instead.
Loading history...
682
}
683
window.setTimeout('smf_sessionKeepAlive();', 1200000);
0 ignored issues
show
introduced by
Using setTimeout with a string argument to run code is slow and may pose a security risk. Consider using a function instead.
Loading history...
684
685
// Set a theme option through javascript.
686
function smf_setThemeOption(theme_var, theme_value, theme_id, theme_cur_session_id, theme_cur_session_var, theme_additional_vars)
687
{
688
	// Compatibility.
689
	if (theme_cur_session_id == null)
690
		theme_cur_session_id = smf_session_id;
691
	if (typeof(theme_cur_session_var) == 'undefined')
692
		theme_cur_session_var = 'sesc';
693
694
	if (theme_additional_vars == null)
695
		theme_additional_vars = '';
696
697
	var tempImage = new Image();
698
	tempImage.src = smf_prepareScriptUrl(smf_scripturl) + 'action=jsoption;var=' + theme_var + ';val=' + theme_value + ';' + theme_cur_session_var + '=' + theme_cur_session_id + theme_additional_vars + (theme_id == null ? '' : '&th=' + theme_id) + ';time=' + (new Date().getTime());
699
}
700
701
// Shows the page numbers by clicking the dots (in compact view).
702
function expandPages(spanNode, baseLink, firstPage, lastPage, perPage)
703
{
704
	var replacement = '', i, oldLastPage = 0;
705
	var perPageLimit = 50;
706
707
	// Prevent too many pages to be loaded at once.
708
	if ((lastPage - firstPage) / perPage > perPageLimit)
709
	{
710
		oldLastPage = lastPage;
711
		lastPage = firstPage + perPageLimit * perPage;
712
	}
713
714
	// Calculate the new pages.
715
	for (i = firstPage; i < lastPage; i += perPage)
716
		replacement += baseLink.replace(/%1\$d/, i).replace(/%2\$s/, 1 + i / perPage).replace(/%%/g, '%');
717
718
	// Add the new page links.
719
	$(spanNode).before(replacement);
720
721
	if (oldLastPage)
722
		// Access the raw DOM element so the native onclick event can be overridden.
723
		spanNode.onclick = function ()
724
		{
725
			expandPages(spanNode, baseLink, lastPage, oldLastPage, perPage);
726
		};
727
	else
728
		$(spanNode).remove();
729
}
730
731
function smc_preCacheImage(sSrc)
732
{
733
	if (!('smc_aCachedImages' in window))
734
		window.smc_aCachedImages = [];
735
736
	if (!in_array(sSrc, window.smc_aCachedImages))
737
	{
738
		var oImage = new Image();
739
		oImage.src = sSrc;
740
	}
741
}
742
743
744
// *** smc_Cookie class.
745
function smc_Cookie(oOptions)
746
{
747
	this.opt = oOptions;
748
	this.oCookies = {};
749
	this.init();
750
}
751
752
smc_Cookie.prototype.init = function()
753
{
754
	if ('cookie' in document && document.cookie != '')
755
	{
756
		var aCookieList = document.cookie.split(';');
757
		for (var i = 0, n = aCookieList.length; i < n; i++)
758
		{
759
			var aNameValuePair = aCookieList[i].split('=');
760
			this.oCookies[aNameValuePair[0].replace(/^\s+|\s+$/g, '')] = decodeURIComponent(aNameValuePair[1]);
761
		}
762
	}
763
}
764
765
smc_Cookie.prototype.get = function(sKey)
766
{
767
	return sKey in this.oCookies ? this.oCookies[sKey] : null;
768
}
769
770
smc_Cookie.prototype.set = function(sKey, sValue)
771
{
772
	document.cookie = sKey + '=' + encodeURIComponent(sValue);
773
}
774
775
776
// *** smc_Toggle class.
777
function smc_Toggle(oOptions)
778
{
779
	this.opt = oOptions;
780
	this.bCollapsed = false;
781
	this.oCookie = null;
782
	this.init();
783
}
784
785
smc_Toggle.prototype.init = function ()
786
{
787
	// The master switch can disable this toggle fully.
788
	if ('bToggleEnabled' in this.opt && !this.opt.bToggleEnabled)
789
		return;
790
791
	// If cookies are enabled and they were set, override the initial state.
792
	if ('oCookieOptions' in this.opt && this.opt.oCookieOptions.bUseCookie)
793
	{
794
		// Initialize the cookie handler.
795
		this.oCookie = new smc_Cookie({});
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like smc_Cookie should be capitalized.
Loading history...
796
797
		// Check if the cookie is set.
798
		var cookieValue = this.oCookie.get(this.opt.oCookieOptions.sCookieName)
799
		if (cookieValue != null)
800
			this.opt.bCurrentlyCollapsed = cookieValue == '1';
801
	}
802
803
	// Initialize the images to be clickable.
804
	if ('aSwapImages' in this.opt)
805
	{
806
		for (var i = 0, n = this.opt.aSwapImages.length; i < n; i++)
807
		{
808
			this.opt.aSwapImages[i].isCSS = (typeof this.opt.aSwapImages[i].srcCollapsed == 'undefined');
809
			if (this.opt.aSwapImages[i].isCSS)
810
			{
811
				if (!this.opt.aSwapImages[i].cssCollapsed)
812
					this.opt.aSwapImages[i].cssCollapsed = 'toggle_down';
813
				if (!this.opt.aSwapImages[i].cssExpanded)
814
					this.opt.aSwapImages[i].cssExpanded = 'toggle_up';
815
			}
816
			else
817
			{
818
				// Preload the collapsed image.
819
				smc_preCacheImage(this.opt.aSwapImages[i].srcCollapsed);
820
			}
821
822
			// Display the image in case it was hidden.
823
			$('#' + this.opt.aSwapImages[i].sId).show();
824
			var oImage = document.getElementById(this.opt.aSwapImages[i].sId);
825
			if (typeof(oImage) == 'object' && oImage != null)
826
			{
827
				oImage.instanceRef = this;
828
				oImage.onclick = function () {
829
					this.instanceRef.toggle();
830
					this.blur();
831
				}
832
				oImage.style.cursor = 'pointer';
833
			}
834
		}
835
	}
836
837
	// Initialize links.
838
	if ('aSwapLinks' in this.opt)
839
	{
840
		for (var i = 0, n = this.opt.aSwapLinks.length; i < n; i++)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable n already seems to be declared on line 806. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 806. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
841
		{
842
			var oLink = document.getElementById(this.opt.aSwapLinks[i].sId);
843
			if (typeof(oLink) == 'object' && oLink != null)
844
			{
845
				// Display the link in case it was hidden.
846
				if (oLink.style.display == 'none')
847
					oLink.style.display = '';
848
849
				oLink.instanceRef = this;
850
				oLink.onclick = function () {
851
					this.instanceRef.toggle();
852
					this.blur();
853
					return false;
854
				}
855
			}
856
		}
857
	}
858
859
	// If the init state is set to be collapsed, collapse it.
860
	if (this.opt.bCurrentlyCollapsed)
861
		this.changeState(true, true);
862
}
863
864
// Collapse or expand the section.
865
smc_Toggle.prototype.changeState = function(bCollapse, bInit)
866
{
867
	// Default bInit to false.
868
	bInit = typeof(bInit) == 'undefined' ? false : true;
869
870
	// Handle custom function hook before collapse.
871
	if (!bInit && bCollapse && 'funcOnBeforeCollapse' in this.opt)
872
	{
873
		this.tmpMethod = this.opt.funcOnBeforeCollapse;
874
		this.tmpMethod();
875
		delete this.tmpMethod;
876
	}
877
878
	// Handle custom function hook before expand.
879
	else if (!bInit && !bCollapse && 'funcOnBeforeExpand' in this.opt)
880
	{
881
		this.tmpMethod = this.opt.funcOnBeforeExpand;
882
		this.tmpMethod();
883
		delete this.tmpMethod;
884
	}
885
886
	// Loop through all the images that need to be toggled.
887
	if ('aSwapImages' in this.opt)
888
	{
889
		for (var i = 0, n = this.opt.aSwapImages.length; i < n; i++)
890
		{
891
			if (this.opt.aSwapImages[i].isCSS)
892
			{
893
				$('#' + this.opt.aSwapImages[i].sId).toggleClass(this.opt.aSwapImages[i].cssCollapsed, bCollapse).toggleClass(this.opt.aSwapImages[i].cssExpanded, !bCollapse).attr('title', bCollapse ? this.opt.aSwapImages[i].altCollapsed : this.opt.aSwapImages[i].altExpanded);
894
			}
895
			else
896
			{
897
				var oImage = document.getElementById(this.opt.aSwapImages[i].sId);
898
				if (typeof(oImage) == 'object' && oImage != null)
899
				{
900
					// Only (re)load the image if it's changed.
901
					var sTargetSource = bCollapse ? this.opt.aSwapImages[i].srcCollapsed : this.opt.aSwapImages[i].srcExpanded;
902
					if (oImage.src != sTargetSource)
903
						oImage.src = sTargetSource;
904
905
					oImage.alt = oImage.title = bCollapse ? this.opt.aSwapImages[i].altCollapsed : this.opt.aSwapImages[i].altExpanded;
906
				}
907
			}
908
		}
909
	}
910
911
	// Loop through all the links that need to be toggled.
912
	if ('aSwapLinks' in this.opt)
913
	{
914
		for (var i = 0, n = this.opt.aSwapLinks.length; i < n; i++)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable n already seems to be declared on line 889. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 889. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
915
		{
916
			var oLink = document.getElementById(this.opt.aSwapLinks[i].sId);
917
			if (typeof(oLink) == 'object' && oLink != null)
918
				setInnerHTML(oLink, bCollapse ? this.opt.aSwapLinks[i].msgCollapsed : this.opt.aSwapLinks[i].msgExpanded);
919
		}
920
	}
921
922
	// Now go through all the sections to be collapsed.
923
	for (var i = 0, n = this.opt.aSwappableContainers.length; i < n; i++)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 889. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
Comprehensibility Naming Best Practice introduced by
The variable n already seems to be declared on line 889. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
924
	{
925
		if (this.opt.aSwappableContainers[i] == null)
926
			continue;
927
928
		var oContainer = document.getElementById(this.opt.aSwappableContainers[i]);
929
		if (typeof(oContainer) == 'object' && oContainer != null)
930
		{
931
			if (!!this.opt.bNoAnimate || bInit)
932
			{
933
				$(oContainer).toggle(!bCollapse);
934
			}
935
			else
936
			{
937
				if (bCollapse)
938
				{
939
					if (this.opt.aHeader != null && this.opt.aHeader.hasClass('cat_bar'))
940
						$(this.opt.aHeader).addClass('collapsed');
941
					$(oContainer).slideUp();
942
				}
943
				else
944
				{
945
					if (this.opt.aHeader != null && this.opt.aHeader.hasClass('cat_bar'))
946
						$(this.opt.aHeader).removeClass('collapsed');
947
					$(oContainer).slideDown();
948
				}
949
			}
950
		}
951
	}
952
953
	// Update the new state.
954
	this.bCollapsed = bCollapse;
955
956
	// Update the cookie, if desired.
957
	if ('oCookieOptions' in this.opt && this.opt.oCookieOptions.bUseCookie)
958
		this.oCookie.set(this.opt.oCookieOptions.sCookieName, this.bCollapsed | 0);
959
960
	if (!bInit && 'oThemeOptions' in this.opt && this.opt.oThemeOptions.bUseThemeSettings)
961
		smf_setThemeOption(this.opt.oThemeOptions.sOptionName, this.bCollapsed | 0, 'sThemeId' in this.opt.oThemeOptions ? this.opt.oThemeOptions.sThemeId : null, smf_session_id, smf_session_var, 'sAdditionalVars' in this.opt.oThemeOptions ? this.opt.oThemeOptions.sAdditionalVars : null);
962
}
963
964
smc_Toggle.prototype.toggle = function()
965
{
966
	// Change the state by reversing the current state.
967
	this.changeState(!this.bCollapsed);
968
}
969
970
971
function ajax_indicator(turn_on)
972
{
973
	if (ajax_indicator_ele == null)
974
	{
975
		ajax_indicator_ele = document.getElementById('ajax_in_progress');
976
977
		if (ajax_indicator_ele == null && typeof(ajax_notification_text) != null)
978
		{
979
			create_ajax_indicator_ele();
980
		}
981
	}
982
983
	if (ajax_indicator_ele != null)
984
	{
985
		ajax_indicator_ele.style.display = turn_on ? 'block' : 'none';
986
	}
987
}
988
989
function create_ajax_indicator_ele()
990
{
991
	// Create the div for the indicator.
992
	ajax_indicator_ele = document.createElement('div');
993
994
	// Set the id so it'll load the style properly.
995
	ajax_indicator_ele.id = 'ajax_in_progress';
996
997
	// Set the text.  (Note:  You MUST append here and not overwrite.)
998
	ajax_indicator_ele.innerHTML += ajax_notification_text;
999
1000
	// Finally attach the element to the body.
1001
	document.body.appendChild(ajax_indicator_ele);
1002
}
1003
1004
function createEventListener(oTarget)
1005
{
1006
	if (!('addEventListener' in oTarget))
1007
	{
1008
		if (oTarget.attachEvent)
1009
		{
1010
			oTarget.addEventListener = function (sEvent, funcHandler, bCapture) {
0 ignored issues
show
Unused Code introduced by
The parameter bCapture is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1011
				oTarget.attachEvent('on' + sEvent, funcHandler);
1012
			}
1013
			oTarget.removeEventListener = function (sEvent, funcHandler, bCapture) {
0 ignored issues
show
Unused Code introduced by
The parameter bCapture is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1014
				oTarget.detachEvent('on' + sEvent, funcHandler);
1015
			}
1016
		}
1017
		else
1018
		{
1019
			oTarget.addEventListener = function (sEvent, funcHandler, bCapture) {
0 ignored issues
show
Unused Code introduced by
The parameter bCapture is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1020
				oTarget['on' + sEvent] = funcHandler;
1021
			}
1022
			oTarget.removeEventListener = function (sEvent, funcHandler, bCapture) {
0 ignored issues
show
Unused Code introduced by
The parameter funcHandler is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter bCapture is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1023
				oTarget['on' + sEvent] = null;
1024
			}
1025
		}
1026
	}
1027
}
1028
1029
// This function will retrieve the contents needed for the jump to boxes.
1030
function grabJumpToContent(elem)
0 ignored issues
show
Unused Code introduced by
The parameter elem is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1031
{
1032
	var oXMLDoc = getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + 'action=xmlhttp;sa=jumpto;xml');
1033
	var aBoardsAndCategories = [];
1034
1035
	ajax_indicator(true);
1036
1037
	oXMLDoc.done(function(data, textStatus, jqXHR){
0 ignored issues
show
Unused Code introduced by
The parameter textStatus is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter jqXHR is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1038
1039
		var items = $(data).find('item');
1040
			items.each(function(i) {
1041
			aBoardsAndCategories[i] = {
1042
				id: parseInt($(this).attr('id')),
1043
				isCategory: $(this).attr('type') == 'category',
1044
				name: this.firstChild.nodeValue.removeEntities(),
1045
				is_current: false,
1046
				childLevel: parseInt($(this).attr('childlevel'))
1047
			}
1048
		});
1049
1050
		ajax_indicator(false);
1051
1052
		for (var i = 0, n = aJumpTo.length; i < n; i++)
1053
		{
1054
			aJumpTo[i].fillSelect(aBoardsAndCategories);
1055
		}
1056
	});
1057
}
1058
1059
// This'll contain all JumpTo objects on the page.
1060
var aJumpTo = new Array();
1061
1062
// *** JumpTo class.
1063
function JumpTo(oJumpToOptions)
1064
{
1065
	this.opt = oJumpToOptions;
1066
	this.dropdownList = null;
1067
	this.showSelect();
1068
1069
	// Register a change event after the select has been created.
1070
	$('#' + this.opt.sContainerId).one('mouseenter', function() {
1071
		grabJumpToContent(this);
1072
	});
1073
}
1074
1075
// Show the initial select box (onload). Method of the JumpTo class.
1076
JumpTo.prototype.showSelect = function ()
1077
{
1078
	var sChildLevelPrefix = '';
1079
	for (var i = this.opt.iCurBoardChildLevel; i > 0; i--)
1080
		sChildLevelPrefix += this.opt.sBoardChildLevelIndicator;
1081
	setInnerHTML(document.getElementById(this.opt.sContainerId), this.opt.sJumpToTemplate.replace(/%select_id%/, this.opt.sContainerId + '_select').replace(/%dropdown_list%/, '<select ' + (this.opt.bDisabled == true ? 'disabled ' : '') + (this.opt.sClassName != undefined ? 'class="' + this.opt.sClassName + '" ' : '') + 'name="' + (this.opt.sCustomName != undefined ? this.opt.sCustomName : this.opt.sContainerId + '_select') + '" id="' + this.opt.sContainerId + '_select"><option value="' + (this.opt.bNoRedirect != undefined && this.opt.bNoRedirect == true ? this.opt.iCurBoardId : '?board=' + this.opt.iCurBoardId + '.0') + '">' + sChildLevelPrefix + this.opt.sBoardPrefix + this.opt.sCurBoardName.removeEntities() + '</option></select>&nbsp;' + (this.opt.sGoButtonLabel != undefined ? '<input type="button" class="button" value="' + this.opt.sGoButtonLabel + '" onclick="window.location.href = \'' + smf_prepareScriptUrl(smf_scripturl) + 'board=' + this.opt.iCurBoardId + '.0\';">' : '')));
1082
	this.dropdownList = document.getElementById(this.opt.sContainerId + '_select');
1083
}
1084
1085
// Fill the jump to box with entries. Method of the JumpTo class.
1086
JumpTo.prototype.fillSelect = function (aBoardsAndCategories)
1087
{
1088
	var iIndexPointer = 0;
0 ignored issues
show
Unused Code introduced by
The variable iIndexPointer seems to be never used. Consider removing it.
Loading history...
1089
1090
	// Create an option that'll be above and below the category.
1091
	var oDashOption = document.createElement('option');
1092
	oDashOption.appendChild(document.createTextNode(this.opt.sCatSeparator));
1093
	oDashOption.disabled = 'disabled';
1094
	oDashOption.value = '';
1095
1096
	if ('onbeforeactivate' in document)
1097
		this.dropdownList.onbeforeactivate = null;
1098
	else
1099
		this.dropdownList.onfocus = null;
1100
1101
	if (this.opt.bNoRedirect)
1102
		this.dropdownList.options[0].disabled = 'disabled';
1103
1104
	// Create a document fragment that'll allowing inserting big parts at once.
1105
	var oListFragment = document.createDocumentFragment();
1106
1107
	// Loop through all items to be added.
1108
	for (var i = 0, n = aBoardsAndCategories.length; i < n; i++)
1109
	{
1110
		var j, sChildLevelPrefix, oOption;
1111
1112
		// If we've reached the currently selected board add all items so far.
1113
		if (!aBoardsAndCategories[i].isCategory && aBoardsAndCategories[i].id == this.opt.iCurBoardId)
1114
		{
1115
				this.dropdownList.insertBefore(oListFragment, this.dropdownList.options[0]);
1116
				oListFragment = document.createDocumentFragment();
1117
				continue;
1118
		}
1119
1120
		if (aBoardsAndCategories[i].isCategory)
1121
			oListFragment.appendChild(oDashOption.cloneNode(true));
1122
		else
1123
			for (j = aBoardsAndCategories[i].childLevel, sChildLevelPrefix = ''; j > 0; j--)
1124
				sChildLevelPrefix += this.opt.sBoardChildLevelIndicator;
1125
1126
		oOption = document.createElement('option');
1127
		oOption.appendChild(document.createTextNode((aBoardsAndCategories[i].isCategory ? this.opt.sCatPrefix : sChildLevelPrefix + this.opt.sBoardPrefix) + aBoardsAndCategories[i].name));
0 ignored issues
show
Bug introduced by
The variable sChildLevelPrefix seems to not be initialized for all possible execution paths.
Loading history...
1128
		if (!this.opt.bNoRedirect)
1129
			oOption.value = aBoardsAndCategories[i].isCategory ? '#c' + aBoardsAndCategories[i].id : '?board=' + aBoardsAndCategories[i].id + '.0';
1130
		else
1131
		{
1132
			if (aBoardsAndCategories[i].isCategory)
1133
				oOption.disabled = 'disabled';
1134
			else
1135
				oOption.value = aBoardsAndCategories[i].id;
1136
		}
1137
		oListFragment.appendChild(oOption);
1138
1139
		if (aBoardsAndCategories[i].isCategory)
1140
			oListFragment.appendChild(oDashOption.cloneNode(true));
1141
	}
1142
1143
	// Add the remaining items after the currently selected item.
1144
	this.dropdownList.appendChild(oListFragment);
1145
1146
	// Add an onchange action
1147
	if (!this.opt.bNoRedirect)
1148
		this.dropdownList.onchange = function() {
1149
			if (this.selectedIndex > 0 && this.options[this.selectedIndex].value)
1150
				window.location.href = smf_scripturl + this.options[this.selectedIndex].value.substr(smf_scripturl.indexOf('?') == -1 || this.options[this.selectedIndex].value.substr(0, 1) != '?' ? 0 : 1);
1151
		}
1152
}
1153
1154
// A global array containing all IconList objects.
1155
var aIconLists = new Array();
1156
1157
// *** IconList object.
1158
function IconList(oOptions)
1159
{
1160
	if (!window.XMLHttpRequest)
1161
		return;
1162
1163
	this.opt = oOptions;
1164
	this.bListLoaded = false;
1165
	this.oContainerDiv = null;
1166
	this.funcMousedownHandler = null;
1167
	this.funcParent = this;
1168
	this.iCurMessageId = 0;
1169
	this.iCurTimeout = 0;
1170
1171
	// Add backwards compatibility with old themes.
1172
	if (!('sSessionVar' in this.opt))
1173
		this.opt.sSessionVar = 'sesc';
1174
1175
	this.initIcons();
1176
}
1177
1178
// Replace all message icons by icons with hoverable and clickable div's.
1179
IconList.prototype.initIcons = function ()
1180
{
1181
	for (var i = document.images.length - 1, iPrefixLength = this.opt.sIconIdPrefix.length; i >= 0; i--)
1182
		if (document.images[i].id.substr(0, iPrefixLength) == this.opt.sIconIdPrefix)
1183
			setOuterHTML(document.images[i], '<div title="' + this.opt.sLabelIconList + '" onclick="' + this.opt.sBackReference + '.openPopup(this, ' + document.images[i].id.substr(iPrefixLength) + ')" onmouseover="' + this.opt.sBackReference + '.onBoxHover(this, true)" onmouseout="' + this.opt.sBackReference + '.onBoxHover(this, false)" style="background: ' + this.opt.sBoxBackground + '; cursor: pointer; padding: 3px; text-align: center;"><img src="' + document.images[i].src + '" alt="' + document.images[i].alt + '" id="' + document.images[i].id + '"></div>');
1184
}
1185
1186
// Event for the mouse hovering over the original icon.
1187
IconList.prototype.onBoxHover = function (oDiv, bMouseOver)
1188
{
1189
	oDiv.style.border = bMouseOver ? this.opt.iBoxBorderWidthHover + 'px solid ' + this.opt.sBoxBorderColorHover : '';
1190
	oDiv.style.background = bMouseOver ? this.opt.sBoxBackgroundHover : this.opt.sBoxBackground;
1191
	oDiv.style.padding = bMouseOver ? (3 - this.opt.iBoxBorderWidthHover) + 'px' : '3px'
1192
}
1193
1194
// Show the list of icons after the user clicked the original icon.
1195
IconList.prototype.openPopup = function (oDiv, iMessageId)
1196
{
1197
	this.iCurMessageId = iMessageId;
1198
1199
	if (!this.bListLoaded && this.oContainerDiv == null)
1200
	{
1201
		// Create a container div.
1202
		this.oContainerDiv = document.createElement('div');
1203
		this.oContainerDiv.id = 'iconList';
1204
		this.oContainerDiv.style.display = 'none';
1205
		this.oContainerDiv.style.cursor = 'pointer';
1206
		this.oContainerDiv.style.position = 'absolute';
1207
		this.oContainerDiv.style.background = this.opt.sContainerBackground;
1208
		this.oContainerDiv.style.border = this.opt.sContainerBorder;
1209
		this.oContainerDiv.style.padding = '6px 0px';
1210
		document.body.appendChild(this.oContainerDiv);
1211
1212
		// Start to fetch its contents.
1213
		ajax_indicator(true);
1214
		sendXMLDocument.call(this, smf_prepareScriptUrl(smf_scripturl) + 'action=xmlhttp;sa=messageicons;board=' + this.opt.iBoardId + ';xml', '', this.onIconsReceived);
1215
1216
		createEventListener(document.body);
1217
	}
1218
1219
	// Set the position of the container.
1220
	var aPos = smf_itemPos(oDiv);
1221
1222
	this.oContainerDiv.style.top = (aPos[1] + oDiv.offsetHeight) + 'px';
1223
	this.oContainerDiv.style.left = (aPos[0] - 1) + 'px';
1224
	this.oClickedIcon = oDiv;
1225
1226
	if (this.bListLoaded)
1227
		this.oContainerDiv.style.display = 'block';
1228
1229
	document.body.addEventListener('mousedown', this.onWindowMouseDown, false);
1230
}
1231
1232
// Setup the list of icons once it is received through xmlHTTP.
1233
IconList.prototype.onIconsReceived = function (oXMLDoc)
1234
{
1235
	var icons = oXMLDoc.getElementsByTagName('smf')[0].getElementsByTagName('icon');
1236
	var sItems = '';
1237
1238
	for (var i = 0, n = icons.length; i < n; i++)
1239
		sItems += '<span onmouseover="' + this.opt.sBackReference + '.onItemHover(this, true)" onmouseout="' + this.opt.sBackReference + '.onItemHover(this, false);" onmousedown="' + this.opt.sBackReference + '.onItemMouseDown(this, \'' + icons[i].getAttribute('value') + '\');" style="padding: 2px 3px; line-height: 20px; border: ' + this.opt.sItemBorder + '; background: ' + this.opt.sItemBackground + '"><img src="' + icons[i].getAttribute('url') + '" alt="' + icons[i].getAttribute('name') + '" title="' + icons[i].firstChild.nodeValue + '" style="vertical-align: middle"></span>';
1240
1241
	setInnerHTML(this.oContainerDiv, sItems);
1242
	this.oContainerDiv.style.display = 'block';
1243
	this.bListLoaded = true;
1244
1245
	if (is_ie)
1246
		this.oContainerDiv.style.width = this.oContainerDiv.clientWidth + 'px';
1247
1248
	ajax_indicator(false);
1249
}
1250
1251
// Event handler for hovering over the icons.
1252
IconList.prototype.onItemHover = function (oDiv, bMouseOver)
1253
{
1254
	oDiv.style.background = bMouseOver ? this.opt.sItemBackgroundHover : this.opt.sItemBackground;
1255
	oDiv.style.border = bMouseOver ? this.opt.sItemBorderHover : this.opt.sItemBorder;
1256
	if (this.iCurTimeout != 0)
1257
		window.clearTimeout(this.iCurTimeout);
1258
	if (bMouseOver)
1259
		this.onBoxHover(this.oClickedIcon, true);
1260
	else
1261
		this.iCurTimeout = window.setTimeout(this.opt.sBackReference + '.collapseList();', 500);
1262
}
1263
1264
// Event handler for clicking on one of the icons.
1265
IconList.prototype.onItemMouseDown = function (oDiv, sNewIcon)
1266
{
1267
	if (this.iCurMessageId != 0)
1268
	{
1269
		ajax_indicator(true);
1270
		this.tmpMethod = getXMLDocument;
1271
		var oXMLDoc = this.tmpMethod(smf_prepareScriptUrl(smf_scripturl) + 'action=jsmodify;topic=' + this.opt.iTopicId + ';msg=' + this.iCurMessageId + ';' + smf_session_var + '=' + smf_session_id + ';icon=' + sNewIcon + ';xml'),
1272
		oThis = this;
1273
		delete this.tmpMethod;
1274
		ajax_indicator(false);
1275
1276
		oXMLDoc.done(function(data, textStatus, jqXHR){
0 ignored issues
show
Unused Code introduced by
The parameter jqXHR is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter textStatus is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1277
			oMessage = $(data).find('message')
0 ignored issues
show
Bug introduced by
The variable oMessage seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.oMessage.
Loading history...
1278
			curMessageId = oMessage.attr('id').replace( /^\D+/g, '');
0 ignored issues
show
Bug introduced by
The variable curMessageId seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.curMessageId.
Loading history...
1279
1280
			if (oMessage.find('error').length == 0)
1281
			{
1282
				if (oThis.opt.bShowModify && oMessage.find('modified').length != 0)
1283
					$('#modified_' + curMessageId).html(oMessage.find('modified').text());
1284
1285
				oThis.oClickedIcon.getElementsByTagName('img')[0].src = oDiv.getElementsByTagName('img')[0].src;
1286
			}
1287
		});
1288
	}
1289
}
1290
1291
// Event handler for clicking outside the list (will make the list disappear).
1292
IconList.prototype.onWindowMouseDown = function ()
1293
{
1294
	for (var i = aIconLists.length - 1; i >= 0; i--)
1295
	{
1296
		aIconLists[i].funcParent.tmpMethod = aIconLists[i].collapseList;
1297
		aIconLists[i].funcParent.tmpMethod();
1298
		delete aIconLists[i].funcParent.tmpMethod;
1299
	}
1300
}
1301
1302
// Collapse the list of icons.
1303
IconList.prototype.collapseList = function()
1304
{
1305
	this.onBoxHover(this.oClickedIcon, false);
1306
	this.oContainerDiv.style.display = 'none';
1307
	this.iCurMessageId = 0;
1308
	document.body.removeEventListener('mousedown', this.onWindowMouseDown, false);
1309
}
1310
1311
// Handy shortcuts for getting the mouse position on the screen - only used for IE at the moment.
1312
function smf_mousePose(oEvent)
1313
{
1314
	var x = 0;
1315
	var y = 0;
1316
1317
	if (oEvent.pageX)
1318
	{
1319
		y = oEvent.pageY;
1320
		x = oEvent.pageX;
1321
	}
1322
	else if (oEvent.clientX)
1323
	{
1324
		x = oEvent.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
1325
		y = oEvent.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
1326
	}
1327
1328
	return [x, y];
1329
}
1330
1331
// Short function for finding the actual position of an item.
1332
function smf_itemPos(itemHandle)
1333
{
1334
	var itemX = 0;
1335
	var itemY = 0;
1336
1337
	if ('offsetParent' in itemHandle)
1338
	{
1339
		itemX = itemHandle.offsetLeft;
1340
		itemY = itemHandle.offsetTop;
1341
		while (itemHandle.offsetParent && typeof(itemHandle.offsetParent) == 'object')
1342
		{
1343
			itemHandle = itemHandle.offsetParent;
1344
			itemX += itemHandle.offsetLeft;
1345
			itemY += itemHandle.offsetTop;
1346
		}
1347
	}
1348
	else if ('x' in itemHandle)
1349
	{
1350
		itemX = itemHandle.x;
1351
		itemY = itemHandle.y;
1352
	}
1353
1354
	return [itemX, itemY];
1355
}
1356
1357
// This function takes the script URL and prepares it to allow the query string to be appended to it.
1358
function smf_prepareScriptUrl(sUrl)
1359
{
1360
	return sUrl.indexOf('?') == -1 ? sUrl + '?' : sUrl + (sUrl.charAt(sUrl.length - 1) == '?' || sUrl.charAt(sUrl.length - 1) == '&' || sUrl.charAt(sUrl.length - 1) == ';' ? '' : ';');
1361
}
1362
1363
var aOnloadEvents = new Array();
1364
function addLoadEvent(fNewOnload)
1365
{
1366
	// If there's no event set, just set this one
1367
	if (typeof(fNewOnload) == 'function' && (!('onload' in window) || typeof(window.onload) != 'function'))
1368
		window.onload = fNewOnload;
1369
1370
	// If there's just one event, setup the array.
1371
	else if (aOnloadEvents.length == 0)
1372
	{
1373
		aOnloadEvents[0] = window.onload;
1374
		aOnloadEvents[1] = fNewOnload;
1375
		window.onload = function() {
1376
			for (var i = 0, n = aOnloadEvents.length; i < n; i++)
1377
			{
1378
				if (typeof(aOnloadEvents[i]) == 'function')
1379
					aOnloadEvents[i]();
1380
				else if (typeof(aOnloadEvents[i]) == 'string')
1381
					eval(aOnloadEvents[i]);
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
1382
			}
1383
		}
1384
	}
1385
1386
	// This isn't the first event function, add it to the list.
1387
	else
1388
		aOnloadEvents[aOnloadEvents.length] = fNewOnload;
1389
}
1390
1391
// Get the text in a code tag.
1392
function smfSelectText(oCurElement, bActOnElement)
1393
{
1394
	// The place we're looking for is one div up, and next door - if it's auto detect.
1395
	if (typeof(bActOnElement) == 'boolean' && bActOnElement)
1396
		var oCodeArea = document.getElementById(oCurElement);
1397
	else
1398
		var oCodeArea = oCurElement.parentNode.nextSibling;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable oCodeArea already seems to be declared on line 1396. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1399
1400
	if (typeof(oCodeArea) != 'object' || oCodeArea == null)
1401
		return false;
1402
1403
	// Start off with my favourite, internet explorer.
1404
	if ('createTextRange' in document.body)
1405
	{
1406
		var oCurRange = document.body.createTextRange();
1407
		oCurRange.moveToElementText(oCodeArea);
1408
		oCurRange.select();
1409
	}
1410
	// Firefox at el.
1411
	else if (window.getSelection)
1412
	{
1413
		var oCurSelection = window.getSelection();
1414
		// Safari is special!
1415
		if (oCurSelection.setBaseAndExtent)
1416
		{
1417
			var oLastChild = oCodeArea.lastChild;
1418
			oCurSelection.setBaseAndExtent(oCodeArea, 0, oLastChild, 'innerText' in oLastChild ? oLastChild.innerText.length : oLastChild.textContent.length);
1419
		}
1420
		else
1421
		{
1422
			var curRange = document.createRange();
1423
			curRange.selectNodeContents(oCodeArea);
1424
1425
			oCurSelection.removeAllRanges();
1426
			oCurSelection.addRange(curRange);
1427
		}
1428
	}
1429
1430
	return false;
1431
}
1432
1433
// A function used to clean the attachments on post page
1434
function cleanFileInput(idElement)
1435
{
1436
	// Simpler solutions work in Opera, IE, Safari and Chrome.
1437
	if (is_opera || is_ie || is_safari || is_chrome)
1438
	{
1439
		document.getElementById(idElement).outerHTML = document.getElementById(idElement).outerHTML;
1440
	}
1441
	// What else can we do? By the way, this doesn't work in Chrome and Mac's Safari.
1442
	else
1443
	{
1444
		document.getElementById(idElement).type = 'input';
1445
		document.getElementById(idElement).type = 'file';
1446
	}
1447
}
1448
1449
function applyWindowClasses(oList)
1450
{
1451
	var bAlternate = false;
1452
	oListItems = oList.getElementsByTagName("LI");
0 ignored issues
show
Bug introduced by
The variable oListItems seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.oListItems.
Loading history...
1453
	for (i = 0; i < oListItems.length; i++)
0 ignored issues
show
Bug introduced by
The variable i seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.i.
Loading history...
1454
	{
1455
		// Skip dummies.
1456
		if (oListItems[i].id == "")
1457
			continue;
1458
		oListItems[i].className = "windowbg" + (bAlternate ? "2" : "");
1459
		bAlternate = !bAlternate;
1460
	}
1461
}
1462
1463
function reActivate()
1464
{
1465
	document.forms.postmodify.message.readOnly = false;
1466
}
1467
1468
function putOnSocks()
1469
{
1470
	var clicks = 0;
1471
	var firstClick = 0;
1472
	var lastClick = 0;
1473
	$('body').click(function() {
1474
		if (!firstClick) {
1475
			firstClick = Date.now();
1476
			setTimeout(function() {
1477
				firstClick = 0;
1478
				lastClick = 0;
1479
			}, 1500)
1480
		}
1481
		lastClick = Date.now();
1482
1483
		clicks++;
1484
		if (clicks >= 25 && lastClick - firstClick <= 15000) {
1485
			$('body, a').css({'cursor': 'url("data:image/png;base64,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"), auto'})
1486
		}
1487
	});
1488
}
1489
1490
// The actual message icon selector.
1491
function showimage()
1492
{
1493
	document.images.icons.src = icon_urls[document.forms.postmodify.icon.options[document.forms.postmodify.icon.selectedIndex].value];
1494
}
1495
1496
function pollOptions()
1497
{
1498
	var expire_time = document.getElementById('poll_expire');
1499
1500
	if (isEmptyText(expire_time) || expire_time.value == 0)
1501
	{
1502
		document.forms.postmodify.poll_hide[2].disabled = true;
1503
		if (document.forms.postmodify.poll_hide[2].checked)
1504
			document.forms.postmodify.poll_hide[1].checked = true;
1505
	}
1506
	else
1507
		document.forms.postmodify.poll_hide[2].disabled = false;
1508
}
1509
1510
function generateDays(offset)
1511
{
1512
	// Work around JavaScript's lack of support for default values...
1513
	offset = typeof(offset) != 'undefined' ? offset : '';
1514
1515
	var days = 0, selected = 0;
1516
	var dayElement = document.getElementById("day" + offset), yearElement = document.getElementById("year" + offset), monthElement = document.getElementById("month" + offset);
1517
1518
	var monthLength = [
1519
		31, 28, 31, 30,
1520
		31, 30, 31, 31,
1521
		30, 31, 30, 31
1522
	];
1523
	if (yearElement.options[yearElement.selectedIndex].value % 4 == 0)
1524
		monthLength[1] = 29;
1525
1526
	selected = dayElement.selectedIndex;
1527
	while (dayElement.options.length)
1528
		dayElement.options[0] = null;
1529
1530
	days = monthLength[monthElement.value - 1];
1531
1532
	for (i = 1; i <= days; i++)
0 ignored issues
show
Bug introduced by
The variable i seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.i.
Loading history...
1533
		dayElement.options[dayElement.length] = new Option(i, i);
1534
1535
	if (selected < days)
1536
		dayElement.selectedIndex = selected;
1537
}
1538
1539
function toggleLinked(form)
1540
{
1541
	form.board.disabled = !form.link_to_board.checked;
1542
}
1543
1544
function initSearch()
1545
{
1546
	if (document.forms.searchform.search.value.indexOf("%u") != -1)
1547
		document.forms.searchform.search.value = unescape(document.forms.searchform.search.value);
1548
}
1549
1550
function selectBoards(ids, aFormID)
1551
{
1552
	var toggle = true;
1553
	var aForm = document.getElementById(aFormID);
1554
1555
	for (i = 0; i < ids.length; i++)
0 ignored issues
show
Bug introduced by
The variable i seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.i.
Loading history...
1556
		toggle = toggle & aForm["brd" + ids[i]].checked;
1557
1558
	for (i = 0; i < ids.length; i++)
1559
		aForm["brd" + ids[i]].checked = !toggle;
1560
}
1561
1562
function updateRuleDef(optNum)
1563
{
1564
	if (document.getElementById("ruletype" + optNum).value == "gid")
1565
	{
1566
		document.getElementById("defdiv" + optNum).style.display = "none";
1567
		document.getElementById("defseldiv" + optNum).style.display = "";
1568
	}
1569
	else if (document.getElementById("ruletype" + optNum).value == "bud" || document.getElementById("ruletype" + optNum).value == "")
1570
	{
1571
		document.getElementById("defdiv" + optNum).style.display = "none";
1572
		document.getElementById("defseldiv" + optNum).style.display = "none";
1573
	}
1574
	else
1575
	{
1576
		document.getElementById("defdiv" + optNum).style.display = "";
1577
		document.getElementById("defseldiv" + optNum).style.display = "none";
1578
	}
1579
}
1580
1581
function updateActionDef(optNum)
1582
{
1583
	if (document.getElementById("acttype" + optNum).value == "lab")
1584
	{
1585
		document.getElementById("labdiv" + optNum).style.display = "";
1586
	}
1587
	else
1588
	{
1589
		document.getElementById("labdiv" + optNum).style.display = "none";
1590
	}
1591
}
1592
1593
function smc_resize(selector)
1594
{
1595
1596
	var allElements = [];
1597
1598
	$(selector).each(function(){
1599
1600
		$thisElement = $(this);
0 ignored issues
show
Bug introduced by
The variable $thisElement seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.$thisElement.
Loading history...
1601
1602
		// Get rid of the width and height attributes.
1603
		$thisElement.removeAttr('width').removeAttr('height');
1604
1605
		// Get the default vars.
1606
		$thisElement.basedElement = $thisElement.parent();
1607
		$thisElement.defaultWidth = $thisElement.width();
1608
		$thisElement.defaultHeight = $thisElement.height();
1609
		$thisElement.aspectRatio = $thisElement.defaultHeight / $thisElement.defaultWidth;
1610
1611
		allElements.push($thisElement);
1612
1613
	});
1614
1615
	$(window).resize(function(){
1616
1617
		$(allElements).each(function(){
1618
1619
			_innerElement = this;
0 ignored issues
show
Bug introduced by
The variable _innerElement seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window._innerElement.
Loading history...
1620
1621
			// Get the new width and height.
1622
			var newWidth = _innerElement.basedElement.width();
1623
			var newHeight = (newWidth * _innerElement.aspectRatio) <= _innerElement.defaultHeight ? (newWidth * _innerElement.aspectRatio) : _innerElement.defaultHeight;
1624
1625
			// If the new width is lower than the "default width" then apply some resizing. No? then go back to our default sizes
1626
			var applyResize = (newWidth <= _innerElement.defaultWidth),
1627
				applyWidth = !applyResize ? _innerElement.defaultWidth : newWidth,
1628
				applyHeight = !applyResize ? _innerElement.defaultHeight : newHeight;
1629
1630
			// Gotta check the applied width and height is actually something!
1631
			if (applyWidth <= 0 && applyHeight <= 0) {
1632
				applyWidth = _innerElement.defaultWidth;
1633
				applyHeight = _innerElement.defaultHeight;
1634
			}
1635
1636
			// Finally resize the element!
1637
			_innerElement.width(applyWidth).height(applyHeight);
1638
		});
1639
1640
	// Kick off one resize to fix all elements on page load.
1641
	}).resize();
1642
}
1643
1644
$(function()
1645
{
1646
	$('.buttonlist > .dropmenu').each(function(index, item)
1647
	{
1648
		$(item).prev().click(function(e)
1649
		{
1650
			e.stopPropagation();
1651
			e.preventDefault();
1652
1653
			if ($(item).is(':visible'))
1654
			{
1655
				$(item).css('display', 'none');
1656
1657
				return true;
1658
			}
1659
1660
			$(item).css('display', 'block');
1661
			$(item).css('top', $(this).offset().top + $(this).height());
1662
			$(item).css('left', Math.max($(this).offset().left - $(item).width() + $(this).outerWidth(), 0));
1663
			$(item).height($(item).find('div:first').height());
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...
1664
		});
1665
		$(document).click(function()
1666
		{
1667
			$(item).css('display', 'none');
1668
		});
1669
	});
1670
1671
	putOnSocks();
1672
1673
	// Generic confirmation message.
1674
	$(document).on('click', '.you_sure', function()
1675
	{
1676
		var custom_message = $(this).attr('data-confirm');
1677
1678
		return confirm(custom_message ? custom_message.replace(/-n-/g, "\n") : smf_you_sure);
1679
	});
1680
1681
	// Generic event for smfSelectText()
1682
	$('.smf_select_text').on('click', function(e) {
1683
1684
		e.preventDefault();
1685
1686
		// Do you want to target yourself?
1687
		var actOnElement = $(this).attr('data-actonelement');
1688
1689
		return typeof actOnElement !== "undefined" ? smfSelectText(actOnElement, true) : smfSelectText(this);
1690
	});
1691
});
1692