7
(function(window, $, undefined) {
10
JSGRID_DATA_KEY = JSGRID,
11
JSGRID_ROW_DATA_KEY = "JSGridItem",
12
JSGRID_EDIT_ROW_DATA_KEY = "JSGridEditRow",
14
SORT_ORDER_ASC = "asc",
15
SORT_ORDER_DESC = "desc",
17
FIRST_PAGE_PLACEHOLDER = "{first}",
18
PAGES_PLACEHOLDER = "{pages}",
19
PREV_PAGE_PLACEHOLDER = "{prev}",
20
NEXT_PAGE_PLACEHOLDER = "{next}",
21
LAST_PAGE_PLACEHOLDER = "{last}",
22
PAGE_INDEX_PLACEHOLDER = "{pageIndex}",
23
PAGE_COUNT_PLACEHOLDER = "{pageCount}",
24
ITEM_COUNT_PLACEHOLDER = "{itemCount}",
26
EMPTY_HREF = "javascript:void(0);";
28
var getOrApply = function(value, context) {
29
if($.isFunction(value)) {
30
return value.apply(context, $.makeArray(arguments).slice(2));
35
var normalizePromise = function(promise) {
38
if(promise && promise.then) {
39
promise.then(function() {
40
d.resolve.apply(d, arguments);
42
d.reject.apply(d, arguments);
51
var defaultController = {
59
function Grid(element, config) {
60
var $element = $(element);
62
$element.data(JSGRID_DATA_KEY, this);
64
this._container = $element;
69
this._editingRow = null;
70
this._sortField = null;
71
this._sortOrder = SORT_ORDER_ASC;
72
this._firstDisplayingPage = 1;
86
rowClick: function(args) {
88
this.editItem($(args.event.target).closest("tr"));
91
rowDoubleClick: $.noop,
93
noDataContent: "Not found",
94
noDataRowClass: "jsgrid-nodata-row",
97
headerRowRenderer: null,
98
headerRowClass: "jsgrid-header-row",
99
headerCellClass: "jsgrid-header-cell",
102
filterRowRenderer: null,
103
filterRowClass: "jsgrid-filter-row",
106
insertRowRenderer: null,
107
insertRowClass: "jsgrid-insert-row",
110
editRowRenderer: null,
111
editRowClass: "jsgrid-edit-row",
113
confirmDeleting: true,
114
deleteConfirm: "Are you sure?",
117
selectedRowClass: "jsgrid-selected-row",
118
oddRowClass: "jsgrid-row",
119
evenRowClass: "jsgrid-alt-row",
120
cellClass: "jsgrid-cell",
123
sortableClass: "jsgrid-header-sortable",
124
sortAscClass: "jsgrid-header-sort jsgrid-header-sort-asc",
125
sortDescClass: "jsgrid-header-sort jsgrid-header-sort-desc",
128
pagerContainer: null,
132
pagerFormat: "Pages: {first} {prev} {pages} {next} {last} {pageIndex} of {pageCount}",
133
pagePrevText: "Prev",
134
pageNextText: "Next",
135
pageFirstText: "First",
136
pageLastText: "Last",
137
pageNavigatorNextText: "...",
138
pageNavigatorPrevText: "...",
139
pagerContainerClass: "jsgrid-pager-container",
140
pagerClass: "jsgrid-pager",
141
pagerNavButtonClass: "jsgrid-pager-nav-button",
142
pagerNavButtonInactiveClass: "jsgrid-pager-nav-inactive-button",
143
pageClass: "jsgrid-pager-page",
144
currentPageClass: "jsgrid-pager-current-page",
146
customLoading: false,
150
controller: defaultController,
152
loadIndication: true,
153
loadIndicationDelay: 500,
154
loadMessage: "Please, wait...",
157
invalidMessage: "Invalid data entered!",
159
invalidNotify: function(args) {
160
var messages = $.map(args.errors, function(error) {
161
return error.message || null;
164
window.alert([this.invalidMessage].concat(messages).join("\n"));
168
onRefreshing: $.noop,
170
onPageChanged: $.noop,
171
onItemDeleting: $.noop,
172
onItemDeleted: $.noop,
173
onItemInserting: $.noop,
174
onItemInserted: $.noop,
175
onItemEditing: $.noop,
176
onItemUpdating: $.noop,
177
onItemUpdated: $.noop,
178
onItemInvalid: $.noop,
179
onDataLoading: $.noop,
180
onDataLoaded: $.noop,
181
onOptionChanging: $.noop,
182
onOptionChanged: $.noop,
185
invalidClass: "jsgrid-invalid",
187
containerClass: "jsgrid",
188
tableClass: "jsgrid-table",
189
gridHeaderClass: "jsgrid-grid-header",
190
gridBodyClass: "jsgrid-grid-body",
192
_init: function(config) {
193
$.extend(this, config);
194
this._initLoadStrategy();
195
this._initController();
197
this._attachWindowLoadResize();
198
this._attachWindowResizeCallback();
199
this._callEventHandler(this.onInit)
202
loadStrategy: function() {
203
return this.pageLoading
204
? new jsGrid.loadStrategies.PageLoadingStrategy(this)
205
: new jsGrid.loadStrategies.DirectLoadingStrategy(this);
208
_initLoadStrategy: function() {
209
this._loadStrategy = getOrApply(this.loadStrategy, this);
212
_initController: function() {
213
this._controller = $.extend({}, defaultController, getOrApply(this.controller, this));
216
renderTemplate: function(source, context, config) {
218
for(var key in config) {
219
args.push(config[key]);
222
args.unshift(source, context);
224
source = getOrApply.apply(null, args);
225
return (source === undefined || source === null) ? "" : source;
228
loadIndicator: function(config) {
229
return new jsGrid.LoadIndicator(config);
232
validation: function(config) {
233
return jsGrid.Validation && new jsGrid.Validation(config);
236
_initFields: function() {
238
self.fields = $.map(self.fields, function(field) {
239
if($.isPlainObject(field)) {
240
var fieldConstructor = (field.type && jsGrid.fields[field.type]) || jsGrid.Field;
241
field = new fieldConstructor(field);
248
_attachWindowLoadResize: function() {
249
$(window).on("load", $.proxy(this._refreshSize, this));
252
_attachWindowResizeCallback: function() {
253
if(this.updateOnResize) {
254
$(window).on("resize", $.proxy(this._refreshSize, this));
258
_detachWindowResizeCallback: function() {
259
$(window).off("resize", this._refreshSize);
262
option: function(key, value) {
263
var optionChangingEventArgs,
264
optionChangedEventArgs;
266
if(arguments.length === 1)
269
optionChangingEventArgs = {
274
this._callEventHandler(this.onOptionChanging, optionChangingEventArgs);
276
this._handleOptionChange(optionChangingEventArgs.option, optionChangingEventArgs.newValue);
278
optionChangedEventArgs = {
279
option: optionChangingEventArgs.option,
280
value: optionChangingEventArgs.newValue
282
this._callEventHandler(this.onOptionChanged, optionChangedEventArgs);
285
fieldOption: function(field, key, value) {
286
field = this._normalizeField(field);
288
if(arguments.length === 2)
295
_handleOptionChange: function(name, value) {
306
case "rowDoubleClick":
307
case "noDataRowClass":
308
case "noDataContent":
310
case "selectedRowClass":
313
this._refreshContent();
315
case "pageButtonCount":
319
case "pageFirstText":
321
case "pageNavigatorNextText":
322
case "pageNavigatorPrevText":
324
case "pagerNavButtonClass":
326
case "currentPageClass":
327
case "pagerRenderer":
328
this._refreshPager();
344
this._initLoadStrategy();
348
this.openPage(value);
354
case "editRowRenderer":
358
case "updateOnResize":
359
this._detachWindowResizeCallback();
360
this._attachWindowResizeCallback();
362
case "invalidNotify":
363
case "invalidMessage":
371
destroy: function() {
372
this._detachWindowResizeCallback();
374
this._container.removeData(JSGRID_DATA_KEY);
379
return this.autoload ? this.loadData() : $.Deferred().resolve().promise();
382
_renderGrid: function() {
385
this._container.addClass(this.containerClass)
386
.css("position", "relative")
387
.append(this._createHeader())
388
.append(this._createBody());
390
this._pagerContainer = this._createPagerContainer();
391
this._loadIndicator = this._createLoadIndicator();
392
this._validation = this._createValidation();
397
_createLoadIndicator: function() {
398
return getOrApply(this.loadIndicator, this, {
399
message: this.loadMessage,
400
shading: this.loadShading,
401
container: this._container
405
_createValidation: function() {
406
return getOrApply(this.validation, this);
412
clearTimeout(this._loadingTimer);
414
this._pagerContainer && this._pagerContainer.empty();
416
this._container.empty()
417
.css({ position: "", width: "", height: "" });
420
_createHeader: function() {
421
var $headerRow = this._headerRow = this._createHeaderRow(),
422
$filterRow = this._filterRow = this._createFilterRow(),
423
$insertRow = this._insertRow = this._createInsertRow();
425
var $headerGrid = this._headerGrid = $("<table>").addClass(this.tableClass)
430
var $header = this._header = $("<div>").addClass(this.gridHeaderClass)
431
.addClass(this._scrollBarWidth() ? "jsgrid-header-scrollbar" : "")
432
.append($headerGrid);
437
_createBody: function() {
438
var $content = this._content = $("<tbody>");
440
var $bodyGrid = this._bodyGrid = $("<table>").addClass(this.tableClass)
443
var $body = this._body = $("<div>").addClass(this.gridBodyClass)
445
.on("scroll", $.proxy(function(e) {
446
this._header.scrollLeft(e.target.scrollLeft);
452
_createPagerContainer: function() {
453
var pagerContainer = this.pagerContainer || $("<div>").appendTo(this._container);
454
return $(pagerContainer).addClass(this.pagerContainerClass);
457
_eachField: function(callBack) {
459
$.each(this.fields, function(index, field) {
461
callBack.call(self, field, index);
466
_createHeaderRow: function() {
467
if($.isFunction(this.headerRowRenderer))
468
return $(this.renderTemplate(this.headerRowRenderer, this));
470
var $result = $("<tr>").addClass(this.headerRowClass);
472
this._eachField(function(field, index) {
473
var $th = this._prepareCell("<th>", field, "headercss", this.headerCellClass)
474
.append(this.renderTemplate(field.headerTemplate, field))
477
if(this.sorting && field.sorting) {
478
$th.addClass(this.sortableClass)
479
.on("click", $.proxy(function() {
488
_prepareCell: function(cell, field, cssprop, cellClass) {
489
return $(cell).css("width", field.width)
490
.addClass(cellClass || this.cellClass)
491
.addClass((cssprop && field[cssprop]) || field.css)
492
.addClass(field.align ? ("jsgrid-align-" + field.align) : "");
495
_createFilterRow: function() {
496
if($.isFunction(this.filterRowRenderer))
497
return $(this.renderTemplate(this.filterRowRenderer, this));
499
var $result = $("<tr>").addClass(this.filterRowClass);
501
this._eachField(function(field) {
502
this._prepareCell("<td>", field, "filtercss")
503
.append(this.renderTemplate(field.filterTemplate, field))
510
_createInsertRow: function() {
511
if($.isFunction(this.insertRowRenderer))
512
return $(this.renderTemplate(this.insertRowRenderer, this));
514
var $result = $("<tr>").addClass(this.insertRowClass);
516
this._eachField(function(field) {
517
this._prepareCell("<td>", field, "insertcss")
518
.append(this.renderTemplate(field.insertTemplate, field))
525
_callEventHandler: function(handler, eventParams) {
526
handler.call(this, $.extend(eventParams, {
534
this._resetSorting();
536
return this._loadStrategy.reset();
539
_resetPager: function() {
540
this._firstDisplayingPage = 1;
544
_resetSorting: function() {
545
this._sortField = null;
546
this._sortOrder = SORT_ORDER_ASC;
547
this._clearSortingCss();
550
refresh: function() {
551
this._callEventHandler(this.onRefreshing);
555
this._refreshHeading();
556
this._refreshFiltering();
557
this._refreshInserting();
558
this._refreshContent();
559
this._refreshPager();
562
this._callEventHandler(this.onRefreshed);
565
_refreshHeading: function() {
566
this._headerRow.toggle(this.heading);
569
_refreshFiltering: function() {
570
this._filterRow.toggle(this.filtering);
573
_refreshInserting: function() {
574
this._insertRow.toggle(this.inserting);
577
_refreshContent: function() {
578
var $content = this._content;
581
if(!this.data.length) {
582
$content.append(this._createNoDataRow());
586
var indexFrom = this._loadStrategy.firstDisplayIndex();
587
var indexTo = this._loadStrategy.lastDisplayIndex();
589
for(var itemIndex = indexFrom; itemIndex < indexTo; itemIndex++) {
590
var item = this.data[itemIndex];
591
$content.append(this._createRow(item, itemIndex));
595
_createNoDataRow: function() {
596
var amountOfFields = 0;
597
this._eachField(function() {
601
return $("<tr>").addClass(this.noDataRowClass)
602
.append($("<td>").addClass(this.cellClass).attr("colspan", amountOfFields)
603
.append(this.renderTemplate(this.noDataContent, this)));
606
_createRow: function(item, itemIndex) {
609
if($.isFunction(this.rowRenderer)) {
610
$result = this.renderTemplate(this.rowRenderer, this, { item: item, itemIndex: itemIndex });
613
this._renderCells($result, item);
616
$result.addClass(this._getRowClasses(item, itemIndex))
617
.data(JSGRID_ROW_DATA_KEY, item)
618
.on("click", $.proxy(function(e) {
621
itemIndex: itemIndex,
625
.on("dblclick", $.proxy(function(e) {
626
this.rowDoubleClick({
628
itemIndex: itemIndex,
634
this._attachRowHover($result);
640
_getRowClasses: function(item, itemIndex) {
642
classes.push(((itemIndex + 1) % 2) ? this.oddRowClass : this.evenRowClass);
643
classes.push(getOrApply(this.rowClass, this, item, itemIndex));
644
return classes.join(" ");
647
_attachRowHover: function($row) {
648
var selectedRowClass = this.selectedRowClass;
649
$row.hover(function() {
650
$(this).addClass(selectedRowClass);
653
$(this).removeClass(selectedRowClass);
658
_renderCells: function($row, item) {
659
this._eachField(function(field) {
660
$row.append(this._createCell(item, field));
665
_createCell: function(item, field) {
667
var fieldValue = this._getItemFieldValue(item, field);
669
var args = { value: fieldValue, item : item };
670
if($.isFunction(field.cellRenderer)) {
671
$result = this.renderTemplate(field.cellRenderer, field, args);
673
$result = $("<td>").append(this.renderTemplate(field.itemTemplate || fieldValue, field, args));
676
return this._prepareCell($result, field);
679
_getItemFieldValue: function(item, field) {
680
var props = field.name.split('.');
681
var result = item[props.shift()];
683
while(result && props.length) {
684
result = result[props.shift()];
690
_setItemFieldValue: function(item, field, value) {
691
var props = field.name.split('.');
695
while(current && props.length) {
697
prop = props.shift();
698
current = item[prop];
702
while(props.length) {
703
item = item[prop] = {};
704
prop = props.shift();
711
sort: function(field, order) {
712
if($.isPlainObject(field)) {
717
this._clearSortingCss();
718
this._setSortingParams(field, order);
719
this._setSortingCss();
720
return this._loadStrategy.sort();
723
_clearSortingCss: function() {
724
this._headerRow.find("th")
725
.removeClass(this.sortAscClass)
726
.removeClass(this.sortDescClass);
729
_setSortingParams: function(field, order) {
730
field = this._normalizeField(field);
731
order = order || ((this._sortField === field) ? this._reversedSortOrder(this._sortOrder) : SORT_ORDER_ASC);
733
this._sortField = field;
734
this._sortOrder = order;
737
_normalizeField: function(field) {
738
if($.isNumeric(field)) {
739
return this.fields[field];
742
if(typeof field === "string") {
743
return $.grep(this.fields, function(f) {
744
return f.name === field;
751
_reversedSortOrder: function(order) {
752
return (order === SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC);
755
_setSortingCss: function() {
756
var fieldIndex = this._visibleFieldIndex(this._sortField);
758
this._headerRow.find("th").eq(fieldIndex)
759
.addClass(this._sortOrder === SORT_ORDER_ASC ? this.sortAscClass : this.sortDescClass);
762
_visibleFieldIndex: function(field) {
763
return $.inArray(field, $.grep(this.fields, function(f) { return f.visible; }));
766
_sortData: function() {
767
var sortFactor = this._sortFactor(),
768
sortField = this._sortField;
771
this.data.sort(function(item1, item2) {
772
return sortFactor * sortField.sortingFunc(item1[sortField.name], item2[sortField.name]);
777
_sortFactor: function() {
778
return this._sortOrder === SORT_ORDER_ASC ? 1 : -1;
781
_itemsCount: function() {
782
return this._loadStrategy.itemsCount();
785
_pagesCount: function() {
786
var itemsCount = this._itemsCount(),
787
pageSize = this.pageSize;
788
return Math.floor(itemsCount / pageSize) + (itemsCount % pageSize ? 1 : 0);
791
_refreshPager: function() {
792
var $pagerContainer = this._pagerContainer;
793
$pagerContainer.empty();
796
$pagerContainer.append(this._createPager());
799
var showPager = this.paging && this._pagesCount() > 1;
800
$pagerContainer.toggle(showPager);
803
_createPager: function() {
806
if($.isFunction(this.pagerRenderer)) {
807
$result = $(this.pagerRenderer({
808
pageIndex: this.pageIndex,
809
pageCount: this._pagesCount()
812
$result = $("<div>").append(this._createPagerByFormat());
815
$result.addClass(this.pagerClass);
820
_createPagerByFormat: function() {
821
var pageIndex = this.pageIndex,
822
pageCount = this._pagesCount(),
823
itemCount = this._itemsCount(),
824
pagerParts = this.pagerFormat.split(" ");
826
return $.map(pagerParts, $.proxy(function(pagerPart) {
827
var result = pagerPart;
829
if(pagerPart === PAGES_PLACEHOLDER) {
830
result = this._createPages();
831
} else if(pagerPart === FIRST_PAGE_PLACEHOLDER) {
832
result = this._createPagerNavButton(this.pageFirstText, 1, pageIndex > 1);
833
} else if(pagerPart === PREV_PAGE_PLACEHOLDER) {
834
result = this._createPagerNavButton(this.pagePrevText, pageIndex - 1, pageIndex > 1);
835
} else if(pagerPart === NEXT_PAGE_PLACEHOLDER) {
836
result = this._createPagerNavButton(this.pageNextText, pageIndex + 1, pageIndex < pageCount);
837
} else if(pagerPart === LAST_PAGE_PLACEHOLDER) {
838
result = this._createPagerNavButton(this.pageLastText, pageCount, pageIndex < pageCount);
839
} else if(pagerPart === PAGE_INDEX_PLACEHOLDER) {
841
} else if(pagerPart === PAGE_COUNT_PLACEHOLDER) {
843
} else if(pagerPart === ITEM_COUNT_PLACEHOLDER) {
847
return $.isArray(result) ? result.concat([" "]) : [result, " "];
851
_createPages: function() {
852
var pageCount = this._pagesCount(),
853
pageButtonCount = this.pageButtonCount,
854
firstDisplayingPage = this._firstDisplayingPage,
857
if(firstDisplayingPage > 1) {
858
pages.push(this._createPagerPageNavButton(this.pageNavigatorPrevText, this.showPrevPages));
861
for(var i = 0, pageNumber = firstDisplayingPage; i < pageButtonCount && pageNumber <= pageCount; i++, pageNumber++) {
862
pages.push(pageNumber === this.pageIndex
863
? this._createPagerCurrentPage()
864
: this._createPagerPage(pageNumber));
867
if((firstDisplayingPage + pageButtonCount - 1) < pageCount) {
868
pages.push(this._createPagerPageNavButton(this.pageNavigatorNextText, this.showNextPages));
874
_createPagerNavButton: function(text, pageIndex, isActive) {
875
return this._createPagerButton(text, this.pagerNavButtonClass + (isActive ? "" : " " + this.pagerNavButtonInactiveClass),
876
isActive ? function() { this.openPage(pageIndex); } : $.noop);
879
_createPagerPageNavButton: function(text, handler) {
880
return this._createPagerButton(text, this.pagerNavButtonClass, handler);
883
_createPagerPage: function(pageIndex) {
884
return this._createPagerButton(pageIndex, this.pageClass, function() {
885
this.openPage(pageIndex);
889
_createPagerButton: function(text, css, handler) {
890
var $link = $("<a>").attr("href", EMPTY_HREF)
892
.on("click", $.proxy(handler, this));
894
return $("<span>").addClass(css).append($link);
897
_createPagerCurrentPage: function() {
899
.addClass(this.pageClass)
900
.addClass(this.currentPageClass)
901
.text(this.pageIndex);
904
_refreshSize: function() {
905
this._refreshHeight();
906
this._refreshWidth();
909
_refreshWidth: function() {
910
var width = (this.width === "auto") ? this._getAutoWidth() : this.width;
912
this._container.width(width);
915
_getAutoWidth: function() {
916
var $headerGrid = this._headerGrid,
917
$header = this._header;
919
$headerGrid.width("auto");
921
var contentWidth = $headerGrid.outerWidth();
922
var borderWidth = $header.outerWidth() - $header.innerWidth();
924
$headerGrid.width("");
926
return contentWidth + borderWidth;
929
_scrollBarWidth: (function() {
933
if(result === undefined) {
934
var $ghostContainer = $("<div style='width:50px;height:50px;overflow:hidden;position:absolute;top:-10000px;left:-10000px;'></div>");
935
var $ghostContent = $("<div style='height:100px;'></div>");
936
$ghostContainer.append($ghostContent).appendTo("body");
937
var width = $ghostContent.innerWidth();
938
$ghostContainer.css("overflow-y", "auto");
939
var widthExcludingScrollBar = $ghostContent.innerWidth();
940
$ghostContainer.remove();
941
result = width - widthExcludingScrollBar;
947
_refreshHeight: function() {
948
var container = this._container,
949
pagerContainer = this._pagerContainer,
950
height = this.height,
953
container.height(height);
955
if(height !== "auto") {
956
height = container.height();
958
nonBodyHeight = this._header.outerHeight(true);
959
if(pagerContainer.parents(container).length) {
960
nonBodyHeight += pagerContainer.outerHeight(true);
963
this._body.outerHeight(height - nonBodyHeight);
967
showPrevPages: function() {
968
var firstDisplayingPage = this._firstDisplayingPage,
969
pageButtonCount = this.pageButtonCount;
971
this._firstDisplayingPage = (firstDisplayingPage > pageButtonCount) ? firstDisplayingPage - pageButtonCount : 1;
973
this._refreshPager();
976
showNextPages: function() {
977
var firstDisplayingPage = this._firstDisplayingPage,
978
pageButtonCount = this.pageButtonCount,
979
pageCount = this._pagesCount();
981
this._firstDisplayingPage = (firstDisplayingPage + 2 * pageButtonCount > pageCount)
982
? pageCount - pageButtonCount + 1
983
: firstDisplayingPage + pageButtonCount;
985
this._refreshPager();
988
openPage: function(pageIndex) {
989
if(pageIndex < 1 || pageIndex > this._pagesCount())
992
this._setPage(pageIndex);
993
this._loadStrategy.openPage(pageIndex);
996
_setPage: function(pageIndex) {
997
var firstDisplayingPage = this._firstDisplayingPage,
998
pageButtonCount = this.pageButtonCount;
1000
this.pageIndex = pageIndex;
1002
if(pageIndex < firstDisplayingPage) {
1003
this._firstDisplayingPage = pageIndex;
1006
if(pageIndex > firstDisplayingPage + pageButtonCount - 1) {
1007
this._firstDisplayingPage = pageIndex - pageButtonCount + 1;
1010
this._callEventHandler(this.onPageChanged, {
1011
pageIndex: pageIndex
1015
_controllerCall: function(method, param, isCanceled, doneCallback) {
1017
return $.Deferred().reject().promise();
1019
this._showLoading();
1021
var controller = this._controller;
1022
if(!controller || !controller[method]) {
1023
throw Error("controller has no method '" + method + "'");
1026
return normalizePromise(controller[method](param))
1027
.done($.proxy(doneCallback, this))
1028
.fail($.proxy(this._errorHandler, this))
1029
.always($.proxy(this._hideLoading, this));
1032
_errorHandler: function() {
1033
this._callEventHandler(this.onError, {
1034
args: $.makeArray(arguments)
1038
_showLoading: function() {
1039
if(!this.loadIndication)
1042
clearTimeout(this._loadingTimer);
1044
this._loadingTimer = setTimeout($.proxy(function() {
1045
this._loadIndicator.show();
1046
}, this), this.loadIndicationDelay);
1049
_hideLoading: function() {
1050
if(!this.loadIndication)
1053
clearTimeout(this._loadingTimer);
1054
this._loadIndicator.hide();
1057
search: function(filter) {
1058
this._resetSorting();
1060
return this.loadData(filter);
1063
loadData: function(filter) {
1064
filter = filter || (this.filtering ? this.getFilter() : {});
1066
$.extend(filter, this._loadStrategy.loadParams(), this._sortingParams());
1068
var args = this._callEventHandler(this.onDataLoading, {
1072
return this._controllerCall("loadData", filter, args.cancel, function(loadedData) {
1076
this._loadStrategy.finishLoad(loadedData);
1078
this._callEventHandler(this.onDataLoaded, {
1084
getFilter: function() {
1086
this._eachField(function(field) {
1087
if(field.filtering) {
1088
this._setItemFieldValue(result, field, field.filterValue());
1094
_sortingParams: function() {
1095
if(this.sorting && this._sortField) {
1097
sortField: this._sortField.name,
1098
sortOrder: this._sortOrder
1104
getSorting: function() {
1105
var sortingParams = this._sortingParams();
1107
field: sortingParams.sortField,
1108
order: sortingParams.sortOrder
1112
clearFilter: function() {
1113
var $filterRow = this._createFilterRow();
1114
this._filterRow.replaceWith($filterRow);
1115
this._filterRow = $filterRow;
1116
return this.search();
1119
insertItem: function(item) {
1120
var insertingItem = item || this._getValidatedInsertItem();
1123
return $.Deferred().reject().promise();
1125
var args = this._callEventHandler(this.onItemInserting, {
1129
return this._controllerCall("insertItem", insertingItem, args.cancel, function(insertedItem) {
1130
insertedItem = insertedItem || insertingItem;
1131
this._loadStrategy.finishInsert(insertedItem);
1133
this._callEventHandler(this.onItemInserted, {
1139
_getValidatedInsertItem: function() {
1140
var item = this._getInsertItem();
1141
return this._validateItem(item, this._insertRow) ? item : null;
1144
_getInsertItem: function() {
1146
this._eachField(function(field) {
1147
if(field.inserting) {
1148
this._setItemFieldValue(result, field, field.insertValue());
1154
_validateItem: function(item, $row) {
1155
var validationErrors = [];
1159
itemIndex: this._rowIndex($row),
1163
this._eachField(function(field) {
1164
if(!field.validate ||
1165
($row === this._insertRow && !field.inserting) ||
1166
($row === this._getEditRow() && !field.editing))
1169
var fieldValue = this._getItemFieldValue(item, field);
1171
var errors = this._validation.validate($.extend({
1173
rules: field.validate
1176
this._setCellValidity($row.children().eq(this._visibleFieldIndex(field)), errors);
1181
validationErrors.push.apply(validationErrors,
1182
$.map(errors, function(message) {
1183
return { field: field, message: message };
1187
if(!validationErrors.length)
1190
var invalidArgs = $.extend({
1191
errors: validationErrors
1193
this._callEventHandler(this.onItemInvalid, invalidArgs);
1194
this.invalidNotify(invalidArgs);
1199
_setCellValidity: function($cell, errors) {
1201
.toggleClass(this.invalidClass, !!errors.length)
1202
.attr("title", errors.join("\n"));
1205
clearInsert: function() {
1206
var insertRow = this._createInsertRow();
1207
this._insertRow.replaceWith(insertRow);
1208
this._insertRow = insertRow;
1212
editItem: function(item) {
1213
var $row = this.rowByItem(item);
1215
this._editRow($row);
1219
rowByItem: function(item) {
1220
if(item.jquery || item.nodeType)
1223
return this._content.find("tr").filter(function() {
1224
return $.data(this, JSGRID_ROW_DATA_KEY) === item;
1228
_editRow: function($row) {
1232
var item = $row.data(JSGRID_ROW_DATA_KEY);
1234
var args = this._callEventHandler(this.onItemEditing, {
1237
itemIndex: this._itemIndex(item)
1243
if(this._editingRow) {
1247
var $editRow = this._createEditRow(item);
1249
this._editingRow = $row;
1251
$editRow.insertBefore($row);
1252
$row.data(JSGRID_EDIT_ROW_DATA_KEY, $editRow);
1255
_createEditRow: function(item) {
1256
if($.isFunction(this.editRowRenderer)) {
1257
return $(this.renderTemplate(this.editRowRenderer, this, { item: item, itemIndex: this._itemIndex(item) }));
1260
var $result = $("<tr>").addClass(this.editRowClass);
1262
this._eachField(function(field) {
1263
var fieldValue = this._getItemFieldValue(item, field);
1265
this._prepareCell("<td>", field, "editcss")
1266
.append(this.renderTemplate(field.editTemplate || "", field, { value: fieldValue, item: item }))
1273
updateItem: function(item, editedItem) {
1274
if(arguments.length === 1) {
1278
var $row = item ? this.rowByItem(item) : this._editingRow;
1279
editedItem = editedItem || this._getValidatedEditedItem();
1284
return this._updateRow($row, editedItem);
1287
_getValidatedEditedItem: function() {
1288
var item = this._getEditedItem();
1289
return this._validateItem(item, this._getEditRow()) ? item : null;
1292
_updateRow: function($updatingRow, editedItem) {
1293
var updatingItem = $updatingRow.data(JSGRID_ROW_DATA_KEY),
1294
updatingItemIndex = this._itemIndex(updatingItem),
1295
updatedItem = $.extend(true, {}, updatingItem, editedItem);
1297
var args = this._callEventHandler(this.onItemUpdating, {
1300
itemIndex: updatingItemIndex,
1301
previousItem: updatingItem
1304
return this._controllerCall("updateItem", updatedItem, args.cancel, function(loadedUpdatedItem) {
1305
var previousItem = $.extend(true, {}, updatingItem);
1306
updatedItem = loadedUpdatedItem || $.extend(true, updatingItem, editedItem);
1308
var $updatedRow = this._finishUpdate($updatingRow, updatedItem, updatingItemIndex);
1310
this._callEventHandler(this.onItemUpdated, {
1313
itemIndex: updatingItemIndex,
1314
previousItem: previousItem
1319
_rowIndex: function(row) {
1320
return this._content.children().index($(row));
1323
_itemIndex: function(item) {
1324
return $.inArray(item, this.data);
1327
_finishUpdate: function($updatingRow, updatedItem, updatedItemIndex) {
1329
this.data[updatedItemIndex] = updatedItem;
1331
var $updatedRow = this._createRow(updatedItem, updatedItemIndex);
1332
$updatingRow.replaceWith($updatedRow);
1336
_getEditedItem: function() {
1338
this._eachField(function(field) {
1340
this._setItemFieldValue(result, field, field.editValue());
1346
cancelEdit: function() {
1347
if(!this._editingRow)
1350
this._getEditRow().remove();
1351
this._editingRow.show();
1352
this._editingRow = null;
1355
_getEditRow: function() {
1356
return this._editingRow && this._editingRow.data(JSGRID_EDIT_ROW_DATA_KEY);
1359
deleteItem: function(item) {
1360
var $row = this.rowByItem(item);
1365
if(this.confirmDeleting && !window.confirm(getOrApply(this.deleteConfirm, this, $row.data(JSGRID_ROW_DATA_KEY))))
1368
return this._deleteRow($row);
1371
_deleteRow: function($row) {
1372
var deletingItem = $row.data(JSGRID_ROW_DATA_KEY),
1373
deletingItemIndex = this._itemIndex(deletingItem);
1375
var args = this._callEventHandler(this.onItemDeleting, {
1378
itemIndex: deletingItemIndex
1381
return this._controllerCall("deleteItem", deletingItem, args.cancel, function() {
1382
this._loadStrategy.finishDelete(deletingItem, deletingItemIndex);
1384
this._callEventHandler(this.onItemDeleted, {
1387
itemIndex: deletingItemIndex
1393
$.fn.jsGrid = function(config) {
1394
var args = $.makeArray(arguments),
1395
methodArgs = args.slice(1),
1398
this.each(function() {
1399
var $element = $(this),
1400
instance = $element.data(JSGRID_DATA_KEY),
1404
if(typeof config === "string") {
1405
methodResult = instance[config].apply(instance, methodArgs);
1406
if(methodResult !== undefined && methodResult !== instance) {
1407
result = methodResult;
1411
instance._detachWindowResizeCallback();
1412
instance._init(config);
1416
new Grid($element, config);
1425
var setDefaults = function(config) {
1426
var componentPrototype;
1428
if($.isPlainObject(config)) {
1429
componentPrototype = Grid.prototype;
1431
componentPrototype = fields[config].prototype;
1432
config = arguments[1] || {};
1435
$.extend(componentPrototype, config);
1440
var locale = function(lang) {
1441
var localeConfig = $.isPlainObject(lang) ? lang : locales[lang];
1444
throw Error("unknown locale " + lang);
1446
setLocale(jsGrid, localeConfig);
1449
var setLocale = function(obj, localeConfig) {
1450
$.each(localeConfig, function(field, value) {
1451
if($.isPlainObject(value)) {
1452
setLocale(obj[field] || obj[field[0].toUpperCase() + field.slice(1)], value);
1456
if(obj.hasOwnProperty(field)) {
1459
obj.prototype[field] = value;
1467
setDefaults: setDefaults,
1475
(function(jsGrid, $, undefined) {
1477
function LoadIndicator(config) {
1481
LoadIndicator.prototype = {
1484
message: "Loading...",
1488
shaderClass: "jsgrid-load-shader",
1489
loadPanelClass: "jsgrid-load-panel",
1491
_init: function(config) {
1492
$.extend(true, this, config);
1494
this._initContainer();
1496
this._initLoadPanel();
1499
_initContainer: function() {
1500
this._container = $(this.container);
1503
_initShader: function() {
1507
this._shader = $("<div>").addClass(this.shaderClass)
1510
position: "absolute",
1517
.appendTo(this._container);
1520
_initLoadPanel: function() {
1521
this._loadPanel = $("<div>").addClass(this.loadPanelClass)
1525
position: "absolute",
1530
.appendTo(this._container);
1534
var $loadPanel = this._loadPanel.show();
1536
var actualWidth = $loadPanel.outerWidth();
1537
var actualHeight = $loadPanel.outerHeight();
1540
marginTop: -actualHeight / 2,
1541
marginLeft: -actualWidth / 2
1544
this._shader.show();
1548
this._loadPanel.hide();
1549
this._shader.hide();
1554
jsGrid.LoadIndicator = LoadIndicator;
1558
(function(jsGrid, $, undefined) {
1560
function DirectLoadingStrategy(grid) {
1564
DirectLoadingStrategy.prototype = {
1566
firstDisplayIndex: function() {
1567
var grid = this._grid;
1568
return grid.option("paging") ? (grid.option("pageIndex") - 1) * grid.option("pageSize") : 0;
1571
lastDisplayIndex: function() {
1572
var grid = this._grid;
1573
var itemsCount = grid.option("data").length;
1575
return grid.option("paging")
1576
? Math.min(grid.option("pageIndex") * grid.option("pageSize"), itemsCount)
1580
itemsCount: function() {
1581
return this._grid.option("data").length;
1584
openPage: function(index) {
1585
this._grid.refresh();
1588
loadParams: function() {
1593
this._grid._sortData();
1594
this._grid.refresh();
1595
return $.Deferred().resolve().promise();
1599
this._grid.refresh();
1600
return $.Deferred().resolve().promise();
1603
finishLoad: function(loadedData) {
1604
this._grid.option("data", loadedData);
1607
finishInsert: function(insertedItem) {
1608
var grid = this._grid;
1609
grid.option("data").push(insertedItem);
1613
finishDelete: function(deletedItem, deletedItemIndex) {
1614
var grid = this._grid;
1615
grid.option("data").splice(deletedItemIndex, 1);
1621
function PageLoadingStrategy(grid) {
1623
this._itemsCount = 0;
1626
PageLoadingStrategy.prototype = {
1628
firstDisplayIndex: function() {
1632
lastDisplayIndex: function() {
1633
return this._grid.option("data").length;
1636
itemsCount: function() {
1637
return this._itemsCount;
1640
openPage: function(index) {
1641
this._grid.loadData();
1644
loadParams: function() {
1645
var grid = this._grid;
1647
pageIndex: grid.option("pageIndex"),
1648
pageSize: grid.option("pageSize")
1653
return this._grid.loadData();
1657
return this._grid.loadData();
1660
finishLoad: function(loadedData) {
1661
this._itemsCount = loadedData.itemsCount;
1662
this._grid.option("data", loadedData.data);
1665
finishInsert: function(insertedItem) {
1666
this._grid.search();
1669
finishDelete: function(deletedItem, deletedItemIndex) {
1670
this._grid.search();
1674
jsGrid.loadStrategies = {
1675
DirectLoadingStrategy: DirectLoadingStrategy,
1676
PageLoadingStrategy: PageLoadingStrategy
1681
(function(jsGrid, $, undefined) {
1683
var isDefined = function(val) {
1684
return typeof(val) !== "undefined" && val !== null;
1687
var sortStrategies = {
1688
string: function(str1, str2) {
1689
if(!isDefined(str1) && !isDefined(str2))
1692
if(!isDefined(str1))
1695
if(!isDefined(str2))
1698
return ("" + str1).localeCompare("" + str2);
1701
number: function(n1, n2) {
1705
date: function(dt1, dt2) {
1709
numberAsString: function(n1, n2) {
1710
return parseFloat(n1) - parseFloat(n2);
1714
jsGrid.sortStrategies = sortStrategies;
1718
(function(jsGrid, $, undefined) {
1720
function Validation(config) {
1724
Validation.prototype = {
1726
_init: function(config) {
1727
$.extend(true, this, config);
1730
validate: function(args) {
1733
$.each(this._normalizeRules(args.rules), function(_, rule) {
1734
if(rule.validator(args.value, args.item, rule.param))
1737
var errorMessage = $.isFunction(rule.message) ? rule.message(args.value, args.item) : rule.message;
1738
errors.push(errorMessage);
1744
_normalizeRules: function(rules) {
1745
if(!$.isArray(rules))
1748
return $.map(rules, $.proxy(function(rule) {
1749
return this._normalizeRule(rule);
1753
_normalizeRule: function(rule) {
1754
if(typeof rule === "string")
1755
rule = { validator: rule };
1757
if($.isFunction(rule))
1758
rule = { validator: rule };
1760
if($.isPlainObject(rule))
1761
rule = $.extend({}, rule);
1763
throw Error("wrong validation config specified");
1765
if($.isFunction(rule.validator))
1768
return this._applyNamedValidator(rule, rule.validator);
1771
_applyNamedValidator: function(rule, validatorName) {
1772
delete rule.validator;
1774
var validator = validators[validatorName];
1776
throw Error("unknown validator \"" + validatorName + "\"");
1778
if($.isFunction(validator)) {
1779
validator = { validator: validator };
1782
return $.extend({}, validator, rule);
1786
jsGrid.Validation = Validation;
1791
message: "Field is required",
1792
validator: function(value) {
1793
return value !== undefined && value !== null && value !== "";
1798
message: "Field value length is out of the defined range",
1799
validator: function(value, _, param) {
1800
return value.length >= param[0] && value.length <= param[1];
1805
message: "Field value is too short",
1806
validator: function(value, _, param) {
1807
return value.length >= param;
1812
message: "Field value is too long",
1813
validator: function(value, _, param) {
1814
return value.length <= param;
1819
message: "Field value is not matching the defined pattern",
1820
validator: function(value, _, param) {
1821
if(typeof param === "string") {
1822
param = new RegExp("^(?:" + param + ")$");
1824
return param.test(value);
1829
message: "Field value is out of the defined range",
1830
validator: function(value, _, param) {
1831
return value >= param[0] && value <= param[1];
1836
message: "Field value is too small",
1837
validator: function(value, _, param) {
1838
return value >= param;
1843
message: "Field value is too large",
1844
validator: function(value, _, param) {
1845
return value <= param;
1850
jsGrid.validators = validators;
1854
(function(jsGrid, $, undefined) {
1856
function Field(config) {
1857
$.extend(true, this, config);
1858
this.sortingFunc = this._getSortingFunc();
1875
headerTemplate: function() {
1876
return (this.title === undefined || this.title === null) ? this.name : this.title;
1879
itemTemplate: function(value, item) {
1883
filterTemplate: function() {
1887
insertTemplate: function() {
1891
editTemplate: function(value, item) {
1892
this._value = value;
1893
return this.itemTemplate(value, item);
1896
filterValue: function() {
1900
insertValue: function() {
1904
editValue: function() {
1908
_getSortingFunc: function() {
1909
var sorter = this.sorter;
1911
if($.isFunction(sorter)) {
1915
if(typeof sorter === "string") {
1916
return jsGrid.sortStrategies[sorter];
1919
throw Error("wrong sorter for the field \"" + this.name + "\"!");
1923
jsGrid.Field = Field;
1927
(function(jsGrid, $, undefined) {
1929
var Field = jsGrid.Field;
1931
function TextField(config) {
1932
Field.call(this, config);
1935
TextField.prototype = new Field({
1940
filterTemplate: function() {
1944
var grid = this._grid,
1945
$result = this.filterControl = this._createTextBox();
1947
if(this.autosearch) {
1948
$result.on("keypress", function(e) {
1949
if(e.which === 13) {
1959
insertTemplate: function() {
1963
return this.insertControl = this._createTextBox();
1966
editTemplate: function(value) {
1968
return this.itemTemplate.apply(this, arguments);
1970
var $result = this.editControl = this._createTextBox();
1975
filterValue: function() {
1976
return this.filterControl.val();
1979
insertValue: function() {
1980
return this.insertControl.val();
1983
editValue: function() {
1984
return this.editControl.val();
1987
_createTextBox: function() {
1988
return $("<input>").attr("type", "text")
1989
.prop("readonly", !!this.readOnly);
1993
jsGrid.fields.text = jsGrid.TextField = TextField;
1997
(function(jsGrid, $, undefined) {
1999
var TextField = jsGrid.TextField;
2001
function NumberField(config) {
2002
TextField.call(this, config);
2005
NumberField.prototype = new TextField({
2011
filterValue: function() {
2012
return this.filterControl.val()
2013
? parseInt(this.filterControl.val() || 0, 10)
2017
insertValue: function() {
2018
return this.insertControl.val()
2019
? parseInt(this.insertControl.val() || 0, 10)
2023
editValue: function() {
2024
return this.editControl.val()
2025
? parseInt(this.editControl.val() || 0, 10)
2029
_createTextBox: function() {
2030
return $("<input>").attr("type", "number")
2031
.prop("readonly", !!this.readOnly);
2035
jsGrid.fields.number = jsGrid.NumberField = NumberField;
2039
(function(jsGrid, $, undefined) {
2041
var TextField = jsGrid.TextField;
2043
function TextAreaField(config) {
2044
TextField.call(this, config);
2047
TextAreaField.prototype = new TextField({
2049
insertTemplate: function() {
2053
return this.insertControl = this._createTextArea();
2056
editTemplate: function(value) {
2058
return this.itemTemplate.apply(this, arguments);
2060
var $result = this.editControl = this._createTextArea();
2065
_createTextArea: function() {
2066
return $("<textarea>").prop("readonly", !!this.readOnly);
2070
jsGrid.fields.textarea = jsGrid.TextAreaField = TextAreaField;
2074
(function(jsGrid, $, undefined) {
2076
var NumberField = jsGrid.NumberField;
2077
var numberValueType = "number";
2078
var stringValueType = "string";
2080
function SelectField(config) {
2082
this.selectedIndex = -1;
2083
this.valueField = "";
2084
this.textField = "";
2086
if(config.valueField && config.items.length) {
2087
var firstItemValue = config.items[0][config.valueField];
2088
this.valueType = (typeof firstItemValue) === numberValueType ? numberValueType : stringValueType;
2091
this.sorter = this.valueType;
2093
NumberField.call(this, config);
2096
SelectField.prototype = new NumberField({
2099
valueType: numberValueType,
2101
itemTemplate: function(value) {
2102
var items = this.items,
2103
valueField = this.valueField,
2104
textField = this.textField,
2108
resultItem = $.grep(items, function(item, index) {
2109
return item[valueField] === value;
2113
resultItem = items[value];
2116
var result = (textField ? resultItem[textField] : resultItem);
2118
return (result === undefined || result === null) ? "" : result;
2121
filterTemplate: function() {
2125
var grid = this._grid,
2126
$result = this.filterControl = this._createSelect();
2128
if(this.autosearch) {
2129
$result.on("change", function(e) {
2137
insertTemplate: function() {
2141
return this.insertControl = this._createSelect();
2144
editTemplate: function(value) {
2146
return this.itemTemplate.apply(this, arguments);
2148
var $result = this.editControl = this._createSelect();
2149
(value !== undefined) && $result.val(value);
2153
filterValue: function() {
2154
var val = this.filterControl.val();
2155
return this.valueType === numberValueType ? parseInt(val || 0, 10) : val;
2158
insertValue: function() {
2159
var val = this.insertControl.val();
2160
return this.valueType === numberValueType ? parseInt(val || 0, 10) : val;
2163
editValue: function() {
2164
var val = this.editControl.val();
2165
return this.valueType === numberValueType ? parseInt(val || 0, 10) : val;
2168
_createSelect: function() {
2169
var $result = $("<select>"),
2170
valueField = this.valueField,
2171
textField = this.textField,
2172
selectedIndex = this.selectedIndex;
2174
$.each(this.items, function(index, item) {
2175
var value = valueField ? item[valueField] : index,
2176
text = textField ? item[textField] : item;
2178
var $option = $("<option>")
2179
.attr("value", value)
2183
$option.prop("selected", (selectedIndex === index));
2186
$result.prop("disabled", !!this.readOnly);
2192
jsGrid.fields.select = jsGrid.SelectField = SelectField;
2196
(function(jsGrid, $, undefined) {
2198
var Field = jsGrid.Field;
2200
function CheckboxField(config) {
2201
Field.call(this, config);
2204
CheckboxField.prototype = new Field({
2210
itemTemplate: function(value) {
2211
return this._createCheckbox().prop({
2217
filterTemplate: function() {
2221
var grid = this._grid,
2222
$result = this.filterControl = this._createCheckbox();
2229
$result.on("click", function() {
2232
if($cb.prop("readOnly")) {
2238
else if(!$cb.prop("checked")) {
2246
if(this.autosearch) {
2247
$result.on("click", function() {
2255
insertTemplate: function() {
2259
return this.insertControl = this._createCheckbox();
2262
editTemplate: function(value) {
2264
return this.itemTemplate.apply(this, arguments);
2266
var $result = this.editControl = this._createCheckbox();
2267
$result.prop("checked", value);
2271
filterValue: function() {
2272
return this.filterControl.get(0).indeterminate
2274
: this.filterControl.is(":checked");
2277
insertValue: function() {
2278
return this.insertControl.is(":checked");
2281
editValue: function() {
2282
return this.editControl.is(":checked");
2285
_createCheckbox: function() {
2286
return $("<input>").attr("type", "checkbox");
2290
jsGrid.fields.checkbox = jsGrid.CheckboxField = CheckboxField;
2294
(function(jsGrid, $, undefined) {
2296
var Field = jsGrid.Field;
2298
function ControlField(config) {
2299
Field.call(this, config);
2300
this._configInitialized = false;
2303
ControlField.prototype = new Field({
2304
css: "jsgrid-control-field",
2312
buttonClass: "jsgrid-button",
2313
modeButtonClass: "jsgrid-mode-button",
2315
modeOnButtonClass: "jsgrid-mode-on-button",
2316
searchModeButtonClass: "jsgrid-search-mode-button",
2317
insertModeButtonClass: "jsgrid-insert-mode-button",
2318
editButtonClass: "jsgrid-edit-button",
2319
deleteButtonClass: "jsgrid-delete-button",
2320
searchButtonClass: "jsgrid-search-button",
2321
clearFilterButtonClass: "jsgrid-clear-filter-button",
2322
insertButtonClass: "jsgrid-insert-button",
2323
updateButtonClass: "jsgrid-update-button",
2324
cancelEditButtonClass: "jsgrid-cancel-edit-button",
2326
searchModeButtonTooltip: "Switch to searching",
2327
insertModeButtonTooltip: "Switch to inserting",
2328
editButtonTooltip: "Edit",
2329
deleteButtonTooltip: "Delete",
2330
searchButtonTooltip: "Search",
2331
clearFilterButtonTooltip: "Clear filter",
2332
insertButtonTooltip: "Insert",
2333
updateButtonTooltip: "Update",
2334
cancelEditButtonTooltip: "Cancel edit",
2338
clearFilterButton: true,
2339
modeSwitchButton: true,
2341
_initConfig: function() {
2342
this._hasFiltering = this._grid.filtering;
2343
this._hasInserting = this._grid.inserting;
2345
if(this._hasInserting && this.modeSwitchButton) {
2346
this._grid.inserting = false;
2349
this._configInitialized = true;
2352
headerTemplate: function() {
2353
if(!this._configInitialized) {
2357
var hasFiltering = this._hasFiltering;
2358
var hasInserting = this._hasInserting;
2360
if(!this.modeSwitchButton || (!hasFiltering && !hasInserting))
2363
if(hasFiltering && !hasInserting)
2364
return this._createFilterSwitchButton();
2366
if(hasInserting && !hasFiltering)
2367
return this._createInsertSwitchButton();
2369
return this._createModeSwitchButton();
2372
itemTemplate: function(value, item) {
2373
var $result = $([]);
2375
if(this.editButton) {
2376
$result = $result.add(this._createEditButton(item));
2379
if(this.deleteButton) {
2380
$result = $result.add(this._createDeleteButton(item));
2386
filterTemplate: function() {
2387
var $result = this._createSearchButton();
2388
return this.clearFilterButton ? $result.add(this._createClearFilterButton()) : $result;
2391
insertTemplate: function() {
2392
return this._createInsertButton();
2395
editTemplate: function() {
2396
return this._createUpdateButton().add(this._createCancelEditButton());
2399
_createFilterSwitchButton: function() {
2400
return this._createOnOffSwitchButton("filtering", this.searchModeButtonClass, true);
2403
_createInsertSwitchButton: function() {
2404
return this._createOnOffSwitchButton("inserting", this.insertModeButtonClass, false);
2407
_createOnOffSwitchButton: function(option, cssClass, isOnInitially) {
2408
var isOn = isOnInitially;
2410
var updateButtonState = $.proxy(function() {
2411
$button.toggleClass(this.modeOnButtonClass, isOn);
2414
var $button = this._createGridButton(this.modeButtonClass + " " + cssClass, "", function(grid) {
2416
grid.option(option, isOn);
2417
updateButtonState();
2420
updateButtonState();
2425
_createModeSwitchButton: function() {
2426
var isInserting = false;
2428
var updateButtonState = $.proxy(function() {
2429
$button.attr("title", isInserting ? this.searchModeButtonTooltip : this.insertModeButtonTooltip)
2430
.toggleClass(this.insertModeButtonClass, !isInserting)
2431
.toggleClass(this.searchModeButtonClass, isInserting);
2434
var $button = this._createGridButton(this.modeButtonClass, "", function(grid) {
2435
isInserting = !isInserting;
2436
grid.option("inserting", isInserting);
2437
grid.option("filtering", !isInserting);
2438
updateButtonState();
2441
updateButtonState();
2446
_createEditButton: function(item) {
2447
return this._createGridButton(this.editButtonClass, this.editButtonTooltip, function(grid, e) {
2448
grid.editItem(item);
2449
e.stopPropagation();
2453
_createDeleteButton: function(item) {
2454
return this._createGridButton(this.deleteButtonClass, this.deleteButtonTooltip, function(grid, e) {
2455
grid.deleteItem(item);
2456
e.stopPropagation();
2460
_createSearchButton: function() {
2461
return this._createGridButton(this.searchButtonClass, this.searchButtonTooltip, function(grid) {
2466
_createClearFilterButton: function() {
2467
return this._createGridButton(this.clearFilterButtonClass, this.clearFilterButtonTooltip, function(grid) {
2472
_createInsertButton: function() {
2473
return this._createGridButton(this.insertButtonClass, this.insertButtonTooltip, function(grid) {
2474
grid.insertItem().done(function() {
2480
_createUpdateButton: function() {
2481
return this._createGridButton(this.updateButtonClass, this.updateButtonTooltip, function(grid, e) {
2483
e.stopPropagation();
2487
_createCancelEditButton: function() {
2488
return this._createGridButton(this.cancelEditButtonClass, this.cancelEditButtonTooltip, function(grid, e) {
2490
e.stopPropagation();
2494
_createGridButton: function(cls, tooltip, clickHandler) {
2495
var grid = this._grid;
2497
return $("<input>").addClass(this.buttonClass)
2503
.on("click", function(e) {
2504
clickHandler(grid, e);
2508
editValue: function() {
2514
jsGrid.fields.control = jsGrid.ControlField = ControlField;