Sorting Number in Table - javascript

I had try to create a table that able to sort the data ascending nor descending by clicking on the table header.
However the data number is not sorting correctly based on 1,3,9,10 instead of 1,10,11,12. Is there any way to sort the number?
function sortTable(table, col, reverse) {
var tb = table.tBodies[0], // use `<tbody>` to ignore `<thead>` and `<tfoot>` rows
tr = Array.prototype.slice.call(tb.rows, 0), // put rows into array
i;
reverse = -((+reverse) || -1);
tr = tr.sort(function (a, b) { // sort rows
return reverse // `-1 *` if want opposite order
* (a.cells[col].textContent.trim() // using `.textContent.trim()` for test
.localeCompare(b.cells[col].textContent.trim())
);
});
for(i = 0; i < tr.length; ++i) tb.appendChild(tr[i]); // append each row in order
}
function makeSortable(table) {
var th = table.tHead, i;
th && (th = th.rows[0]) && (th = th.cells);
if (th) i = th.length;
else return; // if no `<thead>` then do nothing
while (--i >= 0) (function (i) {
var dir = 1;
th[i].addEventListener('click', function () {
sortTable(table, i, (dir = 1 - dir))
});
}(i));
}
function makeAllSortable(parent) {
parent = parent || document.body;
var t = parent.getElementsByTagName('table'), i = t.length;
while (--i >= 0) makeSortable(t[i]);
}
window.onload = function () {makeAllSortable();};
<table class="table table-striped table-bordered table-hover" id="Tabla">
<thead>
<tr style="cursor:pointer">
<th>Fruit<span class="glyphicon pull-right" aria-hidden="true"></span></th>
<th>Number<span class="glyphicon pull-right" aria-hidden="true"></span></th>
</tr>
</thead>
<tr>
<td>Apple</td>
<td>11757.915</td>
</tr>
<tr>
<td>Orange</td>
<td>36407.996</td>
</tr>
<tr>
<td>Watermelon</td>
<td>9115.118</td>
</tr>
</table>

You must add the options to sort numerically to the localCompare call
// --8<-------
.localeCompare(b.cells[col].textContent.trim(), undefined, {numeric: true})
// ------->8--
function sortTable(table, col, reverse) {
var tb = table.tBodies[0], // use `<tbody>` to ignore `<thead>` and `<tfoot>` rows
tr = Array.prototype.slice.call(tb.rows, 0), // put rows into array
i;
reverse = -((+reverse) || -1);
tr = tr.sort(function(a, b) { // sort rows
return reverse // `-1 *` if want opposite order
*
(a.cells[col].textContent.trim() // using `.textContent.trim()` for test
.localeCompare(b.cells[col].textContent.trim(), undefined, {numeric: true})
);
});
for (i = 0; i < tr.length; ++i) tb.appendChild(tr[i]); // append each row in order
}
function makeSortable(table) {
var th = table.tHead,
i;
th && (th = th.rows[0]) && (th = th.cells);
if (th) i = th.length;
else return; // if no `<thead>` then do nothing
while (--i >= 0)(function(i) {
var dir = 1;
th[i].addEventListener('click', function() {
sortTable(table, i, (dir = 1 - dir))
});
}(i));
}
function makeAllSortable(parent) {
parent = parent || document.body;
var t = parent.getElementsByTagName('table'),
i = t.length;
while (--i >= 0) makeSortable(t[i]);
}
window.onload = function() {
makeAllSortable();
};
<table class="table table-striped table-bordered table-hover" id="Tabla">
<thead>
<tr style="cursor:pointer">
<th>Fruit<span class="glyphicon pull-right" aria-hidden="true"></span></th>
<th>Number<span class="glyphicon pull-right" aria-hidden="true" data-sort="numerically"></span></th>
</tr>
</thead>
<tr>
<td>Apple</td>
<td>11757.915</td>
</tr>
<tr>
<td>Orange</td>
<td>36407.996</td>
</tr>
<tr>
<td>Watermelon</td>
<td>9115.118</td>
</tr>
</table>

Related

How to sort table by date column

