Issues (1686)

themes/default/scripts/topic.js (43 issues)

1
/*!
2
 * @package   ElkArte Forum
3
 * @copyright ElkArte Forum contributors
4
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
5
 *
6
 * This file contains code covered by:
7
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
8
 *
9
 * @version 2.0 dev
10
 */
11
12
/**
13
 * This file contains javascript associated with the topic viewing including
14
 * Quick Modify, Quick Reply, In Topic Moderation, thumbnail expansion etc
15
 */
16
17
/**
18
 * *** QuickModifyTopic object.
19
 * Used to quick edit a topic subject by double-clicking next to the subject name
20
 * in a topic listing
21
 *
22
 * @param {object} oOptions
23
 */
24
function QuickModifyTopic (oOptions)
25
{
26
	this.opt = oOptions;
27
	this.aHidePrefixes = this.opt.aHidePrefixes;
28
	this.iCurTopicId = 0;
29
	this.sCurMessageId = '';
30
	this.sBuffSubject = '';
31
	this.oSavetipElem = false;
32
	this.oCurSubjectDiv = null;
33
	this.oTopicModHandle = document;
34
	this.bInEditMode = false;
35
	this.bMouseOnDiv = false;
36
	this.init();
37
}
38
39
// Used to initialise the object event handlers
40
QuickModifyTopic.prototype.init = function() {
41
	// Detect and act on keypress
42
	this.oTopicModHandle.onkeydown = this.modify_topic_keypress.bind(this);
43
44
	// Used to detect when we've stopped editing.
45
	this.oTopicModHandle.onclick = this.modify_topic_click.bind(this);
46
};
47
48
// called from the double click in the div
49
QuickModifyTopic.prototype.modify_topic = function(topic_id, first_msg_id) {
50
	// already editing
51
	if (this.bInEditMode)
52
	{
53
		// Same message then just return, otherwise drop out of this edit.
54
		if (this.iCurTopicId === topic_id)
55
		{
56
			return;
57
		}
58
59
		this.modify_topic_cancel();
60
	}
61
62
	this.bInEditMode = true;
63
	this.bMouseOnDiv = true;
64
	this.iCurTopicId = topic_id;
65
66
	// Get the topics current subject
67
	ajax_indicator(true);
68
	sendXMLDocument.call(this, elk_prepareScriptUrl(elk_scripturl) + 'action=quotefast;quote=' + first_msg_id + ';modify;api=xml', '', this.onDocReceived_modify_topic);
0 ignored issues
show
The variable sendXMLDocument seems to be never declared. If this is a global, consider adding a /** global: sendXMLDocument */ comment.

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

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

Loading history...
The variable elk_scripturl seems to be never declared. If this is a global, consider adding a /** global: elk_scripturl */ comment.

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

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

Loading history...
69
};
70
71
// Callback function from the modify_topic ajax call
72
QuickModifyTopic.prototype.onDocReceived_modify_topic = function(XMLDoc) {
73
	// If it is not valid then clean up
74
	if (!XMLDoc || !XMLDoc.getElementsByTagName('message'))
75
	{
76
		this.modify_topic_cancel();
77
		ajax_indicator(false);
78
		return true;
79
	}
80
81
	this.sCurMessageId = XMLDoc.getElementsByTagName('message')[0].getAttribute('id');
82
	this.oCurSubjectDiv = document.getElementById('msg_' + this.sCurMessageId.substring(4));
83
	this.sBuffSubject = this.oCurSubjectDiv.innerHTML;
84
85
	// Hide the tooltip text, don't want them for this element during the edit
86
	if (typeof SiteTooltip === 'function')
0 ignored issues
show
The variable SiteTooltip seems to be never declared. If this is a global, consider adding a /** global: SiteTooltip */ comment.

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

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

Loading history...
87
	{
88
		this.oSavetipElem = this.oCurSubjectDiv.parentElement;
89
		if (this.oSavetipElem)
90
		{
91
			this.sSavetip = this.oSavetipElem.dataset.title;
92
			this.oSavetipElem.dataset.title = '';
93
		}
94
	}
95
96
	// Here we hide any other things they want hidden on edit.
97
	this.set_hidden_topic_areas('none');
98
99
	// Show we are in edit mode and allow the edit
100
	ajax_indicator(false);
101
	this.modify_topic_show_edit(XMLDoc.getElementsByTagName('subject')[0].childNodes[0].nodeValue);
0 ignored issues
show
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...
102
};
103
104
// Cancel out of an edit and return things to back to what they were
105
QuickModifyTopic.prototype.modify_topic_cancel = function() {
106
	this.oCurSubjectDiv.innerHTML = this.sBuffSubject;
107
	this.set_hidden_topic_areas('');
108
	this.bInEditMode = false;
109
110
	// Put back the hover text
111
	if (this.oSavetipElem)
112
	{
113
		this.oSavetipElem.dataset.title = this.sSavetip;
114
	}
115
116
	return false;
117
};
118
119
// Simply restore/show any hidden bits during topic editing.
120
QuickModifyTopic.prototype.set_hidden_topic_areas = function(set_style) {
121
	for (let i = 0; i < this.aHidePrefixes.length; i++)
122
	{
123
		if (document.getElementById(this.aHidePrefixes[i] + this.sCurMessageId.substring(4)) !== null)
124
		{
125
			document.getElementById(this.aHidePrefixes[i] + this.sCurMessageId.substring(4)).style.display = set_style;
126
		}
127
	}
128
};
129
130
// For templating, shown that an inline edit is being made.
131
QuickModifyTopic.prototype.modify_topic_show_edit = function(subject) {
132
	// Just template the subject.
133
	this.oCurSubjectDiv.innerHTML = '<input type="text" name="subject" value="' + subject + '" size="60" style="width: 95%;" maxlength="80" class="input_text" autocomplete="off" /><input type="hidden" name="topic" value="' + this.iCurTopicId + '" /><input type="hidden" name="msg" value="' + this.sCurMessageId.substring(4) + '" />';
134
135
	// Attach mouse over and out events to this new div
136
	this.oCurSubjectDiv.onmouseout = this.modify_topic_mouseout.bind(this);
137
	this.oCurSubjectDiv.onmouseover = this.modify_topic_mouseover.bind(this);
138
};
139
140
// Yup that's right, save it
141
QuickModifyTopic.prototype.modify_topic_save = function(cur_session_id, cur_session_var) {
0 ignored issues
show
The parameter cur_session_id 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...
The parameter cur_session_var 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...
142
	if (!this.bInEditMode)
143
	{
144
		return true;
145
	}
146
147
	// Send in the call to save the updated topic subject
148
	ajax_indicator(true);
149
	let formData = serialize(document.forms.quickModForm); // includes sessionID
150
	sendXMLDocument.call(this, elk_prepareScriptUrl(elk_scripturl) + 'action=jsmodify;api=xml', formData, this.modify_topic_done);
0 ignored issues
show
The variable elk_scripturl seems to be never declared. If this is a global, consider adding a /** global: elk_scripturl */ comment.

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

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

Loading history...
The variable sendXMLDocument seems to be never declared. If this is a global, consider adding a /** global: sendXMLDocument */ comment.

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

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

Loading history...
151
152
	return false;
153
};
154
155
// Done with the edit, if all went well show the new topic title
156
QuickModifyTopic.prototype.modify_topic_done = function(XMLDoc) {
157
	ajax_indicator(false);
158
159
	// If it is not valid then clean up
160
	if (!XMLDoc || !XMLDoc.getElementsByTagName('subject'))
161
	{
162
		this.modify_topic_cancel();
163
		return true;
164
	}
165
166
	let message = XMLDoc.getElementsByTagName('elk')[0].getElementsByTagName('message')[0],
167
		subject = message.getElementsByTagName('subject')[0],
168
		error = message.getElementsByTagName('error')[0];
169
170
	// No subject or other error?
171
	if (!subject || error)
172
	{
173
		return false;
174
	}
175
176
	this.modify_topic_hide_edit(subject.childNodes[0].nodeValue);
177
	this.set_hidden_topic_areas('');
178
	this.bInEditMode = false;
179
180
	// Redo tooltips if they are on since we just pulled the rug out on this one
181
	if (this.oSavetipElem)
182
	{
183
		this.oSavetipElem.dataset.title = this.sSavetip;
184
	}
185
186
	return false;
187
};
188
189
// Done with the edit, put in new subject and link.
190
QuickModifyTopic.prototype.modify_topic_hide_edit = function(subject) {
191
	// Re-template the subject!
192
	this.oCurSubjectDiv.innerHTML = '<a href="' + elk_scripturl + '?topic=' + this.iCurTopicId + '.0">' + subject + '<' + '/a>';
0 ignored issues
show
The variable elk_scripturl seems to be never declared. If this is a global, consider adding a /** global: elk_scripturl */ comment.

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

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

Loading history...
193
};
194
195
// keypress event ... like enter or escape
196
QuickModifyTopic.prototype.modify_topic_keypress = function(oEvent) {
197
	if (typeof (oEvent.keyCode) !== 'undefined' && this.bInEditMode)
198
	{
199
		if (oEvent.keyCode === 27)
200
		{
201
			this.modify_topic_cancel();
202
			if (typeof (oEvent.preventDefault) === 'undefined')
203
			{
204
				oEvent.returnValue = false;
205
			}
206
			else
207
			{
208
				oEvent.preventDefault();
209
			}
210
		}
211
		else if (oEvent.keyCode === 13)
212
		{
213
			this.modify_topic_save(elk_session_id, elk_session_var);
0 ignored issues
show
The variable elk_session_var seems to be never declared. If this is a global, consider adding a /** global: elk_session_var */ comment.

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

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

Loading history...
The variable elk_session_id seems to be never declared. If this is a global, consider adding a /** global: elk_session_id */ comment.

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

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

Loading history...
214
			if (typeof (oEvent.preventDefault) === 'undefined')
215
			{
216
				oEvent.returnValue = false;
217
			}
218
			else
219
			{
220
				oEvent.preventDefault();
221
			}
222
		}
223
	}
224
};
225
226
// A click event to signal the finish of the edit
227
QuickModifyTopic.prototype.modify_topic_click = function(oEvent) {
0 ignored issues
show
The parameter oEvent 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...
228
	if (this.bInEditMode && !this.bMouseOnDiv)
229
	{
230
		this.modify_topic_save(elk_session_id, elk_session_var);
0 ignored issues
show
The variable elk_session_var seems to be never declared. If this is a global, consider adding a /** global: elk_session_var */ comment.

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

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

Loading history...
The variable elk_session_id seems to be never declared. If this is a global, consider adding a /** global: elk_session_id */ comment.

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

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

Loading history...
231
	}
232
};
233
234
// Moved out of the editing div
235
QuickModifyTopic.prototype.modify_topic_mouseout = function(oEvent) {
0 ignored issues
show
The parameter oEvent 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...
236
	this.bMouseOnDiv = false;
237
};
238
239
// Moved back over the editing div
240
QuickModifyTopic.prototype.modify_topic_mouseover = function(oEvent) {
241
	this.bMouseOnDiv = true;
242
	oEvent.preventDefault();
243
};
244
245
/**
246
 * QuickReply object, this allows for selecting the quote button and
247
 * having the quote appear in the quick reply box
248
 *
249
 * @param {type} oOptions
250
 */
