Project

General

Profile

Download (21.6 KB) Statistics
| Branch: | Tag: | Revision:
1
/* ====================================================================
2
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
3
 *
4
 *	Redistribution and use in source and binary forms, with or without modification,
5
 *	are permitted provided that the following conditions are met:
6
 *
7
 *	1. Redistributions of source code must retain the above copyright notice,
8
 *		this list of conditions and the following disclaimer.
9
 *
10
 *	2. Redistributions in binary form must reproduce the above copyright
11
 *		notice, this list of conditions and the following disclaimer in
12
 *		the documentation and/or other materials provided with the
13
 *		distribution.
14
 *
15
 *	3. All advertising materials mentioning features or use of this software
16
 *		must display the following acknowledgment:
17
 *		"This product includes software developed by the pfSense Project
18
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
19
 *
20
 *	4. The names "pfSense" and "pfSense Project" must not be used to
21
 *		 endorse or promote products derived from this software without
22
 *		 prior written permission. For written permission, please contact
23
 *		 coreteam@pfsense.org.
24
 *
25
 *	5. Products derived from this software may not be called "pfSense"
26
 *		nor may "pfSense" appear in their names without prior written
27
 *		permission of the Electric Sheep Fencing, LLC.
28
 *
29
 *	6. Redistributions of any form whatsoever must retain the following
30
 *		acknowledgment:
31
 *
32
 *	"This product includes software developed by the pfSense Project
33
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
34
 *
35
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
36
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
39
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
41
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
46
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
47
 *
48
 *	====================================================================
49
 *
50
 */
