Using dropdown to add/remove search boxes - javascript

I've got a drop down that will have 3 categories in it. I would like to to have so that when the user chooses a category, the correct number of search boxes for that category will appear. I then want the text that is inputted into the search boxes to be saved as a variable in the URL. Here is what I got
http://jsfiddle.net/2yWzc/1/
HTML:
<form class="list" action="table.php" method="get">
<table>
<tbody>
<tr class="name">
<td>First Name:</td>
<td><input type="text" class="searchBox" name="q1" /></td>
</tr>
<tr class="name">
<td>Last Name:</td>
<td><input type="text" class="searchBox" name="q2" /></td>
</tr>
<tr class="owner">
<td>Owner:</td>
<td><input type="text" class="searchBox" name="q1" /></td>
</tr>
<tr class="dlp">
<td>Text 1:</td>
<td><input type="text" class="searchBox" name="q1" /></td>
</tr>
<tr class="dlp">
<td>Text 2:</td>
<td><input type="text" class="searchBox" name="q2" /></td>
</tr>
<tr class="dlp">
<td>Text 3:</td>
<td><input type="text" class="searchBox" name="q3" /></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="SEARCH" /></td>
</tr>
</tbody>
</table>
<br>
<select id="options">
<option value="name">Option 1</option>
<option value="owner">Option 2</option>
<option value="dlp">Option 3</option>
</select>
</form>
JS:
$('#options').change(function() {
if ($(this).val() == 'name') {
$('.name').show();
$('.owner').hide();
$('.dlp').hide();
} else if ($(this).val() == 'owner') {
$('.name').hide();
$('.owner').show();
$('.dlp').hide();
} else if ($(this).val() == 'dlp') {
$('.name').hide();
$('.owner').hide();
$('.dlp').show();
}
});
$(function(){
$('form').bind('change', function () {
var url = $(this).val();
if (url) {
window.location = url;
}
return false;
});
});
This shows the correct number of searchboxes, but it doesn't save the text in the search boxes in the variables. It also seems like it isn't a good way to do it (If you know of the proper way, point me in the right direction. This was the only thing I could do that would work). Before this, I had 1 search box per category, so my JS code was this
(function($) {
$(function(){
$('form').bind('change', function () {
var url = $(this).val(); // get selected value
if (url) { // require a URL
window.location = url; // redirect
}
return false;
});
});
})(jQuery)​;
However, I have no idea how to get it to work for multiple search boxes. I only want variables for the search boxes that are shown to be passed via URL (again, so maybe this isn't the proper way?).
Can anyone help me out? Thanks

You can try the below.
An event handler is bound to the form's submit event. Within it, we get a list of all input elements with type="text" that are present inside the form and pick up only the fields that are visible using the filter method. Then we traverse the list of elements and form the dataString. This dataString is of the format q=a&q1=b and will be appended to the URL. encodeURIComponent is used to encode the dataString.
$(function(){
var dataString = "";
var url="sample.htm";
var count=0;
$('form').bind('submit', function () {
$('form input[type="text"]').filter(':visible').each(function(){
if(count === 0)
dataString += this.name + "=" + $(this).val();
else
dataString += '&' + this.name + "=" + $(this).val();
count++;
});
//console.log(dataString);
dataString += "&t="+$("#options").val(); //appending the value of the select box
if (url) {
window.location = url + "?" + encodeURIComponent(dataString); //added for the URL encode
}
return false;
});
});
Updated Working Demo

Not the best way to do that, but here is an update to your jsfiddle that will do what you want
$(function(){
$('input[type=submit]').bind('click', function () {
count = 0;
url = '';
$("input[type=text]").each(function(){
if($(this).is(":visible")){
count++;
if(count != 1){
url += "&";
}
url += "var"+count+"="+$(this).val();
}
});
if(count != 0){
url += "&";
}
url += "count="+count;
url += "&option="+$("#options").val();
alert(url);
return false;
});
});
http://jsfiddle.net/2yWzc/4/

Related

Rewriting JavaScript code with consequent numbers in the names of ids

I'm trying to apply a function to input field with ids that contain consequent numbers (ie. price1, price2, price3), etc.
There's no problem with the first row of field that are defined for a start. But further input fields are dynamically added by a jQuery function and their number is not known in advance.
I hoped it would be an easy loop to apply:
var i=1;
$("#quantity"+i).keyup(function() {
var price= $("#price"+i).val();
var quantity= $(this).val();
var value= price*quantity;
var value=value.toFixed(2); /* rounding the value to two digits after period */
value=value.toString().replace(/\./g, ',') /* converting periods to commas */
$("#value"+i).val(value);
});
So far so good - the outcome of the multiplication properly displays in the id="value1" field after the "quantity" field is filled up.
Now further fields should follow the pattern and calculate the value when the quantity is entered - like this:
[price2] * [quantity2] = [value2]
[price3] * [quantity3] = [value3]
etc.
So the code follows:
$('#add_field').click(function(){ /* do the math after another row of fields is added */
var allfields=$('[id^="quantity"]');
var limit=(allfields.length); /* count all fields where id starts with "quantity" - for the loop */
for (var count = 2; count < limit; count++) { /* starting value is now 2 */
$("#quantity"+count).keyup(function() {
var cena = $("#price"+count).val();
var quantity= $("#quantity"+count).val();
var value= price*quantity;
var value=value.toFixed(2);
value=value.toString().replace(/\./g, ',')
$("#value"+count).val(value);
});
}
});
The problem is that all further "value" fields are only calculated when "quantity2" is (re)entered and the "value2" is not calculated at all.
I guess there's a mistake while addressing fields and/or triggering the calculation.
How should I correct the code?
Just in case the "add_field" function is needed to solve the problem:
$(document).ready(function(){
var i=1;
$('#add_field').click(function(){
i++;
$('#offer').append('<tr id="row'+i+'">
<td><input type="text" name="prod_num[]" id="prod_num'+i+'" placeholder="Product number (6 digits)"></td><td><input type="text" name="prod_name[]" disabled></td>
<td><input type="text" name="cena[]" id="price'+i+'" placeholder="Enter your price"></td>
<td><input type="text" name="quantity[]" id="quantity'+i+'" placeholder="Enter quantity"></td>
<td><input type="text" name="value[]" id="value'+i+'" disabled></td>
<td><button type="button" name="remove_field" id="'+i+'" class="button_remove">X</button></td></tr>');
});
Incrementing IDs is a lot more trouble than it is worth, especially when you start removing rows as well as adding them.
This can all be done using common classes and traversing within the specific row instance.
To account for future rows use event delegation.
Simplified example:
// store a row copy on page load
const $storedRow = $('#myTable tr').first().clone()
// delegate event listener to permanent ancestor
$('#myTable').on('input', '.qty, .price', function(){
const $row = $(this).closest('tr'),
price = $row.find('.price').val(),
qty = $row.find('.qty').val();
$row.find('.total').val(price*qty)
});
$('button').click(function(){
// insert a copy of the stored row
// delegated events will work seamlessly on new rows also
const $newRow = $storedRow.clone();
const prodName = 'Product XYZ';// get real value from user input
$newRow.find('.prod-name').text(prodName)//
$('#myTable').append($newRow)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>Add row</button>
<table id="myTable">
<tr>
<td class="prod-name">Product 1</td>
<td>Qty:<input type="number" class="qty" value="0"></td>
<td>Price:<input type="number" class="price" value="0"></td>
<td>Total:<input type="text" class="total" value="0" readonly></td>
</tr>
<tr>
<td class="prod-name">Product 2</td>
<td>Qty:<input type="number" class="qty" value="0"></td>
<td>Price:<input type="number" class="price" value="0"></td>
<td>Total:<input type="text" class="total" value="0" readonly></td>
</tr>
</table>
Understanding Event Delegation
The first thing to consider is that you can get the length of a selector. So for example:
var count = $("input").length;
If there is one, value here would be 1. if there are four, the value would be 4.
You can also use .each() option to itereate each of the items in the selector.
$('#add_field').click(function(){
var allFields = $('[id^="quantity"]');
allFields.each(function(i, el){
var c = i + 1;
$(el).keyup(function() {
var price = parseFloat($("#price" + c).val());
var quantity = parseInt($(el).val());
var value = price * quantity;
value = value.toFixed(2);
value = value.toString().replace(/\./g, ',');
$("#value" + c).val(value);
});
});
});
You could also create relationship based on the ID itself.
$(function() {
function calcTotal(price, qnty) {
return (parseFloat(price) * parseInt(qnty)).toFixed(2);
}
$('#add_field').click(function() {
var rowClone = $("#row-1").clone(true);
var c = $("tbody tr[id^='row']").length + 1;
rowClone.attr("id", "row-" + c);
$("input:eq(0)", rowClone).val("").attr("id", "prod_num-" + c);
$("input:eq(1)", rowClone).val("").attr("id", "price-" + c);
$("input:eq(2)", rowClone).val("").attr("id", "quantity-" + c);
$("input:eq(3)", rowClone).val("").attr("id", "value-" + c);
$("button", rowClone).attr("id", "remove-" + c);
rowClone.appendTo("table tbody");
});
$("table tbody").on("keyup", "[id^='quantity']", function(e) {
var $self = $(this);
var id = $self.attr("id").substr(-1);
if ($("#price-" + id).val() != "" && $self.val() != "") {
$("#value-" + id).val(calcTotal($("#price-" + id).val(), $self.val()));
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="add_field">Add Field</button>
<br />
<h2>Product</h2>
<table>
<thead>
<tr>
<td>Number</td>
<td>Name</td>
<td>Price</td>
<td>Quantity</td>
<td>Total</td>
<td></td>
</thead>
<tbody>
<tr id="row-1">
<td><input type="text" name="prod_num[]" id="prod_num-1" placeholder="Product number (6 digits)"></td>
<td><input type="text" name="prod_name[]" disabled></td>
<td><input type="text" name="cena[]" id="price-1" placeholder="Enter your price"></td>
<td><input type="text" name="quantity[]" id="quantity-1" placeholder="Enter quantity"></td>
<td><input type="text" name="value[]" id="value-1" disabled></td>
<td><button type="button" name="remove_field" id="remove-1" class="button_remove">X</button></td>
</tr>
</tbody>
</table>

Find Checkbox in HTML Table Using JQuery Find Method

I have a HTML table which has the following structure:
<table id="myTable">
<tr>
<td><input type="text" name="FullName" value="Tom" /></td>
<td><input type="checkbox" name="isActive" /></td>
<td>Edit
</tr>
</table>
When the user clicks the 'edit' link, a Javascript function is called (see below). In this function I need to get the data from the table, i.e., FullName and whether or not isActive has been checked.
$("#namedTutors").on('click', '.editTutor', function () {
var tr = $(this).closest("tr");
var fullName = tr.find("input[name=FullName]").val();
});
I can get the FullName easy enough, but I'm having difficulties retrieving the data to see if isActive has been checked/ticked or not.
Could someone please help.
Thanks.
You could select the ckeckbox input by name [name=isActive] then use the .is(':checked') to check whether the ckeckbox is checked or not, like:
$("#namedTutors").on('click', '.editTutor', function() {
var tr = $(this).closest("tr");
var fullName = tr.find("input[name=FullName]").val();
var isActive = tr.find("input[name=isActive]").is(':checked');
console.log( isActive );
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="namedTutors">
<tr>
<td><input type="text" name="FullName" value="Tom" /></td>
<td><input type="checkbox" name="isActive" /></td>
<td>Edit
</tr>
</table>
if(tr.find('input[name="isActive"]:checked').length) {
console.log('it is checked');
}

Adding more values from another field with JavaScript

Need help to solve a JavaScript problem.
i am working on an invoice in which i want to add more values to quantity field.
i am trying with script given in JSFiddle.
The problem is when i click on edit , it should popup a dialog box and by entering data in add field it should be added to current quantity of a specific item.
https://jsfiddle.net/programmer/LLmrp94y/16/
JS script
$(document).on('change', '.addQty', function () {
id_arr = $(this).attr('id');
id = id_arr.split("_");
add = $('#add_'+id[1]).val();
qty = $('#quantity_'+id[1]).val();
if (add != '' && typeof (add) != "undefined") {
$('#add_'+id[1]).val();
added = parseFloat(qty) + parseFloat(add);
$('#qtY_'+id[1]).val(added);
priceAfter = $('#price_'+id[1]).val();
$('#Total_'+id[1]).val((parseFloat(priceAfter) * parseFloat(added)).toFixed(2));
} else {
$('#quantity_'+id[1]).val(qty);
$('#Total_'+id[1]).val((parseFloat(price) * parseFloat(qty)).toFixed(2));
}
});
I made it work by doing the following :
adding an id to your edit buttons, so we can retrieve the id of the line currently being edited
replacing your 'onchange' function by a addQuantity function that takes a parameter : the id of the line being edited.
fixing a couple issues with the ids used in the code written to calculate the new quantity and the new price
Also, I replaced your php code by hard coded ids. You're going to have to replace them.
EDIT : Since you don't want to show the current quantity in the dialog, I had to change the logic and update the table after close has been clicked. Otherwise it caused too many issues. Hope you like it.
$(document).ready(function() {
calculateEachItemSubCost();
});
function calculateEachItemSubCost() {
var qtys = document.getElementsByClassName('quantity');
var price = document.getElementsByClassName('price');
var item_costs = document.getElementsByClassName('totalLinePrice');
for (var i = 0; i < item_costs.length; ++i) {
item_costs[i].value = parseFloat(qtys[i].value) * parseFloat(price[i].value).toFixed(2);
}
}
/* new function that replaces your 'onchange' listener. It handles the adding of a quantity on a given line, identified by the id parameter */
function addQuantity(id) {
var add, added, priceAfter;
add = $('#addedQuantity').val();
console.log("Adding " + add + " on line " + id);
if (add != '' && typeof add != "undefined") {
;
added = parseInt($('.add').val()) + parseInt($('#quantity_' + id).val())
$('#quantity_' + id).val(added);
priceAfter = $('#price_' + id).val();
$('#total_' + id).val((parseFloat(priceAfter) * parseFloat(added)).toFixed(2));
} else {
$('#quantity_' + id).val(qty);
$('#Total_' + id).val((parseFloat(price) * parseFloat(qty)).toFixed(2));
}
}
$(document).on('click', '.editnow', function(event) {
var lineId, quantityField;
// retrieving the id of the line that was clicked on
lineId = event.target.id.split("_")[1];
quantityField = $("#quantity_" + lineId);
$(".add").val("");
$("#edit").dialog({
show: "fold",
hide: "fold",
modal: true,
title: "Edit",
zIndex: 10000,
close: function(event, ui) {
addQuantity(lineId);
$(this).hide();
}
});
});
#edit{
display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/ui-lightness/jquery-ui.css"/>
<!DOCTYPE html>
<!-- Begin page content -->
<h1 class="text-center title">Invoice</h1>
<table>
<thead>
<tr>
<th width="38%">Item Name</th>
<th width="15%">Price</th>
<th width="15%">Quantity</th>
<th width="15%">Total</th>
<th width="15%">Edit</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="text" value="samsung galaxy s6" id="itemName_1" ></td>
<td><input type="number" value="500" id="price_1" class="price"></td>
<td><input type="number" value="1" id="quantity_1" class="quantity"></td>
<td><input type="number" value="" id="total_1" class="totalLinePrice"></td>
<td><button type="button" class="editnow" id="edit_1"> Edit </button></td>
</tr>
<tr>
<td><input type="text" value="samsung galaxy s7" id="itemName_2" ></td>
<td><input type="number" value="700" id="price_2" class="price"></td>
<td><input type="number" value="1" id="quantity_2" class="quantity"></td>
<td><input type="number" value="" id="total_2" class="totalLinePrice"></td>
<td><button type="button" class="editnow" id="edit_2"> Edit </button></td>
</tr>
</tbody>
</table>
<div id="edit">
<table>
<tr>
<th>Add</th>
</tr>
<tr>
<td><input type="number" class="add" id="addedQuantity"></td>
</tr>
</table>
</div>
Your updated JSFiddle
I have edited it, but it does not work because of the php values not working, of course. I've added id to Edit buttons, and getting value from dialog. Based on the button id, you can enter value to corresponding quantity field
<button type="button" id="edit_<?php $i; ?>" class="editnow"> Edit </button>
Yes: function () {
var id = $(this).attr('id');
id = id.substring(id.indexOf('_')+1);
alert($('#quantityVal').val()); // just check the value
$('#quantity_'+id).val($('#quantityVal').val());
$(this).dialog("close");
},
Edit dialog number field
<td><input type="number" class="add" id="quantityVal"></td>
https://jsfiddle.net/LLmrp94y/12/

Using jQuery to validate value in comparison to other input value

So I have a base Hour input field and I'm trying to validate the other input fields so that once the base hour is added the other input values can only be as large as the first base rate Hours column (first input). Or put another way the one input field becomes the max number value once it is entered. So if the base is 12 for the Hours column the second and third rate can be no larger than 12. The tricky part is add new row feature means all new rows for the hour column have to adhere to the rule as well. I have been trying to figure it out for a bit, any help would be appreciated.
Here is the fiddle: http://jsfiddle.net/uuzhuom9/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
$(document).ready(function () {
$('#calculate').on('click', function() {
$('.hours-table tr').each(function() {
var hours = $(this).find('input.hours').val();
var rate = $(this).find('input.rate').val();
var dateTotal = (hours * rate);
$(this).find('input.date-total').val(dateTotal);
// total += parseInt($("input.date-total").val());
var sum = 0;
//iterate through each textboxes and add the values
$("input.date-total").each(function () {
//add only if the value is number
if (!isNaN($(this).val()) && $(this).val().length != 0) {
sum += parseFloat(this.value);
}
});
if (sum < 0) {
alert("Total can't be negative");
$('#grandtotal-new').val('');
} else {
$("#grandtotal-new").val(sum)
}
}); //END .each
return false;
}); // END click
});
$(function(){
var counter = 4;
$('a.add-row').click(function(event){
event.preventDefault();
counter++;
var newRow = jQuery('<tr><td><input type="text" value="" /></td><td><input type="text" class="hours" name="rate-0' + counter + '"/></td><td><input type="text" class="rate" name="rate-0' + counter + '"/></td><td><input type="text" class="date-total" readonly name="date-total-0' + counter + '"/></td></tr>');
$('table.hours-table').append(newRow);
});
});
</script>
The html is:
<table class="hours-table">
<tr>
<th>Item</th>
<th>Hours</th>
<th>Hourly Rate</th>
<th>Total</th>
</tr>
<tr>
<td>Base Rate:</td>
<td class="hours"><input type="number" class="hours" id="base-hours" name="hours-01" max="???" min="???" value="" /></td>
<td class="rate"><input min="0" class="rate" name="rate-01" value="200" readonly /></td>
<td class="date-total"><input type="text" class="date-total" name="date-total-0" readonly /></td>
</tr>
<tr>
<td>Second Rate:</td>
<td class="hours"><input type="number" class="hours" name="hours-02" max="???" min="???" value="" /></td>
<td class="rate"><input type="text" class="rate" name="rate-02" value="-20" readonly /></td>
<td class="date-total"><input type="text" class="date-total" name="date-total-1" readonly /></td>
</tr>
<tr>
<td>Third Rate:</td>
<td class="hours"><input type="number" class="hours" name="hours-03" max="???" min="???" value="" /></td>
<td class="rate"><input type="text" class="rate" name="rate-03" value="10" readonly /></td>
<td class="date-total"><input type="text" class="date-total" name="date-total-2" readonly/></td>
</tr>
</table>
Add New Rule<br />
<button type="button" id='calculate' class="btn btn-inverse btn- mini">Calculate</button>
The Grand total is: <input type="number" id='grandtotal-new' min="???"/>
Just validate them on blur of each .hours input as below:
DEMO
$(document).on('blur','.hours',function(){
var current=$(this);
if(!(current).is('input.hours:first'))
{
if(current.val()>$('input.hours:first').val())
current.val('');
}
});
This will check value of input on blur and clears it if it is greater than first one
UPDATE:
DEMO
parse the value before checking as below:
$(document).on('blur','.hours',function(){
var current=$(this);
if(!(current).is('input.hours:first'))
{
if(parseInt(current.val())>parseInt($('input.hours:first').val()))
current.val('');
}
});
UPDATE 2
Based on OPs comments here is the way to achieve the requirements mentioned by him.
DEMO
$(document).on('blur','.hours',function(e){
var current=$(this);
var base=$('input.hours:first');
var total=0;
var other=$('input.hours:not(:first)');
if(base.val()==="")
{
alert('Enter Base First');
current.val('');
base.focus();
e.stopPropagation();
return;
}
$.each($(other),function(index,value){
if(value.value!=="")
total+=parseInt(parseInt(value.value));
});
console.log(total);
if(!(current).is(base))
{
if(parseInt(current.val())>parseInt(base.val()))
{
current.val('');
}
else if(total>parseInt($('input.hours:first').val()))
current.val('');
}
});
add this at the beginning of your jquery script.
this will limit the other hours input to whatever is on base-hours, including newly added rows.
var baseRate = 0;
$(".hours-table").on("input","input.hours",function() {
if ($(this).attr('id') == 'base-hours'){
baseRate = $(this).val();
}else if ($(this).val() > baseRate){
$(this).val(baseRate);
}
});
and then further down below you have a syntax/logic error on your add row function. replace this line with this corrected line, and you might wanna move your counter++ after this line.
var newRow = jQuery('<tr><td><input type="text" value="" /></td><td><input type="text" class="hours" name="rate-0' + counter + '"/></td><td><input type="text" class="rate" name="rate-0' + counter + '"/></td><td><input type="text" class="date-total" readonly name="date-total-0' + counter + '"/></td></tr>');
check this jsfiddle - http://jsfiddle.net/uuzhuom9/8/

jQuery remove row with matching file name as hidden

I have a markup like this
<table id="data">
<tr>
<td>Name</td>
<td>Test</td>
</tr>
<tr>
<td>
<input type="hidden" id="file-name" value="file.doc">
</td>
<td><input type="text" value="Test 1"></td>
</tr>
<tr>
<td>
<input type="hidden" id="file-name" value="file1.docx">
</td>
<td><input type="text" value="Test 2"></td>
</tr>
<tr>
<td>
<input type="hidden" id="file-name" value="file.pdf">
</td>
<td><input type="text" value="Test 3"></td>
</tr>
</table>
Remove File
In that markup you can see I have file name as hidden fields and under the table I have a remove file tag. So the thing is like this when I will click on the remove file then it will remove that entire row(tr tag) inside where the filename
file.doc is present. So for that I have made my js like this
<script type="text/javascript">
$(document).ready(function() {
$('#remove').click(function(e) {
var FileName = 'file.doc';
var GetRowVal = $('table#data td #file-name').val();
if(GetRowVal == FileName ) {
var Parent = $(GetRowVal).parent().remove();
}
else {
console.log(' error');
}
});
});
</script>
But it is not removing the row. So can someone kindly tell me whats the issue here? Any help and suggestions will be really apprecaible. Thanks
There are duplicate id's in your Html,just correct that issue and try below answer :
<script type="text/javascript">
$(document).ready(function() {
$('#remove').click(function(e) {
e.preventDefault();
var FileName = 'file.doc';
$('input[type="hidden"]').each(function(){
if( $(this).val() == FileName )
{
$(this).closest('tr').remove();
}
});
});
});
</script>
Following code return Array of Jquery Object.
Then function .val() cannot have meaning.
$('table#data td #file-name');
You have two probles in this code:
first : Ids are not unique.
second:
var GetRowVal = $('table#data td #file-name').val();
will hold only value eg. file.doc
so you can't later
remove the object in this line:
var Parent = $(GetRowVal).parent().remove();
so to repair it first change id to class like here:
<input type="hidden" class="file-name" value="file.doc">
and later You can modify your code:
$(document).ready(function() {
$('#remove').click(function(e) {
var GetRowVal = $(".file-name[value='file.doc']");
$(GetRowVal).parent().parent().remove();
});
});
Here jsfiddle

Categories

Resources