251
function QuickReply (oOptions)
252
{
253
	this.opt = oOptions;
254
	this.bCollapsed = this.opt.bDefaultCollapsed;
255
256
	// If the initial state is to be collapsed, collapse it.
257
	if (this.bCollapsed)
258
	{
259
		this.swap(true);
260
	}
261
}
262
263
// When a user presses quote, put it in the quick reply box (if expanded).
264
QuickReply.prototype.quote = function(iMessageId, xDeprecated) {
0 ignored issues
show
The parameter xDeprecated 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...
265
	ajax_indicator(true);
266
267
	// Collapsed on a quote, then simply got to the full post screen
268
	if (this.bCollapsed)
269
	{
270
		// Instead of going to full post screen, lets expand the collapsed QR
271
		this.swap(false, false);
272
273
		//window.location.href = elk_prepareScriptUrl(this.opt.sScriptUrl) + 'action=post;quote=' + iMessageId + ';topic=' + this.opt.iTopicId + '.' + this.opt.iStart;
274
		//return false;
275
	}
276
277
	// Insert the quote
278
	insertQuoteFast(iMessageId);
279
280
	// Move the view to the quick reply box.
281
	this.delay(250).then(() => document.getElementById(this.opt.sJumpAnchor).scrollIntoView());
282
283
	return false;
284
};
285
286
QuickReply.prototype.delay = function(time) {
287
	return new Promise(resolve => setTimeout(resolve, time));
288
};
289
290
// The function handling the swapping of the quick reply area
291
QuickReply.prototype.swap = function(bInit, bSavestate) {
292
	let oQuickReplyContainer = document.getElementById(this.opt.sClassId),
293
		sEditorId = this.opt.sEditorId || this.opt.sContainerId;
294
295
	// Default bInit to false and bSavestate to true
296
	bInit = typeof (bInit) !== 'undefined' ? bInit : false;
297
	bSavestate = typeof (bSavestate) === 'undefined' ? true : bSavestate;
298
299
	// Flip our current state if not responding to an initial loading
300
	if (!bInit)
301
	{
302
		this.bCollapsed = !this.bCollapsed;
303
	}
304
305
	// Swap the class on the expcol image as needed
306
	let sTargetClass = this.bCollapsed ? this.opt.sClassExpanded : this.opt.sClassCollapsed;
307
308
	if (oQuickReplyContainer.className !== sTargetClass)
309
	{
310
		oQuickReplyContainer.className = sTargetClass;
311
	}
312
313
	// And show the new title
314
	oQuickReplyContainer.title = oQuickReplyContainer.title = this.bCollapsed ? this.opt.sTitleCollapsed : this.opt.sTitleExpanded;
315
316
	// Show or hide away
317
	if (this.bCollapsed)
318
	{
319
		document.getElementById(this.opt.sContainerId).slideUp();
320
	}
321
	else
322
	{
323
		document.getElementById(this.opt.sContainerId).slideDown(250, function() {
324
			// Force the editor to a min height, otherwise its just 2 lines
325
			let instance = sceditor.instance(document.getElementById(sEditorId));
0 ignored issues
show
The variable sceditor seems to be never declared. If this is a global, consider adding a /** global: sceditor */ comment.

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

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

Loading history...
326
			if (instance)
327
			{
328
				instance.height(250);
329
			}
330
		});
331
	}
332
333
	// Using a cookie for guests?
334
	if (bSavestate && 'oCookieOptions' in this.opt && this.opt.oCookieOptions.bUseCookie)
335
	{
336
		this.oCookie.set(this.opt.oCookieOptions.sCookieName, this.bCollapsed ? '1' : '0');
337
	}
338
339
	// Save the expand /collapse preference
340
	if (!bInit && bSavestate && 'oThemeOptions' in this.opt && this.opt.oThemeOptions.bUseThemeSettings)
341
	{
342
		elk_setThemeOption(this.opt.oThemeOptions.sOptionName, this.bCollapsed ? '1' : '0', 'sThemeId' in this.opt.oThemeOptions ? this.opt.oThemeOptions.sThemeId : null, 'sAdditionalVars' in this.opt.oThemeOptions ? this.opt.oThemeOptions.sAdditionalVars : null);
343
	}
344
};
345
346
/**
347
 * QuickModify object.
348
 * This will allow for the quick editing of a post via ajax
349
 *
350
 * @param {object} oOptions
351
 */
