Completed
Pull Request — development (#3082)
by John
14:20
created

themes/default/scripts/PersonalMessage.js   F

Complexity

Total Complexity 81
Complexity/F 5.79

Size

Lines of Code 435
Function Count 14

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
nc 1
dl 0
loc 435
rs 1.5789
c 0
b 0
f 0
wmc 81
mnd 7
bc 38
fnc 14
bpm 2.7142
cpm 5.7857
noi 32

How to fix   Complexity   

Complexity

Complex classes like themes/default/scripts/PersonalMessage.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
 * @name      ElkArte Forum
3
 * @copyright ElkArte Forum contributors
4
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
5
 *
6
 * This file contains code covered by:
7
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
8
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
9
 *
10
 * @version 1.1
11
 */
12
13
/**
14
 * This file contains javascript surrounding personal messages send form.
15
 */
16
17
/**
18
 * Personal message Send controller
19
 *
20
 * sSelf: instance name
21
 * sSessionId:
22
 * sSessionVar:
23
 * sTextDeleteItem: text string to show when
24
 * sToControlId: id of the to auto suggest input
25
 * aToRecipients: array of members its going to
26
 * aBccRecipients: array of member to BCC
27
 * sBccControlId: id of the bcc auto suggest input
28
 * sBccDivId: container holding the bbc input
29
 * sBccDivId2: container holding the bcc chosen name
30
 * sBccLinkId: link to show/hide the bcc names
31
 * sBccLinkContainerId: container for the above
32
 * bBccShowByDefault: boolean to show it on or off
33
 * sShowBccLinkTemplate:
34
 *
35
 * @param {type} oOptions
36
 */
37
function elk_PersonalMessageSend(oOptions)
38
{
39
	this.opt = oOptions;
40
	this.oBccDiv = null;
41
	this.oBccDiv2 = null;
42
	this.oToAutoSuggest = null;
43
	this.oBccAutoSuggest = null;
44
	this.oToListContainer = null;
45
	this.init();
46
}
47
48
// Initialise the PM recipient selection area
49
elk_PersonalMessageSend.prototype.init = function()
50
{
51
	if (!this.opt.bBccShowByDefault)
52
	{
53
		// Hide the BCC control.
54
		this.oBccDiv = document.getElementById(this.opt.sBccDivId);
55
		this.oBccDiv.style.display = 'none';
56
		this.oBccDiv2 = document.getElementById(this.opt.sBccDivId2);
57
		this.oBccDiv2.style.display = 'none';
58
59
		// Show the link to bet the BCC control back.
60
		var oBccLinkContainer = document.getElementById(this.opt.sBccLinkContainerId);
61
		oBccLinkContainer.style.display = 'inline';
62
		oBccLinkContainer.innerHTML = this.opt.sShowBccLinkTemplate;
63
64
		// Make the link show the BCC control.
65
		var oBccLink = document.getElementById(this.opt.sBccLinkId);
66
		oBccLink.onclick = function() {
67
			this.showBcc();
68
			return false;
69
		}.bind(this);
70
	}
71
72
	var oToControl = document.getElementById(this.opt.sToControlId);
73
	this.oToAutoSuggest = new smc_AutoSuggest({
74
		sSessionId: this.opt.sSessionId,
75
		sSessionVar: this.opt.sSessionVar,
76
		sSuggestId: 'to_suggest',
77
		sControlId: this.opt.sToControlId,
78
		sSearchType: 'member',
79
		sPostName: 'recipient_to',
80
		sURLMask: 'action=profile;u=%item_id%',
81
		sTextDeleteItem: this.opt.sTextDeleteItem,
82
		bItemList: true,
83
		sItemListContainerId: 'to_item_list_container',
84
		aListItems: this.opt.aToRecipients
85
	});
86
	this.oToAutoSuggest.registerCallback('onBeforeAddItem', this.callbackAddItem.bind(this);
0 ignored issues
show
Bug introduced by
) was expected, but instead ; was given.
Loading history...
Coding Style introduced by
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
87
88
	this.oBccAutoSuggest = new smc_AutoSuggest({
89
		sSessionId: this.opt.sSessionId,
90
		sSessionVar: this.opt.sSessionVar,
91
		sSuggestId: 'bcc_suggest',
92
		sControlId: this.opt.sBccControlId,
93
		sSearchType: 'member',
94
		sPostName: 'recipient_bcc',
95
		sURLMask: 'action=profile;u=%item_id%',
96
		sTextDeleteItem: this.opt.sTextDeleteItem,
97
		bItemList: true,
98
		sItemListContainerId: 'bcc_item_list_container',
99
		aListItems: this.opt.aBccRecipients
100
	});
101
	this.oBccAutoSuggest.registerCallback('onBeforeAddItem', this.callbackAddItem.bind(this);
0 ignored issues
show
Bug introduced by
) was expected, but instead ; was given.
Loading history...
Coding Style introduced by
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
102
};
103
104
// Show the bbc fields
105
elk_PersonalMessageSend.prototype.showBcc = function()
106
{
107
	// No longer hide it, show it to the world!
108
	this.oBccDiv.style.display = 'block';
109
	this.oBccDiv2.style.display = 'block';
110
};
111
112
// Prevent items to be added twice or to both the 'To' and 'Bcc'.
113
elk_PersonalMessageSend.prototype.callbackAddItem = function(oAutoSuggestInstance, sSuggestId)
114
{
115
	this.oToAutoSuggest.deleteAddedItem(sSuggestId);
116
	this.oBccAutoSuggest.deleteAddedItem(sSuggestId);
117
118
	return true;
119
};
120
121
/**
122
 * Populate the label selection pulldown after a message is selected
123
 */
124
function loadLabelChoices()
125
{
126
	var listing = document.forms.pmFolder.elements,
127
		theSelect = document.forms.pmFolder.pm_action,
128
		add,
129
		remove,
130
		toAdd = {length: 0},
131
		toRemove = {length: 0};
132
133
	if (theSelect.childNodes.length === 0)
134
		return;
135
136
	// This is done this way for internationalization reasons.
137
	if (!('-1' in allLabels))
138
	{
139
		for (var o = 0; o < theSelect.options.length; o++)
140
			if (theSelect.options[o].value.substr(0, 4) === "rem_")
141
				allLabels[theSelect.options[o].value.substr(4)] = theSelect.options[o].text;
142
	}
143
144
	for (var i = 0; i < listing.length; i++)
145
	{
146
		if (listing[i].name !== "pms[]" || !listing[i].checked)
147
			continue;
148
149
		var alreadyThere = [],
150
			x;
151
152
		for (x in currentLabels[listing[i].value])
153
		{
154
			if (!(x in toRemove))
155
			{
156
				toRemove[x] = allLabels[x];
157
				toRemove.length++;
158
			}
159
			alreadyThere[x] = allLabels[x];
160
		}
161
162
		for (x in allLabels)
163
		{
164
			if (!(x in alreadyThere))
165
			{
166
				toAdd[x] = allLabels[x];
167
				toAdd.length++;
168
			}
169
		}
170
	}
171
172
	while (theSelect.options.length > 2)
173
		theSelect.options[2] = null;
174
175
	if (toAdd.length !== 0)
176
	{
177
		theSelect.options[theSelect.options.length] = new Option(txt_pm_msg_label_apply);
178
		theSelect.options[theSelect.options.length - 1].innerHTML = txt_pm_msg_label_apply;
179
		theSelect.options[theSelect.options.length - 1].className = 'jump_to_header';
180
		theSelect.options[theSelect.options.length - 1].disabled = true;
181
182
		for (i in toAdd)
183
		{
184
			if (i !== "length")
185
				theSelect.options[theSelect.options.length] = new Option(toAdd[i], "add_" + i);
186
		}
187
	}
188
189
	if (toRemove.length !== 0)
190
	{
191
		theSelect.options[theSelect.options.length] = new Option(txt_pm_msg_label_remove);
192
		theSelect.options[theSelect.options.length - 1].innerHTML = txt_pm_msg_label_remove;
193
		theSelect.options[theSelect.options.length - 1].className = 'jump_to_header';
194
		theSelect.options[theSelect.options.length - 1].disabled = true;
195
196
		for (i in toRemove)
197
		{
198
			if (i !== "length")
199
				theSelect.options[theSelect.options.length] = new Option(toRemove[i], "rem_" + i);
200
		}
201
	}
202
}
203
204
/**
205
 * Rebuild the rule description!
206
 * @todo: string concatenation is bad for internationalization
207
 */
208
function rebuildRuleDesc()
209
{
210
	// Start with nothing.
211
	var text = "",
212
		joinText = "",
213
		actionText = "",
214
		hadBuddy = false,
215
		foundCriteria = false,
216
		foundAction = false,
217
		curNum,
218
		curVal,
219
		curDef;
220
221
	// GLOBAL strings, convert to objects
222
	/** global: groups */
223
	if (typeof groups === "string")
224
		groups = JSON.parse(groups);
225
	/** global: labels */
226
	if (typeof labels === "string")
227
		labels = JSON.parse(labels);
228
	/** global: rules */
229
	if (typeof rules === "string")
230
		rules = JSON.parse(rules);
231
232
	for (var i = 0; i < document.forms.addrule.elements.length; i++)
233
	{
234
		if (document.forms.addrule.elements[i].id.substr(0, 8) === "ruletype")
235
		{
236
			if (foundCriteria)
237
				joinText = document.getElementById("logic").value === 'and' ? ' ' + txt_pm_readable_and + ' ' : ' ' + txt_pm_readable_or + ' ';
238
			else
239
				joinText = '';
240
241
			foundCriteria = true;
242
243
			curNum = document.forms.addrule.elements[i].id.match(/\d+/);
244
			curVal = document.forms.addrule.elements[i].value;
245
246
			if (curVal === "gid")
247
				curDef = document.getElementById("ruledefgroup" + curNum).value.php_htmlspecialchars();
248
			else if (curVal !== "bud")
249
				curDef = document.getElementById("ruledef" + curNum).value.php_htmlspecialchars();
250
			else
251
				curDef = "";
252
253
			// What type of test is this?
254
			if (curVal === "mid" && curDef)
255
				text += joinText + txt_pm_readable_member.replace("{MEMBER}", curDef);
256
			else if (curVal === "gid" && curDef && groups[curDef])
257
				text += joinText + txt_pm_readable_group.replace("{GROUP}", groups[curDef]);
258
			else if (curVal === "sub" && curDef)
259
				text += joinText + txt_pm_readable_subject.replace("{SUBJECT}", curDef);
260
			else if (curVal === "msg" && curDef)
261
				text += joinText + txt_pm_readable_body.replace("{BODY}", curDef);
262
			else if (curVal === "bud" && !hadBuddy)
263
			{
264
				text += joinText + txt_pm_readable_buddy;
265
				hadBuddy = true;
266
			}
267
		}
268
269
		if (document.forms.addrule.elements[i].id.substr(0, 7) === "acttype")
270
		{
271
			if (foundAction)
272
				joinText = ' ' + txt_pm_readable_and + ' ';
273
			else
274
				joinText = "";
275
276
			foundAction = true;
277
278
			curNum = document.forms.addrule.elements[i].id.match(/\d+/);
279
			curVal = document.forms.addrule.elements[i].value;
280
281
			if (curVal === "lab")
282
				curDef = document.getElementById("labdef" + curNum).value.php_htmlspecialchars();
283
			else
284
				curDef = "";
285
286
			// Now pick the actions.
287
			if (curVal === "lab" && curDef && labels[curDef])
288
				actionText += joinText + txt_pm_readable_label.replace("{LABEL}", labels[curDef]);
289
			else if (curVal === "del")
290
				actionText += joinText + txt_pm_readable_delete;
291
		}
292
	}
293
294
	// If still nothing make it default!
295
	if (text === "" || !foundCriteria)
296
		text = txt_pm_rule_not_defined;
297
	else
298
	{
299
		if (actionText !== "")
300
			text += ' ' + txt_pm_readable_then + ' ' + actionText;
301
		text = txt_pm_readable_start + text + txt_pm_readable_end;
302
	}
303
304
	// Set the actual HTML!
305
	document.getElementById("ruletext").innerHTML = text;
306
}
307
308
function initUpdateRulesActions()
309
{
310
	/**
311
	 * Maintains the personal message rule options to conform with the rule choice
312
	 * so that the form only makes available the proper choices (input, select, none, etc)
313
	 */
314
315
	// Handy shortcuts
316
	var $criteria = $('#criteria'),
317
		$actions = $('#actions');
318
319
	$criteria.on('change', '[name^="ruletype"]', function() {
320
		var optNum = $(this).data('optnum');
321
322
		if (document.getElementById("ruletype" + optNum).value === "gid")
323
		{
324
			document.getElementById("defdiv" + optNum).style.display = "none";
325
			document.getElementById("defseldiv" + optNum).style.display = "inline";
326
		}
327
		else if (document.getElementById("ruletype" + optNum).value === "bud" || document.getElementById("ruletype" + optNum).value === "")
328
		{
329
			document.getElementById("defdiv" + optNum).style.display = "none";
330
			document.getElementById("defseldiv" + optNum).style.display = "none";
331
		}
332
		else
333
		{
334
			document.getElementById("defdiv" + optNum).style.display = "inline";
335
			document.getElementById("defseldiv" + optNum).style.display = "none";
336
		}
337
	});
338
339
	/**
340
	* Maintains the personal message rule action options to conform with the action choice
341
	* so that the form only makes available the proper choice
342
	*/
343
	$actions.on('change', '[name^="acttype"]', function() {
344
		var optNum = $(this).data('actnum');
345
346
		if (document.getElementById("acttype" + optNum).value === "lab")
347
		{
348
			document.getElementById("labdiv" + optNum).style.display = "inline";
349
		}
350
		else
351
		{
352
			document.getElementById("labdiv" + optNum).style.display = "none";
353
		}
354
	});
355
356
	// Trigger a change on the existing in order to let the function run
357
	$criteria.find('[name^="ruletype"]').change();
358
	$actions.find('[name^="acttype"]').change();
359
360
	// Make sure the description is rebuilt every time something changes, even on elements not yet existing
361
	$criteria.on('change keyup',
362
		'[name^="ruletype"], [name^="ruledefgroup"], [name^="ruledef"], [name^="acttype"], [name^="labdef"], #logic',
363
		function() {
364
			rebuildRuleDesc();
365
	});
366
367
	// Make sure the description is rebuilt every time something changes, even on elements not yet existing
368
	$('#criteria, #actions').on('change keyup',
369
		'[name^="ruletype"], [name^="ruledefgroup"], [name^="ruledef"], [name^="acttype"], [name^="labdef"], #logic',
370
		function() {
371
			rebuildRuleDesc();
372
	});
373
374
	// Rebuild once at the beginning to ensure everything is correct
375
	rebuildRuleDesc();
376
}
377
378
/**
379
 * Add a new rule criteria for PM filtering
380
 */
381
function addCriteriaOption()
382
{
383
	if (criteriaNum === 0)
384
	{
385
		for (var i = 0; i < document.forms.addrule.elements.length; i++)
386
			if (document.forms.addrule.elements[i].id.substr(0, 8) === "ruletype")
387
				criteriaNum++;
388
	}
389
	criteriaNum++;
390
391
	// Global strings, convert to objects
392
	/** global: groups */
393
	if (typeof groups === "string")
394
		groups = JSON.parse(groups);
395
	/** global: labels */
396
	if (typeof labels === "string")
397
		labels = JSON.parse(labels);
398
	/** global: rules */
399
	if (typeof rules === "string")
400
		rules = JSON.parse(rules);
401
402
	// rules select
403
	var rules_option = '',
404
		index = '';
405
406
	for (index in rules)
407
	{
408
		if (rules.hasOwnProperty(index))
409
			rules_option += '<option value="' + index + '">' + rules[index] + '</option>';
410
	}
411
412
	// group selections
413
	var group_option = '';
414
415
	for (index in groups)
416
		group_option += '<option value="' + index + '">' + groups[index] + '</option>';
417
418
	setOuterHTML(document.getElementById("criteriaAddHere"), '<br />' +
419
		'<select class="criteria" name="ruletype[' + criteriaNum + ']" id="ruletype' + criteriaNum + '" data-optnum="' + criteriaNum + '">' +
420
			'<option value="">' + txt_pm_rule_criteria_pick + ':</option>' + rules_option + '' +
421
		'</select>&nbsp;' +
422
		'<span id="defdiv' + criteriaNum + '" class="hide">' +
423
			'<input type="text" name="ruledef[' + criteriaNum + ']" id="ruledef' + criteriaNum + '" value="" class="input_text" />' +
424
		'</span>' +
425
		'<span id="defseldiv' + criteriaNum + '" class="hide">' +
426
			'<select class="criteria" name="ruledefgroup[' + criteriaNum + ']" id="ruledefgroup' + criteriaNum + '">' +
427
				'<option value="">' + txt_pm_rule_sel_group + '</option>' + group_option +
428
			'</select>' +
429
		'</span>' +
430
		'<span id="criteriaAddHere"></span>');
431
432
	return false;
433
}
434
435
/**
436
 * Add a new action for a defined PM rule
437
 */
438
function addActionOption()
439
{
440
	if (actionNum === 0)
441
	{
442
		for (var i = 0; i < document.forms.addrule.elements.length; i++)
443
			if (document.forms.addrule.elements[i].id.substr(0, 7) === "acttype")
444
				actionNum++;
445
	}
446
	actionNum++;
447
448
	// Label selections
449
	var label_option = '',
450
		index = '';
451
452
	if (typeof labels === "string")
453
		labels = JSON.parse(labels);
454
	for (index in labels)
455
		label_option += '<option value="' + index + '">' + labels[index] + '</option>';
456
457
	setOuterHTML(document.getElementById("actionAddHere"), '<br />' +
458
		'<select name="acttype[' + actionNum + ']" id="acttype' + actionNum + '" data-actnum="' + actionNum + '">' +
459
			'<option value="">' + txt_pm_rule_sel_action + ':</option>' +
460
			'<option value="lab">' + txt_pm_rule_label + '</option>' +
461
			'<option value="del">' + txt_pm_rule_delete + '</option>' +
462
		'</select>&nbsp;' +
463
		'<span id="labdiv' + actionNum + '" class="hide">' +
464
		'<select name="labdef[' + actionNum + ']" id="labdef' + actionNum + '">' +
465
			'<option value="">' + txt_pm_rule_sel_label + '</option>' + label_option +
466
		'</select></span>' +
467
		'<span id="actionAddHere"></span>');
468
}
469