51

    
52
// These helper functions are used on many/most UI pages to hide/show/disable/enable form elements where required
53

    
54
// Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden
55
function hideInput(id, hide) {
56
	if (hide)
57
		$('#' + id).parent().parent('div').addClass('hidden');
58
	else
59
		$('#' + id).parent().parent('div').removeClass('hidden');
60
}
61

    
62
// Hides the <div> in which the specified group input element lives so that the input,
63
// its label and help text are hidden
64
function hideGroupInput(id, hide) {
65
	if (hide)
66
		$('#' + id).parent('div').addClass('hidden');
67
	else
68
		$('#' + id).parent('div').removeClass('hidden');
69
}
70

    
71
// Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden
72
function hideCheckbox(id, hide) {
73
	if (hide)
74
		$('#' + id).parent().parent().parent('div').addClass('hidden');
75
	else
76
		$('#' + id).parent().parent().parent('div').removeClass('hidden');
77
}
78

    
79
// Disables the specified input element
80
function disableInput(id, disable) {
81
	$('#' + id).prop("disabled", disable);
82
}
83

    
84
// Hides all elements of the specified class. This will usually be a section
85
function hideClass(s_class, hide) {
86
	if (hide)
87
		$('.' + s_class).hide();
88
	else
89
		$('.' + s_class).show();
90
}
91

    
92
// Hides all elements of the specified class assigned to a group. This will usually be a group
93
function hideGroupClass(s_class, hide) {
94
	if (hide)
95
		$('.' + s_class).parent().parent().parent().hide();
96
	else
97
		$('.' + s_class).parent().parent().parent().show();
98
}
99

    
100
function hideSelect(id, hide) {
101
	if (hide)
102
		$('#' + id).parent('div').parent('div').addClass('hidden');
103
	else
104
		$('#' + id).parent('div').parent('div').removeClass('hidden');
105
}
106

    
107
function hideMultiCheckbox(id, hide) {
108
	if (hide)
109
		$("[name=" + id + "]").parent().addClass('hidden');
110
	else
111
		$("[name=" + id + "]").parent().removeClass('hidden');
112
}
113

    
114
// Hides the <div> in which the specified IP address element lives so that the input, its label and help text are hidden
115
function hideIpAddress(id, hide) {
116
	if (hide)
117
		$('#' + id).parent().parent().parent('div').addClass('hidden');
118
	else
119
		$('#' + id).parent().parent().parent('div').removeClass('hidden');
120
}
121

    
122
// Hides all elements of the specified class belonging to a multiselect.
123
function hideMultiClass(s_class, hide) {
124
	if (hide)
125
		$('.' + s_class).parent().parent().hide();
126
	else
127
		$('.' + s_class).parent().parent().show();
128
}
129

    
130
// Hides div whose label contains the specified text. (Good for StaticText)
131
function hideLabel(text, hide) {
132

    
133
	var element = $('label:contains(' + text + ')');
134

    
135
	if (hide)
136
		element.parent('div').addClass('hidden');
137
	else
138
		element.parent('div').removeClass('hidden');
139
}
140

    
141
// Hides the '/' and the subnet mask of an Ip_Address/subnet_mask group
142
function hideMask(name, hide) {
143
	if(hide) {
144
		$('[id^=' + name + ']').hide();
145
		$('[id^=' + name + ']').prev('span').hide();
146
		$('[id^=' + name + ']').parent('div').removeClass('input-group');
147
	} else {
148
		$('[id^=' + name + ']').show();
149
		$('[id^=' + name + ']').prev('span').show();
150
		$('[id^=' + name + ']').parent('div').addClass('input-group');
151
	}
152
}
153

    
154
// Toggle table row checkboxes and background colors on the pages that use sortable tables:
155
//	/usr/local/www/firewall_nat.php
156
//	/usr/local/www/firewall_nat_1to1.php
157
//	/usr/local/www/firewall_nat_out.php
158
//	/usr/local/www/firewall_rules.php
159
//	/usr/local/www/vpn_ipsec.php
160
// Striping of the tables is handled here, NOT with the Bootstrap table-striped class because it would
161
// get confused when rows are sorted or deleted.
162

    
163
function fr_toggle(id, prefix) {
164
	if (!prefix)
165
		prefix = 'fr';
166

    
167
	var checkbox = document.getElementById(prefix + 'c' + id);
168
	checkbox.checked = !checkbox.checked;
169
	fr_bgcolor(id, prefix);
170
}
171

    
172
// Change background color of selected row based on state of checkbox
173
function fr_bgcolor(id, prefix) {
174
	if (!prefix)
175
		prefix = 'fr';
176

    
177
	var row = $('#' + prefix + id);
178

    
179
	if ($('#' + prefix + 'c' + id).prop('checked') ) {
180
		row.addClass('active');
181
	} else {
182
		row.removeClass('active');
183
	}
184
}
185

    
186
// The following functions are used by Form_Groups assigned a class of "repeatable" and provide the ability
187
// to add/delete rows of sequentially numbered elements, their labels and their help text
188
// See firewall_aliases_edit.php for an example
189

    
190
// NOTE: retainhelp is a global var that when defined prevents any help text from being deleted as lines are inserted.
191
// IOW it causes every row to have help text, not just the last row
192

    
193
function setMasks() {
194
	// Find all ipaddress masks and make dynamic based on address family of input
195
	$('span.pfIpMask + select').each(function (idx, select){
196
		var input = $(select).prevAll('input[type=text]');
197

    
198
		input.on('change', function(e){
199
			var isV6 = (input.val().indexOf(':') != -1), min = 0, max = 128;
200
			if (!isV6)
201
				max = 32;
202

    
203
			if (input.val() == "")
204
				return;
205

    
206
			while (select.options.length > max)
207
				select.remove(0);
208

    
209
			if (select.options.length < max) {
210
				for (var i=select.options.length; i<=max; i++)
211
					select.options.add(new Option(i, i), 0);
212
			}
213
		});
214

    
215
		// Fire immediately
216
		input.change();
217
	});
218
}
219

    
220
// Complicated function to move all help text associated with this input id to the same id
221
// on the row above. That way if you delete the last row, you don't lose the help
222
function moveHelpText(id) {
223
	$('#' + id).parent('div').parent('div').find('input, select, checkbox').each(function() {	 // For each <span></span>
224
		var fromId = this.id;
225
		var toId = decrStringInt(fromId);
226
		var helpSpan;
227

    
228
		if (!$(this).hasClass('pfIpMask') && !$(this).hasClass('btn')) {
229
			if ($('#' + decrStringInt(fromId)).parent('div').hasClass('input-group')) {
230
				helpSpan = $('#' + fromId).parent('div').parent('div').find('span:last').clone();
231
			} else {
232
				helpSpan = $('#' + fromId).parent('div').find('span:last').clone();
233
			}
234
			if ($(helpSpan).hasClass('help-block')) {
235
				if ($('#' + decrStringInt(fromId)).parent('div').hasClass('input-group')) {
236
					$('#' + decrStringInt(fromId)).parent('div').after(helpSpan);
237
				} else {
238
					$('#' + decrStringInt(fromId)).after(helpSpan);
239
				}
240
			}
241
		}
242
	});
243
}
244

    
245
// Increment the number at the end of the string
246
function bumpStringInt( str )	{
247
  var data = str.match(/(\D*)(\d+)(\D*)/), newStr = "";
248

    
249
  if (data)
250
	newStr = data[ 1 ] + ( Number( data[ 2 ] ) + 1 ) + data[ 3 ];
251

    
252
  return newStr || str;
253
}
254

    
255
// Decrement the number at the end of the string
256
function decrStringInt( str )	{
257
  var data = str.match(/(\D*)(\d+)(\D*)/), newStr = "";
258

    
259
  if (data)
260
	newStr = data[ 1 ] + ( Number( data[ 2 ] ) - 1 ) + data[ 3 ];
261

    
262
  return newStr || str;
263
}
264

    
265
// Called after a delete so that there are no gaps in the numbering. Most of the time the config system doesn't care about
266
// gaps, but I do :)
267
function renumber() {
268
	var idx = 0;
269

    
270
	$('.repeatable').each(function() {
271

    
272
		$(this).find('input').each(function() {
273
			$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
274
			$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
275
		});
276

    
277
		$(this).find('select').each(function() {
278
			$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
279
			$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
280
		});
281

    
282
//		$(this).find('label').attr('for', $(this).find('label').attr('for').replace(/\d+$/, "") + idx);
283

    
284
		idx++;
285
	});
286
}
287

    
288
function delete_row(rowDelBtn) {
289
	var rowLabel;
290

    
291
	// If we are deleting row zero, we need to save/restore the label
292
	if (rowDelBtn == "deleterow0") {
293
		rowLabel = $('#' + rowDelBtn).parent('div').parent('div').find('label').text();
294
	}
295

    
296
	$('#' + rowDelBtn).parent('div').parent('div').remove();
297
	renumber();
298
	checkLastRow();
299

    
300
	if (rowDelBtn == "deleterow0") {
301
		$('#' + rowDelBtn).parent('div').parent('div').find('label').text(rowLabel);
302
	}
303
}
304

    
305
function checkLastRow() {
306
	if ($('.repeatable').length <= 1) {
307
		$('#deleterow0').hide();
308
	} else {
309
		$('[id^=deleterow]').show();
310
	}
311
}
312

    
313
function add_row() {
314
	// Find the last repeatable group
315
	var lastRepeatableGroup = $('.repeatable:last');
316

    
317
	// Clone it
318
	var newGroup = lastRepeatableGroup.clone();
319
	// Increment the suffix number for each input element in the new group
320
	$(newGroup).find('input').each(function() {
321
		$(this).prop("id", bumpStringInt(this.id));
322
		$(this).prop("name", bumpStringInt(this.name));
323
		if (!$(this).is('[id^=delete]'))
324
			$(this).val('');
325
	});
326

    
327
	// Increment the suffix number for the deleterow button element in the new group
328
	$(newGroup).find('[id^=deleterow]').each(function() {
329
		$(this).prop("id", bumpStringInt(this.id));
330
		$(this).prop("name", bumpStringInt(this.name));
331
	});
332

    
333
	// Do the same for selectors
334
	$(newGroup).find('select').each(function() {
335
		$(this).prop("id", bumpStringInt(this.id));
336
		$(this).prop("name", bumpStringInt(this.name));
337
		// If this selector lists mask bits, we need it to be reset to all 128 options
338
		// and no items selected, so that automatic v4/v6 selection still works
339
		if ($(this).is('[id^=address_subnet]')) {
340
			$(this).empty();
341
			for (idx=128; idx>0; idx--) {
342
				$(this).append($('<option>', {
343
					value: idx,
344
					text: idx
345
				}));
346
			}
347
		}
348
	});
349

    
350
	// And for "for" tags
351
//	$(newGroup).find('label').attr('for', bumpStringInt($(newGroup).find('label').attr('for')));
352

    
353
	$(newGroup).find('label').text(""); // Clear the label. We only want it on the very first row
354

    
355
	// Insert the updated/cloned row
356
	$(lastRepeatableGroup).after(newGroup);
357

    
358
	// Delete any help text from the group we have cloned
359
	$(lastRepeatableGroup).find('.help-block').each(function() {
360
		if ((typeof retainhelp) == "undefined")
361
			$(this).remove();
362
	});
363

    
364
	setMasks();
365

    
366
	checkLastRow();
367

    
368
	// Autocomplete
369
	if ( typeof addressarray !== 'undefined') {
370
		$('[id^=address]').each(function() {
371
			if (this.id.substring(0, 8) != "address_") {
372
				$(this).autocomplete({
373
					source: addressarray
374
				});
375
			}
376
		});
377
	}
378

    
379
	// Now that we are no longer cloning the event handlers, we need to remove and re-add after a new row
380
	// has been added to the table
381
	$('[id^=delete]').unbind();
382
	$('[id^=delete]').click(function(event) {
383
		if ($('.repeatable').length > 1) {
384
			if ((typeof retainhelp) == "undefined")
385
				moveHelpText(event.target.id);
386

    
387
			delete_row(event.target.id);
388
		} else {
389
			alert('The last row may not be deleted.');
390
		}
391
	});
392

    
393
}
394

    
395
// These are action buttons, not submit buttons
396
$('[id^=addrow]').prop('type','button');
397
$('[id^=delete]').prop('type','button');
398

    
399
// on click . .
400
$('[id^=addrow]').click(function() {
401
	add_row();
402
});
403

    
404
$('[id^=delete]').click(function(event) {
405
	if ($('.repeatable').length > 1) {
406
		if ((typeof retainhelp) == "undefined")
407
			moveHelpText(event.target.id);
408

    
409
		delete_row(event.target.id);
410
	} else {
411
		alert('The last row may not be deleted.');
412
	}
413
});
414

    
415
// "More information" handlers --------------------------------------------------------------------
416

    
417
// If there is an infoblock, automatically add an info icon that toggles its display
418

    
419
var sfx = 0;
420

    
421
$('.infoblock').each(function() {
422
	// If the block has the class "blockopen" it is initially open
423
	if (! $(this).hasClass("blockopen")) {
424
		$(this).hide();
425
	} else {
426
		$(this).removeClass("blockopen");
427
	}
428

    
429
	// Add the "i" icon before the infoblock, incrementing the icon id for each block (in case there are multiple infoblocks on a page)
430
	$(this).before('<i class="fa fa-info-circle icon-pointer" style="color: #337AB7; font-size:20px; margin-left: 10px; margin-bottom: 10px;" id="showinfo' + sfx.toString() + '" title="More information"></i>');
431
	$(this).removeClass("infoblock");
432
	$(this).addClass("infoblock" + sfx.toString());
433
	sfx++;
434
});
435

    
436
// Show the help on clicking the info icon
437
$('[id^="showinfo"]').click(function() {
438
	var id = $(this).attr("id");
439

    
440
	$('.' + "infoblock" + id.substr(8)).toggle();
441
	document.getSelection().removeAllRanges();		// Ensure the text is un-selected (Chrome browser quirk)
442
});
443
// ------------------------------------------------------------------------------------------------
444

    
445
// Put a dummy row into any empty table to keep IE happy
446
$('tbody').each(function(){
447
	$(this).html($.trim($(this).html()))
448
});
449

    
450
$('tbody:empty').html("<tr><td></td></tr>");
451

    
452
// Hide configuration button for panels without configuration
453
$('.container .panel-heading a.config').each(function (idx, el){
454
	var config = $(el).parents('.panel').children('.panel-footer');
455
	if (config.length == 1)
456
		$(el).removeClass('hidden');
457
});
458

    
459
// Initial state & toggle icons of collapsed panel
460
$('.container .panel-heading a[data-toggle="collapse"]').each(function (idx, el){
461
	var body = $(el).parents('.panel').children('.panel-body')
462
	var isOpen = body.hasClass('in');
463

    
464
	$(el).children('i').toggleClass('fa-plus-circle', !isOpen);
465
	$(el).children('i').toggleClass('fa-minus-circle', isOpen);
466

    
467
	body.on('shown.bs.collapse', function(){
468
		$(el).children('i').toggleClass('fa-minus-circle', true);
469
		$(el).children('i').toggleClass('fa-plus-circle', false);
470
	});
471

    
472
	body.on('hidden.bs.collapse', function(){
473
		$(el).children('i').toggleClass('fa-minus-circle', false);
474
		$(el).children('i').toggleClass('fa-plus-circle', true);
475
	});
476
});
477

    
478
// Separator bar stuff ------------------------------------------------------------------------
479

    
480
// Globals
481
gColor = 'bg-info';
482
newSeparator = false;
483
saving = false;
484
dirty = false;
485

    
486
$("#addsep").prop('type' ,'button');
487

    
488
$("#addsep").click(function() {
489
	if (newSeparator) {
490
		return(false);
491
	}
492

    
493
	gColor = 'bg-info';
494
	// Insert a temporary bar in which the user can enter some optional text
495
	sepcols = $( "#ruletable tr th" ).length - 2;
496

    
497
	$('#ruletable > tbody:last').append('<tr>' +
498
		'<td class="' + gColor + '" colspan="' + sepcols + '"><input id="newsep" placeholder="' + svbtnplaceholder + '" class="col-md-12" type="text" /></td>' +
499
		'<td class="' + gColor + '" colspan="2"><button class="btn btn-primary btn-sm" id="btnnewsep"><i class="fa fa-save icon-embed-btn"></i>' + svtxt + '</button>' +
500
		'<button class="btn btn-info btn-sm" id="btncncsep"><i class="fa fa-undo icon-embed-btn"></i>' + cncltxt + '</button>' +
501
		'&nbsp;&nbsp;&nbsp;&nbsp;' +
502
		'&nbsp;&nbsp;<a id="sepclrblue" value="bg-info"><i class="fa fa-circle text-info icon-pointer"></i></a>' +
503
		'&nbsp;&nbsp;<a id="sepclrred" value="bg-danger"><i class="fa fa-circle text-danger icon-pointer"></i></a>' +
504
		'&nbsp;&nbsp;<a id="sepclrgreen" value="bg-success"><i class="fa fa-circle text-success icon-pointer"></i></a>' +
505
		'&nbsp;&nbsp;<a id="sepclrorange" value="bg-warning"><i class="fa fa-circle text-warning icon-pointer"></i></button>' +
506
		'</td></tr>');
507

    
508
	$('#newsep').focus();
509
	newSeparator = true;
510

    
511
	$("#btnnewsep").prop('type' ,'button');
512

    
513
	// Watch escape and enter keys
514
	$('#newsep').keyup(function(e) {
515
		if (e.which == 27) {
516
			$('#btncncsep').trigger('click');
517
		}
518
	});
519

    
520
	$('#newsep').keypress(function(e) {
521
		if (e.which == 13) {
522
			$('#btnnewsep').trigger('click');
523
		}
524
	});
525

    
526
	handle_colors();
527

    
528
	// Replace the temporary separator bar with the final version containing the
529
	// user's text and a delete icon
530
	$("#btnnewsep").click(function() {
531
		var septext = escapeHtml($('#newsep').val());
532
		sepcols = $( "#ruletable tr th" ).length - 1;
533

    
534
		$(this).parents('tr').replaceWith('<tr class="ui-sortable-handle separator">' +
535
			'<td class="' + gColor + '" colspan="' + sepcols + '">' + '<span class="' + gColor + '">' + septext + '</span></td>' +
536
			'<td class="' + gColor + '"><a href="#"><i class="fa fa-trash sepdel"></i></a>' +
537
			'</td></tr>');
538

    
539
		$('#order-store').removeAttr('disabled');
540
		newSeparator = false;
541
		dirty = true;
542
	});
543

    
544
	// Cancel button
545
	$('#btncncsep').click(function(e) {
546
		e.preventDefault();
547
		$(this).parents('tr').remove();
548
		newSeparator = false;
549
	});
550
});
551

    
552
// Delete a separator row
553
$(function(){
554
	$('table').on('click','tr a .sepdel',function(e){
555
		e.preventDefault();
556
		$(this).parents('tr').remove();
557
		$('#order-store').removeAttr('disabled');
558
		dirty = true;
559
	});
560
});
561

    
562
// Compose an input array containing the row #, color and text for each separator
563
function save_separators() {
564
	var row = 0;
565
	var sepinput;
566
	var sepnum = 0;
567

    
568
	$('#ruletable > tbody > tr').each(function() {
569
		if ($(this).hasClass('separator')) {
570
			seprow = $(this).next('tr').attr("id");
571
			if (seprow == undefined) {
572
				seprow = "fr" + row;
573
			}
574

    
575
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][row]" value="' + seprow + '"></input>';
576
			$('form').append(sepinput);
577
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][text]" value="' + escapeHtml($(this).find('td').text()) + '"></input>';
578
			$('form').append(sepinput);
579
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][color]" value="' + $(this).find('td').prop('class') + '"></input>';
580
			$('form').append(sepinput);
581
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][if]" value="' + iface + '"></input>';
582
			$('form').append(sepinput);
