Project

General

Profile

Download (21.9 KB) Statistics
| Branch: | Tag: | Revision:
1 8acd654a Renato Botelho
/*
2
 * pfSenseHelpers.js
3
 *
4
 * part of pfSense (https://www.pfsense.org)
5 2a2396a6 Renato Botelho
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
6 8acd654a Renato Botelho
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions are met:
10
 *
11
 * 1. Redistributions of source code must retain the above copyright notice,
12
 *    this list of conditions and the following disclaimer.
13
 *
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in
16
 *    the documentation and/or other materials provided with the
17
 *    distribution.
18
 *
19
 * 3. All advertising materials mentioning features or use of this software
20
 *    must display the following acknowledgment:
21
 *    "This product includes software developed by the pfSense Project
22
 *    for use in the pfSense® software distribution. (http://www.pfsense.org/).
23
 *
24
 * 4. The names "pfSense" and "pfSense Project" must not be used to
25
 *    endorse or promote products derived from this software without
26
 *    prior written permission. For written permission, please contact
27
 *    coreteam@pfsense.org.
28
 *
29
 * 5. Products derived from this software may not be called "pfSense"
30
 *    nor may "pfSense" appear in their names without prior written
31
 *    permission of the Electric Sheep Fencing, LLC.
32
 *
33
 * 6. Redistributions of any form whatsoever must retain the following
34
 *    acknowledgment:
35
 *
36
 * "This product includes software developed by the pfSense Project
37
 * for use in the pfSense software distribution (http://www.pfsense.org/).
38
 *
39
 * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
40
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
43
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50
 * OF THE POSSIBILITY OF SUCH DAMAGE.
51 08ec2d99 Stephen Beaver
 */