352
function QuickModify (oOptions)
353
{
354
	this.opt = oOptions;
355
	this.bInEditMode = false;
356
	this.sCurMessageId = '';
357
	this.oCurMessageDiv = null;
358
	this.oCurInfoDiv = null;
359
	this.oCurSubjectDiv = null;
360
	this.oMsgIcon = null;
361
	this.sMessageBuffer = '';
362
	this.sSubjectBuffer = '';
363
	this.sInfoBuffer = '';
364
	this.aAccessKeys = [];
365
366
	// Show the edit buttons
367
	let aShowQuickModify = document.getElementsByClassName(this.opt.sClassName);
368
	for (let i = 0, length = aShowQuickModify.length; i < length; i++)
369
	{
370
		aShowQuickModify[i].style.display = 'inline';
371
	}
372
}
373
374
// Function called when a user presses the edit button.
375
QuickModify.prototype.modifyMsg = function(iMessageId) {
376
	// Removes the accesskeys from the quickreply inputs and saves them in an array to use them later
377
	if (typeof (this.opt.sFormRemoveAccessKeys) !== 'undefined')
378
	{
379
		if (typeof (document.forms[this.opt.sFormRemoveAccessKeys]))
380
		{
381
			let aInputs = document.forms[this.opt.sFormRemoveAccessKeys].getElementsByTagName('input');
382
			for (let i = 0; i < aInputs.length; i++)
383
			{
384
				if (aInputs[i].accessKey !== '')
385
				{
386
					this.aAccessKeys[aInputs[i].name] = aInputs[i].accessKey;
387
					aInputs[i].accessKey = '';
388
				}
389
			}
390
		}
391
	}
392
393
	// First cancel if there's another message still being edited.
394
	if (this.bInEditMode)
395
	{
396
		this.modifyCancel();
397
	}
398
399
	// At least NOW we're in edit mode
400
	this.bInEditMode = true;
401
402
	// Send out the XMLhttp request to get more info
403
	ajax_indicator(true);
404
	sendXMLDocument.call(this, elk_prepareScriptUrl(elk_scripturl) + 'action=quotefast;quote=' + iMessageId + ';modify;api=xml', '', this.onMessageReceived);
0 ignored issues
show
The variable elk_scripturl seems to be never declared. If this is a global, consider adding a /** global: elk_scripturl */ comment.

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

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

Loading history...
The variable sendXMLDocument seems to be never declared. If this is a global, consider adding a /** global: sendXMLDocument */ comment.

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

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

Loading history...
405
};
406
407
// The callback function used for the XMLhttp request retrieving the message.
408
QuickModify.prototype.onMessageReceived = function(XMLDoc) {
409
	let sBodyText = '',
410
		sSubjectText;
411
412
	// No longer show the 'loading...' sign.
413
	ajax_indicator(false);
414
415
	// Grab the message ID.
416
	this.sCurMessageId = XMLDoc.getElementsByTagName('message')[0].getAttribute('id');
417
418
	// Show the message icon if it was hidden and its set
419
	if (this.opt.sIconHide !== null)
420
	{
421
		this.oMsgIcon = document.getElementById('messageicon_' + this.sCurMessageId.replace('msg_', ''));
422
		if (this.oMsgIcon !== null && getComputedStyle(this.oMsgIcon).getPropertyValue('display') === 'none')
423
		{
424
			this.oMsgIcon.style.display = 'inline';
425
		}
426
	}
427
428
	// If this is not valid then simply give up.
429
	if (!document.getElementById(this.sCurMessageId))
430
	{
431
		if ('console' in window && console.info)
432
		{
433
			console.info('no id');
434
		}
435
436
		return this.modifyCancel();
437
	}
438
439
	// Replace the body part.
440
	for (let i = 0; i < XMLDoc.getElementsByTagName("message")[0].childNodes.length; i++)
441
	{
442
		sBodyText += XMLDoc.getElementsByTagName('message')[0].childNodes[i].nodeValue;
443
	}
444
445
	this.oCurMessageDiv = document.getElementById(this.sCurMessageId);
446
	this.sMessageBuffer = this.oCurMessageDiv.innerHTML;
447
448
	// Actually create the content
449
	this.oCurMessageDiv.innerHTML = this.opt.sTemplateBodyEdit.replace(/%msg_id%/g, this.sCurMessageId.substring(4)).replace(/%body%/, sBodyText);
450
451
	// Save and hide the existing subject div
452
	if (this.opt.sIDSubject !== null)
453
	{
454
		this.oCurSubjectDiv = document.getElementById(this.opt.sIDSubject + this.sCurMessageId.substring(4));
455
		if (this.oCurSubjectDiv !== null)
456
		{
457
			this.oCurSubjectDiv.style.display = 'none';
458
			this.sSubjectBuffer = this.oCurSubjectDiv.innerHTML;
459
		}
460
	}
461
462
	// Save the info div, then open an input field on it
463
	sSubjectText = XMLDoc.getElementsByTagName('subject')[0].childNodes[0].nodeValue;
464
	if (this.opt.sIDInfo !== null)
465
	{
466
		this.oCurInfoDiv = document.getElementById(this.opt.sIDInfo + this.sCurMessageId.substring(4));
467
		if (this.oCurInfoDiv !== null)
468
		{
469
			this.sInfoBuffer = this.oCurInfoDiv.innerHTML;
470
			this.oCurInfoDiv.innerHTML = this.opt.sTemplateSubjectEdit.replace(/%subject%/, sSubjectText);
471
		}
472
	}
473
474
	// Position the editor in the window
475
	document.getElementById('info_' + this.sCurMessageId.substring(this.sCurMessageId.lastIndexOf('_') + 1)).scrollIntoView();
476
477
	// Handle custom function hook before showing the new select.
478
	if ('funcOnAfterCreate' in this.opt)
479
	{
480
		this.tmpMethod = this.opt.funcOnAfterCreate;
481
		this.tmpMethod(this);
482
		delete this.tmpMethod;
483
	}
484
485
	return true;
486
};
487
488
// Function in case the user presses cancel (or other circumstances cause it).
489
QuickModify.prototype.modifyCancel = function() {
490
	// Roll back the HTML to its original state.
491
	if (this.oCurMessageDiv)
492
	{
493
		this.oCurMessageDiv.innerHTML = this.sMessageBuffer;
494
		this.oCurInfoDiv.innerHTML = this.sInfoBuffer;
495
496
		if (this.oCurSubjectDiv !== null)
497
		{
498
			this.oCurSubjectDiv.innerHTML = this.sSubjectBuffer;
499
			this.oCurSubjectDiv.style.display = '';
500
		}
501
	}
502
503
	// Hide the message icon if we are doing that
504
	if (this.opt.sIconHide)
505
	{
506
		let oCurrentMsgIcon = document.getElementById('msg_icon_' + this.sCurMessageId.replace('msg_', ''));
507
508
		if (oCurrentMsgIcon !== null && oCurrentMsgIcon.src.indexOf(this.opt.sIconHide) > 0)
509
		{
510
			this.oMsgIcon.style.display = 'none';
511
		}
512
	}
513
514
	// No longer in edit mode, that's right.
515
	this.bInEditMode = false;
516
517
	// Let's put back the accesskeys to their original place
518
	if (typeof (this.opt.sFormRemoveAccessKeys) !== 'undefined')
519
	{
520
		if (typeof (document.forms[this.opt.sFormRemoveAccessKeys]))
521
		{
522
			let aInputs = document.forms[this.opt.sFormRemoveAccessKeys].getElementsByTagName('input');
523
			for (let i = 0; i < aInputs.length; i++)
524
			{
525
				if (typeof (this.aAccessKeys[aInputs[i].name]) !== 'undefined')
526
				{
527
					aInputs[i].accessKey = this.aAccessKeys[aInputs[i].name];
528
				}
529
			}
530
		}
531
	}
532
533
	return false;
534
};
535
536
// The function called after a user wants to save his precious message.
537
QuickModify.prototype.modifySave = function() {
538
	let i = 0,
539
		formData = '';
540
541
	// We cannot save if we weren't in edit mode.
542
	if (!this.bInEditMode)
543
	{
544
		return true;
545
	}
546
547
	this.bInEditMode = false;
548
549
	// Let's put back the accesskeys to their original place
550
	if (typeof (this.opt.sFormRemoveAccessKeys) !== 'undefined')
551
	{
552
		if (typeof (document.forms[this.opt.sFormRemoveAccessKeys]))
553
		{
554
			let aInputs = document.forms[this.opt.sFormRemoveAccessKeys].getElementsByTagName('input');
555
			for (i = 0; i < aInputs.length; i++)
556
			{
557
				if (typeof (this.aAccessKeys[aInputs[i].name]) !== 'undefined')
558
				{
559
					aInputs[i].accessKey = this.aAccessKeys[aInputs[i].name];
560
				}
561
			}
562
		}
563
	}
564
565
	// Send in the XMLhttp request and let's hope for the best.
566
	ajax_indicator(true);
567
	formData = serialize(document.forms.quickModForm); // uses form sessionID
568
	sendXMLDocument.call(this, elk_prepareScriptUrl(this.opt.sScriptUrl) + 'action=jsmodify;;api=xml', formData, this.onModifyDone);
0 ignored issues
show
The variable sendXMLDocument seems to be never declared. If this is a global, consider adding a /** global: sendXMLDocument */ comment.

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

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

Loading history...
569
570
	return false;
571
};
572
573
// Callback function of the XMLhttp request sending the modified message.
574
QuickModify.prototype.onModifyDone = function(XMLDoc) {
575
	let oErrordiv;
576
577
	// We've finished the loading stuff.
578
	ajax_indicator(false);
579
580
	// If we didn't get a valid document, just cancel.
581
	if (!XMLDoc || !XMLDoc.getElementsByTagName('elk')[0])
582
	{
583
		// Mozilla will nicely tell us what's wrong.
584
		if (typeof XMLDoc.childNodes !== 'undefined' && XMLDoc.childNodes.length > 0 && XMLDoc.firstChild.nodeName === 'parsererror')
585
		{
586
			oErrordiv = document.getElementById('error_box');
587
			oErrordiv.innerHTML = XMLDoc.firstChild.textContent;
588
			oErrordiv.style.display = '';
589
		}
590
		else
591
		{
592
			this.modifyCancel();
593
		}
594
		return;
595
	}
596
597
	let message = XMLDoc.getElementsByTagName('elk')[0].getElementsByTagName('message')[0],
598
		oBody = message.getElementsByTagName('body')[0],
599
		oSubject = message.getElementsByTagName('subject')[0],
600
		oModified = message.getElementsByTagName('modified')[0],
601
		oError = message.getElementsByTagName('error')[0];
602
603
	document.forms.quickModForm.message.classList.remove('border_error');
604
	document.forms.quickModForm.subject.classList.remove('border_error');
605
606
	if (oBody)
607
	{
608
		// Show new body.
609
		let bodyText = '';
610
		for (let i = 0; i < oBody.childNodes.length; i++)
611
		{
612
			bodyText += oBody.childNodes[i].nodeValue;
613
		}
614
615
		this.sMessageBuffer = this.opt.sTemplateBodyNormal.replace(/%body%/, bodyText);
616
		this.oCurMessageDiv.innerHTML = this.sMessageBuffer;
617
618
		// Show new subject div, update in case it changed
619
		let sSubjectText = oSubject.childNodes[0].nodeValue;
620
621
		this.sSubjectBuffer = this.opt.sTemplateSubjectNormal.replace(/%subject%/, sSubjectText);
622
		this.oCurSubjectDiv.innerHTML = this.sSubjectBuffer;
623
		this.oCurSubjectDiv.style.display = '';
624
625
		// If this is the first message, also update the category header.
626
		if (oSubject.getAttribute('is_first') === '1')
627
		{
628
			let subjectHeader = document.getElementById('topic_subject');
629
			if (subjectHeader)
630
			{
631
				subjectHeader.innerHTML = this.sSubjectBuffer;
632
			}
633
		}
634
635
		// Show this message as 'modified'.
636
		if (this.opt.bShowModify)
637
		{
638
			let modifiedSpan = document.querySelector('#modified_' + this.sCurMessageId.substring(4));
639
			if (modifiedSpan)
640
			{
641
				modifiedSpan.innerHTML = oModified.childNodes[0].nodeValue;
642
			}
643
		}
644
645
		// Restore the info bar div
646
		this.oCurInfoDiv.innerHTML = this.sInfoBuffer;
647
648
		// Show this message as 'modified on x by y'.
649
		if (this.opt.bShowModify)
650
		{
651
			let modified_element = document.getElementById('modified_' + this.sCurMessageId.substring(4));
652
			modified_element.innerHTML = message.getElementsByTagName('modified')[0].childNodes[0].nodeValue;
653
654
			// Just in case it's the first time the message is modified and the element is hidden
655
			modified_element.style.display = 'block';
656
		}
657
658
		// Hide the icon if we were told to
659
		if (this.opt.sIconHide !== null)
660
		{
661
			let oCurrentMsgIcon = document.getElementById('msg_icon_' + this.sCurMessageId.replace('msg_', ''));
662
			if (oCurrentMsgIcon !== null && oCurrentMsgIcon.src.indexOf(this.opt.sIconHide) > 0)
663
			{
664
				this.oMsgIcon.style.display = 'none';
665
			}
666
		}
667
668
		// Re embed any video links if the feature is available
669
		if (typeof $.fn.linkifyvideo === 'function')
670
		{
671
			$().linkifyvideo(oEmbedtext, this.sCurMessageId);
0 ignored issues
show
The variable oEmbedtext seems to be never declared. If this is a global, consider adding a /** global: oEmbedtext */ comment.

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

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

Loading history...
672
		}
673
674
		// Hello, Sweetie
675
		let spoilerHeader = document.getElementById(this.sCurMessageId).querySelector('.spoilerheader');
676
		if (spoilerHeader)
677
		{
678
			spoilerHeader.addEventListener('click', function() {
679
				let content = this.nextElementSibling.children;
680
				for (let i = 0; i < content.length; i++)
681
				{
682
					content[i].slideToggle(150);
683
				}
684
			});
685
		}
686
687
		// Re-Fix quote blocks
688
		if (typeof elk_quotefix === 'function')
0 ignored issues
show
The variable elk_quotefix seems to be never declared. If this is a global, consider adding a /** global: elk_quotefix */ comment.

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

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

Loading history...
689
		{
690
			elk_quotefix();
691
		}
692
693
		// Re-Fix code blocks
694
		if (typeof elk_codefix === 'function')
0 ignored issues
show
The variable elk_codefix seems to be never declared. If this is a global, consider adding a /** global: elk_codefix */ comment.

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

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

Loading history...
695
		{
696
			elk_codefix();
697
		}
698
699
		// Re-Fix quote blocks
700
		if (typeof elk_quotefix === 'function')
701
		{
702
			elk_quotefix();
703
		}
704
705
		// And pretty the code
706
		if (typeof prettyPrint === 'function')
0 ignored issues
show
The variable prettyPrint seems to be never declared. If this is a global, consider adding a /** global: prettyPrint */ comment.

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

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

Loading history...
707
		{
708
			prettyPrint();
709
		}
710
	}
711
	else if (oError)
712
	{
713
		oErrordiv = document.getElementById('error_box');
714
		oErrordiv.innerHTML = oError.childNodes[0].nodeValue;
715
		oErrordiv.style.display = '';
716
717
		if (oError.getAttribute('in_body') === '1')
718
		{
719
			document.forms.quickModForm.message.classList.add('border_error');
720
		}
721
722
		if (oError.getAttribute('in_subject') === '1')
723
		{
724
			document.forms.quickModForm.subject.classList.add('border_error');
725
		}
726
	}
727
};
728
729
/**
730
 * Quick Moderation for the topic view
731
 *
732
 * @param {type} oOptions
733
 */
