1
0
Fork 0
mirror of https://github.com/koalyptus/TableFilter.git synced 2024-04-25 11:40:47 +02:00
TableFilter/test/test-api.js
2017-11-01 22:11:24 +11:00

796 lines
24 KiB
JavaScript

var tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
col_types: ['string', 'string', 'number', 'number', 'number']
});
tf.init();
var tf1 = new TableFilter('demo1', {
base_path: '../dist/tablefilter/',
col_types: ['string', 'string', 'number', 'number', 'number'],
extensions: [{
name: 'colsVisibility',
at_start: [1, 2],
on_loaded: colsVisibilityTests
}]
});
tf1.registerExtension(
{
init: function() {},
destroy: function() {}
},
'myExtension'
);
tf1.init();
module('Sanity checks');
test('TableFilter object', function() {
deepEqual(tf instanceof TableFilter, true, 'TableFilter instanciated');
deepEqual(tf.id, 'demo', 'id check');
deepEqual(tf.getFiltersRowIndex(), 0, 'Filters row index');
deepEqual(tf.getHeadersRowIndex(), 1, 'Headers row index');
deepEqual(tf.getCellsNb(), 5, 'Cells collection length');
deepEqual(tf.getRowsNb(), 7, 'Rows collection length');
deepEqual(tf.getFilterableRowsNb(), 7, 'Number of filterable rows');
deepEqual(tf.getFilterId(0), 'flt0_demo', 'Filter DOM element id');
deepEqual(tf.getStartRowIndex(), 2, 'Start of filterable rows');
deepEqual(tf.getLastRowIndex(), 8, 'Last row index');
deepEqual(tf.dom().nodeName, 'TABLE', 'Working DOM element type');
deepEqual(
tf.getHeadersText(),
[
'From','Destination','Road Distance (km)', 'By Air (hrs)',
'By Rail (hrs)'
],
'Headers text');
deepEqual(
tf.getCellValue(tf.getHeaderElement(1)),
'Destination',
'Column header text'
);
deepEqual(tf.getValidRowsNb(), 0, 'Number of valid rows before filtering');
deepEqual(
tf.getCellData(tf.dom().rows[3].cells[2]),
982,
'getCellData returns typed value'
);
});
module('Public methods');
test('Filters DOM elements', function() {
deepEqual(tf.getFilterElement(1).nodeName, 'INPUT',
'Filter\'s DOM element');
deepEqual(tf.getHeaderElement(2).nodeName, 'TH',
'Header\'s DOM element');
});
test('Get filters values', function() {
equal(tf.getFilterValue(0), '', 'Column 0 filter value');
tf.setFilterValue(0, 'Syd');
tf.setFilterValue(1, 'Ade');
deepEqual(tf.getFilterValue(0), 'Syd', 'Column 0 filter value');
deepEqual(tf.getFiltersValue(), ['Syd', 'Ade', '', '', '']);
});
test('Filter table', function() {
tf.clearFilters();
deepEqual(tf.getFilterValue(0), '',
'Column 0 filter value after clearing filters');
tf.setFilterValue(0, 'Syd');
tf.filter();
deepEqual(tf.getValidRowsNb(), 4, 'Filtered rows number');
});
test('Filter table with range', function() {
tf.clearFilters();
tf.setFilterValue(2, '>1500 && <=2871');
tf.filter();
deepEqual(tf.getValidRows().length, 3, 'Filtered rows number');
});
test('Filter table with operator', function() {
tf.clearFilters();
tf.setFilterValue(1, '{Bri');
tf.filter();
deepEqual(tf.getValidRows().length, 2, 'Filtered rows number');
});
test('Get active filter ID', function() {
//setup
tf.getFilterElement(0).focus();
//act
var id = tf.getActiveFilterId();
//assert
deepEqual(id, 'flt0_demo', 'Active filter ID');
});
test('Set active filter ID', function() {
//act
tf.setActiveFilterId('hello');
//assert
deepEqual(tf.getActiveFilterId(), 'hello', 'Active filter ID');
});
test('Get column index from filter ID', function() {
//set
var filterId = 'flt3_demo';
//act
var colIndex = tf.getColumnIndexFromFilterId(filterId);
//assert
deepEqual(colIndex, 3, 'Column index');
});
test('Activate filter for a specified column', function() {
//set
var filterId = 'flt3_demo';
//act
tf.activateFilter(3);
//assert
deepEqual(tf.getActiveFilterId(), filterId, 'Filter active');
});
test('Activate filter not called if colIndex not a number', function() {
//set
var originalSetActiveFilterId = tf.setActiveFilterId;
var hit = 0;
tf.setActiveFilterId = function() { hit++; };
//act
tf.activateFilter(undefined);
//assert
deepEqual(hit, 0, 'setActiveFilterId never called');
tf.setActiveFilterId = originalSetActiveFilterId;
});
test('Clear filters', function() {
tf.clearFilters();
deepEqual(tf.getValidRowsNb(), 7, 'Filtered rows number');
deepEqual(tf.getFiltersValue(), ['', '', '', '', '']);
});
test('Can get feature', function() {
var feature = tf.feature('help');
deepEqual(typeof feature, 'object', 'Feature instance');
deepEqual(feature.feature, 'help', 'Feature name');
});
test('can iterate columns', function() {
// setup
var counter = [];
// act
tf.eachCol(
function(idx) {
counter.push(idx);
},
function(idx) {
return idx === 2;
},
function(idx) {
return idx === 4;
}
);
// assert
deepEqual(counter, [0, 1, 3], 'column iterator conditions met');
});
test('Get table data', function() {
deepEqual(tf.getColValues(0),
[
'Sydney','Sydney','Sydney',
'Sydney','Adelaide','Adelaide','Adelaide'
],
'Get specified column values'
);
deepEqual(tf.getColValues(0, true),
[
'From','Sydney','Sydney','Sydney',
'Sydney','Adelaide','Adelaide','Adelaide'
],
'Get specified column values including column header'
);
deepEqual(
tf.getColValues(2, false, true),
[1412,982,286,872,2781,1533,2045],
'Get specified column typed values'
);
deepEqual(
tf.getTableData(),
[
[2, ['Sydney','Adelaide','1412','1.4','25.3']],
[3, ['Sydney','Brisbane','982','1.5','16']],
[4, ['Sydney','Canberra','286','.6','4.3']],
[5, ['Sydney','Melbourne','872','1.1','10.5']],
[6, ['Adelaide','Perth','2781','3.1','38']],
[7, ['Adelaide','Alice Springs','1533','2','20.25']],
[8, ['Adelaide','Brisbane','2045','2.15','40']]
],
'Get table data'
);
deepEqual(
tf.getTableData(true),
[
[1, ['From','Destination','Road Distance (km)',
'By Air (hrs)','By Rail (hrs)']],
[2, ['Sydney','Adelaide','1412','1.4','25.3']],
[3, ['Sydney','Brisbane','982','1.5','16']],
[4, ['Sydney','Canberra','286','.6','4.3']],
[5, ['Sydney','Melbourne','872','1.1','10.5']],
[6, ['Adelaide','Perth','2781','3.1','38']],
[7, ['Adelaide','Alice Springs','1533','2','20.25']],
[8, ['Adelaide','Brisbane','2045','2.15','40']]
],
'Get table data including columns headers'
);
deepEqual(
tf.getTableData(false, false, true),
[
[2, ['Sydney','Adelaide',1412,1.4,25.3]],
[3, ['Sydney','Brisbane',982,1.5,16]],
[4, ['Sydney','Canberra',286,0.6,4.3]],
[5, ['Sydney','Melbourne',872,1.1,10.5]],
[6, ['Adelaide','Perth',2781,3.1,38]],
[7, ['Adelaide','Alice Springs',1533,2,20.25]],
[8, ['Adelaide','Brisbane',2045,2.15,40]]
],
'Get table typed data'
);
tf.setFilterValue(0, 'Adelaide');
tf.filter();
deepEqual(
tf.getFilteredValues(),
[
[6, ['Adelaide','Perth','2781','3.1','38']],
[7, ['Adelaide','Alice Springs','1533','2','20.25']],
[8, ['Adelaide','Brisbane','2045','2.15','40']]
],
'Get filtered table data'
);
deepEqual(
tf.getFilteredValues(true),
[
[1, ['From','Destination','Road Distance (km)',
'By Air (hrs)','By Rail (hrs)']],
[6, ['Adelaide','Perth','2781','3.1','38']],
[7, ['Adelaide','Alice Springs','1533','2','20.25']],
[8, ['Adelaide','Brisbane','2045','2.15','40']]
],
'Get filtered table values including columns headers'
);
deepEqual(
tf.getFilteredData(),
[
[6, ['Adelaide','Perth',2781,3.1,38]],
[7, ['Adelaide','Alice Springs',1533,2,20.25]],
[8, ['Adelaide','Brisbane',2045,2.15,40]]
],
'Get filtered typed data'
);
deepEqual(
tf.getFilteredDataCol(0),
['Adelaide','Adelaide','Adelaide'],
'Get specified column filtered values'
);
deepEqual(
tf.getFilteredDataCol(0, true),
['From','Adelaide','Adelaide','Adelaide'],
'Get specified column filtered values including header'
);
deepEqual(
tf.getFilteredDataCol(2, false, true),
[2781,1533,2045],
'Get specified column filtered typed values'
);
deepEqual(
tf.getFilteredColumnData(2),
[2781,1533,2045],
'Get filtered column data'
);
deepEqual(
tf.getFilteredColumnData(2, true),
['Road Distance (km)',2781,1533,2045],
'Get filtered column data with headers'
);
deepEqual(
tf.getVisibleColumnData(2),
[2781,1533,2045],
'Get visible column data'
);
deepEqual(
tf.getVisibleColumnData(2, true),
['Road Distance (km)',2781,1533,2045],
'Get visible column data with headers'
);
deepEqual(
tf.getFilteredColumnValues(2),
['2781','1533','2045'],
'Get filtered column data'
);
deepEqual(
tf.getFilteredColumnValues(2, true),
['Road Distance (km)','2781','1533','2045'],
'Get filtered column data with headers'
);
deepEqual(
tf.getVisibleColumnValues(2),
['2781','1533','2045'],
'Get visible column data'
);
deepEqual(
tf.getVisibleColumnValues(2, true),
['Road Distance (km)','2781','1533','2045'],
'Get visible column data with headers'
);
tf.clearFilters();
tf.filter();
});
test('Destroy', function() {
tf.destroy();
deepEqual(tf.isInitialized(), false, 'Filters removed');
tf = null;
});
module('TableFilter instance with different filter types');
test('Test filter types', function() {
tf = null;
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
col_0: 'select',
col_1: 'multiple',
col_2: 'checklist',
col_types: ['string', 'string', 'number', 'number', 'number']
});
tf.init();
deepEqual(tf.getFilterType(0), 'select', 'Colmun 0 filter type');
deepEqual(tf.getFilterType(1), 'multiple', 'Colmun 1 filter type');
deepEqual(tf.getFilterType(2), 'checklist', 'Colmun 2 filter type');
deepEqual(tf.getFilterType(3), 'input', 'Colmun 3 filter type');
deepEqual(tf.getFiltersByType('select'), ['flt0_demo'],
'Get select filter(s)');
deepEqual(tf.getFiltersByType('multiple'), ['flt1_demo'],
'Get multiple filter(s)');
deepEqual(tf.getFiltersByType('checklist'), ['flt2_demo'],
'Get checklist filter(s)');
deepEqual(tf.getFiltersByType('input'), ['flt3_demo','flt4_demo'],
'Get input filter(s)');
});
test('Get filters values', function() {
tf.setFilterValue(0, 'Sydney');
tf.setFilterValue(1, 'Adelaide');
deepEqual(tf.getFilterValue(0), 'Sydney', 'Column 0 filter value');
deepEqual(tf.getFiltersValue(),
['Sydney', ['Adelaide'], '', '', '']);
});
test('Filter table', function() {
tf.clearFilters();
tf.setFilterValue(1, 'Alice Springs');
tf.filter();
deepEqual(tf.getValidRows().length, 1, 'Filtered rows number');
});
test('Can filter table with multiple filter', function() {
tf.clearFilters();
tf.setFilterValue(1, ['Alice Springs', 'Canberra']);
tf.filter();
deepEqual(tf.getValidRows().length, 2, 'Filtered rows number');
});
test('Can filter table with checklist filter', function() {
tf.clearFilters();
tf.setFilterValue(2, ['2045', '2781']);
tf.filter();
deepEqual(tf.getValidRows().length, 2, 'Filtered rows number');
});
test('Can select dropdown options with or operator', function() {
tf.setFilterValue(1, '');
tf.setFilterValue(1, 'Brisbane || Melbourne');
deepEqual(tf.getFilterValue(1), ['Brisbane', 'Melbourne'],
'Column 2 filter values');
});
test('Can select dropdown options with array', function() {
tf.setFilterValue(1, '');
tf.setFilterValue(1, ['Canberra', 'Perth']);
deepEqual(tf.getFilterValue(1), ['Canberra', 'Perth'],
'Column 2 filter values');
});
test('Can select checklist options', function() {
tf.setFilterValue(2, '2045 || 2781');
deepEqual(tf.getFilterValue(2), ['2045', '2781'],
'Column 2 filter values');
});
test('Can select checklist options with array', function() {
tf.setFilterValue(2, '');
tf.setFilterValue(2, ['1412', '982']);
deepEqual(tf.getFilterValue(2), ['982', '1412'],
'Column 2 filter values');
});
test('get and set filter value can handle out of range column index',
function() {
// act
tf.setFilterValue(11, '');
// assert
deepEqual(tf.getFilterValue(11), '',
'return empty string for inexistent filter');
});
module('TableFilter with pop-up filters');
test('Sanity checks', function() {
tf.destroy();
tf = null;
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
popup_filters: true,
col_types: ['string', 'string', 'number', 'number', 'number']
});
tf.init();
deepEqual(tf.getFiltersRowIndex(), 0, 'Filters row index');
deepEqual(tf.getHeadersRowIndex(), 1, 'Headers row index');
deepEqual(tf.getFilterElement(1).nodeName, 'INPUT',
'Filter\'s DOM element');
deepEqual(tf.getHeaderElement(2).nodeName, 'TH',
'Header\'s DOM element');
});
test('Get filters values', function() {
tf.setFilterValue(0, 'Sydney');
tf.setFilterValue(1, 'Adelaide');
deepEqual(tf.getFilterValue(0), 'Sydney', 'Column 0 filter value');
deepEqual(tf.getFiltersValue(), ['Sydney', 'Adelaide', '', '', '']);
});
// Test case for issue 165
test('Test getCellValue with white spaces', function() {
// setup
var cell = document.createElement('td');
cell.textContent ='\t\thello world\t\t\t';
// act
var result = tf.getCellValue(cell);
//assert
deepEqual(result, 'hello world', 'Expected text with no white spaces');
});
test('Filter table', function() {
tf.clearFilters();
tf.setFilterValue(1, 'Adelaide');
tf.filter();
deepEqual(tf.getValidRows().length, 1, 'Filtered rows number');
});
module('TableFilter with grid-layout');
test('Sanity checks', function() {
tf.destroy();
tf = null;
tf = new TableFilter('demo', {
base_path: '../dist/tablefilter/',
grid_layout: true,
col_0: 'select',
col_1: 'multiple',
col_2: 'checklist',
col_types: ['string', 'string', 'number', 'number', 'number']
});
tf.init();
deepEqual(tf.getFilterType(0), 'select', 'Colmun 0 filter type');
deepEqual(tf.getFilterType(1), 'multiple', 'Colmun 1 filter type');
deepEqual(tf.getFilterType(2), 'checklist', 'Colmun 2 filter type');
deepEqual(tf.getFilterType(3), 'input', 'Colmun 3 filter type');
deepEqual(tf.getFiltersByType('select'), ['flt0_demo'],
'Get select filter(s)');
deepEqual(tf.getFiltersByType('multiple'), ['flt1_demo'],
'Get multiple filter(s)');
deepEqual(tf.getFiltersByType('checklist'), ['flt2_demo'],
'Get checklist filter(s)');
deepEqual(tf.getFiltersByType('input'), ['flt3_demo','flt4_demo'],
'Get input filter(s)');
deepEqual(tf.getFiltersRowIndex(), 1, 'Filters row index');
deepEqual(tf.getHeadersRowIndex(), 0, 'Headers row index');
deepEqual(tf.getFilterElement(1).nodeName, 'SELECT',
'Filter\'s DOM element');
deepEqual(tf.getHeaderElement(2).nodeName, 'TH',
'Header\'s DOM element');
});
test('Get filters values', function() {
tf.setFilterValue(0, 'Sydney');
tf.setFilterValue(1, 'Adelaide');
deepEqual(tf.getFilterValue(0), 'Sydney', 'Column 0 filter value');
deepEqual(tf.getFiltersValue(), ['Sydney', ['Adelaide'], '', '', '']);
});
test('Filter table', function() {
tf.clearFilters();
tf.setFilterValue(1, 'Adelaide');
tf.filter();
deepEqual(tf.getValidRows().length, 1, 'Filtered rows number');
});
test('Clear filters', function() {
tf.clearFilters();
deepEqual(tf.getValidRowsNb(), 7, 'Filtered rows number');
deepEqual(tf.getFiltersValue(), ['', '', '', '', '']);
});
test('Get table data', function() {
deepEqual(tf.getColValues(0),
[
'Sydney','Sydney','Sydney',
'Sydney','Adelaide','Adelaide','Adelaide'
],
'Get specified column values'
);
deepEqual(tf.getColValues(0, true),
[
'From','Sydney','Sydney','Sydney',
'Sydney','Adelaide','Adelaide','Adelaide'
],
'Get specified column values including column header'
);
deepEqual(tf.getColumnValues(0),
[
'Sydney','Sydney','Sydney',
'Sydney','Adelaide','Adelaide','Adelaide'
],
'Get specified column values'
);
deepEqual(tf.getColumnValues(0, true),
[
'From','Sydney','Sydney','Sydney',
'Sydney','Adelaide','Adelaide','Adelaide'
],
'Get specified column values including column header'
);
deepEqual(tf.getColumnData(2),
[1412, 982, 286, 872, 2781, 1533, 2045],
'Get specified column data'
);
deepEqual(tf.getColumnData(2, true),
['Road Distance (km)', 1412, 982, 286, 872, 2781, 1533, 2045],
'Get specified column data including column header'
);
deepEqual(
tf.getTableData(),
[
[0, ['Sydney','Adelaide','1412','1.4','25.3']],
[1, ['Sydney','Brisbane','982','1.5','16']],
[2, ['Sydney','Canberra','286','.6','4.3']],
[3, ['Sydney','Melbourne','872','1.1','10.5']],
[4, ['Adelaide','Perth','2781','3.1','38']],
[5, ['Adelaide','Alice Springs','1533','2','20.25']],
[6, ['Adelaide','Brisbane','2045','2.15','40']]
],
'Get table values with getTableData()'
);
deepEqual(
tf.getData(),
[
[0, ['Sydney','Adelaide',1412,1.4,25.3]],
[1, ['Sydney','Brisbane',982,1.5,16]],
[2, ['Sydney','Canberra',286,0.6,4.3]],
[3, ['Sydney','Melbourne',872,1.1,10.5]],
[4, ['Adelaide','Perth',2781,3.1,38]],
[5, ['Adelaide','Alice Springs',1533,2,20.25]],
[6, ['Adelaide','Brisbane',2045,2.15,40]]
],
'Get table data'
);
deepEqual(
tf.getValues(),
[
[0, ['Sydney','Adelaide','1412','1.4','25.3']],
[1, ['Sydney','Brisbane','982','1.5','16']],
[2, ['Sydney','Canberra','286','.6','4.3']],
[3, ['Sydney','Melbourne','872','1.1','10.5']],
[4, ['Adelaide','Perth','2781','3.1','38']],
[5, ['Adelaide','Alice Springs','1533','2','20.25']],
[6, ['Adelaide','Brisbane','2045','2.15','40']]
],
'Get table values'
);
tf.clearFilters();
tf.setFilterValue(0, 'Adelaide');
tf.filter();
deepEqual(
tf.getFilteredValues(),
[
[4, ['Adelaide','Perth','2781','3.1','38']],
[5, ['Adelaide','Alice Springs','1533','2','20.25']],
[6, ['Adelaide','Brisbane','2045','2.15','40']]
],
'Get filtered table values'
);
deepEqual(
tf.getFilteredValues(true),
[
[0, ['From','Destination','Road Distance (km)',
'By Air (hrs)','By Rail (hrs)']],
[4, ['Adelaide','Perth','2781','3.1','38']],
[5, ['Adelaide','Alice Springs','1533','2','20.25']],
[6, ['Adelaide','Brisbane','2045','2.15','40']]
],
'Get filtered table values including columns headers'
);
deepEqual(
tf.getFilteredDataCol(0),
['Adelaide','Adelaide','Adelaide'],
'Get specified column filtered values'
);
deepEqual(
tf.getFilteredDataCol(0, true),
['From','Adelaide','Adelaide','Adelaide'],
'Get specified column filtered values including header'
);
deepEqual(
tf.getFilteredColumnData(2),
[2781,1533,2045],
'Get filtered column data'
);
deepEqual(
tf.getFilteredColumnData(2, true),
['Road Distance (km)',2781,1533,2045],
'Get filtered column data with headers'
);
deepEqual(
tf.getVisibleColumnData(2),
[2781,1533,2045],
'Get visible column data'
);
deepEqual(
tf.getVisibleColumnData(2, true),
['Road Distance (km)',2781,1533,2045],
'Get visible column data with headers'
);
deepEqual(
tf.getFilteredColumnValues(2),
['2781','1533','2045'],
'Get filtered column data'
);
deepEqual(
tf.getFilteredColumnValues(2, true),
['Road Distance (km)','2781','1533','2045'],
'Get filtered column data with headers'
);
deepEqual(
tf.getVisibleColumnValues(2),
['2781','1533','2045'],
'Get visible column data'
);
deepEqual(
tf.getVisibleColumnValues(2, true),
['Road Distance (km)','2781','1533','2045'],
'Get visible column data with headers'
);
tf.clearFilters();
tf.filter();
});
test('Destroy', function() {
tf.destroy();
deepEqual(tf.isInitialized(), false, 'Filters removed');
tf = null;
});
function colsVisibilityTests() { // issue 94
module('Public methods with columns visibility extension');
test('Sanity checks', function() {
tf1.setFilterValue(0, 'Adelaide');
tf1.filter();
deepEqual(
tf1.getFilteredValues(false, true),
[
[6, ['Adelaide','3.1','38']],
[7, ['Adelaide','2','20.25']],
[8, ['Adelaide','2.15','40']]
],
'Get filtered table values with excluded columns'
);
deepEqual(
tf1.getFilteredValues(true, true),
[
[1, ['From','By Air (hrs)','By Rail (hrs)']],
[6, ['Adelaide','3.1','38']],
[7, ['Adelaide','2','20.25']],
[8, ['Adelaide','2.15','40']]
],
'Get filtered table values with headers and excluded columns'
);
deepEqual(
tf1.getTableData(false, true),
[
[2, ['Sydney','1.4','25.3']],
[3, ['Sydney','1.5','16']],
[4, ['Sydney','.6','4.3']],
[5, ['Sydney','1.1','10.5']],
[6, ['Adelaide','3.1','38']],
[7, ['Adelaide','2','20.25']],
[8, ['Adelaide','2.15','40']]
],
'Get table data with excluded columns'
);
deepEqual(
tf1.getTableData(true, true),
[
[1, ['From','By Air (hrs)','By Rail (hrs)']],
[2, ['Sydney','1.4','25.3']],
[3, ['Sydney','1.5','16']],
[4, ['Sydney','.6','4.3']],
[5, ['Sydney','1.1','10.5']],
[6, ['Adelaide','3.1','38']],
[7, ['Adelaide','2','20.25']],
[8, ['Adelaide','2.15','40']]
],
'Get table data with headers and excluded columns'
);
deepEqual(
tf1.getHeadersText(false),
[
'From','Destination','Road Distance (km)', 'By Air (hrs)',
'By Rail (hrs)'
],
'Headers text'
);
deepEqual(
tf1.getHeadersText(true),
['From', 'By Air (hrs)','By Rail (hrs)'],
'Headers text with excluded columns'
);
});
test('Extension Sanity checks', function() {
deepEqual(
tf1.hasExtension('colsVisibility'),
true,
'ColsVisibility in extensions registry'
);
deepEqual(
tf1.hasExtension('myExtension'),
true,
'myExtension in extensions registry'
);
});
test('Destroy', function() {
tf1.destroy();
deepEqual(tf1.isInitialized(), false, 'Filters removed');
tf1 = null;
});
}