52
53 3ece7116 Stephen Beaver
// These helper functions are used on many/most UI pages to hide/show/disable/enable form elements where required
54
55
// Hides the <div> in which the specified input element lives so that the input, its label and help text are hidden
56
function hideInput(id, hide) {
57 8b7ef76b Phil Davis
	if (hide)
58 96c18fe4 Stephen Beaver
		$('#' + id).parent().parent('div').addClass('hidden');
59
	else
60
		$('#' + id).parent().parent('div').removeClass('hidden');
61 3ece7116 Stephen Beaver
}
62
63
// Hides the <div> in which the specified group input element lives so that the input,
64
// its label and help text are hidden
65
function hideGroupInput(id, hide) {
66 8b7ef76b Phil Davis
	if (hide)
67 96c18fe4 Stephen Beaver
		$('#' + id).parent('div').addClass('hidden');
68
	else
69
		$('#' + id).parent('div').removeClass('hidden');
70 3ece7116 Stephen Beaver
}
71
72
// Hides the <div> in which the specified checkbox lives so that the checkbox, its label and help text are hidden
73
function hideCheckbox(id, hide) {
74 8b7ef76b Phil Davis
	if (hide)
75 96c18fe4 Stephen Beaver
		$('#' + id).parent().parent().parent('div').addClass('hidden');
76
	else
77
		$('#' + id).parent().parent().parent('div').removeClass('hidden');
78 3ece7116 Stephen Beaver
}
79
80
// Disables the specified input element
81
function disableInput(id, disable) {
82 96c18fe4 Stephen Beaver
	$('#' + id).prop("disabled", disable);
83 3ece7116 Stephen Beaver
}
84
85
// Hides all elements of the specified class. This will usually be a section
86
function hideClass(s_class, hide) {
87 8b7ef76b Phil Davis
	if (hide)
88 96c18fe4 Stephen Beaver
		$('.' + s_class).hide();
89
	else
90
		$('.' + s_class).show();
91 3ece7116 Stephen Beaver
}
92
93 96c18fe4 Stephen Beaver
function hideSelect(id, hide) {
94 8b7ef76b Phil Davis
	if (hide)
95 96c18fe4 Stephen Beaver
		$('#' + id).parent('div').parent('div').addClass('hidden');
96
	else
97
		$('#' + id).parent('div').parent('div').removeClass('hidden');
98
}
99
100
function hideMultiCheckbox(id, hide) {
101 8b7ef76b Phil Davis
	if (hide)
102 5e7a1d46 Stephen Beaver
		$("[name=" + id + "]").parent().addClass('hidden');
103 96c18fe4 Stephen Beaver
	else
104 5e7a1d46 Stephen Beaver
		$("[name=" + id + "]").parent().removeClass('hidden');
105 96c18fe4 Stephen Beaver
}
106
107 25383c9a Steve Beaver
// 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
108 96c18fe4 Stephen Beaver
function hideIpAddress(id, hide) {
109 8b7ef76b Phil Davis
	if (hide)
110 96c18fe4 Stephen Beaver
		$('#' + id).parent().parent().parent('div').addClass('hidden');
111
	else
112
		$('#' + id).parent().parent().parent('div').removeClass('hidden');
113
}
114
115
// Hides all elements of the specified class belonging to a multiselect.
116
function hideMultiClass(s_class, hide) {
117 8b7ef76b Phil Davis
	if (hide)
118 96c18fe4 Stephen Beaver
		$('.' + s_class).parent().parent().hide();
119
	else
120
		$('.' + s_class).parent().parent().show();
121
}
122
123
// Hides div whose label contains the specified text. (Good for StaticText)
124
function hideLabel(text, hide) {
125
126
	var element = $('label:contains(' + text + ')');
127 3ece7116 Stephen Beaver
128 8b7ef76b Phil Davis
	if (hide)
129 96c18fe4 Stephen Beaver
		element.parent('div').addClass('hidden');
130
	else
131
		element.parent('div').removeClass('hidden');
132 08ec2d99 Stephen Beaver
}
133
134 5925b419 Stephen Beaver
// Hides the '/' and the subnet mask of an Ip_Address/subnet_mask group
135
function hideMask(name, hide) {
136 9488f42b Phil Davis
	if (hide) {
137 5925b419 Stephen Beaver
		$('[id^=' + name + ']').hide();
138
		$('[id^=' + name + ']').prev('span').hide();
139 f212adc8 Phil Davis
		$('[id^=' + name + ']').parent('div').removeClass('input-group');
140 5925b419 Stephen Beaver
	} else {
141
		$('[id^=' + name + ']').show();
142
		$('[id^=' + name + ']').prev('span').show();
143 f212adc8 Phil Davis
		$('[id^=' + name + ']').parent('div').addClass('input-group');
144 5925b419 Stephen Beaver
	}
145
}
146
147 39609bf9 Stephen Beaver
// Toggle table row checkboxes and background colors on the pages that use sortable tables:
148 65a0e193 Stephen Beaver
//	/usr/local/www/firewall_nat.php
149
//	/usr/local/www/firewall_nat_1to1.php
150
//	/usr/local/www/firewall_nat_out.php
151
//	/usr/local/www/firewall_rules.php
152
//	/usr/local/www/vpn_ipsec.php
153 1b6b7fc4 Stephen Beaver
// Striping of the tables is handled here, NOT with the Bootstrap table-striped class because it would
154 65a0e193 Stephen Beaver
// get confused when rows are sorted or deleted.
155
156
function fr_toggle(id, prefix) {
157
	if (!prefix)
158
		prefix = 'fr';
159
160
	var checkbox = document.getElementById(prefix + 'c' + id);
161
	checkbox.checked = !checkbox.checked;
162
	fr_bgcolor(id, prefix);
163
}
164
165 b5ef3ee9 Jared Dillard
// Change background color of selected row based on state of checkbox
166 65a0e193 Stephen Beaver
function fr_bgcolor(id, prefix) {
167
	if (!prefix)
168
		prefix = 'fr';
169
170
	var row = $('#' + prefix + id);
171
172
	if ($('#' + prefix + 'c' + id).prop('checked') ) {
173 b5ef3ee9 Jared Dillard
		row.addClass('active');
174 65a0e193 Stephen Beaver
	} else {
175 b5ef3ee9 Jared Dillard
		row.removeClass('active');
176 65a0e193 Stephen Beaver
	}
177
}
178
179 44fad9cc Stephen Beaver
// The following functions are used by Form_Groups assigned a class of "repeatable" and provide the ability
180
// to add/delete rows of sequentially numbered elements, their labels and their help text
181 08ec2d99 Stephen Beaver
// See firewall_aliases_edit.php for an example
182
183 8b7ef76b Phil Davis
// NOTE: retainhelp is a global var that when defined prevents any help text from being deleted as lines are inserted.
184 44fad9cc Stephen Beaver
// IOW it causes every row to have help text, not just the last row
185
186 08ec2d99 Stephen Beaver
function setMasks() {
187
	// Find all ipaddress masks and make dynamic based on address family of input
188
	$('span.pfIpMask + select').each(function (idx, select){
189
		var input = $(select).prevAll('input[type=text]');
190
191
		input.on('change', function(e){
192
			var isV6 = (input.val().indexOf(':') != -1), min = 0, max = 128;
193
			if (!isV6)
194
				max = 32;
195
196
			if (input.val() == "")
197
				return;
198
199
			while (select.options.length > max)
200
				select.remove(0);
201
202 8b7ef76b Phil Davis
			if (select.options.length < max) {
203 08ec2d99 Stephen Beaver
				for (var i=select.options.length; i<=max; i++)
204
					select.options.add(new Option(i, i), 0);
205
			}
206
		});
207
208
		// Fire immediately
209
		input.change();
210
	});
211
}
212
213
// Complicated function to move all help text associated with this input id to the same id
214
// on the row above. That way if you delete the last row, you don't lose the help
215
function moveHelpText(id) {
216 eb53e873 Stephen Beaver
217
	$('#' + id).parent('div').parent('div').find('input, select, checkbox, button').each(function() {	 // For each <span></span>
218 08ec2d99 Stephen Beaver
		var fromId = this.id;
219
		var toId = decrStringInt(fromId);
220
		var helpSpan;
221
222 eb53e873 Stephen Beaver
223 4c1c9e52 Stephen Beaver
		if (!$(this).hasClass('pfIpMask') && !$(this).hasClass('btn')) {
224 8b7ef76b Phil Davis
			if ($('#' + decrStringInt(fromId)).parent('div').hasClass('input-group')) {
225 39609bf9 Stephen Beaver
				helpSpan = $('#' + fromId).parent('div').parent('div').find('span:last').clone();
226
			} else {
227
				helpSpan = $('#' + fromId).parent('div').find('span:last').clone();
228
			}
229 eb53e873 Stephen Beaver
230 8b7ef76b Phil Davis
			if ($(helpSpan).hasClass('help-block')) {
231
				if ($('#' + decrStringInt(fromId)).parent('div').hasClass('input-group')) {
232 08ec2d99 Stephen Beaver
					$('#' + decrStringInt(fromId)).parent('div').after(helpSpan);
233 8b7ef76b Phil Davis
				} else {
234 08ec2d99 Stephen Beaver
					$('#' + decrStringInt(fromId)).after(helpSpan);
235 39609bf9 Stephen Beaver
				}
236 08ec2d99 Stephen Beaver
			}
237
		}
238
	});
239
}
240
241
// Increment the number at the end of the string
242
function bumpStringInt( str )	{
243
  var data = str.match(/(\D*)(\d+)(\D*)/), newStr = "";
244
245 8b7ef76b Phil Davis
  if (data)
246 08ec2d99 Stephen Beaver
	newStr = data[ 1 ] + ( Number( data[ 2 ] ) + 1 ) + data[ 3 ];
247
248
  return newStr || str;
249
}
250
251
// Decrement the number at the end of the string
252
function decrStringInt( str )	{
253
  var data = str.match(/(\D*)(\d+)(\D*)/), newStr = "";
254
255 8b7ef76b Phil Davis
  if (data)
256 08ec2d99 Stephen Beaver
	newStr = data[ 1 ] + ( Number( data[ 2 ] ) - 1 ) + data[ 3 ];
257
258
  return newStr || str;
259
}
260
261
// Called after a delete so that there are no gaps in the numbering. Most of the time the config system doesn't care about
262
// gaps, but I do :)
263
function renumber() {
264
	var idx = 0;
265
266
	$('.repeatable').each(function() {
267
268
		$(this).find('input').each(function() {
269
			$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
270
			$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
271
		});
272
273
		$(this).find('select').each(function() {
274
			$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
275
			$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
276
		});
277
278 eb53e873 Stephen Beaver
		$(this).find('button').each(function() {
279
			$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
280
			$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
281
		});
282
283 43dc3830 Stephen Beaver
//		$(this).find('label').attr('for', $(this).find('label').attr('for').replace(/\d+$/, "") + idx);
284 08ec2d99 Stephen Beaver
285
		idx++;
286
	});
287
}
288
289 88afaf7d Stephen Beaver
function delete_row(rowDelBtn) {
290 576437f5 Stephen Beaver
	var rowLabel;
291 88afaf7d Stephen Beaver
292
	// If we are deleting row zero, we need to save/restore the label
293 30735b1e Steve Beaver
	if ( (rowDelBtn == "deleterow0") && ((typeof retainhelp) == "undefined")) {
294 8627b174 Stephen Beaver
		rowLabel = $('#' + rowDelBtn).parent('div').parent('div').find('label').text();
295 88afaf7d Stephen Beaver
	}
296
297
	$('#' + rowDelBtn).parent('div').parent('div').remove();
298 eb53e873 Stephen Beaver
299 08ec2d99 Stephen Beaver
	renumber();
300 0bc61baa Stephen Beaver
	checkLastRow();
301 88afaf7d Stephen Beaver
302
	if (rowDelBtn == "deleterow0") {
303 8627b174 Stephen Beaver
		$('#' + rowDelBtn).parent('div').parent('div').find('label').text(rowLabel);
304 88afaf7d Stephen Beaver
	}
305 0bc61baa Stephen Beaver
}
306
307
function checkLastRow() {
308 8b7ef76b Phil Davis
	if ($('.repeatable').length <= 1) {
309 0bc61baa Stephen Beaver
		$('#deleterow0').hide();
310
	} else {
311
		$('[id^=deleterow]').show();
312
	}
313 08ec2d99 Stephen Beaver
}
314
315
function add_row() {
316 44fad9cc Stephen Beaver
	// Find the last repeatable group
317 19c25a53 jskyboo
	var lastRepeatableGroup = $('.repeatable:last');
318
319
	// If the number of repeats exceeds the maximum, do not add another clone
320
	if ($('.repeatable').length >= lastRepeatableGroup.attr('max_repeats')) {
321
		// Alert user if alert message is specified
322
		if (typeof lastRepeatableGroup.attr('max_repeats_alert') !== 'undefined') {
323
			alert(lastRepeatableGroup.attr('max_repeats_alert'));
324
		}
325
		return;
326
	}
327 08ec2d99 Stephen Beaver
328
	// Clone it
329 c24178cd Stephen Beaver
	var newGroup = lastRepeatableGroup.clone();
330 10fca5ac Stephen Beaver
331 44fad9cc Stephen Beaver
	// Increment the suffix number for each input element in the new group
332 08ec2d99 Stephen Beaver
	$(newGroup).find('input').each(function() {
333
		$(this).prop("id", bumpStringInt(this.id));
334
		$(this).prop("name", bumpStringInt(this.name));
335 8b7ef76b Phil Davis
		if (!$(this).is('[id^=delete]'))
336 08ec2d99 Stephen Beaver
			$(this).val('');
337
	});
338
339 359b630c Stephen Beaver
	// Increment the suffix number for the deleterow button element in the new group
340
	$(newGroup).find('[id^=deleterow]').each(function() {
341 0a544256 Phil Davis
		$(this).prop("id", bumpStringInt(this.id));
342
		$(this).prop("name", bumpStringInt(this.name));
343
	});
344
345 08ec2d99 Stephen Beaver
	// Do the same for selectors
346
	$(newGroup).find('select').each(function() {
347
		$(this).prop("id", bumpStringInt(this.id));
348
		$(this).prop("name", bumpStringInt(this.name));
349
		// If this selector lists mask bits, we need it to be reset to all 128 options
350
		// and no items selected, so that automatic v4/v6 selection still works
351 8b7ef76b Phil Davis
		if ($(this).is('[id^=address_subnet]')) {
352 08ec2d99 Stephen Beaver
			$(this).empty();
353 8b7ef76b Phil Davis
			for (idx=128; idx>0; idx--) {
354 08ec2d99 Stephen Beaver
				$(this).append($('<option>', {
355
					value: idx,
356
					text: idx
357
				}));
358
			}
359
		}
360
	});
361
362
	// And for "for" tags
363 43dc3830 Stephen Beaver
//	$(newGroup).find('label').attr('for', bumpStringInt($(newGroup).find('label').attr('for')));
364
365 10fca5ac Stephen Beaver
	$(newGroup).find('label:first').text(""); // Clear the label. We only want it on the very first row
366 08ec2d99 Stephen Beaver
367
	// Insert the updated/cloned row
368
	$(lastRepeatableGroup).after(newGroup);
369
370
	// Delete any help text from the group we have cloned
371
	$(lastRepeatableGroup).find('.help-block').each(function() {
372 8b7ef76b Phil Davis
		if ((typeof retainhelp) == "undefined")
373 08ec2d99 Stephen Beaver
			$(this).remove();
374
	});
375
376
	setMasks();
377
378 0bc61baa Stephen Beaver
	checkLastRow();
379
380 c24178cd Stephen Beaver
	// Autocomplete
381 b6bd3103 Stephen Beaver
	if ( typeof addressarray !== 'undefined') {
382
		$('[id^=address]').each(function() {
383 8b7ef76b Phil Davis
			if (this.id.substring(0, 8) != "address_") {
384 b6bd3103 Stephen Beaver
				$(this).autocomplete({
385
					source: addressarray
386
				});
387
			}
388
		});
389
	}
390 9f6a9f24 Stephen Beaver
391 316c5503 Stephen Beaver
	// Now that we are no longer cloning the event handlers, we need to remove and re-add after a new row
392 9f6a9f24 Stephen Beaver
	// has been added to the table
393
	$('[id^=delete]').unbind();
394
	$('[id^=delete]').click(function(event) {
395 8b7ef76b Phil Davis
		if ($('.repeatable').length > 1) {
396
			if ((typeof retainhelp) == "undefined")
397 9f6a9f24 Stephen Beaver
				moveHelpText(event.target.id);
398
399
			delete_row(event.target.id);
400 8b7ef76b Phil Davis
		} else {
401 b9f7281a NOYB
			alert('The last row may not be deleted.');
402 8b7ef76b Phil Davis
		}
403 9f6a9f24 Stephen Beaver
	});
404
405 08ec2d99 Stephen Beaver
}
406
407
// These are action buttons, not submit buttons
408
$('[id^=addrow]').prop('type','button');
409
$('[id^=delete]').prop('type','button');
410
411
// on click . .
412
$('[id^=addrow]').click(function() {
413
	add_row();
414
});
415
416
$('[id^=delete]').click(function(event) {
417 8b7ef76b Phil Davis
	if ($('.repeatable').length > 1) {
418
		if ((typeof retainhelp) == "undefined")
419 d9f1170e Steve Beaver
			moveHelpText($(this).attr("id"));
420 08ec2d99 Stephen Beaver
421 d9f1170e Steve Beaver
		delete_row($(this).attr("id"));
422 8b7ef76b Phil Davis
	} else {
423 b9f7281a NOYB
		alert('The last row may not be deleted.');
424 8b7ef76b Phil Davis
	}
425 df4957e4 Stephen Beaver
});
426
427 85d29bf5 Stephen Beaver
// "More information" handlers --------------------------------------------------------------------
428 0149ef4d Stephen Beaver
429
// If there is an infoblock, automatically add an info icon that toggles its display
430 c9679d8c Stephen Beaver
431 c95dabdd Stephen Beaver
var sfx = 0;
432 85d29bf5 Stephen Beaver
433 c95dabdd Stephen Beaver
$('.infoblock').each(function() {
434 cca28e34 Phil Davis
	// If the block has the class "blockopen" it is initially open
435 c95dabdd Stephen Beaver
	if (! $(this).hasClass("blockopen")) {
436 85d29bf5 Stephen Beaver
		$(this).hide();
437 c95dabdd Stephen Beaver
	} else {
438
		$(this).removeClass("blockopen");
439 85d29bf5 Stephen Beaver
	}
440
441 cca28e34 Phil Davis
	// Add the "i" icon before the infoblock, incrementing the icon id for each block (in case there are multiple infoblocks on a page)
442 c95dabdd Stephen Beaver
	$(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>');
443
	$(this).removeClass("infoblock");
444
	$(this).addClass("infoblock" + sfx.toString());
445
	sfx++;
446 85d29bf5 Stephen Beaver
});
447 0149ef4d Stephen Beaver
448 5a957b9a Phil Davis
// Show the help on clicking the info icon
449 85d29bf5 Stephen Beaver
$('[id^="showinfo"]').click(function() {
450
	var id = $(this).attr("id");
451
452 c95dabdd Stephen Beaver
	$('.' + "infoblock" + id.substr(8)).toggle();
453 f8619b46 Stephen Beaver
	document.getSelection().removeAllRanges();		// Ensure the text is un-selected (Chrome browser quirk)
454 82afb104 Stephen Beaver
});
455 85d29bf5 Stephen Beaver
// ------------------------------------------------------------------------------------------------
456 82afb104 Stephen Beaver
457 1c72e99f Stephen Beaver
// Put a dummy row into any empty table to keep IE happy
458 82afb104 Stephen Beaver
$('tbody').each(function(){
459
	$(this).html($.trim($(this).html()))
460
});
461
462 e82778da Stephen Beaver
$('tbody:empty').html("<tr><td></td></tr>");
463 d5d5836c Stephen Beaver
464 4a457d8d Stephen Beaver
// Hide configuration button for panels without configuration
465
$('.container .panel-heading a.config').each(function (idx, el){
466
	var config = $(el).parents('.panel').children('.panel-footer');
467
	if (config.length == 1)
468
		$(el).removeClass('hidden');
469
});
470 d5d5836c Stephen Beaver
471 4a457d8d Stephen Beaver
// Initial state & toggle icons of collapsed panel
472
$('.container .panel-heading a[data-toggle="collapse"]').each(function (idx, el){
473
	var body = $(el).parents('.panel').children('.panel-body')
474
	var isOpen = body.hasClass('in');
475 d5d5836c Stephen Beaver
476 4a457d8d Stephen Beaver
	$(el).children('i').toggleClass('fa-plus-circle', !isOpen);
477
	$(el).children('i').toggleClass('fa-minus-circle', isOpen);
478 d5d5836c Stephen Beaver
479 4a457d8d Stephen Beaver
	body.on('shown.bs.collapse', function(){
480
		$(el).children('i').toggleClass('fa-minus-circle', true);
481
		$(el).children('i').toggleClass('fa-plus-circle', false);
482
	});
483 d5d5836c Stephen Beaver
484 4a457d8d Stephen Beaver
	body.on('hidden.bs.collapse', function(){
485
		$(el).children('i').toggleClass('fa-minus-circle', false);
486
		$(el).children('i').toggleClass('fa-plus-circle', true);
487
	});
488 cca28e34 Phil Davis
});
489 7d8552fc Stephen Beaver
490 dca4eadf Stephen Beaver
// Separator bar stuff ------------------------------------------------------------------------
491 7d8552fc Stephen Beaver
492 dca4eadf Stephen Beaver
// Globals
493
gColor = 'bg-info';
494 0f1332d6 Phil Davis
newSeparator = false;
495 dca4eadf Stephen Beaver
saving = false;
496
dirty = false;
497
498
$("#addsep").prop('type' ,'button');
499
500
$("#addsep").click(function() {
501 0f1332d6 Phil Davis
	if (newSeparator) {
502 dca4eadf Stephen Beaver
		return(false);
503
	}
504 7d8552fc Stephen Beaver
505 dca4eadf Stephen Beaver
	gColor = 'bg-info';
506 8b7ef76b Phil Davis
	// Insert a temporary bar in which the user can enter some optional text
507 dca4eadf Stephen Beaver
	sepcols = $( "#ruletable tr th" ).length - 2;
508
509
	$('#ruletable > tbody:last').append('<tr>' +
510
		'<td class="' + gColor + '" colspan="' + sepcols + '"><input id="newsep" placeholder="' + svbtnplaceholder + '" class="col-md-12" type="text" /></td>' +
511
		'<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>' +
512
		'<button class="btn btn-info btn-sm" id="btncncsep"><i class="fa fa-undo icon-embed-btn"></i>' + cncltxt + '</button>' +
513
		'&nbsp;&nbsp;&nbsp;&nbsp;' +
514
		'&nbsp;&nbsp;<a id="sepclrblue" value="bg-info"><i class="fa fa-circle text-info icon-pointer"></i></a>' +
515
		'&nbsp;&nbsp;<a id="sepclrred" value="bg-danger"><i class="fa fa-circle text-danger icon-pointer"></i></a>' +
516
		'&nbsp;&nbsp;<a id="sepclrgreen" value="bg-success"><i class="fa fa-circle text-success icon-pointer"></i></a>' +
517
		'&nbsp;&nbsp;<a id="sepclrorange" value="bg-warning"><i class="fa fa-circle text-warning icon-pointer"></i></button>' +
518
		'</td></tr>');
519
520
	$('#newsep').focus();
521 0f1332d6 Phil Davis
	newSeparator = true;
522 dca4eadf Stephen Beaver
523
	$("#btnnewsep").prop('type' ,'button');
524
525
	// Watch escape and enter keys
526
	$('#newsep').keyup(function(e) {
527 8b7ef76b Phil Davis
		if (e.which == 27) {
528 dca4eadf Stephen Beaver
			$('#btncncsep').trigger('click');
529
		}
530
	});
531 7d8552fc Stephen Beaver
532 dca4eadf Stephen Beaver
	$('#newsep').keypress(function(e) {
533 8b7ef76b Phil Davis
		if (e.which == 13) {
534 dca4eadf Stephen Beaver
			$('#btnnewsep').trigger('click');
535 7d8552fc Stephen Beaver
		}
536 dca4eadf Stephen Beaver
	});
537
538
	handle_colors();
539 7d8552fc Stephen Beaver
540 dca4eadf Stephen Beaver
	// Replace the temporary separator bar with the final version containing the
541
	// user's text and a delete icon
542
	$("#btnnewsep").click(function() {
543
		var septext = escapeHtml($('#newsep').val());
544
		sepcols = $( "#ruletable tr th" ).length - 1;
545
546
		$(this).parents('tr').replaceWith('<tr class="ui-sortable-handle separator">' +
547
			'<td class="' + gColor + '" colspan="' + sepcols + '">' + '<span class="' + gColor + '">' + septext + '</span></td>' +
548
			'<td class="' + gColor + '"><a href="#"><i class="fa fa-trash sepdel"></i></a>' +
549 7d8552fc Stephen Beaver
			'</td></tr>');
550
551 dca4eadf Stephen Beaver
		$('#order-store').removeAttr('disabled');
552 0f1332d6 Phil Davis
		newSeparator = false;
553 dca4eadf Stephen Beaver
		dirty = true;
554
	});
555 7d8552fc Stephen Beaver
556 dca4eadf Stephen Beaver
	// Cancel button
557
	$('#btncncsep').click(function(e) {
558
		e.preventDefault();
559
		$(this).parents('tr').remove();
560 0f1332d6 Phil Davis
		newSeparator = false;
561 dca4eadf Stephen Beaver
	});
562
});
563 7d8552fc Stephen Beaver
564 dca4eadf Stephen Beaver
// Delete a separator row
565
$(function(){
566
	$('table').on('click','tr a .sepdel',function(e){
567
		e.preventDefault();
568
		$(this).parents('tr').remove();
569
		$('#order-store').removeAttr('disabled');
570
		dirty = true;
571
	});
572
});
573 7d8552fc Stephen Beaver
574 8b7ef76b Phil Davis
// Compose an input array containing the row #, color and text for each separator
575 dca4eadf Stephen Beaver
function save_separators() {
576
	var row = 0;
577
	var sepinput;
578
	var sepnum = 0;
579
580
	$('#ruletable > tbody > tr').each(function() {
581
		if ($(this).hasClass('separator')) {
582
			seprow = $(this).next('tr').attr("id");
583
			if (seprow == undefined) {
584
				seprow = "fr" + row;
585 7d8552fc Stephen Beaver
			}
586
587 dca4eadf Stephen Beaver
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][row]" value="' + seprow + '"></input>';
588
			$('form').append(sepinput);
589
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][text]" value="' + escapeHtml($(this).find('td').text()) + '"></input>';
590
			$('form').append(sepinput);
591
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][color]" value="' + $(this).find('td').prop('class') + '"></input>';
592
			$('form').append(sepinput);
593
			sepinput = '<input type="hidden" name="separator[' + sepnum + '][if]" value="' + iface + '"></input>';
594
			$('form').append(sepinput);
595
			sepnum++;
596
		} else {
597
			if ($(this).parent('tbody').hasClass('user-entries')) {
598
				row++;
599
			}
600
		}
601
	});
