/* * jsGrid v1.0.1 (http://js-grid.com) * (c) 2015 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}", EMPTY_HREF = "javascript:void(0);"; var getOrApply = function(value, context) { if($.isFunction(value)) { return value.apply(context, $.makeArray(arguments).slice(2)); } return value; }; 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")); } }, noDataContent: "Not found", noDataRowClass: "jsgrid-nodata-row", heading: true, headerRowRenderer: null, headerRowClass: "jsgrid-header-row", 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", 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", pageClass: "jsgrid-pager-page", currentPageClass: "jsgrid-pager-current-page", pageLoading: false, autoload: false, controller: defaultController, loadIndication: true, loadIndicationDelay: 500, loadMessage: "Please, wait...", loadShading: true, onRefreshing: $.noop, onRefreshed: $.noop, onItemDeleting: $.noop, onItemDeleted: $.noop, onItemInserting: $.noop, onItemInserted: $.noop, onItemUpdating: $.noop, onItemUpdated: $.noop, onDataLoading: $.noop, onDataLoaded: $.noop, onOptionChanging: $.noop, onOptionChanged: $.noop, onError: $.noop, 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(); }, 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)); }, loadIndicator: function(config) { return new jsGrid.LoadIndicator(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); }, _handleOptionChange: function(name, value) { this[name] = value; switch(name) { case "width": case "height": this._refreshSize(); break; case "rowClass": case "rowRenderer": case "rowClick": case "noDataText": 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": 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 "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; default: this.render(); break; } }, destroy: function() { this._detachWindowResizeCallback(); this._clear(); this._container.removeData(JSGRID_DATA_KEY); }, render: 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.refresh(); return this.autoload ? this.loadData() : $.Deferred().resolve().promise(); }, _createLoadIndicator: function() { return getOrApply(this.loadIndicator, this, { message: this.loadMessage, shading: this.loadShading, container: this._container }); }, _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 = $("").addClass(this.tableClass) .append($headerRow) .append($filterRow) .append($insertRow); var $header = this._header = $("
").addClass(this.gridHeaderClass) .append($headerGrid); return $header; }, _createBody: function() { var $content = this._content = $("
"); var $bodyGrid = this._bodyGrid = $("
").addClass(this.tableClass) .append($content); var $body = this._body = $("
").addClass(this.gridBodyClass) .append($bodyGrid); return $body; }, _createPagerContainer: function() { var pagerContainer = this.pagerContainer || $("
").appendTo(this._container); return $(pagerContainer).addClass(this.pagerContainerClass); }, _eachField: function(callBack) { var self = this; $.each(this.fields, function(index, field) { return callBack.call(self, field, index); }); }, _createHeaderRow: function() { if($.isFunction(this.headerRowRenderer)) { return $(this.headerRowRenderer()); } var $result = $("
").addClass(this.headerRowClass); this._eachField(function(field, index) { var $th = $("").addClass(this.filterRowClass); this._eachField(function(field) { $("").addClass(this.insertRowClass); this._eachField(function(field) { $("").addClass(this.noDataRowClass) .append($(""); 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)); 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 = item[field.name]; if($.isFunction(field.cellRenderer)) { $result = $(field.cellRenderer(fieldValue, item)); } else { $result = $("").addClass(this.editRowClass); this._eachField(function(field) { $("
").addClass(field.css) .appendTo($result) .append(field.headerTemplate ? field.headerTemplate() : "") .css("width", field.width); if(this.sorting && field.sorting) { $th.addClass(this.sortableClass) .on("click", $.proxy(function() { this.sort(index); }, this)); } }); return $result; }, _createFilterRow: function() { if($.isFunction(this.filterRowRenderer)) { return $(this.filterRowRenderer()); } var $result = $("
").addClass(field.css) .appendTo($result) .append(field.filterTemplate ? field.filterTemplate() : "") .width(field.width); }); return $result; }, _createInsertRow: function() { if($.isFunction(this.insertRowRenderer)) { return $(this.insertRowRenderer()); } var $result = $("
").addClass(field.css) .appendTo($result) .append(field.insertTemplate ? field.insertTemplate() : "") .width(field.width); }); return $result; }, _callEventHandler: function(handler, eventParams) { return handler.call(this, $.extend(eventParams, { grid: this })); }, reset: function() { this._resetSorting(); this._resetPager(); this.refresh(); }, _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 noDataContent = getOrApply(this.noDataContent, this); return $("
").attr("colspan", this.fields.length).append(noDataContent)); }, _createNoDataContent: function () { return $.isFunction(this.noDataRenderer) ? this.noDataRenderer() : this.noDataText; }, _createRow: function(item, itemIndex) { var $result; if($.isFunction(this.rowRenderer)) { $result = $(this.rowRenderer(item, itemIndex)); } else { $result = $("
").append(field.itemTemplate ? field.itemTemplate(fieldValue, item) : fieldValue); } $result.addClass(field.css) .width(field.width); field.align && $result.addClass("jsgrid-align-" + field.align); return $result; }, 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._normalizeSortingField(field); order = order || ((this._sortField === field) ? this._reversedSortOrder(this._sortOrder) : SORT_ORDER_ASC); this._sortField = field; this._sortOrder = order; }, _normalizeSortingField: 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 = $.inArray(this._sortField, this.fields); this._headerRow.find("th").eq(fieldIndex) .addClass(this._sortOrder === SORT_ORDER_ASC ? this.sortAscClass : this.sortDescClass); }, _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 && this._pagesCount() > 1) { $pagerContainer.show() .append(this._createPager()); } else { $pagerContainer.hide(); } }, _createPager: function() { var pageIndex = this.pageIndex, pageCount = this._pagesCount(), pagerParts = this.pagerFormat.split(" "); pagerParts = $.map(pagerParts, $.proxy(function(pagerPart) { var result = pagerPart; if(pagerPart === PAGES_PLACEHOLDER) { result = this._createPages(); } else if(pagerPart === FIRST_PAGE_PLACEHOLDER) { result = pageIndex > 1 ? this._createPagerNavButton(this.pageFirstText, 1) : ""; } else if(pagerPart === PREV_PAGE_PLACEHOLDER) { result = pageIndex > 1 ? this._createPagerNavButton(this.pagePrevText, pageIndex - 1) : ""; } else if(pagerPart === NEXT_PAGE_PLACEHOLDER) { result = pageIndex < pageCount ? this._createPagerNavButton(this.pageNextText, pageIndex + 1) : ""; } else if(pagerPart === LAST_PAGE_PLACEHOLDER) { result = pageIndex < pageCount ? this._createPagerNavButton(this.pageLastText, pageCount) : ""; } else if(pagerPart === PAGE_INDEX_PLACEHOLDER) { result = pageIndex; } else if(pagerPart === PAGE_COUNT_PLACEHOLDER) { result = pageCount; } return $.isArray(result) ? result.concat([" "]) : [result, " "]; }, this)); var $pager = $("
").addClass(this.pagerClass) .append(pagerParts); return $pager; }, _createPages: function() { var pageCount = this._pagesCount(), pageButtonCount = this.pageButtonCount, firstDisplayingPage = this._firstDisplayingPage, pages = [], pageNumber; 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) { return this._createPagerButton(text, this.pagerNavButtonClass, function() { this.openPage(pageIndex); }); }, _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 = $("").attr("href", EMPTY_HREF) .html(text) .on("click", $.proxy(handler, this)); return $("").addClass(css).append($link); }, _createPagerCurrentPage: function() { return $("") .addClass(this.pageClass) .addClass(this.currentPageClass) .text(this.pageIndex); }, _refreshSize: function() { this._refreshHeight(); this._refreshWidth(); }, _refreshWidth: function() { var $headerGrid = this._headerGrid, $bodyGrid = this._bodyGrid, width = this.width, scrollBarWidth = this._scrollBarWidth(), gridWidth; if(width === "auto") { $headerGrid.width("auto"); gridWidth = $headerGrid.outerWidth(); width = gridWidth + scrollBarWidth; } $headerGrid.width(""); $bodyGrid.width(""); this._header.css("padding-right", scrollBarWidth); this._container.width(width); gridWidth = $headerGrid.outerWidth(); $bodyGrid.width(gridWidth); }, _scrollBarWidth: (function() { var result; return function() { if(result === undefined) { var $ghostContainer = $("
"); var $ghostContent = $("
"); $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; } }, _controllerCall: function(method, param, doneCallback) { this._showLoading(); var controller = this._controller; if(!controller || !controller[method]) { throw new Error("controller has no method '" + method + "'"); } return $.when(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() { clearTimeout(this._loadingTimer); this._loadingTimer = setTimeout($.proxy(function() { this._loadIndicator.show(); }, this), this.loadIndicationDelay); }, _hideLoading: function() { 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()); this._callEventHandler(this.onDataLoading, { filter: filter }); return this._controllerCall("loadData", filter, function(loadedData) { this._loadStrategy.finishLoad(loadedData); this._callEventHandler(this.onDataLoaded, { data: loadedData }); }); }, _getFilter: function() { var result = {}; this._eachField(function(field) { if(field.filtering) { result[field.name] = field.filterValue(); } }); return result; }, _sortingParams: function() { if(this.sorting && this._sortField) { return { sortField: this._sortField.name, sortOrder: this._sortOrder }; } return {}; }, clearFilter: function() { var $filterRow = this._createFilterRow(); this._filterRow.replaceWith($filterRow); this._filterRow = $filterRow; return this.search(); }, insertItem: function(item) { var insertingItem = item || this._getInsertItem(); this._callEventHandler(this.onItemInserting, { item: insertingItem }); return this._controllerCall("insertItem", insertingItem, function(insertedItem) { insertedItem = insertedItem || insertingItem; this._loadStrategy.finishInsert(insertedItem); this._callEventHandler(this.onItemInserted, { item: insertedItem }); }); }, _getInsertItem: function() { var result = {}; this._eachField(function(field) { if(field.inserting) { result[field.name] = field.insertValue(); } }); return result; }, 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; if(this._editingRow) { this.cancelEdit(); } var item = $row.data(JSGRID_ROW_DATA_KEY), $editRow = this._createEditRow(item); this._editingRow = $row; $row.hide(); $editRow.insertAfter($row); $row.data(JSGRID_EDIT_ROW_DATA_KEY, $editRow); }, _createEditRow: function(item) { if($.isFunction(this.editRowRenderer)) { return $(this.editRowRenderer(item, this._itemIndex(item))); } var $result = $("
").addClass(field.css) .appendTo($result) .append(field.editTemplate ? field.editTemplate(item[field.name], item) : "") .width(field.width || "auto"); }); return $result; }, updateItem: function(item, editedItem) { if(arguments.length === 1) { editedItem = item; } var $row = item ? this._rowByItem(item) : this._editingRow; editedItem = editedItem || this._getEditedItem(); return this._updateRow($row, editedItem); }, _updateRow: function($updatingRow, editedItem) { var updatingItem = $updatingRow.data(JSGRID_ROW_DATA_KEY), updatingItemIndex = this._itemIndex(updatingItem); $.extend(updatingItem, editedItem); this._callEventHandler(this.onItemUpdating, { row: $updatingRow, item: updatingItem, itemIndex: updatingItemIndex }); return this._controllerCall("updateItem", updatingItem, function(updatedItem) { updatedItem = updatedItem || updatingItem; this._finishUpdate($updatingRow, updatedItem, updatingItemIndex); this._callEventHandler(this.onItemUpdated, { row: $updatingRow, item: updatedItem, itemIndex: updatingItemIndex }); }); }, _itemIndex: function(item) { return $.inArray(item, this.data); }, _finishUpdate: function($updatedRow, updatedItem, updatedItemIndex) { this.cancelEdit(); this.data[updatedItemIndex] = updatedItem; $updatedRow.replaceWith(this._createRow(updatedItem, updatedItemIndex)); }, _getEditedItem: function() { var result = {}; this._eachField(function(field) { if(field.editing) { result[field.name] = field.editValue(); } }); return result; }, cancelEdit: function() { if(!this._editingRow) { return; } var $row = this._editingRow, $editRow = $row.data(JSGRID_EDIT_ROW_DATA_KEY); $editRow.remove(); $row.show(); this._editingRow = null; }, deleteItem: function(item) { var $row = this._rowByItem(item); if(!$row.length) return; var that = this; if (typeof this.deleteConfirm === 'function') { this.deleteConfirm(this, $row.data(JSGRID_ROW_DATA_KEY), function (result) { if (result) { that._deleteRow($row); } }); } else { 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); this._callEventHandler(this.onItemDeleting, { row: $row, item: deletingItem, itemIndex: deletingItemIndex }); return this._controllerCall("deleteItem", deletingItem, 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; }; window.jsGrid = { Grid: Grid, fields: [] }; }(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 = $("
").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 = $("
").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(); }, 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") }; }, 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 sortStrategies = { string: function(str1, str2) { 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 Field(config) { $.extend(true, this, config); this.sortingFunc = this._getSortingFunc(); } Field.prototype = { name: "", title: "", css: "", align: "", width: 100, filtering: true, inserting: true, editing: true, sorting: true, sorter: "string", // name of SortStrategy or function to compare elements headerTemplate: function() { return this.title || this.name; }, 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, filterTemplate: function() { 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() { var $result = this.insertControl = this._createTextBox(); return $result; }, editTemplate: function(value) { 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 $("").attr("type", "text"); } }); 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", filterValue: function() { return parseInt(this.filterControl.val() || 0, 10); }, insertValue: function() { return parseInt(this.insertControl.val() || 0, 10); }, editValue: function() { return parseInt(this.editControl.val() || 0, 10); }, _createTextBox: function() { return $("").attr("type", "number"); } }); 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() { var $result = this.insertControl = this._createTextArea(); return $result; }, editTemplate: function(value) { var $result = this.editControl = this._createTextArea(); $result.val(value); return $result; }, _createTextArea: function() { return $("