Project

General

Profile

Download (22.4 KB) Statistics
| Branch: | Tag: | Revision:
1
/*
2
 * pfSenseHelpers.js
3
 *
4
 * part of pfSense (https://www.pfsense.org)
5
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
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
// Cause the input to be displayed as a required field by adding the element-required class to the label
24
function setRequired(id, req) {
25
	if (req)
26
		$('#' + id).parent().parent('div').find('span:first').addClass('element-required');
27
	else
28
		$('#' + id).parent().parent('div').find('span:first').removeClass('element-required');
29
}
30

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

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

    
48
// Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden
49
function hideCheckbox(id, hide) {
50
	if (hide)
51
		$('#' + id).parent().parent().parent('div').addClass('hidden');
52
	else
53
		$('#' + id).parent().parent().parent('div').removeClass('hidden');
54
}
55

    
56
// Disables the specified input element
57
function disableInput(id, disable) {
58
	$('#' + id).prop("disabled", disable);
59
}
60

    
61
// Hides all elements of the specified class. This will usually be a section
62
function hideClass(s_class, hide) {
63
	if (hide)
64
		$('.' + s_class).hide();
65
	else
66
		$('.' + s_class).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, any mask selector, 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
// Set the help text for a given input
124
function setHelpText(id, text) {
125
	$('#' + id).parent().parent('div').find('span:nth-child(2)').html(text);
126
}
127

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

    
137
function fr_toggle(id, prefix) {
138
	if (!prefix)
139
		prefix = 'fr';
140

    
141
	var checkbox = document.getElementById(prefix + 'c' + id);
142
	checkbox.checked = !checkbox.checked;
143
	fr_bgcolor(id, prefix);
144
}
145

    
146
// Change background color of selected row based on state of checkbox
147
function fr_bgcolor(id, prefix) {
148
	if (!prefix)
149
		prefix = 'fr';
150

    
151
	var row = $('#' + prefix + id);
152

    
153
	if ($('#' + prefix + 'c' + id).prop('checked') ) {
154
		row.addClass('active');
155
	} else {
156
		row.removeClass('active');
157
	}
158
}
159

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

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

    
167
function setMasks() {
168
	// Find all ipaddress masks and make dynamic based on address family of input
169
	$('span.pfIpMask + select').each(function (idx, select){
170
		var input = $(select).prevAll('input[type=text]');
171

    
172
		input.on('change', function(e){
173
			var isV6 = (input.val().indexOf(':') != -1), min = 0, max = 128;
174
			if (!isV6)
175
				max = 32;
176

    
177
			if (input.val() == "")
178
				return;
179

    
180
			while (select.options.length > max)
181
				select.remove(0);
182

    
183
			if (select.options.length < max) {
184
				for (var i=select.options.length; i<=max; i++)
185
					select.options.add(new Option(i, i), 0);
186
			}
187
		});
188

    
189
		// Fire immediately
190
		input.change();
191
	});
192
}
193

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

    
198
	$('#' + id).parent('div').parent('div').find('input, select, checkbox, button').each(function() {	 // For each <span></span>
199
		var fromId = this.id;
200
		var toId = decrStringInt(fromId);
201
		var helpSpan;
202

    
203

    
204
		if (!$(this).hasClass('pfIpMask') && !$(this).hasClass('btn')) {
205
			if ($('#' + decrStringInt(fromId)).parent('div').hasClass('input-group')) {
206
				helpSpan = $('#' + fromId).parent('div').parent('div').find('span:last').clone();
207
			} else {
208
				helpSpan = $('#' + fromId).parent('div').find('span:last').clone();
209
			}
210

    
211
			if ($(helpSpan).hasClass('help-block')) {
212
				if ($('#' + decrStringInt(fromId)).parent('div').hasClass('input-group')) {
213
					$('#' + decrStringInt(fromId)).parent('div').after(helpSpan);
214
				} else {
215
					$('#' + decrStringInt(fromId)).after(helpSpan);
216
				}
217
			}
218
		}
219
	});
220
}
221

    
222
// Increment the number at the end of the string
223
function bumpStringInt( str )	{
224
  var data = str.match(/(\D*)(\d+)(\D*)/), newStr = "";
225

    
226
  if (data)
227
	newStr = data[ 1 ] + ( Number( data[ 2 ] ) + 1 ) + data[ 3 ];
228

    
229
  return newStr || str;
230
}
231

    
232
// Decrement the number at the end of the string
233
function decrStringInt( str )	{
234
  var data = str.match(/(\D*)(\d+)(\D*)/), newStr = "";
235

    
236
  if (data)
237
	newStr = data[ 1 ] + ( Number( data[ 2 ] ) - 1 ) + data[ 3 ];
238

    
239
  return newStr || str;
240
}
241

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

    
247
	$('.repeatable').each(function() {
248

    
249
		$(this).find('input').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('select').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('button').each(function() {
260
			$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
261
			$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
262
		});
263

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

    
266
		idx++;
267
	});
268
}
269

    
270
function delete_row(rowDelBtn) {
271
	var rowLabel;
272

    
273
	// If we are deleting row zero, we need to save/restore the label
274
	if ( (rowDelBtn == "deleterow0") && ((typeof retainhelp) == "undefined")) {
275
		rowLabel = $('#' + rowDelBtn).parent('div').parent('div').find('label').text();
276
	}
277

    
278
	$('#' + rowDelBtn).parent('div').parent('div').remove();
279

    
280
	renumber();
281
	checkLastRow();
282

    
283
	if (rowDelBtn == "deleterow0") {
284
		$('#' + rowDelBtn).parent('div').parent('div').find('label').text(rowLabel);
285
	}
286
}
287

    
288
function checkLastRow() {
289
	if ($('.repeatable').length <= 1) {
290
		$('#deleterow0').hide();
291
	} else {
292
		$('[id^=deleterow]').show();
293
	}
294
}
295

    
296
function add_row() {
297
	// Find the last repeatable group
298
	var lastRepeatableGroup = $('.repeatable:last');
299

    
300
	// If the number of repeats exceeds the maximum, do not add another clone
301
	if ($('.repeatable').length >= lastRepeatableGroup.attr('max_repeats')) {
302
		// Alert user if alert message is specified
303
		if (typeof lastRepeatableGroup.attr('max_repeats_alert') !== 'undefined') {
304
			alert(lastRepeatableGroup.attr('max_repeats_alert'));
305
		}
306
		return;
307
	}
308

    
309
	// Clone it
310
	var newGroup = lastRepeatableGroup.clone();
311

    
312
	// Increment the suffix number for each input element in the new group
313
	$(newGroup).find('input').each(function() {
314
		$(this).prop("id", bumpStringInt(this.id));
315
		$(this).prop("name", bumpStringInt(this.name));
316
		if (!$(this).is('[id^=delete]'))
317
			$(this).val('');
318
	});
319

    
320
	// Increment the suffix number for the deleterow button element in the new group
321
	$(newGroup).find('[id^=deleterow]').each(function() {
322
		$(this).prop("id", bumpStringInt(this.id));
323
		$(this).prop("name", bumpStringInt(this.name));
324
	});
325

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

    
343
	// And for "for" tags
344
//	$(newGroup).find('label').attr('for', bumpStringInt($(newGroup).find('label').attr('for')));
345

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

    
348
	// Insert the updated/cloned row
349
	$(lastRepeatableGroup).after(newGroup);
350

    
351
	// Delete any help text from the group we have cloned
352
	$(lastRepeatableGroup).find('.help-block').each(function() {
353
		if ((typeof retainhelp) == "undefined")
354
			$(this).remove();
355
	});
356

    
357
	setMasks();
358

    
359
	checkLastRow();
360

    
361
	// Autocomplete
362
	if ( typeof addressarray !== 'undefined') {
363
		$('[id^=address]').each(function() {
364
			if (this.id.substring(0, 8) != "address_") {
365
				$(this).autocomplete({
366
					source: addressarray
367
				});
368
			}
369
		});
370
	}
371

    
372
	// Now that we are no longer cloning the event handlers, we need to remove and re-add after a new row
373
	// has been added to the table
374
	$('[id^=delete]').unbind();
375
	$('[id^=delete]').click(function(event) {
376
		if ($('.repeatable').length > 1) {
377
			if ((typeof retainhelp) == "undefined")
378
				moveHelpText($(this).attr("id"));
379

    
380
			delete_row($(this).attr("id"));
381
		} else {
382
			alert('The last row may not be deleted.');
383
		}
384
	});
385

    
386
}
387

    
388
// These are action buttons, not submit buttons
389
$('[id^=addrow]').prop('type','button');
390
$('[id^=delete]').prop('type','button');
391

    
392
// on click . .
393
$('[id^=addrow]').click(function() {
394
	add_row();
395
});
396

    
397
$('[id^=delete]').click(function(event) {
398
	if ($('.repeatable').length > 1) {
399
		if ((typeof retainhelp) == "undefined")
400
			moveHelpText($(this).attr("id"));
401

    
402
		delete_row($(this).attr("id"));
403
	} else {
404
		alert('The last row may not be deleted.');
405
	}
406
});
407

    
408
// "More information" handlers --------------------------------------------------------------------
409

    
410
// If there is an infoblock, automatically add an info icon that toggles its display
411

    
412
var sfx = 0;
413

    
414
$('.infoblock').each(function() {
415
	// If the block has the class "blockopen" it is initially open
416
	if (! $(this).hasClass("blockopen")) {
417
		$(this).hide();
418
	} else {
419
		$(this).removeClass("blockopen");
420
	}
421

    
422
	// Add the "i" icon before the infoblock, incrementing the icon id for each block (in case there are multiple infoblocks on a page)
423
	$(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>');
424
	$(this).removeClass("infoblock");
425
	$(this).addClass("infoblock" + sfx.toString());
426
	sfx++;
427
});
428

    
429
// Show the help on clicking the info icon
430
$('[id^="showinfo"]').click(function() {
431
	var id = $(this).attr("id");
432

    
433
	$('.' + "infoblock" + id.substr(8)).toggle();
434
	document.getSelection().removeAllRanges();		// Ensure the text is un-selected (Chrome browser quirk)
435
});
436
// ------------------------------------------------------------------------------------------------
437

    
438
// Put a dummy row into any empty table to keep IE happy
439
$('tbody').each(function(){
440
	$(this).html($.trim($(this).html()))
441
});
442

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

    
445
// Hide configuration button for panels without configuration
446
$('.container .panel-heading a.config').each(function (idx, el){
447
	var config = $(el).parents('.panel').children('.panel-footer');
448
	if (config.length == 1)
449
		$(el).removeClass('hidden');
450
});
451

    
452
// Initial state & toggle icons of collapsed panel
453
$('.container .panel-heading a[data-toggle="collapse"]').each(function (idx, el){
454
	var body = $(el).parents('.panel').children('.panel-body')
455
	var isOpen = body.hasClass('in');
456

    
457
	$(el).children('i').toggleClass('fa-plus-circle', !isOpen);
458
	$(el).children('i').toggleClass('fa-minus-circle', isOpen);
459

    
460
	body.on('shown.bs.collapse', function(){
461
		$(el).children('i').toggleClass('fa-minus-circle', true);
462
		$(el).children('i').toggleClass('fa-plus-circle', false);
463
	});
464

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

    
471
// Separator bar stuff ------------------------------------------------------------------------
472

    
473
// Globals
474
gColor = 'bg-info';
475
newSeparator = false;
476
saving = false;
477
dirty = false;
478

    
479
$("#addsep").prop('type' ,'button');
480

    
481
$("#addsep").click(function() {
482
	if (newSeparator) {
483
		return(false);
484
	}
485

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

    
490
	$('#ruletable > tbody:last').append('<tr>' +
491
		'<td class="' + gColor + '" colspan="' + sepcols + '"><input id="newsep" placeholder="' + svbtnplaceholder + '" class="col-md-12" type="text" /></td>' +
492
		'<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>' +
493
		'<button class="btn btn-info btn-sm" id="btncncsep"><i class="fa fa-undo icon-embed-btn"></i>' + cncltxt + '</button>' +
494
		'&nbsp;&nbsp;&nbsp;&nbsp;' +
495
		'&nbsp;&nbsp;<a id="sepclrblue" value="bg-info"><i class="fa fa-circle text-info icon-pointer"></i></a>' +
496
		'&nbsp;&nbsp;<a id="sepclrred" value="bg-danger"><i class="fa fa-circle text-danger icon-pointer"></i></a>' +
497
		'&nbsp;&nbsp;<a id="sepclrgreen" value="bg-success"><i class="fa fa-circle text-success icon-pointer"></i></a>' +
498
		'&nbsp;&nbsp;<a id="sepclrorange" value="bg-warning"><i class="fa fa-circle text-warning icon-pointer"></i></button>' +
499
		'</td></tr>');
500

    
501
	$('#newsep').focus();
502
	newSeparator = true;
503

    
504
	$("#btnnewsep").prop('type' ,'button');
505

    
506
	// Watch escape and enter keys
507
	$('#newsep').keyup(function(e) {
508
		if (e.which == 27) {
509
			$('#btncncsep').trigger('click');
510
		}
511
	});
512

    
513
	$('#newsep').keypress(function(e) {
514
		if (e.which == 13) {
515
			$('#btnnewsep').trigger('click');
516
		}
517
	});
518

    
519
	handle_colors();
520

    
521
	// Replace the temporary separator bar with the final version containing the
522
	// user's text and a delete icon
523
	$("#btnnewsep").click(function() {
524
		var septext = escapeHtml($('#newsep').val());
525
		sepcols = $( "#ruletable tr th" ).length - 1;
526

    
527
		$(this).parents('tr').replaceWith('<tr class="ui-sortable-handle separator">' +
528
			'<td class="' + gColor + '" colspan="' + sepcols + '">' + '<span class="' + gColor + '">' + septext + '</span></td>' +
529
			'<td class="' + gColor + '"><a href="#"><i class="fa fa-trash sepdel"></i></a>' +
530
			'</td></tr>');
531

    
532
		$('#order-store').removeAttr('disabled');
533
		newSeparator = false;
534
		dirty = true;
535
	});
536

    
537
	// Cancel button
538
	$('#btncncsep').click(function(e) {
539
		e.preventDefault();
540
		$(this).parents('tr').remove();
541
		newSeparator = false;
542
	});
543
});
544

    
545
// Delete a separator row
546
$(function(){
547
	$('table').on('click','tr a .sepdel',function(e){
548
		e.preventDefault();
549
		$(this).parents('tr').remove();
550
		$('#order-store').removeAttr('disabled');
551
		dirty = true;
552
	});
553
});
554

    
555
// Compose an input array containing the row #, color and text for each separator
556
function save_separators() {
557
	var row = 0;
558
	var sepinput;
559
	var sepnum = 0;
560

    
561
	$('#ruletable > tbody > tr').each(function() {
562
		if ($(this).hasClass('separator')) {
563
			seprow = $(this).next('tr').attr("id");
564
			if (seprow == undefined) {
565
				seprow = "fr" + row;
566
			}
567

    
568
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][row]" value="' + seprow + '"></input>';
569
			$('form').append(sepinput);
570
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][text]" value="' + escapeHtml($(this).find('td').text()) + '"></input>';
571
			$('form').append(sepinput);
572
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][color]" value="' + $(this).find('td').prop('class') + '"></input>';
573
			$('form').append(sepinput);
574
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][if]" value="' + iface + '"></input>';
575
			$('form').append(sepinput);
576
			sepnum++;
577
		} else {
578
			if ($(this).parent('tbody').hasClass('user-entries')) {
579
				row++;
580
			}
581
		}
582
	});
583
}
584

    
585
function reindex_rules(section) {
586
	var row = 0;
587

    
588
	section.find('tr').each(function() {
589
		if (this.id) {
590
			$(this).attr("id", "fr" + row);
591
			$(this).attr("onclick", "fr_toggle(" + row + ")")
592
			$(this).find('input:checkbox:first').each(function() {
593
				$(this).attr("id", "frc" + row);
594
				$(this).attr("onclick", "fr_toggle(" + row + ")");
595
			});
596

    
597
			row++;
598
		}
599
	});
600
}
601

    
602
function handle_colors() {
603
	$('[id^=sepclr]').prop("type", "button");
604

    
605
	$('[id^=sepclr]').click(function () {
606
		var color =	 $(this).attr('value');
607
		// Clear all the color classes
608
		$(this).parent('td').prop('class', '');
609
		$(this).parent('td').prev('td').prop('class', '');
610
		// Install our new color class
611
		$(this).parent('td').addClass(color);
612
		$(this).parent('td').prev('td').addClass(color);
613
		// Set the global color
614
		gColor = color;
615
	});
616
}
617

    
618
//JS equivalent to PHP htmlspecialchars()
619
function escapeHtml(text) {
620
	var map = {
621
		'&': '&amp;',
622
		'<': '&lt;',
623
		'>': '&gt;',
624
		'"': '&quot;',
625
		"'": '&#039;'
626
	};
627

    
628
	return text.replace(/[&<>"']/g, function(m) { return map[m]; });
629
}
630
// --------------------------------------------------------------------------------------------
631

    
632
// Select every option in the specified multiselect
633
function AllServers(id, selectAll) {
634
   for (i = 0; i < id.length; i++)	   {
635
	   id.eq(i).prop('selected', selectAll);
636
   }
637
}
638

    
639
// Move all selected options from one multiselect to another
640
function moveOptions(From, To)	{
641
	var len = From.length;
642
	var option;
643

    
644
	if (len > 0) {
645
		for (i=0; i<len; i++) {
646
			if (From.eq(i).is(':selected')) {
647
				option = From.eq(i).val();
648
				value  = From.eq(i).text();
649
				To.append(new Option(value, option));
650
				From.eq(i).remove();
651
			}
652
		}
653
	}
654
}
655

    
656
// ------------- Service start/stop/restart functions.
657
// If a start/stop/restart button is clicked, parse the button name and make a POST via AJAX
658
$('[id*=restartservice-], [id*=stopservice-], [id*=startservice-]').click(function(event) {
659
	var args = this.id.split('-');
660
	var action, name, mode_zone, id;
661

    
662
	if (args[0] == "openvpn") {
663
		action = args[1];
664
		name = args[0];
665
		mode_zone = args[2];
666
		id = args[3];
667
	} else if (args[0] == "cpativeportal") {
668
		action = args[1];
669
		name = args[0];
670
		mode_zone = args[2];
671
		id = args[3];
672
	} else {
673
		action = args[0];
674
		name = args[1];
675
	}
676

    
677
	$(this).children('i').removeClass().addClass('fa fa-cog fa-spin text-success');
678
	this.blur();
679

    
680
	ajaxRequest = $.ajax(
681
		{
682
			url: "/status_services.php",
683
			type: "post",
684
			data: {
685
				ajax: 		"ajax",
686
				mode: 		action,
687
				service: 	name,
688
				vpnmode: 	mode_zone,
689
				zone: 		mode_zone,
690
				id: 		id
691
			}
692
		}
693
	);
694

    
695
	// Once the AJAX call has returned, refresh the page to show the new service
696
	ajaxRequest.done(function (response, textStatus, jqXHR) {
697
		location.reload(true);
698
	});
699
});
700

    
701
// The scripts that follow are an EXPERIMENT in using jQuery/Javascript to automatically convert
702
// GET calls to POST calls
703
// Any anchor with the attribute "usepost" usses these functions. In this file "Edit user", "Delete user" and "Add"
704
// have that attribute
705
// These function can be moved to an included file
706

    
707
// Any time an anchor is clicked and the "usepost" attibute is present, convert the href attribute
708
// to POST format, make a POST form and submit it
709
$('a').click(function(e) {
710
	// Does the clicked anchor have the "usepost" attribute?
711
	var attr = $(this).attr('usepost');
712

    
713
	if (typeof attr !== typeof undefined && attr !== false) {
714
		var href = $(this).attr("href");
715

    
716
		postSubmit(get2post(href));
717

    
718
		return false;
719
	}
720
});
721

    
722
// Convert a GET argument list such as ?name=fred&action=delete into an array of POST
723
// parameters such as [[name, fred],[action, delete]]
724
function get2post(getargs) {
725
	var arglist = new Array();
726

    
727
	getargs = getargs.substring(getargs.indexOf("?") + 1);
728
	var argarray = getargs.split('&');
729

    
730
	for (var i=0;i<argarray.length;i++) {
731
		var thisarg = argarray[i].split('=');
732
		var arg = new Array(thisarg[0], thisarg[1]);
733
		arglist[i] = arg;
734
	}
735

    
736
	return arglist;
737
}
738

    
739
// Create a form, add, the POST data and submit it
740
function postSubmit(data) {
741

    
742
    var form = $(document.createElement('form'));
743

    
744
    $(form).attr("method", "POST");
745

    
746
    for (var i=0;i<data.length;i++) {
747
		var input = $("<input>").attr("type", "hidden").attr("name", data[i][0]).val(data[i][1]);
748
		$(form).append($(input));
749
    }
750

    
751
	// The CSRF magic is required because we will be viewing the results of the POST
752
	var input = $("<input>").attr("type", "hidden").attr("name", "__csrf_magic").val($('[name=__csrf_magic]').val());
753
	$(form).append($(input));
754

    
755
    $(form).appendTo('body').submit();
756
}
757

    
(2-2/4)