jquery-grid
Version:
jQuery Grid by Gijgo.com is a plug-in for the jQuery Javascript library. It is a very fast and extandable datagrid, and will add advanced interaction controls to any HTML table. This plugin has build-in integration with Bootstrap and Material Design. Free
1,307 lines (1,119 loc) • 156 kB
JavaScript
/*
* Gijgo Grid v1.9.13
* http://gijgo.com/grid
*
* Copyright 2014, 2019 gijgo.com
* Released under the MIT license
*/
/* global window alert jQuery gj */
/**
*/
gj.grid = {
plugins: {},
messages: {}
};
gj.grid.config = {
base: {
/** The data source for the grid.
*/
dataSource: undefined,
/** An array that holds the configurations of each column from the grid.
*/
columns: [],
/** Auto generate column for each field in the datasource when set to true.
*/
autoGenerateColumns: false,
/** An object that holds the default configuration settings of each column from the grid.
*/
defaultColumnSettings: {
/** If set to true the column will not be displayed in the grid. By default all columns are displayed.
*/
hidden: false,
/** The width of the column. Numeric values are treated as pixels.
* If the width is undefined the width of the column is not set and depends on the with of the table(grid).
*/
width: undefined,
/** Indicates if the column is sortable.
* If set to true the user can click the column header and sort the grid by the column source field.
*/
sortable: false,
/** Indicates the type of the column.
*/
type: 'text',
/** The caption that is going to be displayed in the header of the grid.
*/
title: undefined,
/** The field name to which the column is bound.
* If the column.title is not defined this value is used as column.title.
*/
field: undefined,
/** This setting control the alignment of the text in the cell.
*/
align: undefined,
/** The name(s) of css class(es) that are going to be applied to all cells inside that column, except the header cell.
*/
cssClass: undefined,
/** The name(s) of css class(es) that are going to be applied to the header cell of that column.
*/
headerCssClass: undefined,
/** The text for the cell tooltip.
*/
tooltip: undefined,
/** Css class for icon that is going to be in use for the cell.
* This setting can be in use only with combination of type icon.
*/
icon: undefined,
/** Configuration object with event names as keys and functions as values that are going to be bind to each cell from the column.
* Each function is going to receive event information as a parameter with info in the 'data' field for id, field name and record data.
*/
events: undefined,
/** Format the date when the type of the column is date.
*/
format: 'mm/dd/yyyy',
/** Number of decimal digits after the decimal point.
*/
decimalDigits: undefined,
/** Template for the content in the column.
* Use curly brackets '{}' to wrap the names of data source columns from server response.
*/
tmpl: undefined,
/** If set to true stop event propagation when event occur.
*/
stopPropagation: false,
/** A renderer is an 'interceptor' function which can be used to transform data (value, appearance, etc.) before it is rendered.
*/
renderer: undefined,
/** Function which can be used to customize filtering with local data (javascript sourced data).
*/
filter: undefined
},
mapping: {
/** The name of the object in the server response, that contains array with records, that needs to be display in the grid.
*/
dataField: 'records',
/** The name of the object in the server response, that contains the number of all records on the server.
*/
totalRecordsField: 'total'
},
params: {},
paramNames: {
/** The name of the parameter that is going to send the name of the column for sorting.
* The "sortable" setting for at least one column should be enabled in order this parameter to be in use.
*/
sortBy: 'sortBy',
/** The name of the parameter that is going to send the direction for sorting.
* The "sortable" setting for at least one column should be enabled in order this parameter to be in use.
*/
direction: 'direction'
},
/** The name of the UI library that is going to be in use. Currently we support Bootstrap 3, Bootstrap 4 and Material Design.
*/
uiLibrary: 'materialdesign',
/** The name of the icons library that is going to be in use. Currently we support Material Icons, Font Awesome and Glyphicons.
*/
iconsLibrary: 'materialicons',
/** The type of the row selection.<br/>
* If the type is set to multiple the user will be able to select more then one row from the grid.
*/
selectionType: 'single',
/** The type of the row selection mechanism.
*/
selectionMethod: 'basic',
/** When this setting is enabled the content of the grid will be loaded automatically after the creation of the grid.
*/
autoLoad: true,
/** The text that is going to be displayed if the grid is empty.
*/
notFoundText: undefined,
/** Width of the grid.
*/
width: undefined,
/** Minimum width of the grid.
*/
minWidth: undefined,
/** This configuration option manage the behaviour of the header row height.
* Auto scale if set to to 'autogrow'. All body rows are with the same height if set to 'fixed'.
*/
headerRowHeight: 'fixed',
/** This configuration option manage the behaviour of the body row height.
* Auto scale if set to to 'autogrow'. All body rows are with the same height if set to 'fixed'.
*/
bodyRowHeight: 'autogrow',
/** The size of the font in the grid.
*/
fontSize: undefined,
/** Name of column that contains the record id.
*/
primaryKey: undefined,
/** The language that needs to be in use.
*/
locale: 'en-us',
defaultIconColumnWidth: 70,
defaultCheckBoxColumnWidth: 70,
style: {
wrapper: 'gj-grid-wrapper',
table: 'gj-grid gj-grid-md',
loadingCover: 'gj-grid-loading-cover',
loadingText: 'gj-grid-loading-text',
header: {
cell: undefined,
sortable: 'gj-cursor-pointer gj-unselectable'
},
content: {
rowSelected: 'gj-grid-md-select'
}
},
icons: {
asc: '▲',
desc: '▼'
}
},
bootstrap: {
style: {
wrapper: 'gj-grid-wrapper',
table: 'gj-grid gj-grid-bootstrap gj-grid-bootstrap-3 table table-bordered table-hover',
content: {
rowSelected: 'active'
}
},
iconsLibrary: 'glyphicons',
defaultIconColumnWidth: 34,
defaultCheckBoxColumnWidth: 36
},
bootstrap4: {
style: {
wrapper: 'gj-grid-wrapper',
table: 'gj-grid gj-grid-bootstrap gj-grid-bootstrap-4 table table-bordered table-hover',
content: {
rowSelected: 'active'
}
},
defaultIconColumnWidth: 42,
defaultCheckBoxColumnWidth: 44
},
materialicons: {
icons: {
asc: '<i class="gj-icon arrow-upward" />',
desc: '<i class="gj-icon arrow-downward" />'
}
},
fontawesome: {
icons: {
asc: '<i class="fa fa-sort-amount-asc" aria-hidden="true"></i>',
desc: '<i class="fa fa-sort-amount-desc" aria-hidden="true"></i>'
}
},
glyphicons: {
icons: {
asc: '<span class="glyphicon glyphicon-sort-by-alphabet" />',
desc: '<span class="glyphicon glyphicon-sort-by-alphabet-alt" />'
}
}
};
/**
*/
gj.grid.events = {
/**
* Event fires before addition of an empty row to the grid.
*/
beforeEmptyRowInsert: function ($grid, $row) {
return $grid.triggerHandler('beforeEmptyRowInsert', [$row]);
},
/**
* Event fired before data binding takes place.
*
*/
dataBinding: function ($grid, records) {
return $grid.triggerHandler('dataBinding', [records]);
},
/**
* Event fires after the loading of the data in the grid.
*
*/
dataBound: function ($grid, records, totalRecords) {
return $grid.triggerHandler('dataBound', [records, totalRecords]);
},
/**
* Event fires after insert of a row in the grid during the loading of the data.
*/
rowDataBound: function ($grid, $row, id, record) {
return $grid.triggerHandler('rowDataBound', [$row, id, record]);
},
/**
* Event fires after insert of a cell in the grid during the loading of the data
*
*/
cellDataBound: function ($grid, $displayEl, id, column, record) {
return $grid.triggerHandler('cellDataBound', [$displayEl, id, column, record]);
},
/**
* Event fires on selection of row
*
*/
rowSelect: function ($grid, $row, id, record) {
return $grid.triggerHandler('rowSelect', [$row, id, record]);
},
/**
* Event fires on un selection of row
*
*/
rowUnselect: function ($grid, $row, id, record) {
return $grid.triggerHandler('rowUnselect', [$row, id, record]);
},
/**
* Event fires before deletion of row in the grid.
*/
rowRemoving: function ($grid, $row, id, record) {
return $grid.triggerHandler('rowRemoving', [$row, id, record]);
},
/**
* Event fires when the grid.destroy method is called.
*
*/
destroying: function ($grid) {
return $grid.triggerHandler('destroying');
},
/**
* Event fires when column is hidding
*
*/
columnHide: function ($grid, column) {
return $grid.triggerHandler('columnHide', [column]);
},
/**
* Event fires when column is showing
*
*/
columnShow: function ($grid, column) {
return $grid.triggerHandler('columnShow', [column]);
},
/**
* Event fires when grid is initialized.
*
*/
initialized: function ($grid) {
return $grid.triggerHandler('initialized');
},
/**
* Event fires when the grid data is filtered.
*
*/
dataFiltered: function ($grid, records) {
return $grid.triggerHandler('dataFiltered', [records]);
}
};
/*global gj $*/
gj.grid.methods = {
init: function (jsConfig) {
gj.widget.prototype.init.call(this, jsConfig, 'grid');
gj.grid.methods.initialize(this);
if (this.data('autoLoad')) {
this.reload();
}
return this;
},
getConfig: function (jsConfig, type) {
var config = gj.widget.prototype.getConfig.call(this, jsConfig, type);
gj.grid.methods.setDefaultColumnConfig(config.columns, config.defaultColumnSettings);
return config;
},
setDefaultColumnConfig: function (columns, defaultColumnSettings) {
var column, i;
if (columns && columns.length) {
for (i = 0; i < columns.length; i++) {
column = $.extend(true, {}, defaultColumnSettings);
$.extend(true, column, columns[i]);
columns[i] = column;
}
}
},
getHTMLConfig: function () {
var result = gj.widget.prototype.getHTMLConfig.call(this);
result.columns = [];
this.find('thead > tr > th').each(function () {
var $el = $(this),
title = $el.text(),
config = gj.widget.prototype.getHTMLConfig.call($el);
config.title = title;
if (!config.field) {
config.field = title;
}
if (config.events) {
config.events = gj.grid.methods.eventsParser(config.events);
}
result.columns.push(config);
});
return result;
},
eventsParser: function (events) {
var result = {}, list, i, key, func, position;
list = events.split(',');
for (i = 0; i < list.length; i++) {
position = list[i].indexOf(':');
if (position > 0) {
key = $.trim(list[i].substr(0, position));
func = $.trim(list[i].substr(position + 1, list[i].length));
result[key] = eval('window.' + func); //window[func]; //TODO: eveluate functions from string
}
}
return result;
},
initialize: function ($grid) {
var data = $grid.data(),
$wrapper = $grid.parent('div[data-role="wrapper"]');
gj.grid.methods.localization(data);
if ($wrapper.length === 0) {
$wrapper = $('<div data-role="wrapper" />').addClass(data.style.wrapper); //The css class needs to be added before the wrapping, otherwise doesn't work.
$grid.wrap($wrapper);
} else {
$wrapper.addClass(data.style.wrapper);
}
if (data.width) {
$grid.parent().css('width', data.width);
}
if (data.minWidth) {
$grid.css('min-width', data.minWidth);
}
if (data.fontSize) {
$grid.css('font-size', data.fontSize);
}
if (data.headerRowHeight === 'autogrow') {
$grid.addClass('autogrow-header-row');
}
if (data.bodyRowHeight === 'fixed') {
$grid.addClass('fixed-body-rows');
}
$grid.addClass(data.style.table);
if ('checkbox' === data.selectionMethod) {
data.columns.splice(gj.grid.methods.getColumnPositionNotInRole($grid), 0, {
title: '',
width: data.defaultCheckBoxColumnWidth,
align: 'center',
type: 'checkbox',
role: 'selectRow',
events: {
click: function (e) {
gj.grid.methods.setSelected($grid, e.data.id, $(this).closest('tr'));
}
},
headerCssClass: 'gj-grid-select-all',
stopPropagation: true
});
}
if ($grid.children('tbody').length === 0) {
$grid.append($('<tbody/>'));
}
gj.grid.methods.renderHeader($grid);
gj.grid.methods.appendEmptyRow($grid, ' ');
gj.grid.events.initialized($grid);
},
localization: function (data) {
if (!data.notFoundText) {
data.notFoundText = gj.grid.messages[data.locale].NoRecordsFound;
}
},
renderHeader: function ($grid) {
var data, columns, style, $thead, $row, $cell, $title, i, $checkAllBoxes;
data = $grid.data();
columns = data.columns;
style = data.style.header;
$thead = $grid.children('thead');
if ($thead.length === 0) {
$thead = $('<thead />');
$grid.prepend($thead);
}
$row = $('<tr data-role="caption" />');
for (i = 0; i < columns.length; i += 1) {
$cell = $('<th data-field="' + (columns[i].field || '') + '" />');
if (columns[i].width) {
$cell.attr('width', columns[i].width);
} else if (columns[i].type === 'checkbox') {
$cell.attr('width', data.defaultIconColumnWidth);
}
$cell.addClass(style.cell);
if (columns[i].headerCssClass) {
$cell.addClass(columns[i].headerCssClass);
}
$cell.css('text-align', columns[i].align || 'left');
if ('checkbox' === data.selectionMethod && 'multiple' === data.selectionType &&
'checkbox' === columns[i].type && 'selectRow' === columns[i].role) {
$checkAllBoxes = $cell.find('input[data-role="selectAll"]');
if ($checkAllBoxes.length === 0) {
$checkAllBoxes = $('<input type="checkbox" data-role="selectAll" />');
$cell.append($checkAllBoxes);
$checkAllBoxes.checkbox({ uiLibrary: data.uiLibrary });
}
$checkAllBoxes.off('click').on('click', function () {
if (this.checked) {
$grid.selectAll();
} else {
$grid.unSelectAll();
}
});
} else {
$title = $('<div data-role="title"/>').html(typeof (columns[i].title) === 'undefined' ? columns[i].field : columns[i].title);
$cell.append($title);
if (columns[i].sortable) {
$title.addClass(style.sortable);
$title.on('click', gj.grid.methods.createSortHandler($grid, columns[i]));
}
}
if (columns[i].hidden) {
$cell.hide();
}
$row.append($cell);
}
$thead.empty().append($row);
},
createSortHandler: function ($grid, column) {
return function () {
var data, params = {};
if ($grid.count() > 0) {
data = $grid.data();
params[data.paramNames.sortBy] = column.field;
column.direction = (column.direction === 'asc' ? 'desc' : 'asc');
params[data.paramNames.direction] = column.direction;
$grid.reload(params);
}
};
},
updateHeader: function ($grid) {
var $sortIcon, $cellTitle,
data = $grid.data(),
sortBy = data.params[data.paramNames.sortBy],
direction = data.params[data.paramNames.direction];
$grid.find('thead tr th [data-role="sorticon"]').remove();
if (sortBy) {
position = gj.grid.methods.getColumnPosition($grid.data('columns'), sortBy);
if (position > -1) {
$cellTitle = $grid.find('thead tr th:eq(' + position + ') div[data-role="title"]');
$sortIcon = $('<div data-role="sorticon" class="gj-unselectable" />').append(('desc' === direction) ? data.icons.desc : data.icons.asc);
$cellTitle.after($sortIcon);
}
}
},
useHtmlDataSource: function ($grid, data) {
var dataSource = [], i, j, $cells, record,
$rows = $grid.find('tbody tr[data-role != "empty"]');
for (i = 0; i < $rows.length; i++) {
$cells = $($rows[i]).find('td');
record = {};
for (j = 0; j < $cells.length; j++) {
record[data.columns[j].field] = $($cells[j]).html();
}
dataSource.push(record);
}
data.dataSource = dataSource;
},
startLoading: function ($grid) {
var $tbody, $cover, $loading, width, height, top, data;
gj.grid.methods.stopLoading($grid);
data = $grid.data();
if (0 === $grid.outerHeight()) {
return;
}
$tbody = $grid.children('tbody');
width = $tbody.outerWidth(false);
height = $tbody.outerHeight(false);
top = Math.abs($grid.parent().offset().top - $tbody.offset().top);
$cover = $('<div data-role="loading-cover" />').addClass(data.style.loadingCover).css({
width: width,
height: height,
top: top
});
$loading = $('<div data-role="loading-text">' + gj.grid.messages[data.locale].Loading + '</div>').addClass(data.style.loadingText);
$loading.insertAfter($grid);
$cover.insertAfter($grid);
$loading.css({
top: top + (height / 2) - ($loading.outerHeight(false) / 2),
left: (width / 2) - ($loading.outerWidth(false) / 2)
});
},
stopLoading: function ($grid) {
$grid.parent().find('div[data-role="loading-cover"]').remove();
$grid.parent().find('div[data-role="loading-text"]').remove();
},
appendEmptyRow: function ($grid, caption) {
var data, $row, $cell, $wrapper;
data = $grid.data();
$row = $('<tr data-role="empty"/>');
$cell = $('<td/>').css({ width: '100%', 'text-align': 'center' });
$cell.attr('colspan', gj.grid.methods.countVisibleColumns($grid));
$wrapper = $('<div />').html(caption || data.notFoundText);
$cell.append($wrapper);
$row.append($cell);
gj.grid.events.beforeEmptyRowInsert($grid, $row);
$grid.append($row);
},
autoGenerateColumns: function ($grid, records) {
var names, value, type, i, data = $grid.data();
data.columns = [];
if (records.length > 0) {
names = Object.getOwnPropertyNames(records[0]);
for (i = 0; i < names.length; i++) {
value = records[0][names[i]];
type = 'text';
if (value) {
if (typeof value === 'number') {
type = 'number';
} else if (value.indexOf('/Date(') > -1) {
type = 'date';
}
}
data.columns.push({ field: names[i], type: type });
}
gj.grid.methods.setDefaultColumnConfig(data.columns, data.defaultColumnSettings);
}
gj.grid.methods.renderHeader($grid);
},
loadData: function ($grid) {
var data, records, i, recLen, rowCount, $tbody, $rows, $row;
data = $grid.data();
records = $grid.getAll();
gj.grid.events.dataBinding($grid, records);
recLen = records.length;
gj.grid.methods.stopLoading($grid);
if (data.autoGenerateColumns) {
gj.grid.methods.autoGenerateColumns($grid, records);
}
$tbody = $grid.children('tbody');
if ('checkbox' === data.selectionMethod && 'multiple' === data.selectionType) {
$grid.find('thead input[data-role="selectAll"]').prop('checked', false);
}
$tbody.children('tr').not('[data-role="row"]').remove();
if (0 === recLen) {
$tbody.empty();
gj.grid.methods.appendEmptyRow($grid);
}
$rows = $tbody.children('tr');
rowCount = $rows.length;
for (i = 0; i < rowCount; i++) {
if (i < recLen) {
$row = $rows.eq(i);
gj.grid.methods.renderRow($grid, $row, records[i], i);
} else {
$tbody.find('tr[data-role="row"]:gt(' + (i - 1) + ')').remove();
break;
}
}
for (i = rowCount; i < recLen; i++) {
gj.grid.methods.renderRow($grid, null, records[i], i);
}
gj.grid.events.dataBound($grid, records, data.totalRecords);
},
getId: function (record, primaryKey, position) {
return (primaryKey && record[primaryKey]) ? record[primaryKey] : position;
},
renderRow: function ($grid, $row, record, position) {
var id, $cell, i, data, mode;
data = $grid.data();
if (!$row || $row.length === 0) {
mode = 'create';
$row = $('<tr data-role="row"/>');
$grid.children('tbody').append($row);
} else {
mode = 'update';
$row.removeClass(data.style.content.rowSelected).removeAttr('data-selected').off('click');
}
id = gj.grid.methods.getId(record, data.primaryKey, (position + 1));
$row.attr('data-position', position + 1);
if (data.selectionMethod !== 'checkbox') {
$row.on('click', gj.grid.methods.createRowClickHandler($grid, id));
}
for (i = 0; i < data.columns.length; i++) {
if (mode === 'update') {
$cell = $row.find('td:eq(' + i + ')');
gj.grid.methods.renderCell($grid, $cell, data.columns[i], record, id);
} else {
$cell = gj.grid.methods.renderCell($grid, null, data.columns[i], record, id);
$row.append($cell);
}
}
gj.grid.events.rowDataBound($grid, $row, id, record);
},
renderCell: function ($grid, $cell, column, record, id, mode) {
var $displayEl, key;
if (!$cell || $cell.length === 0) {
$cell = $('<td/>');
$displayEl = $('<div data-role="display" />');
column.align && $cell.css('text-align', column.align);
column.cssClass && $cell.addClass(column.cssClass);
$cell.append($displayEl);
mode = 'create';
} else {
$displayEl = $cell.find('div[data-role="display"]');
mode = 'update';
}
gj.grid.methods.renderDisplayElement($grid, $displayEl, column, record, id, mode);
//remove all event handlers
if ('update' === mode) {
$cell.off();
$displayEl.off();
}
if (column.events) {
for (key in column.events) {
if (column.events.hasOwnProperty(key)) {
$cell.on(key, { id: id, field: column.field, record: record }, gj.grid.methods.createCellEventHandler(column, column.events[key]));
}
}
}
if (column.hidden) {
$cell.hide();
}
gj.grid.events.cellDataBound($grid, $displayEl, id, column, record);
return $cell;
},
createCellEventHandler: function (column, func) {
return function (e) {
if (column.stopPropagation) {
e.stopPropagation();
}
func.call(this, e);
};
},
renderDisplayElement: function ($grid, $displayEl, column, record, id, mode) {
var text, $checkbox;
if ('checkbox' === column.type && gj.checkbox) {
if ('create' === mode) {
$checkbox = $('<input type="checkbox" />').val(id).prop('checked', (record[column.field] ? true : false));
column.role && $checkbox.attr('data-role', column.role);
$displayEl.append($checkbox);
$checkbox.checkbox({ uiLibrary: $grid.data('uiLibrary') });
if (column.role === 'selectRow') {
$checkbox.on('click', function () { return false; });
} else {
$checkbox.prop('disabled', true);
}
} else {
$displayEl.find('input[type="checkbox"]').val(id).prop('checked', (record[column.field] ? true : false));
}
} else if ('icon' === column.type) {
if ('create' === mode) {
$displayEl.append($('<span/>').addClass(column.icon).css({ cursor: 'pointer' }));
$grid.data().uiLibrary === 'bootstrap' && $displayEl.children('span').addClass('glyphicon');
column.stopPropagation = true;
}
} else if (column.tmpl) {
text = column.tmpl;
column.tmpl.replace(/\{(.+?)\}/g, function ($0, $1) {
text = text.replace($0, gj.grid.methods.formatText(record[$1], column));
});
$displayEl.html(text);
} else if (column.renderer && typeof (column.renderer) === 'function') {
text = column.renderer(record[column.field], record, $displayEl.parent(), $displayEl, id, $grid);
if (text) {
$displayEl.html(text);
}
} else {
record[column.field] = gj.grid.methods.formatText(record[column.field], column);
if (!column.tooltip && record[column.field]) {
$displayEl.attr('title', record[column.field]);
}
$displayEl.html(record[column.field]);
}
if (column.tooltip && 'create' === mode) {
$displayEl.attr('title', column.tooltip);
}
},
formatText: function (text, column) {
if (text && ['date', 'time', 'datetime'].indexOf(column.type) > -1) {
text = gj.core.formatDate(gj.core.parseDate(text, column.format), column.format);
} else {
text = (typeof (text) === 'undefined' || text === null) ? '' : text.toString();
}
if (column.decimalDigits && text) {
text = parseFloat(text).toFixed(column.decimalDigits);
}
return text;
},
setRecordsData: function ($grid, response) {
var records = [],
totalRecords = 0,
data = $grid.data();
if ($.isArray(response)) {
records = response;
totalRecords = response.length;
} else if (data && data.mapping && $.isArray(response[data.mapping.dataField])) {
records = response[data.mapping.dataField];
totalRecords = response[data.mapping.totalRecordsField];
if (!totalRecords || isNaN(totalRecords)) {
totalRecords = 0;
}
}
$grid.data('records', records);
$grid.data('totalRecords', totalRecords);
return records;
},
createRowClickHandler: function ($grid, id) {
return function () {
gj.grid.methods.setSelected($grid, id, $(this));
};
},
selectRow: function ($grid, data, $row, id) {
var $checkbox;
$row.addClass(data.style.content.rowSelected);
$row.attr('data-selected', 'true');
if ('checkbox' === data.selectionMethod) {
$checkbox = $row.find('input[type="checkbox"][data-role="selectRow"]');
$checkbox.length && !$checkbox.prop('checked') && $checkbox.prop('checked', true);
if ('multiple' === data.selectionType && $grid.getSelections().length === $grid.count(false)) {
$grid.find('thead input[data-role="selectAll"]').prop('checked', true);
}
}
return gj.grid.events.rowSelect($grid, $row, id, $grid.getById(id));
},
unselectRow: function ($grid, data, $row, id) {
var $checkbox;
if ($row.attr('data-selected') === 'true') {
$row.removeClass(data.style.content.rowSelected);
if ('checkbox' === data.selectionMethod) {
$checkbox = $row.find('td input[type="checkbox"][data-role="selectRow"]');
$checkbox.length && $checkbox.prop('checked') && $checkbox.prop('checked', false);
if ('multiple' === data.selectionType) {
$grid.find('thead input[data-role="selectAll"]').prop('checked', false);
}
}
$row.removeAttr('data-selected');
return gj.grid.events.rowUnselect($grid, $row, id, $grid.getById(id));
}
},
setSelected: function ($grid, id, $row) {
var data = $grid.data();
if (!$row || !$row.length) {
$row = gj.grid.methods.getRowById($grid, id);
}
if ($row) {
if ($row.attr('data-selected') === 'true') {
gj.grid.methods.unselectRow($grid, data, $row, id);
} else {
if ('single' === data.selectionType) {
$row.siblings('[data-selected="true"]').each(function () {
var $row = $(this),
id = gj.grid.methods.getId($row, data.primaryKey, $row.data('position'));
gj.grid.methods.unselectRow($grid, data, $row, id);
});
}
gj.grid.methods.selectRow($grid, data, $row, id);
}
}
return $grid;
},
selectAll: function ($grid) {
var data = $grid.data();
$grid.find('tbody tr[data-role="row"]').each(function () {
var $row = $(this),
position = $row.data('position'),
record = $grid.get(position),
id = gj.grid.methods.getId(record, data.primaryKey, position);
gj.grid.methods.selectRow($grid, data, $row, id);
});
$grid.find('thead input[data-role="selectAll"]').prop('checked', true);
return $grid;
},
unSelectAll: function ($grid) {
var data = $grid.data();
$grid.find('tbody tr').each(function () {
var $row = $(this),
position = $row.data('position'),
record = $grid.get(position),
id = gj.grid.methods.getId(record, data.primaryKey, position);
gj.grid.methods.unselectRow($grid, data, $row, id);
$row.find('input[type="checkbox"][data-role="selectRow"]').prop('checked', false);
});
$grid.find('thead input[data-role="selectAll"]').prop('checked', false);
return $grid;
},
getSelected: function ($grid) {
var result = null, selections, record, position;
selections = $grid.find('tbody>tr[data-selected="true"]');
if (selections.length > 0) {
position = $(selections[0]).data('position');
record = $grid.get(position);
result = gj.grid.methods.getId(record, $grid.data().primaryKey, position);
}
return result;
},
getSelectedRows: function ($grid) {
var data = $grid.data();
return $grid.find('tbody>tr[data-selected="true"]');
},
getSelections: function ($grid) {
var result = [], position, record,
data = $grid.data(),
$selections = gj.grid.methods.getSelectedRows($grid);
if (0 < $selections.length) {
$selections.each(function () {
position = $(this).data('position');
record = $grid.get(position);
result.push(gj.grid.methods.getId(record, data.primaryKey, position));
});
}
return result;
},
getById: function ($grid, id) {
var result = null, i, primaryKey = $grid.data('primaryKey'), records = $grid.data('records');
if (primaryKey) {
for (i = 0; i < records.length; i++) {
if (records[i][primaryKey] == id) {
result = records[i];
break;
}
}
} else {
result = $grid.get(id);
}
return result;
},
getRecVPosById: function ($grid, id) {
var result = id, i, data = $grid.data();
if (data.primaryKey) {
for (i = 0; i < data.dataSource.length; i++) {
if (data.dataSource[i][data.primaryKey] == id) {
result = i;
break;
}
}
}
return result;
},
getRowById: function ($grid, id) {
var records = $grid.getAll(false),
primaryKey = $grid.data('primaryKey'),
$result = undefined,
position,
i;
if (primaryKey) {
for (i = 0; i < records.length; i++) {
if (records[i][primaryKey] == id) {
position = i + 1;
break;
}
}
} else {
position = id;
}
if (position) {
$result = $grid.children('tbody').children('tr[data-position="' + position + '"]');
}
return $result;
},
getByPosition: function ($grid, position) {
return $grid.getAll(false)[position - 1];
},
getColumnPosition: function (columns, field) {
var position = -1, i;
for (i = 0; i < columns.length; i++) {
if (columns[i].field === field) {
position = i;
break;
}
}
return position;
},
getColumnInfo: function ($grid, field) {
var i, result = {}, data = $grid.data();
for (i = 0; i < data.columns.length; i += 1) {
if (data.columns[i].field === field) {
result = data.columns[i];
break;
}
}
return result;
},
getCell: function ($grid, id, field) {
var position, $row, $result = null;
position = gj.grid.methods.getColumnPosition($grid.data('columns'), field);
if (position > -1) {
$row = gj.grid.methods.getRowById($grid, id);
$result = $row.find('td:eq(' + position + ') div[data-role="display"]');
}
return $result;
},
setCellContent: function ($grid, id, field, value) {
var column, $displayEl = gj.grid.methods.getCell($grid, id, field);
if ($displayEl) {
$displayEl.empty();
if (typeof (value) === 'object') {
$displayEl.append(value);
} else {
column = gj.grid.methods.getColumnInfo($grid, field);
gj.grid.methods.renderDisplayElement($grid, $displayEl, column, $grid.getById(id), id, 'update');
}
}
},
clone: function (source) {
var target = [];
$.each(source, function () {
target.push(this.clone());
});
return target;
},
getAll: function ($grid) {
return $grid.data('records');
},
countVisibleColumns: function ($grid) {
var columns, count, i;
columns = $grid.data().columns;
count = 0;
for (i = 0; i < columns.length; i++) {
if (columns[i].hidden !== true) {
count++;
}
}
return count;
},
clear: function ($grid, showNotFoundText) {
var data = $grid.data();
$grid.xhr && $grid.xhr.abort();
$grid.children('tbody').empty();
data.records = [];
gj.grid.methods.stopLoading($grid);
gj.grid.methods.appendEmptyRow($grid, showNotFoundText ? data.notFoundText : ' ');
gj.grid.events.dataBound($grid, [], 0);
return $grid;
},
render: function ($grid, response) {
if (response) {
gj.grid.methods.setRecordsData($grid, response);
gj.grid.methods.updateHeader($grid);
gj.grid.methods.loadData($grid);
}
return $grid;
},
filter: function ($grid) {
var field, column,
data = $grid.data(),
records = data.dataSource.slice();
if (data.params[data.paramNames.sortBy]) {
column = gj.grid.methods.getColumnInfo($grid, data.params[data.paramNames.sortBy]);
records.sort(column.sortable.sorter ? column.sortable.sorter(column.direction, column) : gj.grid.methods.createDefaultSorter(column.direction, column.field));
}
for (field in data.params) {
if (data.params[field] && !data.paramNames[field]) {
column = gj.grid.methods.getColumnInfo($grid, field);
records = $.grep(records, function (record) {
var value = record[field] || '',
searchStr = data.params[field] || '';
return column && typeof (column.filter) === 'function' ? column.filter(value, searchStr) : (value.toUpperCase().indexOf(searchStr.toUpperCase()) > -1);
});
}
}
gj.grid.events.dataFiltered($grid, records);
return records;
},
createDefaultSorter: function (direction, field) {
return function (recordA, recordB) {
var a = (recordA[field] || '').toString(),
b = (recordB[field] || '').toString();
return (direction === 'asc') ? a.localeCompare(b) : b.localeCompare(a);
};
},
destroy: function ($grid, keepTableTag, keepWrapperTag) {
var data = $grid.data();
if (data) {
gj.grid.events.destroying($grid);
gj.grid.methods.stopLoading($grid);
$grid.xhr && $grid.xhr.abort();
$grid.off();
if (keepWrapperTag === false && $grid.parent('div[data-role="wrapper"]').length > 0) {
$grid.unwrap();
}
$grid.removeData();
if (keepTableTag === false) {
$grid.remove();
} else {
$grid.removeClass().empty();
}
$grid.removeAttr('data-type');
}
return $grid;
},
showColumn: function ($grid, field) {
var data = $grid.data(),
position = gj.grid.methods.getColumnPosition(data.columns, field),
$cells;
if (position > -1) {
$grid.find('thead>tr').each(function() {
$(this).children('th').eq(position).show();
});
$.each($grid.find('tbody>tr'), function () {
$(this).children('td').eq(position).show();
});
data.columns[position].hidden = false;
$cells = $grid.find('tbody > tr[data-role="empty"] > td');
if ($cells && $cells.length) {
$cells.attr('colspan', gj.grid.methods.countVisibleColumns($grid));
}
gj.grid.events.columnShow($grid, data.columns[position]);
}
return $grid;
},
hideColumn: function ($grid, field) {
var data = $grid.data(),
position = gj.grid.methods.getColumnPosition(data.columns, field),
$cells;
if (position > -1) {
$grid.find('thead>tr').each(function () {
$(this).children('th').eq(position).hide();
});
$.each($grid.find('tbody>tr'), function () {
$(this).children('td').eq(position).hide();
});
data.columns[position].hidden = true;
$cells = $grid.find('tbody > tr[data-role="empty"] > td');
if ($cells && $cells.length) {
$cells.attr('colspan', gj.grid.methods.countVisibleColumns($grid));
}
gj.grid.events.columnHide($grid, data.columns[position]);
}
return $grid;
},
isLastRecordVisible: function () {
return true;
},
addRow: function ($grid, record) {
var data = $grid.data();
data.totalRecords = $grid.data('totalRecords') + 1;
gj.grid.events.dataBinding($grid, [record]);
data.records.push(record);
if ($.isArray(data.dataSource)) {
data.dataSource.push(record);
}
if (data.totalRecords === 1) {
$grid.children('tbody').empty();
}
if (gj.grid.methods.isLastRecordVisible($grid)) {
gj.grid.methods.renderRow($grid, null, record, $grid.count() - 1);
}
gj.grid.events.dataBound($grid, [record], data.totalRecords);
return $grid;
},
updateRow: function ($grid, id, record) {
var $row = gj.grid.methods.getRowById($grid, id),
data = $grid.data(), position;
data.records[$row.data('position') - 1] = record;
if ($.isArray(data.dataSource)) {
position = gj.grid.methods.getRecVPosById($grid, id);
data.dataSource[position] = record;
}
gj.grid.methods.renderRow($grid, $row, record, $row.index());
return $grid;
},
removeRow: function ($grid, id) {
var position,
data = $grid.data(),
$row = gj.grid.methods.getRowById($grid, id);
gj.grid.events.rowRemoving($grid, $row, id, $grid.getById(id));
if ($.isArray(data.dataSource)) {
position = gj.grid.methods.getRecVPosById($grid, id);
data.dataSource.splice(position, 1);
}
$grid.reload();
return $grid;
},
count: function ($grid, includeAllRecords) {
return includeAllRecords ? $grid.data().totalRecords : $grid.getAll().length;
},
getColumnPositionByRole: function ($grid, role) {
var i, result, columns = $grid.data('columns');
for (i = 0; i < columns.length; i++) {
if (columns[i].role === role) {
result = i;
break;
}
}
return result;
},
getColumnPositionNotInRole: function ($grid) {
var i, result = 0, columns = $grid.data('columns');
for (i = 0; i < columns.length; i++) {
if (!columns[i].role) {
result = i;
break;
}
}
return result;
}
};
/**
*/
gj.grid.widget = function ($grid, jsConfig) {
var self = this,
methods = gj.grid.methods;
/**
* Reload the data in the grid from a data source.
*/
self.reload = function (params) {
methods.startLoading(this);
return gj.widget.prototype.reload.call(this, params);
};
/**
* Clear the content in the grid.
*/
self.clear = function (showNotFoundText) {
return methods.clear(this, showNotFoundText);
};
/**
* Return the number of records in the grid. By default return only the records that are visible in the grid.
*/
self.count = function (includeAllRecords) {
return methods.count(this, includeAllRecords);
};
/**
* Render data in the grid
*/
self.render = function (response) {
return methods.render($grid, response);
};
/**
* Destroy the grid. This method remove all data from the grid and all events attached to the grid.
*/
self.destroy = function (keepTableTag, keepWrapperTag) {
return methods.destroy(this, keepTableTag, keepWrapperTag);
};
/**
* Select a row from the grid based on id parameter.
*/
self.setSelected = function (id) {
return methods.setSelected(this, id);
};
/**
* Return the id of the selected record.
* If the multiple selection method is one this method is going to return only the id of the first selected record.
*/
self.getSelected = function () {
return methods.getSelected(this);
};
/**
* Return an array with the ids of the selected record.
*/
self.getSelections = function () {
return methods.getSelections(this);
};
/**
* Select all records from the grid.
*/
self.selectAll = function () {
return methods.selectAll(this);
};
/**
* Unselect all records from the grid.
*/
self.unSelectAll = function () {
return methods.unSelectAll(this);
};
/**
* Return record by id of the record.
*/
self.getById = function (id) {
return methods.getById(this, id);
};
/**
* Return record from the grid based on position.
*/
self.get = function (position) {
return methods.getByPosition(this, position);
};
/**
* Return an array with all records presented in the grid.
*/
self.getAll = function (includeAllRecords) {
return methods.getAll(this, includeAllRecords);
};
/**
* Show hidden column.
*/
self.showColumn = function (field) {
return methods.showColumn(this, field);
};
/**
* Hide column from the grid.
*/
self.hideColumn = function (field) {
return methods.hideColumn(this, field);
};
/**
* Add new row to the grid.
*/
self.addRow = function (record) {
return methods.addRow(this, record);
};
/**
* Update row data.
*/
self.updateRow = function (id, record) {
return methods.updateRow(this, id, record);
};
//TODO: needs to be removed
self.setCellContent = function (id, index, value) {
methods.setCellContent(this, id, index, value);
};
/**
* Remove row from the grid
*/
self.removeRow = function (id) {
return methods.removeRow(this, id);
};
$.extend($grid, self);
if ('grid' !== $grid.attr('data-type')) {
methods.init.call($grid, jsConfig);
}
return $grid;
}
gj.grid.widget.prototype = new gj.widget();
gj.grid.widget.constructor = gj.grid.widget;
gj.grid.widget.prototype.getConfig = gj.grid.method