734
function InTopicModeration (oOptions)
735
{
736
	this.opt = oOptions;
737
	this.bButtonsShown = false;
738
	this.iNumSelected = 0;
739
740
	this.init();
741
}
742
743
InTopicModeration.prototype.init = function() {
744
	// Add checkboxes w/click event to the messages that can be removed/split
745
	this.opt.aMessageIds.forEach((messageID) => {
746
		// Create the checkbox.
747
		let oCheckbox = document.createElement('input'),
748
			oCheckboxContainer = document.getElementById(this.opt.sCheckboxContainerMask + messageID);
749
750
		oCheckbox.type = 'checkbox';
751
		oCheckbox.name = 'msgs[]';
752
		oCheckbox.value = messageID;
753
		oCheckbox.title = 'checkbox ' + messageID;
754
		oCheckbox.onclick = this.handleClick.bind(this, oCheckbox);
755
756
		// Append it to the container
757
		oCheckboxContainer.appendChild(oCheckbox);
758
		oCheckboxContainer.classList.remove('hide');
759
	});
760
};
761
762
// They clicked a checkbox in a message, now show the button options to them
763
InTopicModeration.prototype.handleClick = function(oCheckbox) {
764
	let oButtonStrip = document.getElementById(this.opt.sButtonStrip),
765
		oButtonStripDisplay = document.getElementById(this.opt.sButtonStripDisplay),
766
		aButtonCounter = ['remove', 'restore', 'split'];
767
768
	if (!this.bButtonsShown && this.opt.sButtonStripDisplay)
769
	{
770
		// Make sure it can go somewhere.
771
		if (typeof oButtonStripDisplay === 'object' && oButtonStripDisplay !== null)
772
		{
773
			oButtonStripDisplay.style.display = '';
774
		}
775
		else
776
		{
777
			let oNewDiv = document.createElement('div'),
778
				oNewList = document.createElement('ul');
779
780
			oNewDiv.id = this.opt.sButtonStripDisplay;
781
			oNewDiv.className = this.opt.sButtonStripClass ? this.opt.sButtonStripClass : 'buttonlist';
782
783
			oNewDiv.appendChild(oNewList);
784
			oButtonStrip.appendChild(oNewDiv);
785
		}
786
787
		// Add the special selected buttons.
788
		aButtonCounter.forEach((addButton) => {
789
			let upperButton = addButton[0].toUpperCase() + addButton.substring(1);
790
791
			// As in bCanRemove etc.
792
			if (this.opt['bCan' + upperButton])
793
			{
794
				elk_addButton(this.opt.sButtonStrip, this.opt.bUseImageButton, {
795
					sId: addButton + '_button',
796
					sText: this.opt['s' + upperButton + 'ButtonLabel'],
797
					sImage: this.opt['s' + upperButton + 'ButtonImage'],
798
					sUrl: '#',
799
					aEvents: [
800
						['click', this.handleSubmit.bind(this, addButton)]
801
					]
802
				});
803
			}
804
		});
805
806
		// Adding these buttons once should be enough.
807
		this.bButtonsShown = true;
808
	}
809
810
	// Keep stats on how many items were selected.
811
	this.iNumSelected += oCheckbox.checked ? 1 : -1;
812
813
	// Show the number of messages selected in each of the buttons.
814
	aButtonCounter.forEach((addButton) => {
815
		let upperButton = addButton[0].toUpperCase() + addButton.substring(1);
816
		if (this.opt['bCan' + upperButton])
817
		{
818
			oButtonStrip.querySelector('#' + addButton + '_button_text').innerHTML = this.opt['s' + upperButton + 'ButtonLabel'] + ' [' + this.iNumSelected + ']';
819
			oButtonStrip.querySelector('#' + addButton + '_button').style.display = this.iNumSelected < 1 ? 'none' : '';
820
		}
821
	});
822
823
	// Toggle the sticky class based on if something is selected
824
	if (this.iNumSelected < 1)
825
	{
826
		oButtonStrip.classList.remove('sticky');
827
	}
828
	else
829
	{
830
		oButtonStrip.classList.add('sticky');
831
	}
832
};
833
834
// Called when the user clicks one of the buttons that we added
835
InTopicModeration.prototype.handleSubmit = function(sSubmitType) {
836
	// Make sure this form isn't submitted in another way than this function.
837
	let oForm = document.getElementById(this.opt.sFormId),
838
		oInput = document.createElement('input');
839
840
	oInput.type = 'hidden';
841
	oInput.name = this.opt.sSessionVar;
842
	oInput.value = this.opt.sSessionId;
843
	oForm.appendChild(oInput);
844
845
	// Set the form action based on the button they clicked
846
	switch (sSubmitType)
847
	{
848
		case 'remove':
849
			if (!confirm(this.opt.sRemoveButtonConfirm))
0 ignored issues
show
Debugging Code Best Practice introduced by
The confirm UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
850
			{
851
				return false;
852
			}
853
854
			oForm.action = oForm.action.replace(/;split_selection=1/, '');
855
			oForm.action = oForm.action.replace(/;restore_selected=1/, '');
856
			break;
857
858
		case 'restore':
859
			if (!confirm(this.opt.sRestoreButtonConfirm))
860
			{
861
				return false;
862
			}
863
864
			oForm.action = oForm.action.replace(/;split_selection=1/, '');
865
			oForm.action += ';restore_selected=1';
866
			break;
867
868
		case 'split':
869
			if (!confirm(this.opt.sRestoreButtonConfirm))
870
			{
871
				return false;
872
			}
873
874
			oForm.action = oForm.action.replace(/;restore_selected=1/, '');
875
			oForm.action += ';split_selection=1';
876
			break;
877
878
		default:
879
			return false;
880
	}
881
882
	oForm.submit();
883
	return true;
884
};
885
886
/**
887
 * Expands an attachment thumbnail when its clicked
888
 *
889
 * @param {string} thumbID
890
 * @param {string} messageID
891
 */