602
}
603 7d8552fc Stephen Beaver
604 dca4eadf Stephen Beaver
function reindex_rules(section) {
605
	var row = 0;
606 7d8552fc Stephen Beaver
607 dca4eadf Stephen Beaver
	section.find('tr').each(function() {
608 8b7ef76b Phil Davis
		if (this.id) {
609 dca4eadf Stephen Beaver
			$(this).attr("id", "fr" + row);
610
			$(this).attr("onclick", "fr_toggle(" + row + ")")
611
			$(this).find('input:checkbox:first').each(function() {
612
				$(this).attr("id", "frc" + row);
613
				$(this).attr("onclick", "fr_toggle(" + row + ")");
614
			});
615 7d8552fc Stephen Beaver
616 dca4eadf Stephen Beaver
			row++;
617
		}
618 7d8552fc Stephen Beaver
	});
619 dca4eadf Stephen Beaver
}
620 7d8552fc Stephen Beaver
621 dca4eadf Stephen Beaver
function handle_colors() {
622
	$('[id^=sepclr]').prop("type", "button");
623
624
	$('[id^=sepclr]').click(function () {
625
		var color =	 $(this).attr('value');
626
		// Clear all the color classes
627
		$(this).parent('td').prop('class', '');
628
		$(this).parent('td').prev('td').prop('class', '');
629
		// Install our new color class
630
		$(this).parent('td').addClass(color);
631
		$(this).parent('td').prev('td').addClass(color);
632
		// Set the global color
633
		gColor = color;
634 7d8552fc Stephen Beaver
	});
635 dca4eadf Stephen Beaver
}
636 7d8552fc Stephen Beaver
637 dca4eadf Stephen Beaver
//JS equivalent to PHP htmlspecialchars()
638
function escapeHtml(text) {
639
	var map = {
640
		'&': '&amp;',
641
		'<': '&lt;',
642
		'>': '&gt;',
643
		'"': '&quot;',
644
		"'": '&#039;'
645
	};
646
647
	return text.replace(/[&<>"']/g, function(m) { return map[m]; });
648
}
649
// --------------------------------------------------------------------------------------------
650 7d8552fc Stephen Beaver
651 dca4eadf Stephen Beaver
// Select every option in the specified multiselect
652
function AllServers(id, selectAll) {
653
   for (i = 0; i < id.length; i++)	   {
654
	   id.eq(i).prop('selected', selectAll);
655
   }
656
}
657 3f597adb Stephen Beaver
658 dca4eadf Stephen Beaver
// Move all selected options from one multiselect to another
659
function moveOptions(From, To)	{
660
	var len = From.length;
661
	var option;
662
663
	if (len > 0) {
664
		for (i=0; i<len; i++) {
665
			if (From.eq(i).is(':selected')) {
666
				option = From.eq(i).val();
667
				value  = From.eq(i).text();
668
				To.append(new Option(value, option));
669
				From.eq(i).remove();
670 7d8552fc Stephen Beaver
			}
671 dca4eadf Stephen Beaver
		}
672 7d8552fc Stephen Beaver
	}
673 dca4eadf Stephen Beaver
}
674 7d8552fc Stephen Beaver
675 2ccf2ede Stephen Beaver
676 dca4eadf Stephen Beaver
// ------------- Service start/stop/restart functions.
677 571e181c Stephen Beaver
// If a start/stop/restart button is clicked, parse the button name and make a POST via AJAX
678 dca4eadf Stephen Beaver
$('[id*=restartservice-], [id*=stopservice-], [id*=startservice-]').click(function(event) {
679
	var args = this.id.split('-');
680
	var action, name, mode_zone, id;
681
682
	if (args[0] == "openvpn") {
683
		action = args[1];
684
		name = args[0];
685
		mode_zone = args[2];
686
		id = args[3];
687
	} else if (args[0] == "cpativeportal") {
688
		action = args[1];
689
		name = args[0];
690
		mode_zone = args[2];
691
		id = args[3];
692
	} else {
693
		action = args[0];
694
		name = args[1];
695 2ccf2ede Stephen Beaver
	}
696
697 dca4eadf Stephen Beaver
	$(this).children('i').removeClass().addClass('fa fa-cog fa-spin text-success');
698 0cb39b48 Stephen Beaver
	this.blur();
699 dca4eadf Stephen Beaver
700
	ajaxRequest = $.ajax(
701
		{
702
			url: "/status_services.php",
703
			type: "post",
704
			data: {
705
				ajax: 		"ajax",
706
				mode: 		action,
707
				service: 	name,
708
				vpnmode: 	mode_zone,
709
				zone: 		mode_zone,
710
				id: 		id
711 2ccf2ede Stephen Beaver
			}
712
		}
713 dca4eadf Stephen Beaver
	);
714
715
	// Once the AJAX call has returned, refresh the page to show the new service
716
	ajaxRequest.done(function (response, textStatus, jqXHR) {
717
		location.reload(true);
718
	});
719
});