Project

General

Profile

Download (20 KB) Statistics
| Branch: | Tag: | Revision:
1
/*
2
 * pfSenseHelpers.js
3
 *
4
 * part of pfSense (https://www.pfsense.org)
5
 * Copyright (c) 2004-2016 Electric Sheep Fencing, LLC
6
 * All rights reserved.
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20

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

    
23
// Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden
24
function hideInput(id, hide) {
25
	if (hide)
26
		$('#' + id).parent().parent('div').addClass('hidden');
27
	else
28
		$('#' + id).parent().parent('div').removeClass('hidden');
29
}
30

    
31
// Hides the <div> in which the specified group input element lives so that the input,
32
// its label and help text are hidden
33
function hideGroupInput(id, hide) {
34
	if (hide)
35
		$('#' + id).parent('div').addClass('hidden');
36
	else
37
		$('#' + id).parent('div').removeClass('hidden');
38
}
39

    
40
// Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden
41
function hideCheckbox(id, hide) {
42
	if (hide)
43
		$('#' + id).parent().parent().parent('div').addClass('hidden');
44
	else
45
		$('#' + id).parent().parent().parent('div').removeClass('hidden');
46
}
47

    
48
// Disables the specified input element
49
function disableInput(id, disable) {
50
	$('#' + id).prop("disabled", disable);
51
}
52

    
53
// Hides all elements of the specified class. This will usually be a section
54
function hideClass(s_class, hide) {
55
	if (hide)
56
		$('.' + s_class).hide();
57
	else
58
		$('.' + s_class).show();
59
}
60

    
61
// Hides all elements of the specified class assigned to a group. This will usually be a group
62
function hideGroupClass(s_class, hide) {
63
	if (hide)
64
		$('.' + s_class).parent().parent().parent().hide();
65
	else
66
		$('.' + s_class).parent().parent().parent().show();
67
}
68

    
69
function hideSelect(id, hide) {
70
	if (hide)
71
		$('#' + id).parent('div').parent('div').addClass('hidden');
72
	else
73
		$('#' + id).parent('div').parent('div').removeClass('hidden');
74
}
75

    
76
function hideMultiCheckbox(id, hide) {
77
	if (hide)
78
		$("[name=" + id + "]").parent().addClass('hidden');
79
	else
80
		$("[name=" + id + "]").parent().removeClass('hidden');
81
}
82

    
83
// Hides the <div> in which the specified IP address element lives so that the input, its label and help text are hidden
84
function hideIpAddress(id, hide) {
85
	if (hide)
86
		$('#' + id).parent().parent().parent('div').addClass('hidden');
87
	else
88
		$('#' + id).parent().parent().parent('div').removeClass('hidden');
89
}
90

    
91
// Hides all elements of the specified class belonging to a multiselect.
92
function hideMultiClass(s_class, hide) {
93
	if (hide)
94
		$('.' + s_class).parent().parent().hide();
95
	else
96
		$('.' + s_class).parent().parent().show();
97
}
98

    
99
// Hides div whose label contains the specified text. (Good for StaticText)
100
function hideLabel(text, hide) {
101

    
102
	var element = $('label:contains(' + text + ')');
103

    
104
	if (hide)
105
		element.parent('div').addClass('hidden');
106
	else
107
		element.parent('div').removeClass('hidden');
108
}
109

    
110
// Hides the '/' and the subnet mask of an Ip_Address/subnet_mask group
111
function hideMask(name, hide) {
112
	if (hide) {
113
		$('[id^=' + name + ']').hide();
114
		$('[id^=' + name + ']').prev('span').hide();
115
		$('[id^=' + name + ']').parent('div').removeClass('input-group');
116
	} else {
117
		$('[id^=' + name + ']').show();
118
		$('[id^=' + name + ']').prev('span').show();
119
		$('[id^=' + name + ']').parent('div').addClass('input-group');
120
	}
121
}
122

    
123
// Toggle table row checkboxes and background colors on the pages that use sortable tables:
124
//	/usr/local/www/firewall_nat.php
125
//	/usr/local/www/firewall_nat_1to1.php
126
//	/usr/local/www/firewall_nat_out.php
127
//	/usr/local/www/firewall_rules.php
128
//	/usr/local/www/vpn_ipsec.php
129
// Striping of the tables is handled here, NOT with the Bootstrap table-striped class because it would
130
// get confused when rows are sorted or deleted.
131

    
132
function fr_toggle(id, prefix) {
133
	if (!prefix)
134
		prefix = 'fr';
135

    
136
	var checkbox = document.getElementById(prefix + 'c' + id);
137
	checkbox.checked = !checkbox.checked;
138
	fr_bgcolor(id, prefix);
139
}
140

    
141
// Change background color of selected row based on state of checkbox
142
function fr_bgcolor(id, prefix) {
143
	if (!prefix)
144
		prefix = 'fr';
145

    
146
	var row = $('#' + prefix + id);
147

    
148
	if ($('#' + prefix + 'c' + id).prop('checked') ) {
149
		row.addClass('active');
150
	} else {
151
		row.removeClass('active');
152
	}
153
}
154

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

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

    
162
function setMasks() {
163
	// Find all ipaddress masks and make dynamic based on address family of input
164
	$('span.pfIpMask + select').each(function (idx, select){
165
		var input = $(select).prevAll('input[type=text]');
166

    
167
		input.on('change', function(e){
168
			var isV6 = (input.val().indexOf(':') != -1), min = 0, max = 128;
169
			if (!isV6)
170
				max = 32;
171

    
172
			if (input.val() == "")
173
				return;
174

    
175
			while (select.options.length > max)
176
				select.remove(0);
177

    
178
			if (select.options.length < max) {
179
				for (var i=select.options.length; i<=max; i++)
180
					select.options.add(new Option(i, i), 0);
181
			}
182
		});
183

    
184
		// Fire immediately
185
		input.change();
186
	});
187
}
188

    
189
// Complicated function to move all help text associated with this input id to the same id
190
// on the row above. That way if you delete the last row, you don't lose the help
191
function moveHelpText(id) {
192

    
193
	$('#' + id).parent('div').parent('div').find('input, select, checkbox, button').each(function() {	 // For each <span></span>
194
		var fromId = this.id;
195
		var toId = decrStringInt(fromId);
196
		var helpSpan;
197

    
198

    
199
		if (!$(this).hasClass('pfIpMask') && !$(this).hasClass('btn')) {
200
			if ($('#' + decrStringInt(fromId)).parent('div').hasClass('input-group')) {
201
				helpSpan = $('#' + fromId).parent('div').parent('div').find('span:last').clone();
202
			} else {
203
				helpSpan = $('#' + fromId).parent('div').find('span:last').clone();
204
			}
205

    
206
			if ($(helpSpan).hasClass('help-block')) {
207
				if ($('#' + decrStringInt(fromId)).parent('div').hasClass('input-group')) {
208
					$('#' + decrStringInt(fromId)).parent('div').after(helpSpan);
209
				} else {
210
					$('#' + decrStringInt(fromId)).after(helpSpan);
211
				}
212
			}
213
		}
214
	});
215
}
216

    
217
// Increment the number at the end of the string
218
function bumpStringInt( str )	{
219
  var data = str.match(/(\D*)(\d+)(\D*)/), newStr = "";
220

    
221
  if (data)
222
	newStr = data[ 1 ] + ( Number( data[ 2 ] ) + 1 ) + data[ 3 ];
223

    
224
  return newStr || str;
225
}
226

    
227
// Decrement the number at the end of the string
228
function decrStringInt( str )	{
229
  var data = str.match(/(\D*)(\d+)(\D*)/), newStr = "";
230

    
231
  if (data)
232
	newStr = data[ 1 ] + ( Number( data[ 2 ] ) - 1 ) + data[ 3 ];
233

    
234
  return newStr || str;
235
}
236

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

    
242
	$('.repeatable').each(function() {
243

    
244
		$(this).find('input').each(function() {
245
			$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
246
			$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
247
		});
248

    
249
		$(this).find('select').each(function() {
250
			$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
251
			$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
252
		});
253

    
254
		$(this).find('button').each(function() {
255
			$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
256
			$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
257
		});
258

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

    
261
		idx++;
262
	});
263
}
264

    
265
function delete_row(rowDelBtn) {
266
	var rowLabel;
267

    
268
	// If we are deleting row zero, we need to save/restore the label
269
	if (rowDelBtn == "deleterow0") {
270
		rowLabel = $('#' + rowDelBtn).parent('div').parent('div').find('label').text();
271
	}
272

    
273
	$('#' + rowDelBtn).parent('div').parent('div').remove();
274

    
275
	renumber();
276
	checkLastRow();
277

    
278
	if (rowDelBtn == "deleterow0") {
279
		$('#' + rowDelBtn).parent('div').parent('div').find('label').text(rowLabel);
280
	}
281
}
282

    
283
function checkLastRow() {
284
	if ($('.repeatable').length <= 1) {
285
		$('#deleterow0').hide();
286
	} else {
287
		$('[id^=deleterow]').show();
288
	}
289
}
290

    
291
function add_row() {
292
	// Find the last repeatable group
293
	var lastRepeatableGroup = $('.repeatable:last');
294

    
295
	// Clone it
296
	var newGroup = lastRepeatableGroup.clone();
297
	// Increment the suffix number for each input element in the new group
298
	$(newGroup).find('input').each(function() {
299
		$(this).prop("id", bumpStringInt(this.id));
300
		$(this).prop("name", bumpStringInt(this.name));
301
		if (!$(this).is('[id^=delete]'))
302
			$(this).val('');
303
	});
304

    
305
	// Increment the suffix number for the deleterow button element in the new group
306
	$(newGroup).find('[id^=deleterow]').each(function() {
307
		$(this).prop("id", bumpStringInt(this.id));
308
		$(this).prop("name", bumpStringInt(this.name));
309
	});
310

    
311
	// Do the same for selectors
312
	$(newGroup).find('select').each(function() {
313
		$(this).prop("id", bumpStringInt(this.id));
314
		$(this).prop("name", bumpStringInt(this.name));
315
		// If this selector lists mask bits, we need it to be reset to all 128 options
316
		// and no items selected, so that automatic v4/v6 selection still works
317
		if ($(this).is('[id^=address_subnet]')) {
318
			$(this).empty();
319
			for (idx=128; idx>0; idx--) {
320
				$(this).append($('<option>', {
321
					value: idx,
322
					text: idx
323
				}));
324
			}
325
		}
326
	});
327

    
328
	// And for "for" tags
329
//	$(newGroup).find('label').attr('for', bumpStringInt($(newGroup).find('label').attr('for')));
330

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

    
333
	// Insert the updated/cloned row
334
	$(lastRepeatableGroup).after(newGroup);
335

    
336
	// Delete any help text from the group we have cloned
337
	$(lastRepeatableGroup).find('.help-block').each(function() {
338
		if ((typeof retainhelp) == "undefined")
339
			$(this).remove();
340
	});
341

    
342
	setMasks();
343

    
344
	checkLastRow();
345

    
346
	// Autocomplete
347
	if ( typeof addressarray !== 'undefined') {
348
		$('[id^=address]').each(function() {
349
			if (this.id.substring(0, 8) != "address_") {
350
				$(this).autocomplete({
351
					source: addressarray
352
				});
353
			}
354
		});
355
	}
356

    
357
	// Now that we are no longer cloning the event handlers, we need to remove and re-add after a new row
358
	// has been added to the table
359
	$('[id^=delete]').unbind();
360
	$('[id^=delete]').click(function(event) {
361
		if ($('.repeatable').length > 1) {
362
			if ((typeof retainhelp) == "undefined")
363
				moveHelpText(event.target.id);
364

    
365
			delete_row(event.target.id);
366
		} else {
367
			alert('The last row may not be deleted.');
368
		}
369
	});
370

    
371
}
372

    
373
// These are action buttons, not submit buttons
374
$('[id^=addrow]').prop('type','button');
375
$('[id^=delete]').prop('type','button');
376

    
377
// on click . .
378
$('[id^=addrow]').click(function() {
379
	add_row();
380
});
381

    
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
// "More information" handlers --------------------------------------------------------------------
394

    
395
// If there is an infoblock, automatically add an info icon that toggles its display
396

    
397
var sfx = 0;
398

    
399
$('.infoblock').each(function() {
400
	// If the block has the class "blockopen" it is initially open
401
	if (! $(this).hasClass("blockopen")) {
402
		$(this).hide();
403
	} else {
404
		$(this).removeClass("blockopen");
405
	}
406

    
407
	// Add the "i" icon before the infoblock, incrementing the icon id for each block (in case there are multiple infoblocks on a page)
408
	$(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>');
409
	$(this).removeClass("infoblock");
410
	$(this).addClass("infoblock" + sfx.toString());
411
	sfx++;
412
});
413

    
414
// Show the help on clicking the info icon
415
$('[id^="showinfo"]').click(function() {
416
	var id = $(this).attr("id");
417

    
418
	$('.' + "infoblock" + id.substr(8)).toggle();
419
	document.getSelection().removeAllRanges();		// Ensure the text is un-selected (Chrome browser quirk)
420
});
421
// ------------------------------------------------------------------------------------------------
422

    
423
// Put a dummy row into any empty table to keep IE happy
424
$('tbody').each(function(){
425
	$(this).html($.trim($(this).html()))
426
});
427

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

    
430
// Hide configuration button for panels without configuration
431
$('.container .panel-heading a.config').each(function (idx, el){
432
	var config = $(el).parents('.panel').children('.panel-footer');
433
	if (config.length == 1)
434
		$(el).removeClass('hidden');
435
});
436

    
437
// Initial state & toggle icons of collapsed panel
438
$('.container .panel-heading a[data-toggle="collapse"]').each(function (idx, el){
439
	var body = $(el).parents('.panel').children('.panel-body')
440
	var isOpen = body.hasClass('in');
441

    
442
	$(el).children('i').toggleClass('fa-plus-circle', !isOpen);
443
	$(el).children('i').toggleClass('fa-minus-circle', isOpen);
444

    
445
	body.on('shown.bs.collapse', function(){
446
		$(el).children('i').toggleClass('fa-minus-circle', true);
447
		$(el).children('i').toggleClass('fa-plus-circle', false);
448
	});
449

    
450
	body.on('hidden.bs.collapse', function(){
451
		$(el).children('i').toggleClass('fa-minus-circle', false);
452
		$(el).children('i').toggleClass('fa-plus-circle', true);
453
	});
454
});
455

    
456
// Separator bar stuff ------------------------------------------------------------------------
457

    
458
// Globals
459
gColor = 'bg-info';
460
newSeparator = false;
461
saving = false;
462
dirty = false;
463

    
464
$("#addsep").prop('type' ,'button');
465

    
466
$("#addsep").click(function() {
467
	if (newSeparator) {
468
		return(false);
469
	}
470

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

    
475
	$('#ruletable > tbody:last').append('<tr>' +
476
		'<td class="' + gColor + '" colspan="' + sepcols + '"><input id="newsep" placeholder="' + svbtnplaceholder + '" class="col-md-12" type="text" /></td>' +
477
		'<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>' +
478
		'<button class="btn btn-info btn-sm" id="btncncsep"><i class="fa fa-undo icon-embed-btn"></i>' + cncltxt + '</button>' +
479
		'&nbsp;&nbsp;&nbsp;&nbsp;' +
480
		'&nbsp;&nbsp;<a id="sepclrblue" value="bg-info"><i class="fa fa-circle text-info icon-pointer"></i></a>' +
481
		'&nbsp;&nbsp;<a id="sepclrred" value="bg-danger"><i class="fa fa-circle text-danger icon-pointer"></i></a>' +
482
		'&nbsp;&nbsp;<a id="sepclrgreen" value="bg-success"><i class="fa fa-circle text-success icon-pointer"></i></a>' +
483
		'&nbsp;&nbsp;<a id="sepclrorange" value="bg-warning"><i class="fa fa-circle text-warning icon-pointer"></i></button>' +
484
		'</td></tr>');
485

    
486
	$('#newsep').focus();
487
	newSeparator = true;
488

    
489
	$("#btnnewsep").prop('type' ,'button');
490

    
491
	// Watch escape and enter keys
492
	$('#newsep').keyup(function(e) {
493
		if (e.which == 27) {
494
			$('#btncncsep').trigger('click');
495
		}
496
	});
497

    
498
	$('#newsep').keypress(function(e) {
499
		if (e.which == 13) {
500
			$('#btnnewsep').trigger('click');
501
		}
502
	});
503

    
504
	handle_colors();
505

    
506
	// Replace the temporary separator bar with the final version containing the
507
	// user's text and a delete icon
508
	$("#btnnewsep").click(function() {
509
		var septext = escapeHtml($('#newsep').val());
510
		sepcols = $( "#ruletable tr th" ).length - 1;
511

    
512
		$(this).parents('tr').replaceWith('<tr class="ui-sortable-handle separator">' +
513
			'<td class="' + gColor + '" colspan="' + sepcols + '">' + '<span class="' + gColor + '">' + septext + '</span></td>' +
514
			'<td class="' + gColor + '"><a href="#"><i class="fa fa-trash sepdel"></i></a>' +
515
			'</td></tr>');
516

    
517
		$('#order-store').removeAttr('disabled');
518
		newSeparator = false;
519
		dirty = true;
520
	});
521

    
522
	// Cancel button
523
	$('#btncncsep').click(function(e) {
524
		e.preventDefault();
525
		$(this).parents('tr').remove();
526
		newSeparator = false;
527
	});
528
});
529

    
530
// Delete a separator row
531
$(function(){
532
	$('table').on('click','tr a .sepdel',function(e){
533
		e.preventDefault();
534
		$(this).parents('tr').remove();
535
		$('#order-store').removeAttr('disabled');
536
		dirty = true;
537
	});
538
});
539

    
540
// Compose an input array containing the row #, color and text for each separator
541
function save_separators() {
542
	var row = 0;
543
	var sepinput;
544
	var sepnum = 0;
545

    
546
	$('#ruletable > tbody > tr').each(function() {
547
		if ($(this).hasClass('separator')) {
548
			seprow = $(this).next('tr').attr("id");
549
			if (seprow == undefined) {
550
				seprow = "fr" + row;
551
			}
552

    
553
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][row]" value="' + seprow + '"></input>';
554
			$('form').append(sepinput);
555
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][text]" value="' + escapeHtml($(this).find('td').text()) + '"></input>';
556
			$('form').append(sepinput);
557
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][color]" value="' + $(this).find('td').prop('class') + '"></input>';
558
			$('form').append(sepinput);
559
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][if]" value="' + iface + '"></input>';
560
			$('form').append(sepinput);
561
			sepnum++;
562
		} else {
563
			if ($(this).parent('tbody').hasClass('user-entries')) {
564
				row++;
565
			}
566
		}
567
	});
568
}
569

    
570
function reindex_rules(section) {
571
	var row = 0;
572

    
573
	section.find('tr').each(function() {
574
		if (this.id) {
575
			$(this).attr("id", "fr" + row);
576
			$(this).attr("onclick", "fr_toggle(" + row + ")")
577
			$(this).find('input:checkbox:first').each(function() {
578
				$(this).attr("id", "frc" + row);
579
				$(this).attr("onclick", "fr_toggle(" + row + ")");
580
			});
581

    
582
			row++;
583
		}
584
	});
585
}
586

    
587
function handle_colors() {
588
	$('[id^=sepclr]').prop("type", "button");
589

    
590
	$('[id^=sepclr]').click(function () {
591
		var color =	 $(this).attr('value');
592
		// Clear all the color classes
593
		$(this).parent('td').prop('class', '');
594
		$(this).parent('td').prev('td').prop('class', '');
595
		// Install our new color class
596
		$(this).parent('td').addClass(color);
597
		$(this).parent('td').prev('td').addClass(color);
598
		// Set the global color
599
		gColor = color;
600
	});
601
}
602

    
603
//JS equivalent to PHP htmlspecialchars()
604
function escapeHtml(text) {
605
	var map = {
606
		'&': '&amp;',
607
		'<': '&lt;',
608
		'>': '&gt;',
609
		'"': '&quot;',
610
		"'": '&#039;'
611
	};
612

    
613
	return text.replace(/[&<>"']/g, function(m) { return map[m]; });
614
}
615
// --------------------------------------------------------------------------------------------
616

    
617
// Select every option in the specified multiselect
618
function AllServers(id, selectAll) {
619
   for (i = 0; i < id.length; i++)	   {
620
	   id.eq(i).prop('selected', selectAll);
621
   }
622
}
623

    
624
// Move all selected options from one multiselect to another
625
function moveOptions(From, To)	{
626
	var len = From.length;
627
	var option;
628

    
629
	if (len > 0) {
630
		for (i=0; i<len; i++) {
631
			if (From.eq(i).is(':selected')) {
632
				option = From.eq(i).val();
633
				value  = From.eq(i).text();
634
				To.append(new Option(value, option));
635
				From.eq(i).remove();
636
			}
637
		}
638
	}
639
}
640

    
641

    
642
// ------------- Service start/stop/restart functions.
643
// If a start/stop/restart button is clicked, parse the button name and make a POST via AJAX
644
$('[id*=restartservice-], [id*=stopservice-], [id*=startservice-]').click(function(event) {
645
	var args = this.id.split('-');
646
	var action, name, mode_zone, id;
647

    
648
	if (args[0] == "openvpn") {
649
		action = args[1];
650
		name = args[0];
651
		mode_zone = args[2];
652
		id = args[3];
653
	} else if (args[0] == "cpativeportal") {
654
		action = args[1];
655
		name = args[0];
656
		mode_zone = args[2];
657
		id = args[3];
658
	} else {
659
		action = args[0];
660
		name = args[1];
661
	}
662

    
663
	$(this).children('i').removeClass().addClass('fa fa-cog fa-spin text-success');
664
	this.blur();
665

    
666
	ajaxRequest = $.ajax(
667
		{
668
			url: "/status_services.php",
669
			type: "post",
670
			data: {
671
				ajax: 		"ajax",
672
				mode: 		action,
673
				service: 	name,
674
				vpnmode: 	mode_zone,
675
				zone: 		mode_zone,
676
				id: 		id
677
			}
678
		}
679
	);
680

    
681
	// Once the AJAX call has returned, refresh the page to show the new service
682
	ajaxRequest.done(function (response, textStatus, jqXHR) {
683
		location.reload(true);
684
	});
685
});
(2-2/3)