892
function expandThumbLB (thumbID, messageID)
893
{
894
	var link = document.getElementById('link_' + thumbID),
895
		siblings = $('a[data-lightboxmessage="' + messageID + '"]'),
896
		navigation = [],
897
		xDown = null,
898
		yDown = null,
899
		$elk_expand_icon = $('<span id="elk_lb_expand"></span>'),
900
		$elk_next_icon = $('<span id="elk_lb_next"></span>'),
901
		$elk_prev_icon = $('<span id="elk_lb_prev"></span>'),
902
		$elk_lightbox = $('#elk_lightbox'),
903
		$elk_lb_content = $('#elk_lb_content'),
904
		ajaxIndicatorOn = function() {
905
			$('<div id="lightbox-loading"><i class="icon icon-xl i-concentric"></i><div>').appendTo($elk_lb_content);
906
			$('html, body').addClass('elk_lb_no_scrolling');
907
		},
908
		ajaxIndicatorOff = function() {
909
			$('#lightbox-loading').remove();
910
		},
911
		closeLightbox = function() {
912
			// Close the lightbox and remove handlers
913
			$elk_expand_icon.off('click');
914
			$elk_next_icon.off('click');
915
			$elk_prev_icon.off('click');
916
			$elk_lightbox.hide();
917
			$elk_lb_content.html('').removeAttr('style').removeClass('expand');
918
			$('html, body').removeClass('elk_lb_no_scrolling');
919
			$(window).off('resize.lb');
920
			$(window).off('keydown.lb');
921
			$(window).off('touchstart.lb');
922
			$(window).off('touchmove.lb');
923
		},
924
		openLightbox = function() {
925
			// Load and open an image in the lightbox
926
			$('<img id="elk_lb_img" src="' + link.href + '">')
927
				.on('load', function() {
928
					var screenWidth = (window.innerWidth ? window.innerWidth : $(window).width()) * (is_mobile ? 0.8 : 0.9),
0 ignored issues
show
If you intend to check if the variable is_mobile is declared in the current environment, consider using typeof is_mobile === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
929
						screenHeight = (window.innerHeight ? window.innerHeight : $(window).height()) * 0.9;
930
931
					$(this).css({
932
						'max-width': Math.floor(screenWidth) + 'px',
933
						'max-height': Math.floor(screenHeight) + 'px'
934
					});
935
936
					// Add expand and prev/next links
937
					if (navigation.length > 1)
938
					{
939
						$elk_lb_content.html($(this)).append($elk_expand_icon).append($elk_next_icon).append($elk_prev_icon);
940
					}
941
					else
942
					{
943
						$elk_lb_content.html($(this)).append($elk_expand_icon);
944
					}
945
946
					ajaxIndicatorOff();
947
				})
948
				.on('error', function() {
949
					// Perhaps a message, but for now make it look like we tried and failed
950
					setTimeout(function() {
951
						ajaxIndicatorOff();
952
						closeLightbox();
953
						window.location = link.href;
954
					}, 1500);
955
				});
956
		},
957
		nextNav = function() {
958
			// Get / Set the next image ID in the array (with wrap around)
959
			thumbID = navigation[($.inArray(thumbID, navigation) + 1) % navigation.length];
960
		},
961
		prevNav = function() {
962
			// Get / Set the previous image ID in the array (with wrap around)
963
			thumbID = navigation[($.inArray(thumbID, navigation) - 1 + navigation.length) % navigation.length];
964
		},
965
		navLightbox = function() {
966
			// Navigate to the next image and show it in the lightbox
967
			$elk_lb_content.html('').removeAttr('style').removeClass('expand');
968
			ajaxIndicatorOn();
969
			$elk_expand_icon.off('click');
970
			$elk_next_icon.off('click');
971
			$elk_prev_icon.off('click');
972
			link = document.getElementById('link_' + thumbID);
973
			openLightbox();
974
			expandLightbox();
975
		},
976
		expandLightbox = function() {
977
			// Add an expand the image to full size when the expand icon is clicked
978
			$elk_expand_icon.on('click', function() {
979
				$('#elk_lb_content').addClass('expand').css({
980
					'height': Math.floor(window.innerHeight * 0.95) + 'px',
981
					'width': Math.floor(window.innerWidth * 0.9) + 'px',
982
					'left': '0'
983
				});
984
				$('#elk_lb_img').removeAttr('style');
985
				$elk_expand_icon.hide();
986
				$(window).off('keydown.lb');
987
				$(window).off('touchmove.lb');
988
			});
989
			$elk_next_icon.on('click', function(event) {
990
				event.preventDefault();
991
				event.stopPropagation();
992
				nextNav();
993
				navLightbox();
994
			});
995
			$elk_prev_icon.on('click', function(event) {
996
				event.preventDefault();
997
				event.stopPropagation();
998
				prevNav();
999
				navLightbox();
1000
			});
1001
		};
1002
1003
	// Create the lightbox container only if needed
1004
	if ($elk_lightbox.length <= 0)
1005
	{
1006
		// For easy manipulation
1007
		$elk_lightbox = $('<div id="elk_lightbox"></div>');
1008
		$elk_lb_content = $('<div id="elk_lb_content"></div>');
1009
1010
		$('body').append($elk_lightbox.append($elk_lb_content));
1011
	}
1012
1013
	// Load the navigation array
1014
	siblings.each(function() {
1015
		navigation[navigation.length] = $(this).data('lightboximage');
1016
	});
1017
1018
	// We should always have at least the thumbID
1019
	if (navigation.length === 0)
1020
	{
1021
		navigation[navigation.length] = thumbID;
1022
	}
1023
1024
	// Load and show the initial lightbox container div
1025
	ajaxIndicatorOn();
1026
	$elk_lightbox.fadeIn(200);
1027
	openLightbox();
1028
	expandLightbox();
1029
1030
	// Click anywhere on the page (except the expand icon) to close the lightbox
1031
	$elk_lightbox.on('click', function(event) {
1032
		if (event.target.id !== $elk_expand_icon.attr('id'))
1033
		{
1034
			event.preventDefault();
1035
			closeLightbox();
1036
		}
1037
	});
1038
1039
	// Provide some keyboard navigation
1040
	$(window).on('keydown.lb', function(event) {
1041
		event.preventDefault();
1042
1043
		// escape
1044
		if (event.which === 27)
1045
		{
1046
			closeLightbox();
1047
		}
1048
1049
		// left
1050
		if (event.which === 37)
1051
		{
1052
			prevNav();
1053
			navLightbox();
1054
		}
1055
1056
		// right
1057
		if (event.which === 39)
1058
		{
1059
			nextNav();
1060
			navLightbox();
1061
		}
1062
	});
1063
1064
	// Make the image size fluid as the browser window changes
1065
	$(window).on('resize.lb', function() {
1066
		// Account for either a normal or expanded view
1067
		var $_elk_lb_content = $('#elk_lb_content');
1068
1069
		if ($_elk_lb_content.hasClass('expand'))
1070
		{
1071
			$_elk_lb_content.css({'height': window.innerHeight * 0.85, 'width': window.innerWidth * 0.9});
1072
		}
1073
		else
1074
		{
1075
			$('#elk_lb_img').css({'max-height': window.innerHeight * 0.9, 'max-width': window.innerWidth * 0.8});
1076
		}
1077
	});
1078
1079
	// Swipe navigation start, record press x/y
1080
	$(window).on('touchstart.lb', function(event) {
1081
		xDown = event.originalEvent.touches[0].clientX;
1082
		yDown = event.originalEvent.touches[0].clientY;
1083
	});
1084
1085
	// Swipe navigation left / right detection
1086
	$(window).on('touchmove.lb', function(event) {
1087
		// No known start point ?
1088
		if (!xDown || !yDown)
1089
		{
1090
			return;
1091
		}
1092
1093
		// Where are we now
1094
		var xUp = event.originalEvent.touches[0].clientX,
1095
			yUp = event.originalEvent.touches[0].clientY,
1096
			xDiff = xDown - xUp,
1097
			yDiff = yDown - yUp;
1098
1099
		// Moved enough to know what direction they are swiping
1100
		if (Math.abs(xDiff) > Math.abs(yDiff))
1101
		{
1102
			if (xDiff > 0)
1103
			{
1104
				// Swipe left
1105
				prevNav();
1106
				navLightbox();
1107
			}
1108
			else
1109
			{
1110
				// Swipe right
1111
				nextNav();
1112
				navLightbox();
1113
			}
1114
		}
1115
1116
		// Reset values
1117
		xDown = null;
1118
		yDown = null;
1119
	});
1120
1121
	return false;
1122
}
1123
1124
/**
1125
 * Provides a way to toggle an ignored message(s) visibility
1126
 *
1127
 * @param {object} msgids
1128
 * @param {string} text
1129
 */
