diff --git a/data/dataxml/uac.service.code.list.PAGE_SIZE_GET.xml b/data/dataxml/uac.service.code.list.PAGE_SIZE_GET.xml new file mode 100644 index 00000000..72f1b24d --- /dev/null +++ b/data/dataxml/uac.service.code.list.PAGE_SIZE_GET.xml @@ -0,0 +1,18 @@ + + + + 3 + + 010 + 10개씩 + + + 050 + 50개씩 + + + 100 + 100개씩 + + + diff --git a/src/main/java/nlib/bbs/web/BoardController.java b/src/main/java/nlib/bbs/web/BoardController.java index 12773c62..472608d9 100644 --- a/src/main/java/nlib/bbs/web/BoardController.java +++ b/src/main/java/nlib/bbs/web/BoardController.java @@ -23,6 +23,7 @@ import nlib.bbs.service.BoardService; import nlib.cmm.NlibCommonController; import nlib.cmm.crypto.AriaCrypto; import nlib.cmm.crypto.NlibPasswordEncoder; +import nlib.cmm.service.CodeService; import nlib.restful.service.DataApiReqVO; import nlib.restful.service.DataApiResVO; import nlib.user.service.NlibLoginVO; @@ -42,6 +43,9 @@ public class BoardController extends NlibCommonController @Resource(name = "ariaCrypto") private AriaCrypto ariaCrypto; + @Resource(name = "codeService") + private CodeService codeService; + @Autowired private AuthenticationManager authenticationManager; @@ -93,11 +97,20 @@ public class BoardController extends NlibCommonController String pageIndex = paramMap.get("pageIndex"); String pageSize = paramMap.get("pageSize"); - // 반환 정보 + // 페이징 사이즈 코드 목록 조회 + DataApiReqVO reqVOforPageSize = new DataApiReqVO(); + reqVOforPageSize.setAuthKey(createAuthKey(req, authentication)); + reqVOforPageSize.setPageSize(0); + reqVOforPageSize.addInfoItem("codeUpperId", "PAGE_SIZE"); + DataApiResVO resVOforPageSize = codeService.listCodes(reqVOforPageSize); + + // 반환 정보 model.addAttribute("pageIndex" , pageIndex); model.addAttribute("pageSize" , pageSize); model.addAttribute("searchKeyword", searchKeyword); + model.addAttribute("pageSizeCodes", resVOforPageSize.getList()); + log.debug("listQnAs > pageSizeCodes : " + resVOforPageSize.getList()); return "nlib/board/listQnAs"; } @@ -134,9 +147,22 @@ public class BoardController extends NlibCommonController // 요청 DataApiResVO resVO = boardService.listQnAs(reqVO); + + + //------------------------------- // JSON변환 응답 처리 - return makeResponseEntityJson(resVO.getList()); + //------------------------------- + // JS-GRID 페이징 처리를 포함한 응답값 처리 + // {data: [{...}], + // itemsCount: 255 + // } + + HashMap retMap = new HashMap(); + retMap.put("data", resVO.getList()); + retMap.put("itemsCount", resVO.getRecordTotCount()); + + return makeResponseEntityJson(retMap); } diff --git a/src/main/java/nlib/cmm/NlibCommonController.java b/src/main/java/nlib/cmm/NlibCommonController.java index 0ec01804..0cc283d2 100644 --- a/src/main/java/nlib/cmm/NlibCommonController.java +++ b/src/main/java/nlib/cmm/NlibCommonController.java @@ -152,4 +152,27 @@ public class NlibCommonController { return ResponseEntity.ok().headers(headers).body(jsonStr); } + + public ResponseEntity makeResponseEntityJson(HashMap retMap) { + + // Convert Json + ObjectMapper mapper = new ObjectMapper(); + String jsonStr = null; + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8"); + + try { + jsonStr = mapper.writeValueAsString(retMap); + log.debug("RETURN DATA > json:" + jsonStr); + } catch (JsonProcessingException e) { + ErrorMessage eMsg = new ErrorMessage("ERR_JSON_CONVERT", "응답객체를 JSON으로 변환 처리 중 오류가 발생하였습니다. : " + e.toString()); + try { jsonStr = mapper.writeValueAsString(eMsg); } catch (JsonProcessingException ee) {} + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).headers(headers).body(jsonStr); + } + + return ResponseEntity.ok().headers(headers).body(jsonStr); + } + } diff --git a/src/main/java/nlib/cmm/service/CodeService.java b/src/main/java/nlib/cmm/service/CodeService.java new file mode 100644 index 00000000..c6f9fcfc --- /dev/null +++ b/src/main/java/nlib/cmm/service/CodeService.java @@ -0,0 +1,13 @@ + +package nlib.cmm.service; + +import org.springframework.stereotype.Service; + +import nlib.restful.service.DataApiReqVO; +import nlib.restful.service.DataApiResVO; + +public interface CodeService +{ + public DataApiResVO listCodes(DataApiReqVO reqVO); + +} \ No newline at end of file diff --git a/src/main/java/nlib/cmm/service/impl/CodeDAO.java b/src/main/java/nlib/cmm/service/impl/CodeDAO.java new file mode 100644 index 00000000..4b3e346b --- /dev/null +++ b/src/main/java/nlib/cmm/service/impl/CodeDAO.java @@ -0,0 +1,28 @@ + +package nlib.cmm.service.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Repository; + +import nlib.cmm.web.MainController; +import nlib.restful.DataApi; +import nlib.restful.service.DataApiReqVO; +import nlib.restful.service.DataApiResVO; + +@Repository("codeDAO") +public class CodeDAO extends DataApi +{ + private static final Logger log = LoggerFactory.getLogger(CodeDAO.class); + + /** + * 주 자원의 URI + */ + public static final String RESOURCE_URI = "/uac/service/code/list"; + + public DataApiResVO listCodes(DataApiReqVO reqVO) { + reqVO.setReqUrl(RESOURCE_URI + "/" + reqVO.getInfoItem("codeUpperId")); + return get(reqVO); + } + +} \ No newline at end of file diff --git a/src/main/java/nlib/cmm/service/impl/CodeServiceImpl.java b/src/main/java/nlib/cmm/service/impl/CodeServiceImpl.java new file mode 100644 index 00000000..0856baeb --- /dev/null +++ b/src/main/java/nlib/cmm/service/impl/CodeServiceImpl.java @@ -0,0 +1,25 @@ + +package nlib.cmm.service.impl; + +import javax.annotation.Resource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import nlib.cmm.service.CodeService; +import nlib.restful.service.DataApiReqVO; +import nlib.restful.service.DataApiResVO; + +@Service("codeService") +public class CodeServiceImpl implements CodeService +{ + private static final Logger log = LoggerFactory.getLogger(CodeServiceImpl.class); + + @Resource(name="codeDAO") + private CodeDAO codeDAO; + + public DataApiResVO listCodes(DataApiReqVO reqVO) { + return codeDAO.listCodes(reqVO); + } +} \ No newline at end of file diff --git a/src/main/java/nlib/cmm/web/CodeController.java b/src/main/java/nlib/cmm/web/CodeController.java new file mode 100644 index 00000000..59e4f934 --- /dev/null +++ b/src/main/java/nlib/cmm/web/CodeController.java @@ -0,0 +1,87 @@ +package nlib.cmm.web; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +import nlib.cmm.NlibCommonController; +import nlib.cmm.service.CodeService; +import nlib.restful.service.DataApiReqVO; +import nlib.restful.service.DataApiResVO; +import nlib.util.StringUtil; + +@Controller +public class CodeController extends NlibCommonController { + + private static final Logger log = LoggerFactory.getLogger(CodeController.class); + + @Resource(name="codeService") + private CodeService codeService; + + @RequestMapping(value="/code/listCodesAjax.do") + public ResponseEntity listCodeAjax(HttpServletRequest req, + Authentication authentication, @RequestBody Map paramMap) { + + String codeSearchKeyword = paramMap.get("codeSearchKeyword"); + String codePageIndex = paramMap.get("codePageIndex"); + String codePageSize = paramMap.get("codePageSize"); + String codeUpperId = paramMap.get("codeUpperId"); + + log.debug("listCodeAjax > codeSearchKeyword = " + codeSearchKeyword); + log.debug("listCodeAjax > codePageIndex = " + codePageIndex); + log.debug("listCodeAjax > codePageSize = " + codePageSize); + log.debug("listCodeAjax > codeUpperId = " + codeUpperId); + + //------------------------------- + // REQ VO 구성 + //------------------------------- + DataApiReqVO reqVO = new DataApiReqVO(); + reqVO.setAuthKey(createAuthKey(req, authentication)); + reqVO.setPageIndex(codePageIndex); + reqVO.setPageSize(codePageSize); + + // 추가 정보 설정 + reqVO.addInfoItem("searchKeyword", codeSearchKeyword); + reqVO.addInfoItem("codeUpperId", codeUpperId); + + // 요청 + DataApiResVO resVO = codeService.listCodes(reqVO); + + //------------------------------- + // JSON변환 응답 처리 + //------------------------------- + // JS-GRID 페이징 처리를 포함한 응답값 처리 + // (1) 페이징을 서버단에서 처리하는 경우 (pagingloading = false) + // {data: [{...}], + // itemsCount: 255 + // } + // (2) 페이징을 클라이언트에서 처리하는 경우 (pagingloading = true) + // {data: [{...}], + // itemsCount: 255 + // } + + // 코드 페이징 사이즈값이 없거나 0인 경우, 서버에서 페이징 처리하지 않고 전체 데이터 전송 + if(StringUtil.isEmpty(codePageSize) || Integer.parseInt(codePageSize) <= 0) { + return makeResponseEntityJson(resVO.getList()); + } + + // 코드 페이징 처리되는 경우 + HashMap retMap = new HashMap(); + retMap.put("data", resVO.getList()); + retMap.put("itemsCount", resVO.getRecordTotCount()); + + return makeResponseEntityJson(retMap); + } + + +} diff --git a/src/main/webapp/WEB-INF/jsp/nlib/board/listQnAs.jsp b/src/main/webapp/WEB-INF/jsp/nlib/board/listQnAs.jsp index 652e6357..5f166124 100644 --- a/src/main/webapp/WEB-INF/jsp/nlib/board/listQnAs.jsp +++ b/src/main/webapp/WEB-INF/jsp/nlib/board/listQnAs.jsp @@ -55,12 +55,12 @@ - + @@ -99,14 +99,12 @@ $("#jsGrid").jsGrid({ controller: { loadData: function (filter) { - var data = $.Deferred(); //====================================== // 요청 정보 구성 : 시작 (각 요청에 맞게 조정) //====================================== var reqUrl = "${pageContext.request.contextPath}/board/listQnAsAjax.do"; - var searchKeyword = document.articleForm.searchKeyword.value; //var pageIndex = document.articleForm.pageIndex.value; var pageIndex = filter.pageIndex; @@ -130,7 +128,11 @@ $("#jsGrid").jsGrid({ dataType: "json", data: JSON.stringify(inputData), }).done(function(response){ - // 그리드 데이터 예시 : [{ "articleNo": 1, "articleType": "01" }, { "articleNo": 2, "articleType": "02" }] + <% + // 그리드 데이터 예시 : + // (1) 페이징 클라이언트에서 수행하는 경우(pageloading = false) : [{ "articleNo": 1, "articleType": "01" }, { "articleNo": 2, "articleType": "02" }] + // (2) 페이징 서버에서 수행하는 경우(pageloading = true) : {data: [{...}], itemsCount: 255} + %> data.resolve(JSON.parse(response)); }); return data.promise(); @@ -162,6 +164,18 @@ $("#jsGrid").jsGrid({ ] }); +$("#pageSize").on("change", function(e) { + //alert(e.target.value); + //$("#jsGrid").jsGrid("option", "pageIndex", 1).("option", "pageSize", e.target.value); + //$("#jsGrid").jsGrid("loadData", {"pageIndex":2,"pageSize":50}); + //fn_search_article(); + //$("#jsGrid").controller.loadData({"pageIndex":2,"pageSize":50}); + //$("#jsGrid").jsGrid("loadData()"); + $("#jsGrid").jsGrid("search", {'pageIndex':'2','pageSize':e.target.value}); + //$("#jsGrid").jsGrid("search", {'pageIndex':2,'pageSize':50}); +}); + +$("#jsGrid").jsGrid("option", "pageIndex", $("#pageIndex").val(), "pageSize", $("#pageSize").val()); // 선택한 게시글 상세 보기로 이동 function fn_view_detail(articleNo) { @@ -170,8 +184,11 @@ function fn_view_detail(articleNo) { $("#articleForm").submit(); } +function fn_search_article() { + $("#jsGrid").jsGrid("loadData"); +} - function fn_search_article() { + function fn_search_article2() { var searchKeyword = document.articleForm.searchKeyword.value; var pageIndex = document.articleForm.pageIndex.value; var pageSize = document.articleForm.pageSize.value; @@ -183,7 +200,7 @@ function fn_view_detail(articleNo) { "pageSize" : pageSize }; - //lert(JSON.stringify(inputData)); + //alert(JSON.stringify(inputData)); $.ajax({ url : "${pageContext.request.contextPath}/board/listQnAsAjax.do", diff --git a/src/main/webapp/js/jsgrid/nlib-jsgrid.js b/src/main/webapp/js/jsgrid/nlib-jsgrid.js index 3dc318ec..1acd03e9 100644 --- a/src/main/webapp/js/jsgrid/nlib-jsgrid.js +++ b/src/main/webapp/js/jsgrid/nlib-jsgrid.js @@ -6,2519 +6,2578 @@ (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", - width: "100%", /* NLIB */ - 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: true, - pagerContainer: null, - pageIndex: 1, - //pageSize: 20, - pageSize: 10, /* NLIB */ - //pageButtonCount: 15, - pageButtonCount: 10, /* NLIB */ - //pagerFormat: "Pages: {first} {prev} {pages} {next} {last}    {pageIndex} of {pageCount}", - pagerFormat: "{first} {prev} {pages} {next} {last}", /* NLIB */ - //pagePrevText: "Prev", - pagePrevText: " < ", /* NLIB */ - //pageNextText: "Next", - pageNextText: " > ", /* NLIB */ - //pageFirstText: "First", - pageFirstText: " << ", /* NLIB */ - //pageLastText: "Last", - pageLastText: " >> ", /* NLIB */ - 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 = $("").addClass(this.tableClass) - .append($headerRow) - .append($filterRow) - .append($insertRow); - - var $header = this._header = $("
").addClass(this.gridHeaderClass) - .addClass(this._scrollBarWidth() ? "jsgrid-header-scrollbar" : "") - .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) - .on("scroll", $.proxy(function(e) { - this._header.scrollLeft(e.target.scrollLeft); - }, this)); - - 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) { - if(field.visible) { - callBack.call(self, field, index); - } - }); - }, - - _createHeaderRow: function() { - if($.isFunction(this.headerRowRenderer)) - return $(this.renderTemplate(this.headerRowRenderer, this)); - - var $result = $("
").addClass(this.headerRowClass); - - this._eachField(function(field, index) { - var $th = this._prepareCell("").addClass(this.filterRowClass); - - this._eachField(function(field) { - this._prepareCell("").addClass(this.insertRowClass); - - this._eachField(function(field) { - this._prepareCell("").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)) - .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 = $("").addClass(this.editRowClass); - - this._eachField(function(field) { - var fieldValue = this._getItemFieldValue(item, field); - - this._prepareCell("
", 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 = $("
", 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 = $("
", 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 $("
").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 = $("
").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 = $("
").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 = $("").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 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 = $("
"); - 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; - } - - 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 = $("
", 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' - }; + 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", + width : "100%", /* NLIB */ + 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 : true, + pagerContainer : null, + pageIndex : 1, + // pageSize: 20, + pageSize : 10, /* NLIB */ + // pageButtonCount: 15, + pageButtonCount : 10, /* NLIB */ + // pagerFormat: "Pages: {first} {prev} {pages} {next} {last} + //    {pageIndex} of {pageCount}", + pagerFormat : "{first} {prev} {pages} {next} {last}", /* NLIB */ + // pagePrevText: "Prev", + pagePrevText : " < ", /* NLIB */ + // pageNextText: "Next", + pageNextText : " > ", /* NLIB */ + // pageFirstText: "First", + pageFirstText : " << ", /* NLIB */ + // pageLastText: "Last", + pageLastText : " >> ", /* NLIB */ + 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, + pageLoading : true, /* NLIB */ + + 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 = $("").addClass( + this.tableClass).append($headerRow).append($filterRow) + .append($insertRow); + + var $header = this._header = $("
").addClass( + this.gridHeaderClass).addClass( + this._scrollBarWidth() ? "jsgrid-header-scrollbar" : "") + .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).on("scroll", $.proxy(function(e) { + this._header.scrollLeft(e.target.scrollLeft); + }, this)); + + 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) { + if (field.visible) { + callBack.call(self, field, index); + } + }); + }, + + _createHeaderRow : function() { + if ($.isFunction(this.headerRowRenderer)) + return $(this.renderTemplate(this.headerRowRenderer, this)); + + var $result = $("
").addClass(this.headerRowClass); + + this._eachField(function(field, index) { + var $th = this._prepareCell("").addClass(this.filterRowClass); + + this._eachField(function(field) { + this._prepareCell("").addClass(this.insertRowClass); + + this._eachField(function(field) { + this._prepareCell("").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)).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 = $("").addClass(this.editRowClass); + + this._eachField(function(field) { + var fieldValue = this._getItemFieldValue(item, field); + + this._prepareCell("
", 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 = $("
", 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 = $("
", 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 $("
").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 = $("
").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 = $("
").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 = $("").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 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 = $("
"); + 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; + } + + 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()); + + $.extend(this._loadStrategy.loadParams(), filter, 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 = $("
", 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); - } + function LoadIndicator(config) { + this._init(config); + } - LoadIndicator.prototype = { + LoadIndicator.prototype = { - container: "body", - message: "Loading...", - shading: true, + container : "body", + message : "Loading...", + shading : true, - zIndex: 1000, - shaderClass: "jsgrid-load-shader", - loadPanelClass: "jsgrid-load-panel", + zIndex : 1000, + shaderClass : "jsgrid-load-shader", + loadPanelClass : "jsgrid-load-panel", - _init: function(config) { - $.extend(true, this, config); + _init : function(config) { + $.extend(true, this, config); - this._initContainer(); - this._initShader(); - this._initLoadPanel(); - }, + this._initContainer(); + this._initShader(); + this._initLoadPanel(); + }, - _initContainer: function() { - this._container = $(this.container); - }, + _initContainer : function() { + this._container = $(this.container); + }, - _initShader: function() { - if(!this.shading) - return; + _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); - }, + 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); - }, + _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(); + show : function() { + var $loadPanel = this._loadPanel.show(); - var actualWidth = $loadPanel.outerWidth(); - var actualHeight = $loadPanel.outerHeight(); + var actualWidth = $loadPanel.outerWidth(); + var actualHeight = $loadPanel.outerHeight(); - $loadPanel.css({ - marginTop: -actualHeight / 2, - marginLeft: -actualWidth / 2 - }); + $loadPanel.css({ + marginTop : -actualHeight / 2, + marginLeft : -actualWidth / 2 + }); - this._shader.show(); - }, + this._shader.show(); + }, - hide: function() { - this._loadPanel.hide(); - this._shader.hide(); - } + hide : function() { + this._loadPanel.hide(); + this._shader.hide(); + } - }; + }; - jsGrid.LoadIndicator = LoadIndicator; + jsGrid.LoadIndicator = LoadIndicator; }(jsGrid, jQuery)); (function(jsGrid, $, undefined) { - function DirectLoadingStrategy(grid) { - this._grid = grid; - } + function DirectLoadingStrategy(grid) { + this._grid = grid; + } - DirectLoadingStrategy.prototype = { + DirectLoadingStrategy.prototype = { - firstDisplayIndex: function() { - var grid = this._grid; - return grid.option("paging") ? (grid.option("pageIndex") - 1) * grid.option("pageSize") : 0; - }, + 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; + 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; - }, + return grid.option("paging") ? Math.min(grid.option("pageIndex") + * grid.option("pageSize"), itemsCount) : itemsCount; + }, - itemsCount: function() { - return this._grid.option("data").length; - }, + itemsCount : function() { + return this._grid.option("data").length; + }, - openPage: function(index) { - this._grid.refresh(); - }, + openPage : function(index) { + this._grid.refresh(); + }, - loadParams: function() { - return {}; - }, + loadParams : function() { + return {}; + }, - sort: function() { - this._grid._sortData(); - this._grid.refresh(); - return $.Deferred().resolve().promise(); - }, + sort : function() { + this._grid._sortData(); + this._grid.refresh(); + return $.Deferred().resolve().promise(); + }, - reset: function() { - this._grid.refresh(); - return $.Deferred().resolve().promise(); - }, + reset : function() { + this._grid.refresh(); + return $.Deferred().resolve().promise(); + }, - finishLoad: function(loadedData) { - this._grid.option("data", loadedData); - }, + finishLoad : function(loadedData) { + this._grid.option("data", loadedData); + }, - finishInsert: function(insertedItem) { - var grid = this._grid; - grid.option("data").push(insertedItem); - grid.refresh(); - }, + 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(); - } - }; + 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; + } - function PageLoadingStrategy(grid) { - this._grid = grid; - this._itemsCount = 0; - } + PageLoadingStrategy.prototype = { - PageLoadingStrategy.prototype = { + firstDisplayIndex : function() { + return 0; + }, - firstDisplayIndex: function() { - return 0; - }, + lastDisplayIndex : function() { + return this._grid.option("data").length; + }, - lastDisplayIndex: function() { - return this._grid.option("data").length; - }, + itemsCount : function() { + return this._itemsCount; + }, - itemsCount: function() { - return this._itemsCount; - }, + openPage : function(index) { + this._grid.loadData(); + }, - openPage: function(index) { - this._grid.loadData(); - }, + loadParams : function() { + var grid = this._grid; + return { + pageIndex : grid.option("pageIndex"), + pageSize : grid.option("pageSize") + }; + }, - loadParams: function() { - var grid = this._grid; - return { - pageIndex: grid.option("pageIndex"), - pageSize: grid.option("pageSize") - }; - }, + reset : function() { + return this._grid.loadData(); + }, - reset: function() { - return this._grid.loadData(); - }, + sort : function() { + return this._grid.loadData(); + }, - sort: function() { - return this._grid.loadData(); - }, + finishLoad : function(loadedData) { + this._itemsCount = loadedData.itemsCount; + this._grid.option("data", loadedData.data); + }, - finishLoad: function(loadedData) { - this._itemsCount = loadedData.itemsCount; - this._grid.option("data", loadedData.data); - }, + finishInsert : function(insertedItem) { + this._grid.search(); + }, - finishInsert: function(insertedItem) { - this._grid.search(); - }, + finishDelete : function(deletedItem, deletedItemIndex) { + this._grid.search(); + } + }; - finishDelete: function(deletedItem, deletedItemIndex) { - this._grid.search(); - } - }; - - jsGrid.loadStrategies = { - DirectLoadingStrategy: DirectLoadingStrategy, - PageLoadingStrategy: PageLoadingStrategy - }; + jsGrid.loadStrategies = { + DirectLoadingStrategy : DirectLoadingStrategy, + PageLoadingStrategy : PageLoadingStrategy + }; }(jsGrid, jQuery)); (function(jsGrid, $, undefined) { - var isDefined = function(val) { - return typeof(val) !== "undefined" && val !== null; - }; + var isDefined = function(val) { + return typeof (val) !== "undefined" && val !== null; + }; - var sortStrategies = { - string: function(str1, str2) { - if(!isDefined(str1) && !isDefined(str2)) - return 0; + var sortStrategies = { + string : function(str1, str2) { + if (!isDefined(str1) && !isDefined(str2)) + return 0; - if(!isDefined(str1)) - return -1; + if (!isDefined(str1)) + return -1; - if(!isDefined(str2)) - return 1; + if (!isDefined(str2)) + return 1; - return ("" + str1).localeCompare("" + str2); - }, + return ("" + str1).localeCompare("" + str2); + }, - number: function(n1, n2) { - return n1 - n2; - }, + number : function(n1, n2) { + return n1 - n2; + }, - date: function(dt1, dt2) { - return dt1 - dt2; - }, + date : function(dt1, dt2) { + return dt1 - dt2; + }, - numberAsString: function(n1, n2) { - return parseFloat(n1) - parseFloat(n2); - } - }; + numberAsString : function(n1, n2) { + return parseFloat(n1) - parseFloat(n2); + } + }; - jsGrid.sortStrategies = sortStrategies; + jsGrid.sortStrategies = sortStrategies; }(jsGrid, jQuery)); (function(jsGrid, $, undefined) { - function Validation(config) { - this._init(config); - } + function Validation(config) { + this._init(config); + } - Validation.prototype = { + Validation.prototype = { - _init: function(config) { - $.extend(true, this, config); - }, + _init : function(config) { + $.extend(true, this, config); + }, - validate: function(args) { - var errors = []; + validate : function(args) { + var errors = []; - $.each(this._normalizeRules(args.rules), function(_, rule) { - if(rule.validator(args.value, args.item, rule.param)) - return; + $.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); - }); + var errorMessage = $.isFunction(rule.message) ? rule.message( + args.value, args.item) : rule.message; + errors.push(errorMessage); + }); - return errors; - }, + return errors; + }, - _normalizeRules: function(rules) { - if(!$.isArray(rules)) - rules = [rules]; + _normalizeRules : function(rules) { + if (!$.isArray(rules)) + rules = [ rules ]; - return $.map(rules, $.proxy(function(rule) { - return this._normalizeRule(rule); - }, this)); - }, + return $.map(rules, $.proxy(function(rule) { + return this._normalizeRule(rule); + }, this)); + }, - _normalizeRule: function(rule) { - if(typeof rule === "string") - rule = { validator: rule }; + _normalizeRule : function(rule) { + if (typeof rule === "string") + rule = { + validator : rule + }; - if($.isFunction(rule)) - rule = { validator: rule }; + if ($.isFunction(rule)) + rule = { + validator : rule + }; - if($.isPlainObject(rule)) - rule = $.extend({}, rule); - else - throw Error("wrong validation config specified"); + if ($.isPlainObject(rule)) + rule = $.extend({}, rule); + else + throw Error("wrong validation config specified"); - if($.isFunction(rule.validator)) - return rule; + if ($.isFunction(rule.validator)) + return rule; - return this._applyNamedValidator(rule, rule.validator); - }, + return this._applyNamedValidator(rule, rule.validator); + }, - _applyNamedValidator: function(rule, validatorName) { - delete rule.validator; + _applyNamedValidator : function(rule, validatorName) { + delete rule.validator; - var validator = validators[validatorName]; - if(!validator) - throw Error("unknown validator \"" + validatorName + "\""); + var validator = validators[validatorName]; + if (!validator) + throw Error("unknown validator \"" + validatorName + "\""); - if($.isFunction(validator)) { - validator = { validator: validator }; - } + if ($.isFunction(validator)) { + validator = { + validator : validator + }; + } - return $.extend({}, validator, rule); - } - }; + return $.extend({}, validator, rule); + } + }; - jsGrid.Validation = Validation; + jsGrid.Validation = Validation; + var validators = { + required : { + message : "Field is required", + validator : function(value) { + return value !== undefined && value !== null && value !== ""; + } + }, - 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]; + } + }, - 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; + } + }, - 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; + } + }, - 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); + } + }, - 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]; + } + }, - 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; + } + }, - 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; + } + } + }; - max: { - message: "Field value is too large", - validator: function(value, _, param) { - return value <= param; - } - } - }; - - jsGrid.validators = validators; + jsGrid.validators = validators; }(jsGrid, jQuery)); (function(jsGrid, $, undefined) { - function Field(config) { - $.extend(true, this, config); - this.sortingFunc = this._getSortingFunc(); - } + function Field(config) { + $.extend(true, this, config); + this.sortingFunc = this._getSortingFunc(); + } - Field.prototype = { - name: "", - title: null, - css: "", - align: "", - width: 100, + 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 + 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; - }, + headerTemplate : function() { + return (this.title === undefined || this.title === null) ? this.name + : this.title; + }, - itemTemplate: function(value, item) { - return value; - }, + itemTemplate : function(value, item) { + return value; + }, - filterTemplate: function() { - return ""; - }, + filterTemplate : function() { + return ""; + }, - insertTemplate: function() { - return ""; - }, + insertTemplate : function() { + return ""; + }, - editTemplate: function(value, item) { - this._value = value; - return this.itemTemplate(value, item); - }, + editTemplate : function(value, item) { + this._value = value; + return this.itemTemplate(value, item); + }, - filterValue: function() { - return ""; - }, + filterValue : function() { + return ""; + }, - insertValue: function() { - return ""; - }, + insertValue : function() { + return ""; + }, - editValue: function() { - return this._value; - }, + editValue : function() { + return this._value; + }, - _getSortingFunc: function() { - var sorter = this.sorter; + _getSortingFunc : function() { + var sorter = this.sorter; - if($.isFunction(sorter)) { - return sorter; - } + if ($.isFunction(sorter)) { + return sorter; + } - if(typeof sorter === "string") { - return jsGrid.sortStrategies[sorter]; - } + if (typeof sorter === "string") { + return jsGrid.sortStrategies[sorter]; + } - throw Error("wrong sorter for the field \"" + this.name + "\"!"); - } - }; + throw Error("wrong sorter for the field \"" + this.name + "\"!"); + } + }; - jsGrid.Field = Field; + jsGrid.Field = Field; }(jsGrid, jQuery)); (function(jsGrid, $, undefined) { - var Field = jsGrid.Field; + var Field = jsGrid.Field; - function TextField(config) { - Field.call(this, config); - } + function TextField(config) { + Field.call(this, config); + } - TextField.prototype = new Field({ + TextField.prototype = new Field({ - autosearch: true, - readOnly: false, + autosearch : true, + readOnly : false, - filterTemplate: function() { - if(!this.filtering) - return ""; + filterTemplate : function() { + if (!this.filtering) + return ""; - var grid = this._grid, - $result = this.filterControl = this._createTextBox(); + 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(); - } - }); - } + if (this.autosearch) { + $result.on("keypress", function(e) { + if (e.which === 13) { + grid.search(); + e.preventDefault(); + } + }); + } - return $result; - }, + return $result; + }, - insertTemplate: function() { - if(!this.inserting) - return ""; + insertTemplate : function() { + if (!this.inserting) + return ""; - return this.insertControl = this._createTextBox(); - }, + return this.insertControl = this._createTextBox(); + }, - editTemplate: function(value) { - if(!this.editing) - return this.itemTemplate.apply(this, arguments); + editTemplate : function(value) { + if (!this.editing) + return this.itemTemplate.apply(this, arguments); - var $result = this.editControl = this._createTextBox(); - $result.val(value); - return $result; - }, + var $result = this.editControl = this._createTextBox(); + $result.val(value); + return $result; + }, - filterValue: function() { - return this.filterControl.val(); - }, + filterValue : function() { + return this.filterControl.val(); + }, - insertValue: function() { - return this.insertControl.val(); - }, + insertValue : function() { + return this.insertControl.val(); + }, - editValue: function() { - return this.editControl.val(); - }, + editValue : function() { + return this.editControl.val(); + }, - _createTextBox: function() { - return $("").attr("type", "text") - .prop("readonly", !!this.readOnly); - } - }); + _createTextBox : function() { + return $("").attr("type", "text").prop("readonly", + !!this.readOnly); + } + }); - jsGrid.fields.text = jsGrid.TextField = TextField; + jsGrid.fields.text = jsGrid.TextField = TextField; }(jsGrid, jQuery)); (function(jsGrid, $, undefined) { - var TextField = jsGrid.TextField; + var TextField = jsGrid.TextField; - function NumberField(config) { - TextField.call(this, config); - } + function NumberField(config) { + TextField.call(this, config); + } - NumberField.prototype = new TextField({ + NumberField.prototype = new TextField({ - sorter: "number", - align: "right", - readOnly: false, + sorter : "number", + align : "right", + readOnly : false, - filterValue: function() { - return this.filterControl.val() - ? parseInt(this.filterControl.val() || 0, 10) - : undefined; - }, + 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; - }, + 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; - }, + editValue : function() { + return this.editControl.val() ? parseInt( + this.editControl.val() || 0, 10) : undefined; + }, - _createTextBox: function() { - return $("").attr("type", "number") - .prop("readonly", !!this.readOnly); - } - }); + _createTextBox : function() { + return $("").attr("type", "number").prop("readonly", + !!this.readOnly); + } + }); - jsGrid.fields.number = jsGrid.NumberField = NumberField; + jsGrid.fields.number = jsGrid.NumberField = NumberField; }(jsGrid, jQuery)); (function(jsGrid, $, undefined) { - var TextField = jsGrid.TextField; + var TextField = jsGrid.TextField; - function TextAreaField(config) { - TextField.call(this, config); - } + function TextAreaField(config) { + TextField.call(this, config); + } - TextAreaField.prototype = new TextField({ + TextAreaField.prototype = new TextField({ - insertTemplate: function() { - if(!this.inserting) - return ""; + insertTemplate : function() { + if (!this.inserting) + return ""; - return this.insertControl = this._createTextArea(); - }, + return this.insertControl = this._createTextArea(); + }, - editTemplate: function(value) { - if(!this.editing) - return this.itemTemplate.apply(this, arguments); + editTemplate : function(value) { + if (!this.editing) + return this.itemTemplate.apply(this, arguments); - var $result = this.editControl = this._createTextArea(); - $result.val(value); - return $result; - }, + var $result = this.editControl = this._createTextArea(); + $result.val(value); + return $result; + }, - _createTextArea: function() { - return $("