I have a table and I want to sort Date column ascending and descending.
I have tried the code below but its not working. Its work when sorting the numbers only.
function sortColumn() {
var table, rows, switching, i, x, y, shouldSwitch;
table = document.getElementById("example");
switching = true;
while (switching) {
switching = false;
rows = table.rows;
for (i = 1; i < (rows.length - 1); i++) {
shouldSwitch = false;
x = rows[i].getElementsByTagName("TD")[6];
y = rows[i + 1].getElementsByTagName("TD")[6];
if (parseInt(x.innerHTML.toLowerCase()) > parseInt(y.innerHTML.toLowerCase())) {
shouldSwitch = true;
break;
}
}
if (shouldSwitch) {
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
}
}
}
Here is a more complex example than you might have been looking for. It makes use of the Array Sort function, which can make use of an optional compareFunction to perform the sort comparison.
$(function() {
function tableToArray(tObj) {
var result = [];
var keys = [];
$("thead th", tObj).each(function(i, el) {
keys.push($(el).text().trim());
});
$("tbody tr").each(function(i, row) {
var temp = {};
$.each(keys, function(j, k) {
temp[k] = $("td", row).eq(j).text().trim();
});
result.push(temp);
});
return result;
}
function replaceTableData(tObj, data) {
var b = $("tbody", tObj);
b.html("");
$.each(data, function(i, row) {
var r = $("<tr>").appendTo(b);
$.each(row, function(j, cell) {
$("<td>").html(cell).appendTo(r);
});
});
}
function compare(a, b) {
var dateA = a.Date;
var dateB = b.Date;
var result = 0;
if (dateA > dateB) {
result = 1;
} else {
result = -1;
}
return result;
}
function sortTable() {
var tData = tableToArray($("table"));
tData.sort(compare);
replaceTableData($("table"), tData);
}
$(".sort-column").click(sortTable);
});
.sort-column {
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<th>ID</th>
<th class="sort-column" data-sort-order="null">Date</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>17/12/1989</td>
<td>Homer</td>
</tr>
<tr>
<td>2</td>
<td>07/09/2019</td>
<td>Marge</td>
</tr>
<tr>
<td>3</td>
<td>01/09/2019</td>
<td>Bart</td>
</tr>
<tr>
<td>4</td>
<td>04/09/2019</td>
<td>Lisa</td>
</tr>
<tr>
<td>5</td>
<td>14/09/2019</td>
<td>Maggie</td>
</tr>
</tbody>
</table>
This sorts based on the text in the Date column. You could get more complex and parse the Date into a Date Object when you compare. But with this date format, it's not really needed.
Update
Moved code into function so it can be called in a Click event. This is a really simple example and if you need more complex actions, consider how that might work or DataTables.

Wrong array method sort() logic

I Cannot understand the array method sort() logic. I had to write an eventListener for the two elements Age and Letter. By clicking on them we can sort our table by age and letter.
All works fine, but I see something strange in the sort() logic. By clicking on the Letter - table must sort by alphabet for elements in the column Letter. By clicking on the Age - table must sort by digits order for elements in the column Age. But it does not sort right.
tbody = document.getElementById('grid');
function tableSort(event) {
var target = event.target;
var action = target.getAttribute('data-type');
var arr = [].slice.call(grid.rows, 1);
var self = this;
this.number = function() {
arr.sort(function(a, b) { // sort by digits in the column "Age"
a.cells[0].innerHTML;
b.cells[0].innerHTML;
return a - b;
});
grid.appendChild(...arr);
}
this.string = function() {
arr.sort(function(a, b) { // sort by words in the column "Letter"
a.cells[1].innerHTML;
b.cells[1].innerHTML;
return a > b;
});
grid.appendChild(...arr);
}
if (action) {
self[action]();
}
}
tbody.addEventListener('click', tableSort);
th {
cursor: pointer;
}
<table id="grid">
<thead>
<tr>
<th data-type="number">Age</th>
<th data-type="string">Letter</th>
</tr>
</thead>
<tbody>
<tr>
<td>5</td>
<td>BBBBB</td>
</tr>
<tr>
<td>12</td>
<td>AAAAA</td>
</tr>
<tr>
<td>1</td>
<td>DDDDD</td>
</tr>
<tr>
<td>9</td>
<td>CCCCC</td>
</tr>
<tr>
<td>2</td>
<td>KKKKK</td>
</tr>
</tbody>
</table>
<script>
</script>
Modified your code and got it working. Here if you need it:
function tableSort(event) {
var target = event.target;
var action = target.getAttribute("data-type");
var arr = [].slice.call(grid.rows, 1);
var self = this;
this.number = function() {
arr.sort(function(a, b) {
// sort by digits in the column "Age"
return Number(a.cells[0].innerHTML) - Number(b.cells[0].innerHTML);
});
arr.forEach(function(item, index) {
grid.appendChild(item);
});
};
this.string = function() {
arr.sort(function(a, b) {
// sort by words in the column "Letter"
var str1 = a.cells[1].innerHTML;
var str2 = b.cells[1].innerHTML;
return str1.localeCompare(str2);
});
arr.forEach(function(item, index) {
grid.appendChild(item);
});
};
if (action) {
self[action]();
}
}
tbody.addEventListener("click", tableSort);
How about this stackoverflow post Sorting HTML table with JavaScript for clarification and the original external article in which I found it with a full example?
Sorting tables with VanillaJS or JQuery
Example:
/**
* Modified and more readable version of the answer by Paul S. to sort a table with ASC and DESC order
* with the <thead> and <tbody> structure easily.
*
* https://stackoverflow.com/a/14268260/4241030
*/
var TableSorter = {
makeSortable: function(table){
// Store context of this in the object
var _this = this;
var th = table.tHead, i;
th && (th = th.rows[0]) && (th = th.cells);
if (th){
i = th.length;
}else{
return; // if no `<thead>` then do nothing
}
// Loop through every <th> inside the header
while (--i >= 0) (function (i) {
var dir = 1;
// Append click listener to sort
th[i].addEventListener('click', function () {
_this._sort(table, i, (dir = 1 - dir));
});
}(i));
},
_sort: function (table, col, reverse) {
var tb = table.tBodies[0], // use `<tbody>` to ignore `<thead>` and `<tfoot>` rows
tr = Array.prototype.slice.call(tb.rows, 0), // put rows into array
i;
reverse = -((+reverse) || -1);
// Sort rows
tr = tr.sort(function (a, b) {
// `-1 *` if want opposite order
return reverse * (
// Using `.textContent.trim()` for test
a.cells[col].textContent.trim().localeCompare(
b.cells[col].textContent.trim()
)
);
});
for(i = 0; i < tr.length; ++i){
// Append rows in new order
tb.appendChild(tr[i]);
}
}
};
window.onload = function(){
TableSorter.makeSortable(document.getElementById("myTable"));
};
table thead th {
cursor: pointer;
}
<table id="myTable">
<thead>
<th data-type="string">Name</th>
<th data-type="number">Age</th>
</thead>
<tbody>
<tr>
<td>John</td>
<td>42</td>
</tr>
<tr>
<td>Laura</td>
<td>39</td>
</tr>
<tr>
<td>Fred</td>
<td>18</td>
</tr>
<tr>
<td>Bod</td>
<td>26</td>
</tr>
</tbody>
</table>

How to display multiple cells in Table Search

I'm wondering if there is a way for this search bar that I'm using to display multiple elements. As it is right now if I search for "123" in a pool with "321" "1234" "123" "12345" The only displayed value would be the first one: "1234". I'd like for all values that match my search to be displayed, therefore this would be the correct search result: "1234" "123" "12345".
Any answer is appreciated.
Here's the current code that I have:
var cells = document.querySelectorAll("#myTable td");
var search = document.getElementById("myInput");
search.addEventListener("keyup", function() {
if (search.value.length > 0 && search.value != '') {
for (var i = 0; i < cells.length; ++i) {
if (cells[i].textContent.toLowerCase().indexOf(search.value.toLowerCase()) === 0) {
cells.forEach(function(element) {
element.style.display = "none";
});
cells[i].style.display = "table-cell";
break;
} else {
cells.forEach(function(element) {
if (cells[i] !== element) {
element.style.display = "table-cell";
}
});
}
}
} else {
cells.forEach(function(element) {
if (cells[i] !== element) {
element.style.display = "table-cell";
}
});
}
});
<input id="myInput">
<table id="myTable">
<tr>
<td>321</td>
<td>123</td>
</tr>
<tr>
<td>1234</td>
<td>abc</td>
</tr>
<tr>
<td>12345</td>
<td>abcde</td>
</tr>
</table>
Your cells selector returns a nodelist this is an arrayish object. That doesn't have the forEach function.
However we can borrow from the Array object:
Array.prototype.forEach
What I did to solve the other problem is create an indexArray as a lookup array. that keeps track of the indices that contained the search string. Then when we loop all the cells we can turn the ones of that don't show up in the lookup array
var cells = document.querySelectorAll("#myTable td");
var search = document.getElementById("myInput");
search.addEventListener("keyup", function() {
var indexArray = []; //look up array
for (var i = 0; i < cells.length; ++i) {
//restore the cells:
cells[i].style.display = "table-cell";
//if search value is found the value will be 0 if it starts a the beginning
if (cells[i].textContent.toLowerCase().indexOf(search.value.toLowerCase()) === 0) {
indexArray.push(i); //push index into lookup
}
}
//loop over all cells
Array.prototype.forEach.call(cells, function(element, index) {
if (indexArray.indexOf(index) === -1) //if index is not present in look up, set display to none
element.style.display = "none";
});
});
<input id="myInput">
<table id="myTable">
<tr>
<td>321</td>
<td>123</td>
</tr>
<tr>
<td>1234</td>
<td>abc</td>
</tr>
<tr>
<td>12345</td>
<td>abcde</td>
</tr>
</table>
below code is enough if you want to show which cell has contain that search; also you can test on jsfiddle https://jsfiddle.net/bzcdomjs/
var cells = document.querySelectorAll("#myTable td");
var search = document.getElementById("myInput");
search.addEventListener("keyup", function() {
for (var i = 0; i < cells.length; ++i) {
cells[i].style.display = "table-cell";
if (search.value.length > 0 && search.value != '') {
if(cells[i].textContent.toLowerCase().indexOf(search.value.toLowerCase()) === -1) {
cells[i].style.display = "none";
}
}
});

jQuery count of specific class an element contains

I have a table and each row in the table has one or more classes depending on the region.
Here is what my table looks like:
<table>
<thead>
<th>Title</th>
<th>Name</th>
</thead>
<tbody>
<tr class="emea apac">
<td>Testing</td>
<td>Bob</td>
</tr>
<tr class="americas">
<td>Testing2</td>
<td>Jim</td>
</tr>
<tr class="emea">
<td>Testing 3</td>
<td>Kyle</td>
</tr>
<tr class="emea americas">
<td>Testing 3</td>
<td>Kyle</td>
</tr>
<tr class="emea apac americas">
<td>Testing 3</td>
<td>Kyle</td>
</tr>
<tr class="apac">
<td>Testing 3</td>
<td>Kyle</td>
</tr>
</tbody>
I am trying to now count specifically how many rows there are where the class is equal to my condition.
For example:
How many rows have ONLY .APAC = 1
How many rows have all 3 of the possible classes? = 1
I started this jsFiddle but couldn't really think of how to approach it from this point: http://jsfiddle.net/carlhussey/gkywznnj/4/
Working from your fiddle (updated)...
$(document).ready(function () {
var apac = 0,
emea = 0,
americas = 0,
combo = 0,
all = 0;
$('table tbody tr').each(function (i, elem) {
var classes = elem.className.split(' '),
hasApac = classes.indexOf('apac') > -1,
hasEmea = classes.indexOf('emea') > -1,
hasAmericas = classes.indexOf('americas') > -1;
apac += (hasApac && !hasEmea && !hasAmericas) ? 1 : 0;
emea += (hasEmea && !hasApac && !hasAmericas) ? 1 : 0;
americas += (hasAmericas && !hasApac && !hasEmea) ? 1 : 0;
if (((hasApac && hasEmea) || (hasApac && hasAmericas) || (hasEmea && hasAmericas)) && classes.length === 2) {
combo += 1;
}
if (hasApac && hasEmea && hasAmericas) {
all += 1;
}
});
$('span[name="apac"]').text(apac);
$('span[name="emea"]').text(emea);
$('span[name="americas"]').text(americas);
$('span[name="combo"]').text(combo);
$('span[name="all"]').text(all);
});
UPDATE
I'm pretty sure jQuery's hasClass method works with IE8, so you could change the .each callback to:
function (i, elem) {
var row = $(elem),
hasApac = row.hasClass('apac'),
hasEmea = row.hasClass('emea'),
hasAmericas = row.hasClass('americas');
apac += (hasApac && !hasEmea && !hasAmericas) ? 1 : 0;
emea += (hasEmea && !hasApac && !hasAmericas) ? 1 : 0;
americas += (hasAmericas && !hasApac && !hasEmea) ? 1 : 0;
if (((hasApac && hasEmea) || (hasApac && hasAmericas) || (hasEmea && hasAmericas)) && elem.className.split(' ').length === 2) {
combo += 1;
}
if (hasApac && hasEmea && hasAmericas) {
all += 1;
}
}
Updated fiddle: http://jsfiddle.net/gkywznnj/6/
var rows = $('tr'),
class = 'americas',
counter = 0;
rows.each(function () {
//If current element have .americas increment counter
if($(this).hasClass(class)) {
counter +=1
}
});
console.log(counter);
http://jsfiddle.net/gkywznnj/8/
Object.size = function(obj) {
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) size++;
}
return size;
};
function countOnlyClass(classFindA)
{
var $trA=$('table tbody tr');
var count=0;
if(classFindA.length>0){
$trA.each(function(){
var c=0;
var m=0;
var $tr=$(this);
var classA = $tr.attr('class').split(' ');
$.each(classA,function(i,cl){
if(classFindA.indexOf(cl)>-1) c++; else m++;
})
if(c>0 && c==classFindA.length && m==0) count++;
})
}
return count;
}
function comboOnlyClass(comboCount)
{
var $trA=$('table tbody tr');
var count=0;
$trA.each(function(){
var countClass = {};
var $tr=$(this);
var classA = $tr.attr('class').split(' ');
$.each(classA,function(i,cl){
if(!cl in countClass )
countClass.cl=1;
})
if(Object.size(classA )==comboCount) count++;
})
return count;
}
var a=countOnlyClass(['apac'])
$('#apac').html(a);
var a=countOnlyClass(['emea'])
$('#emea').html(a);
var a=countOnlyClass(['americas'])
$('#americas').html(a);
var a=countOnlyClass(['apac','emea','americas'])
$('#all').html(a);
var a=comboOnlyClass(2);
$('#combo').html(a);
//var a=comboOnlyClass(1); onlu one class