1130
function ignore_toggles (msgids, text)
1131
{
1132
	for (let i = 0; i < msgids.length; i++)
1133
	{
1134
		let msgid = msgids[i];
1135
1136
		new elk_Toggle({
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new elk_Toggle({Identifi...e)),false,false))))))}) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
Coding Style Best Practice introduced by
By convention, constructors like elk_Toggle should be capitalized.
Loading history...
The variable elk_Toggle seems to be never declared. If this is a global, consider adding a /** global: elk_Toggle */ comment.

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

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

Loading history...
1137
			bToggleEnabled: true,
1138
			bCurrentlyCollapsed: true,
1139
			aSwappableContainers: [
1140
				'msg_' + msgid,
1141
			],
1142
			aSwapLinks: [
1143
				{
1144
					sId: 'msg_' + msgid + '_ignored_link',
1145
					msgExpanded: 'X',
1146
					msgCollapsed: text
1147
				}
1148
			]
1149
		});
1150
	}
1151
}
1152
1153
/**
1154
 * Used to split a topic.
1155
 * Allows selecting a message so it can be moved from the original to the spit topic or back
1156
 *
1157
 * @param {string} direction up / down / reset
1158
 * @param {int} msg_id message id that is being moved
1159
 */
1160
function topicSplitselect (direction, msg_id)
1161
{
1162
	getXMLDocument(elk_prepareScriptUrl(elk_scripturl) + 'action=splittopics;sa=selectTopics;subname=' + topic_subject + ';topic=' + topic_id + '.' + start[0] + ';start2=' + start[1] + ';move=' + direction + ';msg=' + msg_id + ';api=xml', onTopicSplitReceived);
0 ignored issues
show
The variable topic_id seems to be never declared. If this is a global, consider adding a /** global: topic_id */ comment.

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

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

Loading history...
The variable start seems to be never declared. If this is a global, consider adding a /** global: start */ comment.

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

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

Loading history...
The variable elk_scripturl seems to be never declared. If this is a global, consider adding a /** global: elk_scripturl */ comment.

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

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

Loading history...
The variable topic_subject seems to be never declared. If this is a global, consider adding a /** global: topic_subject */ comment.

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

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

Loading history...
1163
1164
	return false;
1165
}
1166
1167
/**
1168
 * Callback function for topicSplitselect
1169
 *
1170
 * @param {xmlCallback} XMLDoc
1171
 */
