/*
* pfSenseHelpers.js
*
* part of pfSense (https://www.pfsense.org)
* Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// These helper functions are used on many/most UI pages to hide/show/disable/enable form elements where required
// Hides the
in which the specified input element lives so that the input, its label and help text are hidden
function hideInput(id, hide) {
if (hide)
$('#' + id).parent().parent('div').addClass('hidden');
else
$('#' + id).parent().parent('div').removeClass('hidden');
}
// Hides the
in which the specified group input element lives so that the input,
// its label and help text are hidden
function hideGroupInput(id, hide) {
if (hide)
$('#' + id).parent('div').addClass('hidden');
else
$('#' + id).parent('div').removeClass('hidden');
}
// Hides the
in which the specified checkbox lives so that the checkbox, its label and help text are hidden
function hideCheckbox(id, hide) {
if (hide)
$('#' + id).parent().parent().parent('div').addClass('hidden');
else
$('#' + id).parent().parent().parent('div').removeClass('hidden');
}
// Disables the specified input element
function disableInput(id, disable) {
$('#' + id).prop("disabled", disable);
}
// Hides all elements of the specified class. This will usually be a section
function hideClass(s_class, hide) {
if (hide)
$('.' + s_class).hide();
else
$('.' + s_class).show();
}
function hideSelect(id, hide) {
if (hide)
$('#' + id).parent('div').parent('div').addClass('hidden');
else
$('#' + id).parent('div').parent('div').removeClass('hidden');
}
function hideMultiCheckbox(id, hide) {
if (hide)
$("[name=" + id + "]").parent().addClass('hidden');
else
$("[name=" + id + "]").parent().removeClass('hidden');
}
// Hides the
in which the specified IP address element lives so that the input, any mask selector, its label and help text are hidden
function hideIpAddress(id, hide) {
if (hide)
$('#' + id).parent().parent().parent('div').addClass('hidden');
else
$('#' + id).parent().parent().parent('div').removeClass('hidden');
}
// Hides all elements of the specified class belonging to a multiselect.
function hideMultiClass(s_class, hide) {
if (hide)
$('.' + s_class).parent().parent().hide();
else
$('.' + s_class).parent().parent().show();
}
// Hides div whose label contains the specified text. (Good for StaticText)
function hideLabel(text, hide) {
var element = $('label:contains(' + text + ')');
if (hide)
element.parent('div').addClass('hidden');
else
element.parent('div').removeClass('hidden');
}
// Hides the '/' and the subnet mask of an Ip_Address/subnet_mask group
function hideMask(name, hide) {
if (hide) {
$('[id^=' + name + ']').hide();
$('[id^=' + name + ']').prev('span').hide();
$('[id^=' + name + ']').parent('div').removeClass('input-group');
} else {
$('[id^=' + name + ']').show();
$('[id^=' + name + ']').prev('span').show();
$('[id^=' + name + ']').parent('div').addClass('input-group');
}
}
// Toggle table row checkboxes and background colors on the pages that use sortable tables:
// /usr/local/www/firewall_nat.php
// /usr/local/www/firewall_nat_1to1.php
// /usr/local/www/firewall_nat_out.php
// /usr/local/www/firewall_rules.php
// /usr/local/www/vpn_ipsec.php
// Striping of the tables is handled here, NOT with the Bootstrap table-striped class because it would
// get confused when rows are sorted or deleted.
function fr_toggle(id, prefix) {
if (!prefix)
prefix = 'fr';
var checkbox = document.getElementById(prefix + 'c' + id);
checkbox.checked = !checkbox.checked;
fr_bgcolor(id, prefix);
}
// Change background color of selected row based on state of checkbox
function fr_bgcolor(id, prefix) {
if (!prefix)
prefix = 'fr';
var row = $('#' + prefix + id);
if ($('#' + prefix + 'c' + id).prop('checked') ) {
row.addClass('active');
} else {
row.removeClass('active');
}
}
// The following functions are used by Form_Groups assigned a class of "repeatable" and provide the ability
// to add/delete rows of sequentially numbered elements, their labels and their help text
// See firewall_aliases_edit.php for an example
// NOTE: retainhelp is a global var that when defined prevents any help text from being deleted as lines are inserted.
// IOW it causes every row to have help text, not just the last row
function setMasks() {
// Find all ipaddress masks and make dynamic based on address family of input
$('span.pfIpMask + select').each(function (idx, select){
var input = $(select).prevAll('input[type=text]');
input.on('change', function(e){
var isV6 = (input.val().indexOf(':') != -1), min = 0, max = 128;
if (!isV6)
max = 32;
if (input.val() == "")
return;
while (select.options.length > max)
select.remove(0);
if (select.options.length < max) {
for (var i=select.options.length; i<=max; i++)
select.options.add(new Option(i, i), 0);
}
});
// Fire immediately
input.change();
});
}
// Complicated function to move all help text associated with this input id to the same id
// on the row above. That way if you delete the last row, you don't lose the help
function moveHelpText(id) {
$('#' + id).parent('div').parent('div').find('input, select, checkbox, button').each(function() { // For each
var fromId = this.id;
var toId = decrStringInt(fromId);
var helpSpan;
if (!$(this).hasClass('pfIpMask') && !$(this).hasClass('btn')) {
if ($('#' + decrStringInt(fromId)).parent('div').hasClass('input-group')) {
helpSpan = $('#' + fromId).parent('div').parent('div').find('span:last').clone();
} else {
helpSpan = $('#' + fromId).parent('div').find('span:last').clone();
}
if ($(helpSpan).hasClass('help-block')) {
if ($('#' + decrStringInt(fromId)).parent('div').hasClass('input-group')) {
$('#' + decrStringInt(fromId)).parent('div').after(helpSpan);
} else {
$('#' + decrStringInt(fromId)).after(helpSpan);
}
}
}
});
}
// Increment the number at the end of the string
function bumpStringInt( str ) {
var data = str.match(/(\D*)(\d+)(\D*)/), newStr = "";
if (data)
newStr = data[ 1 ] + ( Number( data[ 2 ] ) + 1 ) + data[ 3 ];
return newStr || str;
}
// Decrement the number at the end of the string
function decrStringInt( str ) {
var data = str.match(/(\D*)(\d+)(\D*)/), newStr = "";
if (data)
newStr = data[ 1 ] + ( Number( data[ 2 ] ) - 1 ) + data[ 3 ];
return newStr || str;
}
// Called after a delete so that there are no gaps in the numbering. Most of the time the config system doesn't care about
// gaps, but I do :)
function renumber() {
var idx = 0;
$('.repeatable').each(function() {
$(this).find('input').each(function() {
$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
});
$(this).find('select').each(function() {
$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
});
$(this).find('button').each(function() {
$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
$(this).prop("name", this.name.replace(/\d+$/, "") + idx);
});
// $(this).find('label').attr('for', $(this).find('label').attr('for').replace(/\d+$/, "") + idx);
idx++;
});
}
function delete_row(rowDelBtn) {
var rowLabel;
// If we are deleting row zero, we need to save/restore the label
if ( (rowDelBtn == "deleterow0") && ((typeof retainhelp) == "undefined")) {
rowLabel = $('#' + rowDelBtn).parent('div').parent('div').find('label').text();
}
$('#' + rowDelBtn).parent('div').parent('div').remove();
renumber();
checkLastRow();
if (rowDelBtn == "deleterow0") {
$('#' + rowDelBtn).parent('div').parent('div').find('label').text(rowLabel);
}
}
function checkLastRow() {
if ($('.repeatable').length <= 1) {
$('#deleterow0').hide();
} else {
$('[id^=deleterow]').show();
}
}
function add_row() {
// Find the last repeatable group
var lastRepeatableGroup = $('.repeatable:last');
// Clone it
var newGroup = lastRepeatableGroup.clone();
// Increment the suffix number for each input element in the new group
$(newGroup).find('input').each(function() {
$(this).prop("id", bumpStringInt(this.id));
$(this).prop("name", bumpStringInt(this.name));
if (!$(this).is('[id^=delete]'))
$(this).val('');
});
// Increment the suffix number for the deleterow button element in the new group
$(newGroup).find('[id^=deleterow]').each(function() {
$(this).prop("id", bumpStringInt(this.id));
$(this).prop("name", bumpStringInt(this.name));
});
// Do the same for selectors
$(newGroup).find('select').each(function() {
$(this).prop("id", bumpStringInt(this.id));
$(this).prop("name", bumpStringInt(this.name));
// If this selector lists mask bits, we need it to be reset to all 128 options
// and no items selected, so that automatic v4/v6 selection still works
if ($(this).is('[id^=address_subnet]')) {
$(this).empty();
for (idx=128; idx>0; idx--) {
$(this).append($('