Spaces:
Sleeping
Sleeping
/* | |
* jsGrid v1.5.3 (http://js-grid.com) | |
* (c) 2016 Artem Tabalin | |
* Licensed under MIT (https://github.com/tabalinas/jsgrid/blob/master/LICENSE) | |
*/ | |
(function(window, $, undefined) { | |
var JSGRID = "JSGrid", | |
JSGRID_DATA_KEY = JSGRID, | |
JSGRID_ROW_DATA_KEY = "JSGridItem", | |
JSGRID_EDIT_ROW_DATA_KEY = "JSGridEditRow", | |
SORT_ORDER_ASC = "asc", | |
SORT_ORDER_DESC = "desc", | |
FIRST_PAGE_PLACEHOLDER = "{first}", | |
PAGES_PLACEHOLDER = "{pages}", | |
PREV_PAGE_PLACEHOLDER = "{prev}", | |
NEXT_PAGE_PLACEHOLDER = "{next}", | |
LAST_PAGE_PLACEHOLDER = "{last}", | |
PAGE_INDEX_PLACEHOLDER = "{pageIndex}", | |
PAGE_COUNT_PLACEHOLDER = "{pageCount}", | |
ITEM_COUNT_PLACEHOLDER = "{itemCount}", | |
EMPTY_HREF = "javascript:void(0);"; | |
var getOrApply = function(value, context) { | |
if($.isFunction(value)) { | |
return value.apply(context, $.makeArray(arguments).slice(2)); | |
} | |
return value; | |
}; | |
var normalizePromise = function(promise) { | |
var d = $.Deferred(); | |
if(promise && promise.then) { | |
promise.then(function() { | |
d.resolve.apply(d, arguments); | |
}, function() { | |
d.reject.apply(d, arguments); | |
}); | |
} else { | |
d.resolve(promise); | |
} | |
return d.promise(); | |
}; | |
var defaultController = { | |
loadData: $.noop, | |
insertItem: $.noop, | |
updateItem: $.noop, | |
deleteItem: $.noop | |
}; | |
function Grid(element, config) { | |
var $element = $(element); | |
$element.data(JSGRID_DATA_KEY, this); | |
this._container = $element; | |
this.data = []; | |
this.fields = []; | |
this._editingRow = null; | |
this._sortField = null; | |
this._sortOrder = SORT_ORDER_ASC; | |
this._firstDisplayingPage = 1; | |
this._init(config); | |
this.render(); | |
} | |
Grid.prototype = { | |
width: "auto", | |
height: "auto", | |
updateOnResize: true, | |
rowClass: $.noop, | |
rowRenderer: null, | |
rowClick: function(args) { | |
if(this.editing) { | |
this.editItem($(args.event.target).closest("tr")); | |
} | |
}, | |
rowDoubleClick: $.noop, | |
noDataContent: "Not found", | |
noDataRowClass: "jsgrid-nodata-row", | |
heading: true, | |
headerRowRenderer: null, | |
headerRowClass: "jsgrid-header-row", | |
headerCellClass: "jsgrid-header-cell", | |
filtering: false, | |
filterRowRenderer: null, | |
filterRowClass: "jsgrid-filter-row", | |
inserting: false, | |
insertRowRenderer: null, | |
insertRowClass: "jsgrid-insert-row", | |
editing: false, | |
editRowRenderer: null, | |
editRowClass: "jsgrid-edit-row", | |
confirmDeleting: true, | |
deleteConfirm: "Are you sure?", | |
selecting: true, | |
selectedRowClass: "jsgrid-selected-row", | |
oddRowClass: "jsgrid-row", | |
evenRowClass: "jsgrid-alt-row", | |
cellClass: "jsgrid-cell", | |
sorting: false, | |
sortableClass: "jsgrid-header-sortable", | |
sortAscClass: "jsgrid-header-sort jsgrid-header-sort-asc", | |
sortDescClass: "jsgrid-header-sort jsgrid-header-sort-desc", | |
paging: false, | |
pagerContainer: null, | |
pageIndex: 1, | |
pageSize: 20, | |
pageButtonCount: 15, | |
pagerFormat: "Pages: {first} {prev} {pages} {next} {last} {pageIndex} of {pageCount}", | |
pagePrevText: "Prev", | |
pageNextText: "Next", | |
pageFirstText: "First", | |
pageLastText: "Last", | |
pageNavigatorNextText: "...", | |
pageNavigatorPrevText: "...", | |
pagerContainerClass: "jsgrid-pager-container", | |
pagerClass: "jsgrid-pager", | |
pagerNavButtonClass: "jsgrid-pager-nav-button", | |
pagerNavButtonInactiveClass: "jsgrid-pager-nav-inactive-button", | |
pageClass: "jsgrid-pager-page", | |
currentPageClass: "jsgrid-pager-current-page", | |
customLoading: false, | |
pageLoading: false, | |
autoload: false, | |
controller: defaultController, | |
loadIndication: true, | |
loadIndicationDelay: 500, | |
loadMessage: "Please, wait...", | |
loadShading: true, | |
invalidMessage: "Invalid data entered!", | |
invalidNotify: function(args) { | |
var messages = $.map(args.errors, function(error) { | |
return error.message || null; | |
}); | |
window.alert([this.invalidMessage].concat(messages).join("\n")); | |
}, | |
onInit: $.noop, | |
onRefreshing: $.noop, | |
onRefreshed: $.noop, | |
onPageChanged: $.noop, | |
onItemDeleting: $.noop, | |
onItemDeleted: $.noop, | |
onItemInserting: $.noop, | |
onItemInserted: $.noop, | |
onItemEditing: $.noop, | |
onItemUpdating: $.noop, | |
onItemUpdated: $.noop, | |
onItemInvalid: $.noop, | |
onDataLoading: $.noop, | |
onDataLoaded: $.noop, | |
onOptionChanging: $.noop, | |
onOptionChanged: $.noop, | |
onError: $.noop, | |
invalidClass: "jsgrid-invalid", | |
containerClass: "jsgrid", | |
tableClass: "jsgrid-table", | |
gridHeaderClass: "jsgrid-grid-header", | |
gridBodyClass: "jsgrid-grid-body", | |
_init: function(config) { | |
$.extend(this, config); | |
this._initLoadStrategy(); | |
this._initController(); | |
this._initFields(); | |
this._attachWindowLoadResize(); | |
this._attachWindowResizeCallback(); | |
this._callEventHandler(this.onInit) | |
}, | |
loadStrategy: function() { | |
return this.pageLoading | |
? new jsGrid.loadStrategies.PageLoadingStrategy(this) | |
: new jsGrid.loadStrategies.DirectLoadingStrategy(this); | |
}, | |
_initLoadStrategy: function() { | |
this._loadStrategy = getOrApply(this.loadStrategy, this); | |
}, | |
_initController: function() { | |
this._controller = $.extend({}, defaultController, getOrApply(this.controller, this)); | |
}, | |
renderTemplate: function(source, context, config) { | |
args = []; | |
for(var key in config) { | |
args.push(config[key]); | |
} | |
args.unshift(source, context); | |
source = getOrApply.apply(null, args); | |
return (source === undefined || source === null) ? "" : source; | |
}, | |
loadIndicator: function(config) { | |
return new jsGrid.LoadIndicator(config); | |
}, | |
validation: function(config) { | |
return jsGrid.Validation && new jsGrid.Validation(config); | |
}, | |
_initFields: function() { | |
var self = this; | |
self.fields = $.map(self.fields, function(field) { | |
if($.isPlainObject(field)) { | |
var fieldConstructor = (field.type && jsGrid.fields[field.type]) || jsGrid.Field; | |
field = new fieldConstructor(field); | |
} | |
field._grid = self; | |
return field; | |
}); | |
}, | |
_attachWindowLoadResize: function() { | |
$(window).on("load", $.proxy(this._refreshSize, this)); | |
}, | |
_attachWindowResizeCallback: function() { | |
if(this.updateOnResize) { | |
$(window).on("resize", $.proxy(this._refreshSize, this)); | |
} | |
}, | |
_detachWindowResizeCallback: function() { | |
$(window).off("resize", this._refreshSize); | |
}, | |
option: function(key, value) { | |
var optionChangingEventArgs, | |
optionChangedEventArgs; | |
if(arguments.length === 1) | |
return this[key]; | |
optionChangingEventArgs = { | |
option: key, | |
oldValue: this[key], | |
newValue: value | |
}; | |
this._callEventHandler(this.onOptionChanging, optionChangingEventArgs); | |
this._handleOptionChange(optionChangingEventArgs.option, optionChangingEventArgs.newValue); | |
optionChangedEventArgs = { | |
option: optionChangingEventArgs.option, | |
value: optionChangingEventArgs.newValue | |
}; | |
this._callEventHandler(this.onOptionChanged, optionChangedEventArgs); | |
}, | |
fieldOption: function(field, key, value) { | |
field = this._normalizeField(field); | |
if(arguments.length === 2) | |
return field[key]; | |
field[key] = value; | |
this._renderGrid(); | |
}, | |
_handleOptionChange: function(name, value) { | |
this[name] = value; | |
switch(name) { | |
case "width": | |
case "height": | |
this._refreshSize(); | |
break; | |
case "rowClass": | |
case "rowRenderer": | |
case "rowClick": | |
case "rowDoubleClick": | |
case "noDataRowClass": | |
case "noDataContent": | |
case "selecting": | |
case "selectedRowClass": | |
case "oddRowClass": | |
case "evenRowClass": | |
this._refreshContent(); | |
break; | |
case "pageButtonCount": | |
case "pagerFormat": | |
case "pagePrevText": | |
case "pageNextText": | |
case "pageFirstText": | |
case "pageLastText": | |
case "pageNavigatorNextText": | |
case "pageNavigatorPrevText": | |
case "pagerClass": | |
case "pagerNavButtonClass": | |
case "pageClass": | |
case "currentPageClass": | |
case "pagerRenderer": | |
this._refreshPager(); | |
break; | |
case "fields": | |
this._initFields(); | |
this.render(); | |
break; | |
case "data": | |
case "editing": | |
case "heading": | |
case "filtering": | |
case "inserting": | |
case "paging": | |
this.refresh(); | |
break; | |
case "loadStrategy": | |
case "pageLoading": | |
this._initLoadStrategy(); | |
this.search(); | |
break; | |
case "pageIndex": | |
this.openPage(value); | |
break; | |
case "pageSize": | |
this.refresh(); | |
this.search(); | |
break; | |
case "editRowRenderer": | |
case "editRowClass": | |
this.cancelEdit(); | |
break; | |
case "updateOnResize": | |
this._detachWindowResizeCallback(); | |
this._attachWindowResizeCallback(); | |
break; | |
case "invalidNotify": | |
case "invalidMessage": | |
break; | |
default: | |
this.render(); | |
break; | |
} | |
}, | |
destroy: function() { | |
this._detachWindowResizeCallback(); | |
this._clear(); | |
this._container.removeData(JSGRID_DATA_KEY); | |
}, | |
render: function() { | |
this._renderGrid(); | |
return this.autoload ? this.loadData() : $.Deferred().resolve().promise(); | |
}, | |
_renderGrid: function() { | |
this._clear(); | |
this._container.addClass(this.containerClass) | |
.css("position", "relative") | |
.append(this._createHeader()) | |
.append(this._createBody()); | |
this._pagerContainer = this._createPagerContainer(); | |
this._loadIndicator = this._createLoadIndicator(); | |
this._validation = this._createValidation(); | |
this.refresh(); | |
}, | |
_createLoadIndicator: function() { | |
return getOrApply(this.loadIndicator, this, { | |
message: this.loadMessage, | |
shading: this.loadShading, | |
container: this._container | |
}); | |
}, | |
_createValidation: function() { | |
return getOrApply(this.validation, this); | |
}, | |
_clear: function() { | |
this.cancelEdit(); | |
clearTimeout(this._loadingTimer); | |
this._pagerContainer && this._pagerContainer.empty(); | |
this._container.empty() | |
.css({ position: "", width: "", height: "" }); | |
}, | |
_createHeader: function() { | |
var $headerRow = this._headerRow = this._createHeaderRow(), | |
$filterRow = this._filterRow = this._createFilterRow(), | |
$insertRow = this._insertRow = this._createInsertRow(); | |
var $headerGrid = this._headerGrid = $("<table>").addClass(this.tableClass) | |
.append($headerRow) | |
.append($filterRow) | |
.append($insertRow); | |
var $header = this._header = $("<div>").addClass(this.gridHeaderClass) | |
.addClass(this._scrollBarWidth() ? "jsgrid-header-scrollbar" : "") | |
.append($headerGrid); | |
return $header; | |
}, | |
_createBody: function() { | |
var $content = this._content = $("<tbody>"); | |
var $bodyGrid = this._bodyGrid = $("<table>").addClass(this.tableClass) | |
.append($content); | |
var $body = this._body = $("<div>").addClass(this.gridBodyClass) | |
.append($bodyGrid) | |
.on("scroll", $.proxy(function(e) { | |
this._header.scrollLeft(e.target.scrollLeft); | |
}, this)); | |
return $body; | |
}, | |
_createPagerContainer: function() { | |
var pagerContainer = this.pagerContainer || $("<div>").appendTo(this._container); | |
return $(pagerContainer).addClass(this.pagerContainerClass); | |
}, | |
_eachField: function(callBack) { | |
var self = this; | |
$.each(this.fields, function(index, field) { | |
if(field.visible) { | |
callBack.call(self, field, index); | |
} | |
}); | |
}, | |
_createHeaderRow: function() { | |
if($.isFunction(this.headerRowRenderer)) | |
return $(this.renderTemplate(this.headerRowRenderer, this)); | |
var $result = $("<tr>").addClass(this.headerRowClass); | |
this._eachField(function(field, index) { | |
var $th = this._prepareCell("<th>", field, "headercss", this.headerCellClass) | |
.append(this.renderTemplate(field.headerTemplate, field)) | |
.appendTo($result); | |
if(this.sorting && field.sorting) { | |
$th.addClass(this.sortableClass) | |
.on("click", $.proxy(function() { | |
this.sort(index); | |
}, this)); | |
} | |
}); | |
return $result; | |
}, | |
_prepareCell: function(cell, field, cssprop, cellClass) { | |
return $(cell).css("width", field.width) | |
.addClass(cellClass || this.cellClass) | |
.addClass((cssprop && field[cssprop]) || field.css) | |
.addClass(field.align ? ("jsgrid-align-" + field.align) : ""); | |
}, | |
_createFilterRow: function() { | |
if($.isFunction(this.filterRowRenderer)) | |
return $(this.renderTemplate(this.filterRowRenderer, this)); | |
var $result = $("<tr>").addClass(this.filterRowClass); | |
this._eachField(function(field) { | |
this._prepareCell("<td>", field, "filtercss") | |
.append(this.renderTemplate(field.filterTemplate, field)) | |
.appendTo($result); | |
}); | |
return $result; | |
}, | |
_createInsertRow: function() { | |
if($.isFunction(this.insertRowRenderer)) | |
return $(this.renderTemplate(this.insertRowRenderer, this)); | |
var $result = $("<tr>").addClass(this.insertRowClass); | |
this._eachField(function(field) { | |
this._prepareCell("<td>", field, "insertcss") | |
.append(this.renderTemplate(field.insertTemplate, field)) | |
.appendTo($result); | |
}); | |
return $result; | |
}, | |
_callEventHandler: function(handler, eventParams) { | |
handler.call(this, $.extend(eventParams, { | |
grid: this | |
})); | |
return eventParams; | |
}, | |
reset: function() { | |
this._resetSorting(); | |
this._resetPager(); | |
return this._loadStrategy.reset(); | |
}, | |
_resetPager: function() { | |
this._firstDisplayingPage = 1; | |
this._setPage(1); | |
}, | |
_resetSorting: function() { | |
this._sortField = null; | |
this._sortOrder = SORT_ORDER_ASC; | |
this._clearSortingCss(); | |
}, | |
refresh: function() { | |
this._callEventHandler(this.onRefreshing); | |
this.cancelEdit(); | |
this._refreshHeading(); | |
this._refreshFiltering(); | |
this._refreshInserting(); | |
this._refreshContent(); | |
this._refreshPager(); | |
this._refreshSize(); | |
this._callEventHandler(this.onRefreshed); | |
}, | |
_refreshHeading: function() { | |
this._headerRow.toggle(this.heading); | |
}, | |
_refreshFiltering: function() { | |
this._filterRow.toggle(this.filtering); | |
}, | |
_refreshInserting: function() { | |
this._insertRow.toggle(this.inserting); | |
}, | |
_refreshContent: function() { | |
var $content = this._content; | |
$content.empty(); | |
if(!this.data.length) { | |
$content.append(this._createNoDataRow()); | |
return this; | |
} | |
var indexFrom = this._loadStrategy.firstDisplayIndex(); | |
var indexTo = this._loadStrategy.lastDisplayIndex(); | |
for(var itemIndex = indexFrom; itemIndex < indexTo; itemIndex++) { | |
var item = this.data[itemIndex]; | |
$content.append(this._createRow(item, itemIndex)); | |
} | |
}, | |
_createNoDataRow: function() { | |
var amountOfFields = 0; | |
this._eachField(function() { | |
amountOfFields++; | |
}); | |
return $("<tr>").addClass(this.noDataRowClass) | |
.append($("<td>").addClass(this.cellClass).attr("colspan", amountOfFields) | |
.append(this.renderTemplate(this.noDataContent, this))); | |
}, | |
_createRow: function(item, itemIndex) { | |
var $result; | |
if($.isFunction(this.rowRenderer)) { | |
$result = this.renderTemplate(this.rowRenderer, this, { item: item, itemIndex: itemIndex }); | |
} else { | |
$result = $("<tr>"); | |
this._renderCells($result, item); | |
} | |
$result.addClass(this._getRowClasses(item, itemIndex)) | |
.data(JSGRID_ROW_DATA_KEY, item) | |
.on("click", $.proxy(function(e) { | |
this.rowClick({ | |
item: item, | |
itemIndex: itemIndex, | |
event: e | |
}); | |
}, this)) | |
.on("dblclick", $.proxy(function(e) { | |
this.rowDoubleClick({ | |
item: item, | |
itemIndex: itemIndex, | |
event: e | |
}); | |
}, this)); | |
if(this.selecting) { | |
this._attachRowHover($result); | |
} | |
return $result; | |
}, | |
_getRowClasses: function(item, itemIndex) { | |
var classes = []; | |
classes.push(((itemIndex + 1) % 2) ? this.oddRowClass : this.evenRowClass); | |
classes.push(getOrApply(this.rowClass, this, item, itemIndex)); | |
return classes.join(" "); | |
}, | |
_attachRowHover: function($row) { | |
var selectedRowClass = this.selectedRowClass; | |
$row.hover(function() { | |
$(this).addClass(selectedRowClass); | |
}, | |
function() { | |
$(this).removeClass(selectedRowClass); | |
} | |
); | |
}, | |
_renderCells: function($row, item) { | |
this._eachField(function(field) { | |
$row.append(this._createCell(item, field)); | |
}); | |
return this; | |
}, | |
_createCell: function(item, field) { | |
var $result; | |
var fieldValue = this._getItemFieldValue(item, field); | |
var args = { value: fieldValue, item : item }; | |
if($.isFunction(field.cellRenderer)) { | |
$result = this.renderTemplate(field.cellRenderer, field, args); | |
} else { | |
$result = $("<td>").append(this.renderTemplate(field.itemTemplate || fieldValue, field, args)); | |
} | |
return this._prepareCell($result, field); | |
}, | |
_getItemFieldValue: function(item, field) { | |
var props = field.name.split('.'); | |
var result = item[props.shift()]; | |
while(result && props.length) { | |
result = result[props.shift()]; | |
} | |
return result; | |
}, | |
_setItemFieldValue: function(item, field, value) { | |
var props = field.name.split('.'); | |
var current = item; | |
var prop = props[0]; | |
while(current && props.length) { | |
item = current; | |
prop = props.shift(); | |
current = item[prop]; | |
} | |
if(!current) { | |
while(props.length) { | |
item = item[prop] = {}; | |
prop = props.shift(); | |
} | |
} | |
item[prop] = value; | |
}, | |
sort: function(field, order) { | |
if($.isPlainObject(field)) { | |
order = field.order; | |
field = field.field; | |
} | |
this._clearSortingCss(); | |
this._setSortingParams(field, order); | |
this._setSortingCss(); | |
return this._loadStrategy.sort(); | |
}, | |
_clearSortingCss: function() { | |
this._headerRow.find("th") | |
.removeClass(this.sortAscClass) | |
.removeClass(this.sortDescClass); | |
}, | |
_setSortingParams: function(field, order) { | |
field = this._normalizeField(field); | |
order = order || ((this._sortField === field) ? this._reversedSortOrder(this._sortOrder) : SORT_ORDER_ASC); | |
this._sortField = field; | |
this._sortOrder = order; | |
}, | |
_normalizeField: function(field) { | |
if($.isNumeric(field)) { | |
return this.fields[field]; | |
} | |
if(typeof field === "string") { | |
return $.grep(this.fields, function(f) { | |
return f.name === field; | |
})[0]; | |
} | |
return field; | |
}, | |
_reversedSortOrder: function(order) { | |
return (order === SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC); | |
}, | |
_setSortingCss: function() { | |
var fieldIndex = this._visibleFieldIndex(this._sortField); | |
this._headerRow.find("th").eq(fieldIndex) | |
.addClass(this._sortOrder === SORT_ORDER_ASC ? this.sortAscClass : this.sortDescClass); | |
}, | |
_visibleFieldIndex: function(field) { | |
return $.inArray(field, $.grep(this.fields, function(f) { return f.visible; })); | |
}, | |
_sortData: function() { | |
var sortFactor = this._sortFactor(), | |
sortField = this._sortField; | |
if(sortField) { | |
this.data.sort(function(item1, item2) { | |
return sortFactor * sortField.sortingFunc(item1[sortField.name], item2[sortField.name]); | |
}); | |
} | |
}, | |
_sortFactor: function() { | |
return this._sortOrder === SORT_ORDER_ASC ? 1 : -1; | |
}, | |
_itemsCount: function() { | |
return this._loadStrategy.itemsCount(); | |
}, | |
_pagesCount: function() { | |
var itemsCount = this._itemsCount(), | |
pageSize = this.pageSize; | |
return Math.floor(itemsCount / pageSize) + (itemsCount % pageSize ? 1 : 0); | |
}, | |
_refreshPager: function() { | |
var $pagerContainer = this._pagerContainer; | |
$pagerContainer.empty(); | |
if(this.paging) { | |
$pagerContainer.append(this._createPager()); | |
} | |
var showPager = this.paging && this._pagesCount() > 1; | |
$pagerContainer.toggle(showPager); | |
}, | |
_createPager: function() { | |
var $result; | |
if($.isFunction(this.pagerRenderer)) { | |
$result = $(this.pagerRenderer({ | |
pageIndex: this.pageIndex, | |
pageCount: this._pagesCount() | |
})); | |
} else { | |
$result = $("<div>").append(this._createPagerByFormat()); | |
} | |
$result.addClass(this.pagerClass); | |
return $result; | |
}, | |
_createPagerByFormat: function() { | |
var pageIndex = this.pageIndex, | |
pageCount = this._pagesCount(), | |
itemCount = this._itemsCount(), | |
pagerParts = this.pagerFormat.split(" "); | |
return $.map(pagerParts, $.proxy(function(pagerPart) { | |
var result = pagerPart; | |
if(pagerPart === PAGES_PLACEHOLDER) { | |
result = this._createPages(); | |
} else if(pagerPart === FIRST_PAGE_PLACEHOLDER) { | |
result = this._createPagerNavButton(this.pageFirstText, 1, pageIndex > 1); | |
} else if(pagerPart === PREV_PAGE_PLACEHOLDER) { | |
result = this._createPagerNavButton(this.pagePrevText, pageIndex - 1, pageIndex > 1); | |
} else if(pagerPart === NEXT_PAGE_PLACEHOLDER) { | |
result = this._createPagerNavButton(this.pageNextText, pageIndex + 1, pageIndex < pageCount); | |
} else if(pagerPart === LAST_PAGE_PLACEHOLDER) { | |
result = this._createPagerNavButton(this.pageLastText, pageCount, pageIndex < pageCount); | |
} else if(pagerPart === PAGE_INDEX_PLACEHOLDER) { | |
result = pageIndex; | |
} else if(pagerPart === PAGE_COUNT_PLACEHOLDER) { | |
result = pageCount; | |
} else if(pagerPart === ITEM_COUNT_PLACEHOLDER) { | |
result = itemCount; | |
} | |
return $.isArray(result) ? result.concat([" "]) : [result, " "]; | |
}, this)); | |
}, | |
_createPages: function() { | |
var pageCount = this._pagesCount(), | |
pageButtonCount = this.pageButtonCount, | |
firstDisplayingPage = this._firstDisplayingPage, | |
pages = []; | |
if(firstDisplayingPage > 1) { | |
pages.push(this._createPagerPageNavButton(this.pageNavigatorPrevText, this.showPrevPages)); | |
} | |
for(var i = 0, pageNumber = firstDisplayingPage; i < pageButtonCount && pageNumber <= pageCount; i++, pageNumber++) { | |
pages.push(pageNumber === this.pageIndex | |
? this._createPagerCurrentPage() | |
: this._createPagerPage(pageNumber)); | |
} | |
if((firstDisplayingPage + pageButtonCount - 1) < pageCount) { | |
pages.push(this._createPagerPageNavButton(this.pageNavigatorNextText, this.showNextPages)); | |
} | |
return pages; | |
}, | |
_createPagerNavButton: function(text, pageIndex, isActive) { | |
return this._createPagerButton(text, this.pagerNavButtonClass + (isActive ? "" : " " + this.pagerNavButtonInactiveClass), | |
isActive ? function() { this.openPage(pageIndex); } : $.noop); | |
}, | |
_createPagerPageNavButton: function(text, handler) { | |
return this._createPagerButton(text, this.pagerNavButtonClass, handler); | |
}, | |
_createPagerPage: function(pageIndex) { | |
return this._createPagerButton(pageIndex, this.pageClass, function() { | |
this.openPage(pageIndex); | |
}); | |
}, | |
_createPagerButton: function(text, css, handler) { | |
var $link = $("<a>").attr("href", EMPTY_HREF) | |
.html(text) | |
.on("click", $.proxy(handler, this)); | |
return $("<span>").addClass(css).append($link); | |
}, | |
_createPagerCurrentPage: function() { | |
return $("<span>") | |
.addClass(this.pageClass) | |
.addClass(this.currentPageClass) | |
.text(this.pageIndex); | |
}, | |
_refreshSize: function() { | |
this._refreshHeight(); | |
this._refreshWidth(); | |
}, | |
_refreshWidth: function() { | |
var width = (this.width === "auto") ? this._getAutoWidth() : this.width; | |
this._container.width(width); | |
}, | |
_getAutoWidth: function() { | |
var $headerGrid = this._headerGrid, | |
$header = this._header; | |
$headerGrid.width("auto"); | |
var contentWidth = $headerGrid.outerWidth(); | |
var borderWidth = $header.outerWidth() - $header.innerWidth(); | |
$headerGrid.width(""); | |
return contentWidth + borderWidth; | |
}, | |
_scrollBarWidth: (function() { | |
var result; | |
return function() { | |
if(result === undefined) { | |
var $ghostContainer = $("<div style='width:50px;height:50px;overflow:hidden;position:absolute;top:-10000px;left:-10000px;'></div>"); | |
var $ghostContent = $("<div style='height:100px;'></div>"); | |
$ghostContainer.append($ghostContent).appendTo("body"); | |
var width = $ghostContent.innerWidth(); | |
$ghostContainer.css("overflow-y", "auto"); | |
var widthExcludingScrollBar = $ghostContent.innerWidth(); | |
$ghostContainer.remove(); | |
result = width - widthExcludingScrollBar; | |
} | |
return result; | |
}; | |
})(), | |
_refreshHeight: function() { | |
var container = this._container, | |
pagerContainer = this._pagerContainer, | |
height = this.height, | |
nonBodyHeight; | |
container.height(height); | |
if(height !== "auto") { | |
height = container.height(); | |
nonBodyHeight = this._header.outerHeight(true); | |
if(pagerContainer.parents(container).length) { | |
nonBodyHeight += pagerContainer.outerHeight(true); | |
} | |
this._body.outerHeight(height - nonBodyHeight); | |
} | |
}, | |
showPrevPages: function() { | |
var firstDisplayingPage = this._firstDisplayingPage, | |
pageButtonCount = this.pageButtonCount; | |
this._firstDisplayingPage = (firstDisplayingPage > pageButtonCount) ? firstDisplayingPage - pageButtonCount : 1; | |
this._refreshPager(); | |
}, | |
showNextPages: function() { | |
var firstDisplayingPage = this._firstDisplayingPage, | |
pageButtonCount = this.pageButtonCount, | |
pageCount = this._pagesCount(); | |
this._firstDisplayingPage = (firstDisplayingPage + 2 * pageButtonCount > pageCount) | |
? pageCount - pageButtonCount + 1 | |
: firstDisplayingPage + pageButtonCount; | |
this._refreshPager(); | |
}, | |
openPage: function(pageIndex) { | |
if(pageIndex < 1 || pageIndex > this._pagesCount()) | |
return; | |
this._setPage(pageIndex); | |
this._loadStrategy.openPage(pageIndex); | |
}, | |
_setPage: function(pageIndex) { | |
var firstDisplayingPage = this._firstDisplayingPage, | |
pageButtonCount = this.pageButtonCount; | |
this.pageIndex = pageIndex; | |
if(pageIndex < firstDisplayingPage) { | |
this._firstDisplayingPage = pageIndex; | |
} | |
if(pageIndex > firstDisplayingPage + pageButtonCount - 1) { | |
this._firstDisplayingPage = pageIndex - pageButtonCount + 1; | |
} | |
this._callEventHandler(this.onPageChanged, { | |
pageIndex: pageIndex | |
}); | |
}, | |
_controllerCall: function(method, param, isCanceled, doneCallback) { | |
if(isCanceled) | |
return $.Deferred().reject().promise(); | |
this._showLoading(); | |
var controller = this._controller; | |
if(!controller || !controller[method]) { | |
throw Error("controller has no method '" + method + "'"); | |
} | |
return normalizePromise(controller[method](param)) | |
.done($.proxy(doneCallback, this)) | |
.fail($.proxy(this._errorHandler, this)) | |
.always($.proxy(this._hideLoading, this)); | |
}, | |
_errorHandler: function() { | |
this._callEventHandler(this.onError, { | |
args: $.makeArray(arguments) | |
}); | |
}, | |
_showLoading: function() { | |
if(!this.loadIndication) | |
return; | |
clearTimeout(this._loadingTimer); | |
this._loadingTimer = setTimeout($.proxy(function() { | |
this._loadIndicator.show(); | |
}, this), this.loadIndicationDelay); | |
}, | |
_hideLoading: function() { | |
if(!this.loadIndication) | |
return; | |
clearTimeout(this._loadingTimer); | |
this._loadIndicator.hide(); | |
}, | |
search: function(filter) { | |
this._resetSorting(); | |
this._resetPager(); | |
return this.loadData(filter); | |
}, | |
loadData: function(filter) { | |
filter = filter || (this.filtering ? this.getFilter() : {}); | |
$.extend(filter, this._loadStrategy.loadParams(), this._sortingParams()); | |
var args = this._callEventHandler(this.onDataLoading, { | |
filter: filter | |
}); | |
return this._controllerCall("loadData", filter, args.cancel, function(loadedData) { | |
if(!loadedData) | |
return; | |
this._loadStrategy.finishLoad(loadedData); | |
this._callEventHandler(this.onDataLoaded, { | |
data: loadedData | |
}); | |
}); | |
}, | |
getFilter: function() { | |
var result = {}; | |
this._eachField(function(field) { | |
if(field.filtering) { | |
this._setItemFieldValue(result, field, field.filterValue()); | |
} | |
}); | |
return result; | |
}, | |
_sortingParams: function() { | |
if(this.sorting && this._sortField) { | |
return { | |
sortField: this._sortField.name, | |
sortOrder: this._sortOrder | |
}; | |
} | |
return {}; | |
}, | |
getSorting: function() { | |
var sortingParams = this._sortingParams(); | |
return { | |
field: sortingParams.sortField, | |
order: sortingParams.sortOrder | |
}; | |
}, | |
clearFilter: function() { | |
var $filterRow = this._createFilterRow(); | |
this._filterRow.replaceWith($filterRow); | |
this._filterRow = $filterRow; | |
return this.search(); | |
}, | |
insertItem: function(item) { | |
var insertingItem = item || this._getValidatedInsertItem(); | |
if(!insertingItem) | |
return $.Deferred().reject().promise(); | |
var args = this._callEventHandler(this.onItemInserting, { | |
item: insertingItem | |
}); | |
return this._controllerCall("insertItem", insertingItem, args.cancel, function(insertedItem) { | |
insertedItem = insertedItem || insertingItem; | |
this._loadStrategy.finishInsert(insertedItem); | |
this._callEventHandler(this.onItemInserted, { | |
item: insertedItem | |
}); | |
}); | |
}, | |
_getValidatedInsertItem: function() { | |
var item = this._getInsertItem(); | |
return this._validateItem(item, this._insertRow) ? item : null; | |
}, | |
_getInsertItem: function() { | |
var result = {}; | |
this._eachField(function(field) { | |
if(field.inserting) { | |
this._setItemFieldValue(result, field, field.insertValue()); | |
} | |
}); | |
return result; | |
}, | |
_validateItem: function(item, $row) { | |
var validationErrors = []; | |
var args = { | |
item: item, | |
itemIndex: this._rowIndex($row), | |
row: $row | |
}; | |
this._eachField(function(field) { | |
if(!field.validate || | |
($row === this._insertRow && !field.inserting) || | |
($row === this._getEditRow() && !field.editing)) | |
return; | |
var fieldValue = this._getItemFieldValue(item, field); | |
var errors = this._validation.validate($.extend({ | |
value: fieldValue, | |
rules: field.validate | |
}, args)); | |
this._setCellValidity($row.children().eq(this._visibleFieldIndex(field)), errors); | |
if(!errors.length) | |
return; | |
validationErrors.push.apply(validationErrors, | |
$.map(errors, function(message) { | |
return { field: field, message: message }; | |
})); | |
}); | |
if(!validationErrors.length) | |
return true; | |
var invalidArgs = $.extend({ | |
errors: validationErrors | |
}, args); | |
this._callEventHandler(this.onItemInvalid, invalidArgs); | |
this.invalidNotify(invalidArgs); | |
return false; | |
}, | |
_setCellValidity: function($cell, errors) { | |
$cell | |
.toggleClass(this.invalidClass, !!errors.length) | |
.attr("title", errors.join("\n")); | |
}, | |
clearInsert: function() { | |
var insertRow = this._createInsertRow(); | |
this._insertRow.replaceWith(insertRow); | |
this._insertRow = insertRow; | |
this.refresh(); | |
}, | |
editItem: function(item) { | |
var $row = this.rowByItem(item); | |
if($row.length) { | |
this._editRow($row); | |
} | |
}, | |
rowByItem: function(item) { | |
if(item.jquery || item.nodeType) | |
return $(item); | |
return this._content.find("tr").filter(function() { | |
return $.data(this, JSGRID_ROW_DATA_KEY) === item; | |
}); | |
}, | |
_editRow: function($row) { | |
if(!this.editing) | |
return; | |
var item = $row.data(JSGRID_ROW_DATA_KEY); | |
var args = this._callEventHandler(this.onItemEditing, { | |
row: $row, | |
item: item, | |
itemIndex: this._itemIndex(item) | |
}); | |
if(args.cancel) | |
return; | |
if(this._editingRow) { | |
this.cancelEdit(); | |
} | |
var $editRow = this._createEditRow(item); | |
this._editingRow = $row; | |
$row.hide(); | |
$editRow.insertBefore($row); | |
$row.data(JSGRID_EDIT_ROW_DATA_KEY, $editRow); | |
}, | |
_createEditRow: function(item) { | |
if($.isFunction(this.editRowRenderer)) { | |
return $(this.renderTemplate(this.editRowRenderer, this, { item: item, itemIndex: this._itemIndex(item) })); | |
} | |
var $result = $("<tr>").addClass(this.editRowClass); | |
this._eachField(function(field) { | |
var fieldValue = this._getItemFieldValue(item, field); | |
this._prepareCell("<td>", field, "editcss") | |
.append(this.renderTemplate(field.editTemplate || "", field, { value: fieldValue, item: item })) | |
.appendTo($result); | |
}); | |
return $result; | |
}, | |
updateItem: function(item, editedItem) { | |
if(arguments.length === 1) { | |
editedItem = item; | |
} | |
var $row = item ? this.rowByItem(item) : this._editingRow; | |
editedItem = editedItem || this._getValidatedEditedItem(); | |
if(!editedItem) | |
return; | |
return this._updateRow($row, editedItem); | |
}, | |
_getValidatedEditedItem: function() { | |
var item = this._getEditedItem(); | |
return this._validateItem(item, this._getEditRow()) ? item : null; | |
}, | |
_updateRow: function($updatingRow, editedItem) { | |
var updatingItem = $updatingRow.data(JSGRID_ROW_DATA_KEY), | |
updatingItemIndex = this._itemIndex(updatingItem), | |
updatedItem = $.extend(true, {}, updatingItem, editedItem); | |
var args = this._callEventHandler(this.onItemUpdating, { | |
row: $updatingRow, | |
item: updatedItem, | |
itemIndex: updatingItemIndex, | |
previousItem: updatingItem | |
}); | |
return this._controllerCall("updateItem", updatedItem, args.cancel, function(loadedUpdatedItem) { | |
var previousItem = $.extend(true, {}, updatingItem); | |
updatedItem = loadedUpdatedItem || $.extend(true, updatingItem, editedItem); | |
var $updatedRow = this._finishUpdate($updatingRow, updatedItem, updatingItemIndex); | |
this._callEventHandler(this.onItemUpdated, { | |
row: $updatedRow, | |
item: updatedItem, | |
itemIndex: updatingItemIndex, | |
previousItem: previousItem | |
}); | |
}); | |
}, | |
_rowIndex: function(row) { | |
return this._content.children().index($(row)); | |
}, | |
_itemIndex: function(item) { | |
return $.inArray(item, this.data); | |
}, | |
_finishUpdate: function($updatingRow, updatedItem, updatedItemIndex) { | |
this.cancelEdit(); | |
this.data[updatedItemIndex] = updatedItem; | |
var $updatedRow = this._createRow(updatedItem, updatedItemIndex); | |
$updatingRow.replaceWith($updatedRow); | |
return $updatedRow; | |
}, | |
_getEditedItem: function() { | |
var result = {}; | |
this._eachField(function(field) { | |
if(field.editing) { | |
this._setItemFieldValue(result, field, field.editValue()); | |
} | |
}); | |
return result; | |
}, | |
cancelEdit: function() { | |
if(!this._editingRow) | |
return; | |
this._getEditRow().remove(); | |
this._editingRow.show(); | |
this._editingRow = null; | |
}, | |
_getEditRow: function() { | |
return this._editingRow && this._editingRow.data(JSGRID_EDIT_ROW_DATA_KEY); | |
}, | |
deleteItem: function(item) { | |
var $row = this.rowByItem(item); | |
if(!$row.length) | |
return; | |
if(this.confirmDeleting && !window.confirm(getOrApply(this.deleteConfirm, this, $row.data(JSGRID_ROW_DATA_KEY)))) | |
return; | |
return this._deleteRow($row); | |
}, | |
_deleteRow: function($row) { | |
var deletingItem = $row.data(JSGRID_ROW_DATA_KEY), | |
deletingItemIndex = this._itemIndex(deletingItem); | |
var args = this._callEventHandler(this.onItemDeleting, { | |
row: $row, | |
item: deletingItem, | |
itemIndex: deletingItemIndex | |
}); | |
return this._controllerCall("deleteItem", deletingItem, args.cancel, function() { | |
this._loadStrategy.finishDelete(deletingItem, deletingItemIndex); | |
this._callEventHandler(this.onItemDeleted, { | |
row: $row, | |
item: deletingItem, | |
itemIndex: deletingItemIndex | |
}); | |
}); | |
} | |
}; | |
$.fn.jsGrid = function(config) { | |
var args = $.makeArray(arguments), | |
methodArgs = args.slice(1), | |
result = this; | |
this.each(function() { | |
var $element = $(this), | |
instance = $element.data(JSGRID_DATA_KEY), | |
methodResult; | |
if(instance) { | |
if(typeof config === "string") { | |
methodResult = instance[config].apply(instance, methodArgs); | |
if(methodResult !== undefined && methodResult !== instance) { | |
result = methodResult; | |
return false; | |
} | |
} else { | |
instance._detachWindowResizeCallback(); | |
instance._init(config); | |
instance.render(); | |
} | |
} else { | |
new Grid($element, config); | |
} | |
}); | |
return result; | |
}; | |
var fields = {}; | |
var setDefaults = function(config) { | |
var componentPrototype; | |
if($.isPlainObject(config)) { | |
componentPrototype = Grid.prototype; | |
} else { | |
componentPrototype = fields[config].prototype; | |
config = arguments[1] || {}; | |
} | |
$.extend(componentPrototype, config); | |
}; | |
var locales = {}; | |
var locale = function(lang) { | |
var localeConfig = $.isPlainObject(lang) ? lang : locales[lang]; | |
if(!localeConfig) | |
throw Error("unknown locale " + lang); | |
setLocale(jsGrid, localeConfig); | |
}; | |
var setLocale = function(obj, localeConfig) { | |
$.each(localeConfig, function(field, value) { | |
if($.isPlainObject(value)) { | |
setLocale(obj[field] || obj[field[0].toUpperCase() + field.slice(1)], value); | |
return; | |
} | |
if(obj.hasOwnProperty(field)) { | |
obj[field] = value; | |
} else { | |
obj.prototype[field] = value; | |
} | |
}); | |
}; | |
window.jsGrid = { | |
Grid: Grid, | |
fields: fields, | |
setDefaults: setDefaults, | |
locales: locales, | |
locale: locale, | |
version: '1.5.3' | |
}; | |
}(window, jQuery)); | |
(function(jsGrid, $, undefined) { | |
function LoadIndicator(config) { | |
this._init(config); | |
} | |
LoadIndicator.prototype = { | |
container: "body", | |
message: "Loading...", | |
shading: true, | |
zIndex: 1000, | |
shaderClass: "jsgrid-load-shader", | |
loadPanelClass: "jsgrid-load-panel", | |
_init: function(config) { | |
$.extend(true, this, config); | |
this._initContainer(); | |
this._initShader(); | |
this._initLoadPanel(); | |
}, | |
_initContainer: function() { | |
this._container = $(this.container); | |
}, | |
_initShader: function() { | |
if(!this.shading) | |
return; | |
this._shader = $("<div>").addClass(this.shaderClass) | |
.hide() | |
.css({ | |
position: "absolute", | |
top: 0, | |
right: 0, | |
bottom: 0, | |
left: 0, | |
zIndex: this.zIndex | |
}) | |
.appendTo(this._container); | |
}, | |
_initLoadPanel: function() { | |
this._loadPanel = $("<div>").addClass(this.loadPanelClass) | |
.text(this.message) | |
.hide() | |
.css({ | |
position: "absolute", | |
top: "50%", | |
left: "50%", | |
zIndex: this.zIndex | |
}) | |
.appendTo(this._container); | |
}, | |
show: function() { | |
var $loadPanel = this._loadPanel.show(); | |
var actualWidth = $loadPanel.outerWidth(); | |
var actualHeight = $loadPanel.outerHeight(); | |
$loadPanel.css({ | |
marginTop: -actualHeight / 2, | |
marginLeft: -actualWidth / 2 | |
}); | |
this._shader.show(); | |
}, | |
hide: function() { | |
this._loadPanel.hide(); | |
this._shader.hide(); | |
} | |
}; | |
jsGrid.LoadIndicator = LoadIndicator; | |
}(jsGrid, jQuery)); | |
(function(jsGrid, $, undefined) { | |
function DirectLoadingStrategy(grid) { | |
this._grid = grid; | |
} | |
DirectLoadingStrategy.prototype = { | |
firstDisplayIndex: function() { | |
var grid = this._grid; | |
return grid.option("paging") ? (grid.option("pageIndex") - 1) * grid.option("pageSize") : 0; | |
}, | |
lastDisplayIndex: function() { | |
var grid = this._grid; | |
var itemsCount = grid.option("data").length; | |
return grid.option("paging") | |
? Math.min(grid.option("pageIndex") * grid.option("pageSize"), itemsCount) | |
: itemsCount; | |
}, | |
itemsCount: function() { | |
return this._grid.option("data").length; | |
}, | |
openPage: function(index) { | |
this._grid.refresh(); | |
}, | |
loadParams: function() { | |
return {}; | |
}, | |
sort: function() { | |
this._grid._sortData(); | |
this._grid.refresh(); | |
return $.Deferred().resolve().promise(); | |
}, | |
reset: function() { | |
this._grid.refresh(); | |
return $.Deferred().resolve().promise(); | |
}, | |
finishLoad: function(loadedData) { | |
this._grid.option("data", loadedData); | |
}, | |
finishInsert: function(insertedItem) { | |
var grid = this._grid; | |
grid.option("data").push(insertedItem); | |
grid.refresh(); | |
}, | |
finishDelete: function(deletedItem, deletedItemIndex) { | |
var grid = this._grid; | |
grid.option("data").splice(deletedItemIndex, 1); | |
grid.reset(); | |
} | |
}; | |
function PageLoadingStrategy(grid) { | |
this._grid = grid; | |
this._itemsCount = 0; | |
} | |
PageLoadingStrategy.prototype = { | |
firstDisplayIndex: function() { | |
return 0; | |
}, | |
lastDisplayIndex: function() { | |
return this._grid.option("data").length; | |
}, | |
itemsCount: function() { | |
return this._itemsCount; | |
}, | |
openPage: function(index) { | |
this._grid.loadData(); | |
}, | |
loadParams: function() { | |
var grid = this._grid; | |
return { | |
pageIndex: grid.option("pageIndex"), | |
pageSize: grid.option("pageSize") | |
}; | |
}, | |
reset: function() { | |
return this._grid.loadData(); | |
}, | |
sort: function() { | |
return this._grid.loadData(); | |
}, | |
finishLoad: function(loadedData) { | |
this._itemsCount = loadedData.itemsCount; | |
this._grid.option("data", loadedData.data); | |
}, | |
finishInsert: function(insertedItem) { | |
this._grid.search(); | |
}, | |
finishDelete: function(deletedItem, deletedItemIndex) { | |
this._grid.search(); | |
} | |
}; | |
jsGrid.loadStrategies = { | |
DirectLoadingStrategy: DirectLoadingStrategy, | |
PageLoadingStrategy: PageLoadingStrategy | |
}; | |
}(jsGrid, jQuery)); | |
(function(jsGrid, $, undefined) { | |
var isDefined = function(val) { | |
return typeof(val) !== "undefined" && val !== null; | |
}; | |
var sortStrategies = { | |
string: function(str1, str2) { | |
if(!isDefined(str1) && !isDefined(str2)) | |
return 0; | |
if(!isDefined(str1)) | |
return -1; | |
if(!isDefined(str2)) | |
return 1; | |
return ("" + str1).localeCompare("" + str2); | |
}, | |
number: function(n1, n2) { | |
return n1 - n2; | |
}, | |
date: function(dt1, dt2) { | |
return dt1 - dt2; | |
}, | |
numberAsString: function(n1, n2) { | |
return parseFloat(n1) - parseFloat(n2); | |
} | |
}; | |
jsGrid.sortStrategies = sortStrategies; | |
}(jsGrid, jQuery)); | |
(function(jsGrid, $, undefined) { | |
function Validation(config) { | |
this._init(config); | |
} | |
Validation.prototype = { | |
_init: function(config) { | |
$.extend(true, this, config); | |
}, | |
validate: function(args) { | |
var errors = []; | |
$.each(this._normalizeRules(args.rules), function(_, rule) { | |
if(rule.validator(args.value, args.item, rule.param)) | |
return; | |
var errorMessage = $.isFunction(rule.message) ? rule.message(args.value, args.item) : rule.message; | |
errors.push(errorMessage); | |
}); | |
return errors; | |
}, | |
_normalizeRules: function(rules) { | |
if(!$.isArray(rules)) | |
rules = [rules]; | |
return $.map(rules, $.proxy(function(rule) { | |
return this._normalizeRule(rule); | |
}, this)); | |
}, | |
_normalizeRule: function(rule) { | |
if(typeof rule === "string") | |
rule = { validator: rule }; | |
if($.isFunction(rule)) | |
rule = { validator: rule }; | |
if($.isPlainObject(rule)) | |
rule = $.extend({}, rule); | |
else | |
throw Error("wrong validation config specified"); | |
if($.isFunction(rule.validator)) | |
return rule; | |
return this._applyNamedValidator(rule, rule.validator); | |
}, | |
_applyNamedValidator: function(rule, validatorName) { | |
delete rule.validator; | |
var validator = validators[validatorName]; | |
if(!validator) | |
throw Error("unknown validator \"" + validatorName + "\""); | |
if($.isFunction(validator)) { | |
validator = { validator: validator }; | |
} | |
return $.extend({}, validator, rule); | |
} | |
}; | |
jsGrid.Validation = Validation; | |
var validators = { | |
required: { | |
message: "Field is required", | |
validator: function(value) { | |
return value !== undefined && value !== null && value !== ""; | |
} | |
}, | |
rangeLength: { | |
message: "Field value length is out of the defined range", | |
validator: function(value, _, param) { | |
return value.length >= param[0] && value.length <= param[1]; | |
} | |
}, | |
minLength: { | |
message: "Field value is too short", | |
validator: function(value, _, param) { | |
return value.length >= param; | |
} | |
}, | |
maxLength: { | |
message: "Field value is too long", | |
validator: function(value, _, param) { | |
return value.length <= param; | |
} | |
}, | |
pattern: { | |
message: "Field value is not matching the defined pattern", | |
validator: function(value, _, param) { | |
if(typeof param === "string") { | |
param = new RegExp("^(?:" + param + ")$"); | |
} | |
return param.test(value); | |
} | |
}, | |
range: { | |
message: "Field value is out of the defined range", | |
validator: function(value, _, param) { | |
return value >= param[0] && value <= param[1]; | |
} | |
}, | |
min: { | |
message: "Field value is too small", | |
validator: function(value, _, param) { | |
return value >= param; | |
} | |
}, | |
max: { | |
message: "Field value is too large", | |
validator: function(value, _, param) { | |
return value <= param; | |
} | |
} | |
}; | |
jsGrid.validators = validators; | |
}(jsGrid, jQuery)); | |
(function(jsGrid, $, undefined) { | |
function Field(config) { | |
$.extend(true, this, config); | |
this.sortingFunc = this._getSortingFunc(); | |
} | |
Field.prototype = { | |
name: "", | |
title: null, | |
css: "", | |
align: "", | |
width: 100, | |
visible: true, | |
filtering: true, | |
inserting: true, | |
editing: true, | |
sorting: true, | |
sorter: "string", // name of SortStrategy or function to compare elements | |
headerTemplate: function() { | |
return (this.title === undefined || this.title === null) ? this.name : this.title; | |
}, | |
itemTemplate: function(value, item) { | |
return value; | |
}, | |
filterTemplate: function() { | |
return ""; | |
}, | |
insertTemplate: function() { | |
return ""; | |
}, | |
editTemplate: function(value, item) { | |
this._value = value; | |
return this.itemTemplate(value, item); | |
}, | |
filterValue: function() { | |
return ""; | |
}, | |
insertValue: function() { | |
return ""; | |
}, | |
editValue: function() { | |
return this._value; | |
}, | |
_getSortingFunc: function() { | |
var sorter = this.sorter; | |
if($.isFunction(sorter)) { | |
return sorter; | |
} | |
if(typeof sorter === "string") { | |
return jsGrid.sortStrategies[sorter]; | |
} | |
throw Error("wrong sorter for the field \"" + this.name + "\"!"); | |
} | |
}; | |
jsGrid.Field = Field; | |
}(jsGrid, jQuery)); | |
(function(jsGrid, $, undefined) { | |
var Field = jsGrid.Field; | |
function TextField(config) { | |
Field.call(this, config); | |
} | |
TextField.prototype = new Field({ | |
autosearch: true, | |
readOnly: false, | |
filterTemplate: function() { | |
if(!this.filtering) | |
return ""; | |
var grid = this._grid, | |
$result = this.filterControl = this._createTextBox(); | |
if(this.autosearch) { | |
$result.on("keypress", function(e) { | |
if(e.which === 13) { | |
grid.search(); | |
e.preventDefault(); | |
} | |
}); | |
} | |
return $result; | |
}, | |
insertTemplate: function() { | |
if(!this.inserting) | |
return ""; | |
return this.insertControl = this._createTextBox(); | |
}, | |
editTemplate: function(value) { | |
if(!this.editing) | |
return this.itemTemplate.apply(this, arguments); | |
var $result = this.editControl = this._createTextBox(); | |
$result.val(value); | |
return $result; | |
}, | |
filterValue: function() { | |
return this.filterControl.val(); | |
}, | |
insertValue: function() { | |
return this.insertControl.val(); | |
}, | |
editValue: function() { | |
return this.editControl.val(); | |
}, | |
_createTextBox: function() { | |
return $("<input>").attr("type", "text") | |
.prop("readonly", !!this.readOnly); | |
} | |
}); | |
jsGrid.fields.text = jsGrid.TextField = TextField; | |
}(jsGrid, jQuery)); | |
(function(jsGrid, $, undefined) { | |
var TextField = jsGrid.TextField; | |
function NumberField(config) { | |
TextField.call(this, config); | |
} | |
NumberField.prototype = new TextField({ | |
sorter: "number", | |
align: "right", | |
readOnly: false, | |
filterValue: function() { | |
return this.filterControl.val() | |
? parseInt(this.filterControl.val() || 0, 10) | |
: undefined; | |
}, | |
insertValue: function() { | |
return this.insertControl.val() | |
? parseInt(this.insertControl.val() || 0, 10) | |
: undefined; | |
}, | |
editValue: function() { | |
return this.editControl.val() | |
? parseInt(this.editControl.val() || 0, 10) | |
: undefined; | |
}, | |
_createTextBox: function() { | |
return $("<input>").attr("type", "number") | |
.prop("readonly", !!this.readOnly); | |
} | |
}); | |
jsGrid.fields.number = jsGrid.NumberField = NumberField; | |
}(jsGrid, jQuery)); | |
(function(jsGrid, $, undefined) { | |
var TextField = jsGrid.TextField; | |
function TextAreaField(config) { | |
TextField.call(this, config); | |
} | |
TextAreaField.prototype = new TextField({ | |
insertTemplate: function() { | |
if(!this.inserting) | |
return ""; | |
return this.insertControl = this._createTextArea(); | |
}, | |
editTemplate: function(value) { | |
if(!this.editing) | |
return this.itemTemplate.apply(this, arguments); | |
var $result = this.editControl = this._createTextArea(); | |
$result.val(value); | |
return $result; | |
}, | |
_createTextArea: function() { | |
return $("<textarea>").prop("readonly", !!this.readOnly); | |
} | |
}); | |
jsGrid.fields.textarea = jsGrid.TextAreaField = TextAreaField; | |
}(jsGrid, jQuery)); | |
(function(jsGrid, $, undefined) { | |
var NumberField = jsGrid.NumberField; | |
var numberValueType = "number"; | |
var stringValueType = "string"; | |
function SelectField(config) { | |
this.items = []; | |
this.selectedIndex = -1; | |
this.valueField = ""; | |
this.textField = ""; | |
if(config.valueField && config.items.length) { | |
var firstItemValue = config.items[0][config.valueField]; | |
this.valueType = (typeof firstItemValue) === numberValueType ? numberValueType : stringValueType; | |
} | |
this.sorter = this.valueType; | |
NumberField.call(this, config); | |
} | |
SelectField.prototype = new NumberField({ | |
align: "center", | |
valueType: numberValueType, | |
itemTemplate: function(value) { | |
var items = this.items, | |
valueField = this.valueField, | |
textField = this.textField, | |
resultItem; | |
if(valueField) { | |
resultItem = $.grep(items, function(item, index) { | |
return item[valueField] === value; | |
})[0] || {}; | |
} | |
else { | |
resultItem = items[value]; | |
} | |
var result = (textField ? resultItem[textField] : resultItem); | |
return (result === undefined || result === null) ? "" : result; | |
}, | |
filterTemplate: function() { | |
if(!this.filtering) | |
return ""; | |
var grid = this._grid, | |
$result = this.filterControl = this._createSelect(); | |
if(this.autosearch) { | |
$result.on("change", function(e) { | |
grid.search(); | |
}); | |
} | |
return $result; | |
}, | |
insertTemplate: function() { | |
if(!this.inserting) | |
return ""; | |
return this.insertControl = this._createSelect(); | |
}, | |
editTemplate: function(value) { | |
if(!this.editing) | |
return this.itemTemplate.apply(this, arguments); | |
var $result = this.editControl = this._createSelect(); | |
(value !== undefined) && $result.val(value); | |
return $result; | |
}, | |
filterValue: function() { | |
var val = this.filterControl.val(); | |
return this.valueType === numberValueType ? parseInt(val || 0, 10) : val; | |
}, | |
insertValue: function() { | |
var val = this.insertControl.val(); | |
return this.valueType === numberValueType ? parseInt(val || 0, 10) : val; | |
}, | |
editValue: function() { | |
var val = this.editControl.val(); | |
return this.valueType === numberValueType ? parseInt(val || 0, 10) : val; | |
}, | |
_createSelect: function() { | |
var $result = $("<select>"), | |
valueField = this.valueField, | |
textField = this.textField, | |
selectedIndex = this.selectedIndex; | |
$.each(this.items, function(index, item) { | |
var value = valueField ? item[valueField] : index, | |
text = textField ? item[textField] : item; | |
var $option = $("<option>") | |
.attr("value", value) | |
.text(text) | |
.appendTo($result); | |
$option.prop("selected", (selectedIndex === index)); | |
}); | |
$result.prop("disabled", !!this.readOnly); | |
return $result; | |
} | |
}); | |
jsGrid.fields.select = jsGrid.SelectField = SelectField; | |
}(jsGrid, jQuery)); | |
(function(jsGrid, $, undefined) { | |
var Field = jsGrid.Field; | |
function CheckboxField(config) { | |
Field.call(this, config); | |
} | |
CheckboxField.prototype = new Field({ | |
sorter: "number", | |
align: "center", | |
autosearch: true, | |
itemTemplate: function(value) { | |
return this._createCheckbox().prop({ | |
checked: value, | |
disabled: true | |
}); | |
}, | |
filterTemplate: function() { | |
if(!this.filtering) | |
return ""; | |
var grid = this._grid, | |
$result = this.filterControl = this._createCheckbox(); | |
$result.prop({ | |
readOnly: true, | |
indeterminate: true | |
}); | |
$result.on("click", function() { | |
var $cb = $(this); | |
if($cb.prop("readOnly")) { | |
$cb.prop({ | |
checked: false, | |
readOnly: false | |
}); | |
} | |
else if(!$cb.prop("checked")) { | |
$cb.prop({ | |
readOnly: true, | |
indeterminate: true | |
}); | |
} | |
}); | |
if(this.autosearch) { | |
$result.on("click", function() { | |
grid.search(); | |
}); | |
} | |
return $result; | |
}, | |
insertTemplate: function() { | |
if(!this.inserting) | |
return ""; | |
return this.insertControl = this._createCheckbox(); | |
}, | |
editTemplate: function(value) { | |
if(!this.editing) | |
return this.itemTemplate.apply(this, arguments); | |
var $result = this.editControl = this._createCheckbox(); | |
$result.prop("checked", value); | |
return $result; | |
}, | |
filterValue: function() { | |
return this.filterControl.get(0).indeterminate | |
? undefined | |
: this.filterControl.is(":checked"); | |
}, | |
insertValue: function() { | |
return this.insertControl.is(":checked"); | |
}, | |
editValue: function() { | |
return this.editControl.is(":checked"); | |
}, | |
_createCheckbox: function() { | |
return $("<input>").attr("type", "checkbox"); | |
} | |
}); | |
jsGrid.fields.checkbox = jsGrid.CheckboxField = CheckboxField; | |
}(jsGrid, jQuery)); | |
(function(jsGrid, $, undefined) { | |
var Field = jsGrid.Field; | |
function ControlField(config) { | |
Field.call(this, config); | |
this._configInitialized = false; | |
} | |
ControlField.prototype = new Field({ | |
css: "jsgrid-control-field", | |
align: "center", | |
width: 50, | |
filtering: false, | |
inserting: false, | |
editing: false, | |
sorting: false, | |
buttonClass: "jsgrid-button", | |
modeButtonClass: "jsgrid-mode-button", | |
modeOnButtonClass: "jsgrid-mode-on-button", | |
searchModeButtonClass: "jsgrid-search-mode-button", | |
insertModeButtonClass: "jsgrid-insert-mode-button", | |
editButtonClass: "jsgrid-edit-button", | |
deleteButtonClass: "jsgrid-delete-button", | |
searchButtonClass: "jsgrid-search-button", | |
clearFilterButtonClass: "jsgrid-clear-filter-button", | |
insertButtonClass: "jsgrid-insert-button", | |
updateButtonClass: "jsgrid-update-button", | |
cancelEditButtonClass: "jsgrid-cancel-edit-button", | |
searchModeButtonTooltip: "Switch to searching", | |
insertModeButtonTooltip: "Switch to inserting", | |
editButtonTooltip: "Edit", | |
deleteButtonTooltip: "Delete", | |
searchButtonTooltip: "Search", | |
clearFilterButtonTooltip: "Clear filter", | |
insertButtonTooltip: "Insert", | |
updateButtonTooltip: "Update", | |
cancelEditButtonTooltip: "Cancel edit", | |
editButton: true, | |
deleteButton: true, | |
clearFilterButton: true, | |
modeSwitchButton: true, | |
_initConfig: function() { | |
this._hasFiltering = this._grid.filtering; | |
this._hasInserting = this._grid.inserting; | |
if(this._hasInserting && this.modeSwitchButton) { | |
this._grid.inserting = false; | |
} | |
this._configInitialized = true; | |
}, | |
headerTemplate: function() { | |
if(!this._configInitialized) { | |
this._initConfig(); | |
} | |
var hasFiltering = this._hasFiltering; | |
var hasInserting = this._hasInserting; | |
if(!this.modeSwitchButton || (!hasFiltering && !hasInserting)) | |
return ""; | |
if(hasFiltering && !hasInserting) | |
return this._createFilterSwitchButton(); | |
if(hasInserting && !hasFiltering) | |
return this._createInsertSwitchButton(); | |
return this._createModeSwitchButton(); | |
}, | |
itemTemplate: function(value, item) { | |
var $result = $([]); | |
if(this.editButton) { | |
$result = $result.add(this._createEditButton(item)); | |
} | |
if(this.deleteButton) { | |
$result = $result.add(this._createDeleteButton(item)); | |
} | |
return $result; | |
}, | |
filterTemplate: function() { | |
var $result = this._createSearchButton(); | |
return this.clearFilterButton ? $result.add(this._createClearFilterButton()) : $result; | |
}, | |
insertTemplate: function() { | |
return this._createInsertButton(); | |
}, | |
editTemplate: function() { | |
return this._createUpdateButton().add(this._createCancelEditButton()); | |
}, | |
_createFilterSwitchButton: function() { | |
return this._createOnOffSwitchButton("filtering", this.searchModeButtonClass, true); | |
}, | |
_createInsertSwitchButton: function() { | |
return this._createOnOffSwitchButton("inserting", this.insertModeButtonClass, false); | |
}, | |
_createOnOffSwitchButton: function(option, cssClass, isOnInitially) { | |
var isOn = isOnInitially; | |
var updateButtonState = $.proxy(function() { | |
$button.toggleClass(this.modeOnButtonClass, isOn); | |
}, this); | |
var $button = this._createGridButton(this.modeButtonClass + " " + cssClass, "", function(grid) { | |
isOn = !isOn; | |
grid.option(option, isOn); | |
updateButtonState(); | |
}); | |
updateButtonState(); | |
return $button; | |
}, | |
_createModeSwitchButton: function() { | |
var isInserting = false; | |
var updateButtonState = $.proxy(function() { | |
$button.attr("title", isInserting ? this.searchModeButtonTooltip : this.insertModeButtonTooltip) | |
.toggleClass(this.insertModeButtonClass, !isInserting) | |
.toggleClass(this.searchModeButtonClass, isInserting); | |
}, this); | |
var $button = this._createGridButton(this.modeButtonClass, "", function(grid) { | |
isInserting = !isInserting; | |
grid.option("inserting", isInserting); | |
grid.option("filtering", !isInserting); | |
updateButtonState(); | |
}); | |
updateButtonState(); | |
return $button; | |
}, | |
_createEditButton: function(item) { | |
return this._createGridButton(this.editButtonClass, this.editButtonTooltip, function(grid, e) { | |
grid.editItem(item); | |
e.stopPropagation(); | |
}); | |
}, | |
_createDeleteButton: function(item) { | |
return this._createGridButton(this.deleteButtonClass, this.deleteButtonTooltip, function(grid, e) { | |
grid.deleteItem(item); | |
e.stopPropagation(); | |
}); | |
}, | |
_createSearchButton: function() { | |
return this._createGridButton(this.searchButtonClass, this.searchButtonTooltip, function(grid) { | |
grid.search(); | |
}); | |
}, | |
_createClearFilterButton: function() { | |
return this._createGridButton(this.clearFilterButtonClass, this.clearFilterButtonTooltip, function(grid) { | |
grid.clearFilter(); | |
}); | |
}, | |
_createInsertButton: function() { | |
return this._createGridButton(this.insertButtonClass, this.insertButtonTooltip, function(grid) { | |
grid.insertItem().done(function() { | |
grid.clearInsert(); | |
}); | |
}); | |
}, | |
_createUpdateButton: function() { | |
return this._createGridButton(this.updateButtonClass, this.updateButtonTooltip, function(grid, e) { | |
grid.updateItem(); | |
e.stopPropagation(); | |
}); | |
}, | |
_createCancelEditButton: function() { | |
return this._createGridButton(this.cancelEditButtonClass, this.cancelEditButtonTooltip, function(grid, e) { | |
grid.cancelEdit(); | |
e.stopPropagation(); | |
}); | |
}, | |
_createGridButton: function(cls, tooltip, clickHandler) { | |
var grid = this._grid; | |
return $("<input>").addClass(this.buttonClass) | |
.addClass(cls) | |
.attr({ | |
type: "button", | |
title: tooltip | |
}) | |
.on("click", function(e) { | |
clickHandler(grid, e); | |
}); | |
}, | |
editValue: function() { | |
return ""; | |
} | |
}); | |
jsGrid.fields.control = jsGrid.ControlField = ControlField; | |
}(jsGrid, jQuery)); | |