1172
function onTopicSplitReceived (XMLDoc)
1173
{
1174
	let i,
1175
		j,
1176
		pageIndex;
1177
1178
	// Find the selected and not_selected page index containers
1179
	for (i = 0; i < 2; i++)
1180
	{
1181
		pageIndex = XMLDoc.getElementsByTagName('pageIndex')[i];
1182
1183
		// Update the page container with our xml response
1184
		document.getElementById('pageindex_' + pageIndex.getAttribute('section')).innerHTML = pageIndex.firstChild.nodeValue;
1185
		start[i] = pageIndex.getAttribute('startFrom');
0 ignored issues
show
The variable start seems to be never declared. If this is a global, consider adding a /** global: start */ comment.

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

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

Loading history...
1186
	}
1187
1188
	var numChanges = XMLDoc.getElementsByTagName('change').length,
1189
		curChange,
1190
		curSection,
1191
		curAction,
1192
		curId,
1193
		curList,
1194
		newItem,
1195
		sInsertBeforeId,
1196
		oListItems,
1197
		right_arrow = '<i class="icon icon-big i-chevron-circle-right"></i>',
1198
		left_arrow = '<i class="icon icon-big i-chevron-circle-left"></i>';
1199
1200
	// Loop through all changes ajax returned
1201
	for (i = 0; i < numChanges; i++)
1202
	{
1203
		curChange = XMLDoc.getElementsByTagName('change')[i];
1204
		curSection = curChange.getAttribute('section');
1205
		curAction = curChange.getAttribute('curAction');
1206
		curId = curChange.getAttribute('id');
1207
		curList = document.getElementById('messages_' + curSection);
1208
1209
		// Remove it from the source list, so we can insert it in the destination list
1210
		if (curAction === 'remove')
1211
		{
1212
			curList.removeChild(document.getElementById(curSection + '_' + curId));
1213
		}
1214
		// Insert a message.
1215
		else
1216
		{
1217
			// By default, insert the element at the end of the list.
1218
			sInsertBeforeId = null;
1219
1220
			// Loop through the list to try and find an item to insert after.
1221
			oListItems = curList.getElementsByTagName('li');
1222
			for (j = 0; j < oListItems.length; j++)
1223
			{
1224
				if (parseInt(oListItems[j].id.substring(curSection.length + 1)) < curId)
1225
				{
1226
					// This would be a nice place to insert the row.
1227
					sInsertBeforeId = oListItems[j].id;
1228
1229
					// We're done for now. Escape the loop.
1230
					j = oListItems.length + 1;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable j here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
1231
				}
1232
			}
1233
1234
			// Let's create a nice container for the message.
1235
			newItem = document.createElement('li');
1236
			newItem.className = '';
1237
			newItem.id = curSection + '_' + curId;
1238
			newItem.innerHTML = '' +
1239
				'<div class="content">' +
1240
				'   <div class="message_header">' +
1241
				'       <a class="split_icon float' + (curSection === 'selected' ? 'left' : 'right') + '" href="' + elk_prepareScriptUrl(elk_scripturl) + 'action=splittopics;sa=selectTopics;subname=' + topic_subject + ';topic=' + topic_id + '.' + not_selected_start + ';start2=' + selected_start + ';move=' + (curSection === 'selected' ? 'up' : 'down') + ';msg=' + curId + '" onclick="return topicSplitselect(\'' + (curSection === 'selected' ? 'up' : 'down') + '\', ' + curId + ');">' +
0 ignored issues
show
The variable elk_scripturl seems to be never declared. If this is a global, consider adding a /** global: elk_scripturl */ comment.

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

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

Loading history...
The variable topic_id seems to be never declared. If this is a global, consider adding a /** global: topic_id */ comment.

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

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

Loading history...
The variable not_selected_start seems to be never declared. If this is a global, consider adding a /** global: not_selected_start */ comment.

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

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

Loading history...
The variable selected_start seems to be never declared. If this is a global, consider adding a /** global: selected_start */ comment.

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

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

Loading history...
The variable topic_subject seems to be never declared. If this is a global, consider adding a /** global: topic_subject */ comment.

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

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

Loading history...
1242
				(curSection === 'selected' ? left_arrow : right_arrow) +
1243
				'       </a>' +
1244
				'       <strong>' + curChange.getElementsByTagName('subject')[0].firstChild.nodeValue + '</strong> ' + txt_by + ' <strong>' + curChange.getElementsByTagName('poster')[0].firstChild.nodeValue + '</strong>' +
0 ignored issues
show
The variable txt_by seems to be never declared. If this is a global, consider adding a /** global: txt_by */ comment.

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

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

Loading history...
1245
				'       <br />' +
1246
				'       <em>' + curChange.getElementsByTagName('time')[0].firstChild.nodeValue + '</em>' +
1247
				'   </div>' +
1248
				'   <div class="post">' + curChange.getElementsByTagName('body')[0].firstChild.nodeValue + '</div>' +
1249
				'</div>';
1250
1251
			// So, where do we insert it?
1252
			if (typeof sInsertBeforeId === 'string')
1253
			{
1254
				curList.insertBefore(newItem, document.getElementById(sInsertBeforeId));
1255
			}
1256
			else
1257
			{
1258
				curList.appendChild(newItem);
1259
			}
1260
		}
1261
	}
1262
}
1263
1264
/**
1265
 * Quick Moderation for the message listing
1266
 *
1267
 * @param {type} oOptions
1268
 *      - aQmActions: array of possible actions restore, markread, merge, etc
1269
 * 	    - sButtonStrip: string identifying the button container, typically moderationbuttons
1270
 * 		- sButtonStripDisplay: string of ID to attach to UL if creating, typically moderationbuttons_strip
1271
 *      - bUseImageButton: If to show an icon on the button when selected
1272
 * 		- sFormId: The id of the QM form, which we will submit on a button (link) click
1273
 * 	    - bHideStrip: boolean If to initially hide the button strip
1274
 * 	Button Definitions are (remove shown as example)
1275
 * 	    - bCanRemove: boolean, if this button can be shown at all
1276
 * 		- aActionRemove: optional array of specific topic id's that increase the counter (remove in this example)
1277
 * 		- sRemoveButtonLabel: Button text
1278
 * 		- sRemoveButtonImage: svg icon name from css, like "i-delete",
1279
 * 		- sRemoveButtonConfirm: optional, text to show in a confirm dialog
1280
 */
