You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AdminLTE/plugins/jsgrid/jsgrid.js

2517 lines
74 KiB

/*
* 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));