Iterate over table cells, re-using rowspan values

I have a simple HTML table, which uses rowspans in some random columns. An example might look like
A | B |
---|---| C
D | |
---| E |---
F | | G
I'd like to iterate over the rows such that I see rows as A,B,C, D,E,C, then F,E,G.
I think I can probably cobble together something very convoluted using cell.index() to check for "missed" columns in later rows, but I'd like something a little more elegant...
without jquery:
function tableToMatrix(table) {
var M = [];
for (var i = 0; i < table.rows.length; i++) {
var tr = table.rows[i];
M[i] = [];
for (var j = 0, k = 0; j < M[0].length || k < tr.cells.length;) {
var c = (M[i-1]||[])[j];
// first check if there's a continuing cell above with rowSpan
if (c && c.parentNode.rowIndex + c.rowSpan > i) {
M[i].push(...Array.from({length: c.colSpan}, () => c))
j += c.colSpan;
} else if (tr.cells[k]) {
var td = tr.cells[k++];
M[i].push(...Array.from({length: td.colSpan}, () => td));
j += td.colSpan;
}
}
}
return M;
}
var M = tableToMatrix(document.querySelector('table'));
console.table(M.map(r => r.map(c => c.innerText)));
var pre = document.createElement('pre');
pre.innerText = M.map(row => row.map(c => c.innerText).join('\t')).join('\n');
document.body.append(pre);
td {
border: 1px solid rgba(0,0,0,.3);
}
<table>
<tr>
<td colspan=2>A</td>
<td rowspan=2>B</td>
</tr>
<tr>
<td>C</td>
<td rowspan=3>D</td>
</tr>
<tr>
<td rowspan=2>E</td>
<td rowspan=4>F</td>
</tr>
<tr></tr>
<tr>
<td rowspan=2 colspan=2>G</td>
</tr>
<tr></tr>
<tr>
<td rowspan=3 colspan=3>H</td>
</tr>
<tr></tr>
<tr></tr>
<tr>
<td colspan=3>I</td>
</tr>
</table>
Try this:
<table id="tbl">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td colspan="2" rowspan="2">A</td>
<td rowspan="2">C</td>
</tr>
<tr>
<td rowspan="2">E</td>
</tr>
<tr>
<td>F</td>
<td>G</td>
</tr>
</table>
Script:
var finalResult = '';
var totalTds = $('#tbl TR')[0].length;
var trArray = [];
var trArrayValue = [];
var trIndex = 1;
$('#tbl TR').each(function(){
var currentTr = $(this);
var tdIndex = 1;
trArray[trIndex] = [];
trArrayValue[trIndex] = [];
var tdActuallyTraversed = 0;
var colspanCount = 1;
$('#tbl TR').first().children().each(function(){
if(trIndex > 1 && trArray[trIndex - 1][tdIndex] > 1)
{
trArray[trIndex][tdIndex] = trArray[trIndex - 1][tdIndex] - 1;
trArrayValue[trIndex][tdIndex] = trArrayValue[trIndex - 1][tdIndex];
finalResult = finalResult + trArrayValue[trIndex][tdIndex];
}
else
{
if(colspanCount <= 1)
{
colspanCount = currentTr.children().eq(tdActuallyTraversed).attr('colspan') != undefined ? currentTr.children().eq(tdActuallyTraversed).attr('colspan') : 1;
}
if(colspanCount > 1 && tdIndex > 1)
{
trArray[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed + colspanCount).attr('rowspan') != undefined ?currentTr.children().eq(tdActuallyTraversed + colspanCount).attr('rowspan') : 1;
trArrayValue[trIndex][tdIndex] = trArrayValue[trIndex][tdIndex - 1];
colspanCount--;
}
else
{
trArray[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed).attr('rowspan') != undefined ?currentTr.children().eq(tdActuallyTraversed).attr('rowspan') : 1;
trArrayValue[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed).html();
tdActuallyTraversed++;
}
finalResult = finalResult + trArrayValue[trIndex][tdIndex];
}
tdIndex++;
});
trIndex++;
});
alert(finalResult);
Fiddle
i am not sure about the performance, but it works well.
what I understood with your question is: You want to split the merged cell with same value and then iterate the table simply by row.
I've created a JSFiddle that will split the merged cells with the same value. Then you'll have a table that can be iterated simply by rows to get the desired output that you specified.
See it running here http://jsfiddle.net/9PZQj/3/
Here's the complete code:
<table id="tbl" border = "1">
<tr>
<td>A</td>
<td>B</td>
<td rowspan="2">C</td>
</tr>
<tr>
<td>D</td>
<td rowspan="2">E</td>
</tr>
<tr>
<td>F</td>
<td>G</td>
</tr>
</table>
<br>
<div id="test"> </div>
Here's the jquery that is used to manipulate the table's data.
var tempTable = $('#tbl').clone(true);
var tableBody = $(tempTable).children();
$(tableBody).children().each(function(index , item){
var currentRow = item;
$(currentRow).children().each(function(index1, item1){
if($(item1).attr("rowspan"))
{
// copy the cell
var item2 = $(item1).clone(true);
// Remove rowspan
$(item1).removeAttr("rowspan");
$(item2).removeAttr("rowspan");
// last item's index in next row
var indexOfLastElement = $(currentRow).next().last().index();
if(indexOfLastElement <= index1)
{
$(currentRow).next().append(item2)
}
else
{
// intermediate cell insertion at right position
$(item2).insertBefore($(currentRow).next().children().eq(index1))
}
}
});
console.log(currentRow)
});
$('#test').append(tempTable);
You can use this Gist. It supports all the requirements by W3C, even "rowspan=0" (which seems to be only supported by Firefox).

Categories

Resources