1281
function InTopicListModeration (oOptions)
1282
{
1283
	this.opt = oOptions;
1284
	this.bButtonsShown = false;
1285
	this.iNumSelected = 0;
1286
	this.opt.bHideStrip = this.opt.bHideStrip || true;
1287
1288
	this.init();
1289
}
1290
1291
InTopicListModeration.prototype.init = function() {
1292
	// fetch all topic[] checkbox elements
1293
	let eCheckboxes = document.getElementsByName('topics[]'),
1294
		eSelectAll = document.getElementById('select_all');
1295
1296
	// Bind a click event to each action button
1297
	eCheckboxes.forEach((eCheck) => {
1298
		eCheck.onclick = this.handleClick.bind(this, eCheck);
1299
	});
1300
1301
	// Hide the entire strip until needed, use of style is required
1302
	if (this.opt.bHideStrip)
1303
	{
1304
		document.getElementById(this.opt.sButtonStrip).style.display = 'none';
1305
	}
1306
1307
	// Watch for the select/unselect all click
1308
	if (eSelectAll !== null)
1309
	{
1310
		eSelectAll.addEventListener('change', () => {
1311
			this.handleClick();
1312
		});
1313
	}
1314
};
1315
1316
// They clicked a checkbox in the topic listing, show additional button options / counters
1317
InTopicListModeration.prototype.handleClick = function(oCheckbox) {
0 ignored issues
show
The parameter oCheckbox 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...
1318
	let oButtonStrip = document.getElementById(this.opt.sButtonStrip),
1319
		eCheckboxes = document.querySelectorAll('input[name="topics[]"]:checked'),
1320
		aCurrentlySelected = [];
1321
1322
	// Topic array of currently selected (checked) topics
1323
	eCheckboxes.forEach((check) => {
1324
		aCurrentlySelected.push(parseInt(check.value));
1325
	});
1326
1327
	// If the buttons are not there, add them
1328
	if (!this.bButtonsShown && this.opt.sButtonStripDisplay)
1329
	{
1330
		this.addButtons();
1331
	}
1332
1333
	// Keep stats on how many checkboxes are selected.
1334
	this.iNumSelected = aCurrentlySelected.length;
1335
1336
	// Check each action, update counters to show availability for the selected topics
1337
	this.opt.aQmActions.forEach((action) => {
1338
		let upperAction = action[0].toUpperCase() + action.substring(1),
1339
			thisTopics = this.opt['aAction' + upperAction],
1340
			indicator = 0;
1341
1342
		// Update the button counters to show where this action is allowed
1343
		if (this.opt['bCan' + upperAction])
1344
		{
1345
			let button = oButtonStrip.querySelector('#button_strip_' + action),
1346
				buttonAlt = oButtonStrip.querySelector('#button_strip_' + action + '_text');
1347
1348
			// No specified action array, so this can be done everywhere
1349
			if (typeof thisTopics === 'undefined')
1350
			{
1351
				indicator = this.iNumSelected;
1352
			}
1353
			// Availability array, can this be done here (e.g. can approve)
1354
			else
1355
			{
1356
				let filteredArray = thisTopics.filter(value => aCurrentlySelected.includes(value));
1357
1358
				indicator = filteredArray.length;
1359
			}
1360
1361
			// Update the button counter, if zero leave the button hidden
1362
			if (indicator < 1)
1363
			{
1364
				if (buttonAlt !== null)
1365
				{
1366
					buttonAlt.text = this.opt['s' + upperAction + 'ButtonLabel'];
1367
					button.classList.add('hide');
1368
				}
1369
				else
1370
				{
1371
					button.text = this.opt['s' + upperAction + 'ButtonLabel'];
1372
					button.parentElement.classList.add('hide');
1373
				}
1374
			}
1375
			else
1376
			{
1377
				if (buttonAlt !== null)
1378
				{
1379
					buttonAlt.textContent = this.opt['s' + upperAction + 'ButtonLabel'] + ' [' + indicator + ']';
1380
					button.classList.remove('hide');
1381
				}
1382
				else
1383
				{
1384
					button.text = this.opt['s' + upperAction + 'ButtonLabel'] + ' [' + indicator + ']';
1385
					button.parentElement.classList.remove('hide');
1386
				}
1387
			}
1388
		}
1389
	});
1390
1391
	if (this.opt.bCanRestore)
1392
	{
1393
		let restore = oButtonStrip.querySelector('#restore_button_text');
1394
		if (restore)
1395
		{
1396
			restore.innerHTML = this.opt.sRestoreButtonLabel + ' [' + this.iNumSelected + ']';
1397
			if (this.iNumSelected < 1)
1398
			{
1399
				oButtonStrip.querySelector('#restore_button').classList.add('hide');
1400
			}
1401
			else
1402
			{
1403
				oButtonStrip.querySelector('#restore_button').classList.remove('hide');
1404
			}
1405
		}
1406
	}
1407
1408
	// Toggle the sticky class based on if something is selected
1409
	if (this.iNumSelected < 1)
1410
	{
1411
		oButtonStrip.classList.remove('sticky');
1412
		if (this.opt.bHideStrip)
1413
		{
1414
			oButtonStrip.style.display = 'none';
1415
		}
1416
	}
1417
	else
1418
	{
1419
		oButtonStrip.classList.add('sticky');
1420
		oButtonStrip.style.display = '';
1421
	}
1422
};
1423
1424
// Add / modifying buttons as defined in the options
1425
InTopicListModeration.prototype.addButtons = function() {
1426
	this.opt.aQmActions.forEach((action) => {
1427
		let sUpperAction = action[0].toUpperCase() + action.substring(1),
1428
			bCheck = document.getElementById('button_strip_' + action);
1429
1430
		if (!this.opt['bCan' + sUpperAction])
1431
		{
1432
			return;
1433
		}
1434
1435
		// Button does not exist
1436
		if (bCheck === null)
1437
		{
1438
			elk_addButton(this.opt.sButtonStrip, this.opt.bUseImageButton, {
1439
				sId: 'button_strip_' + action,
1440
				sText: this.opt['s' + sUpperAction + 'ButtonLabel'],
1441
				sImage: this.opt['s' + sUpperAction + 'ButtonImage'],
1442
				sUrl: '#',
1443
				aEvents: [
1444
					['click', this.handleSubmit.bind(this, action)]
1445
				]
1446
			});
1447
1448
			return;
1449
		}
1450
1451
		// It exists, but they want the icons.  Do a full replacement
1452
		if (this.opt.bUseImageButton)
1453
		{
1454
			bCheck.parentElement.remove();
1455
			elk_addButton(this.opt.sButtonStrip, this.opt.bUseImageButton, {
1456
				sId: 'button_strip_' + action,
1457
				sText: this.opt['s' + sUpperAction + 'ButtonLabel'],
1458
				sImage: this.opt['s' + sUpperAction + 'ButtonImage'],
1459
				sUrl: '#',
1460
				aEvents: [
1461
					['click', this.handleSubmit.bind(this, action)]
1462
				]
1463
			});
1464
1465
			return;
1466
		}
1467
1468
		// Default, bind a click to the link, which will submit the form
1469
		bCheck.addEventListener('click', this.handleSubmit.bind(this, action));
1470
	});
1471
1472
	// Adding these buttons once is enough.
1473
	this.bButtonsShown = true;
1474
};
1475
1476
// Called when the user clicks one of the buttons that we added
1477
InTopicListModeration.prototype.handleSubmit = function(sSubmitType) {
1478
	// Make sure this form isn't submitted in another way than this function.
1479
	let oForm = document.getElementById(this.opt.sFormId),
1480
		oInput = document.getElementById('qaction'),
1481
		upperAction = sSubmitType[0].toUpperCase() + sSubmitType.substring(1);
1482
1483
	// Set the forms' qaction value based on the button they clicked
1484
	oInput.value = sSubmitType;
1485
1486
	// Any confirmation before we submit
1487
	if (typeof this.opt['s' + upperAction + 'ButtonConfirm'] !== 'undefined')
1488
	{
1489
		if (!confirm(this.opt['s' + upperAction + 'ButtonConfirm']))
0 ignored issues
show
Debugging Code Best Practice introduced by
The confirm UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
1490
		{
1491
			return false;
1492
		}
1493
	}
1494
1495
	// Submit and hope for the best :P
1496
	oForm.submit();
1497
	return true;
1498
};
1499