583
			sepnum++;
584
		} else {
585
			if ($(this).parent('tbody').hasClass('user-entries')) {
586
				row++;
587
			}
588
		}
589
	});
590
}
591

    
592
function reindex_rules(section) {
593
	var row = 0;
594

    
595
	section.find('tr').each(function() {
596
		if (this.id) {
597
			$(this).attr("id", "fr" + row);
598
			$(this).attr("onclick", "fr_toggle(" + row + ")")
599
			$(this).find('input:checkbox:first').each(function() {
600
				$(this).attr("id", "frc" + row);
601
				$(this).attr("onclick", "fr_toggle(" + row + ")");
602
			});
603

    
604
			row++;
605
		}
606
	});
607
}
608

    
609
function handle_colors() {
610
	$('[id^=sepclr]').prop("type", "button");
611

    
612
	$('[id^=sepclr]').click(function () {
613
		var color =	 $(this).attr('value');
614
		// Clear all the color classes
615
		$(this).parent('td').prop('class', '');
616
		$(this).parent('td').prev('td').prop('class', '');
617
		// Install our new color class
618
		$(this).parent('td').addClass(color);
619
		$(this).parent('td').prev('td').addClass(color);
620
		// Set the global color
621
		gColor = color;
622
	});
623
}
624

    
625
//JS equivalent to PHP htmlspecialchars()
626
function escapeHtml(text) {
627
	var map = {
628
		'&': '&amp;',
629
		'<': '&lt;',
630
		'>': '&gt;',
631
		'"': '&quot;',
632
		"'": '&#039;'
633
	};
634

    
635
	return text.replace(/[&<>"']/g, function(m) { return map[m]; });
636
}
637
// --------------------------------------------------------------------------------------------
638

    
639
// Select every option in the specified multiselect
640
function AllServers(id, selectAll) {
641
   for (i = 0; i < id.length; i++)	   {
642
	   id.eq(i).prop('selected', selectAll);
643
   }
644
}
645

    
646
// Move all selected options from one multiselect to another
647
function moveOptions(From, To)	{
648
	var len = From.length;
649
	var option;
650

    
651
	if (len > 0) {
652
		for (i=0; i<len; i++) {
653
			if (From.eq(i).is(':selected')) {
654
				option = From.eq(i).val();
655
				value  = From.eq(i).text();
656
				To.append(new Option(value, option));
657
				From.eq(i).remove();
658
			}
659
		}
660
	}
661
}
662

    
663

    
664
// ------------- Service start/stop/restart functions.
665
// If a start/stop/restart button is clicked, parse the button name and make a POST via AJAX
666
$('[id*=restartservice-], [id*=stopservice-], [id*=startservice-]').click(function(event) {
667
	var args = this.id.split('-');
668
	var action, name, mode_zone, id;
669

    
670
	if (args[0] == "openvpn") {
671
		action = args[1];
672
		name = args[0];
673
		mode_zone = args[2];
674
		id = args[3];
675
	} else if (args[0] == "cpativeportal") {
676
		action = args[1];
677
		name = args[0];
678
		mode_zone = args[2];
679
		id = args[3];
680
	} else {
681
		action = args[0];
682
		name = args[1];
683
	}
684

    
685
	$(this).children('i').removeClass().addClass('fa fa-cog fa-spin text-success');
686
	this.blur();
687

    
688
	ajaxRequest = $.ajax(
689
		{
690
			url: "/status_services.php",
691
			type: "post",
692
			data: {
693
				ajax: 		"ajax",
694
				mode: 		action,
695
				service: 	name,
696
				vpnmode: 	mode_zone,
697
				zone: 		mode_zone,
698
				id: 		id
699
			}
700
		}
701
	);
702

    
703
	// Once the AJAX call has returned, refresh the page to show the new service
704
	ajaxRequest.done(function (response, textStatus, jqXHR) {
705
		location.reload(true);
706
	});
707
});
(2-2/3)