GPQAPP

Форк
0
2516 строк · 74.2 Кб
1
/*
2
 * jsGrid v1.5.3 (http://js-grid.com)
3
 * (c) 2016 Artem Tabalin
4
 * Licensed under MIT (https://github.com/tabalinas/jsgrid/blob/master/LICENSE)
5
 */
6

7
(function(window, $, undefined) {
8

9
    var JSGRID = "JSGrid",
10
        JSGRID_DATA_KEY = JSGRID,
11
        JSGRID_ROW_DATA_KEY = "JSGridItem",
12
        JSGRID_EDIT_ROW_DATA_KEY = "JSGridEditRow",
13

14
        SORT_ORDER_ASC = "asc",
15
        SORT_ORDER_DESC = "desc",
16

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}",
25

26
        EMPTY_HREF = "javascript:void(0);";
27

28
    var getOrApply = function(value, context) {
29
        if($.isFunction(value)) {
30
            return value.apply(context, $.makeArray(arguments).slice(2));
31
        }
32
        return value;
33
    };
34

35
    var normalizePromise = function(promise) {
36
        var d = $.Deferred();
37

38
        if(promise && promise.then) {
39
            promise.then(function() {
40
                d.resolve.apply(d, arguments);
41
            }, function() {
42
                d.reject.apply(d, arguments);
43
            });
44
        } else {
45
            d.resolve(promise);
46
        }
47

48
        return d.promise();
49
    };
50

51
    var defaultController = {
52
        loadData: $.noop,
53
        insertItem: $.noop,
54
        updateItem: $.noop,
55
        deleteItem: $.noop
56
    };
57

58

59
    function Grid(element, config) {
60
        var $element = $(element);
61

62
        $element.data(JSGRID_DATA_KEY, this);
63

64
        this._container = $element;
65

66
        this.data = [];
67
        this.fields = [];
68

69
        this._editingRow = null;
70
        this._sortField = null;
71
        this._sortOrder = SORT_ORDER_ASC;
72
        this._firstDisplayingPage = 1;
73

74
        this._init(config);
75
        this.render();
76
    }
77

78
    Grid.prototype = {
79
        width: "auto",
80
        height: "auto",
81
        updateOnResize: true,
82

83
        rowClass: $.noop,
84
        rowRenderer: null,
85

86
        rowClick: function(args) {
87
            if(this.editing) {
88
                this.editItem($(args.event.target).closest("tr"));
89
            }
90
        },
91
        rowDoubleClick: $.noop,
92

93
        noDataContent: "Not found",
94
        noDataRowClass: "jsgrid-nodata-row",
95

96
        heading: true,
97
        headerRowRenderer: null,
98
        headerRowClass: "jsgrid-header-row",
99
        headerCellClass: "jsgrid-header-cell",
100

101
        filtering: false,
102
        filterRowRenderer: null,
103
        filterRowClass: "jsgrid-filter-row",
104

105
        inserting: false,
106
        insertRowRenderer: null,
107
        insertRowClass: "jsgrid-insert-row",
108

109
        editing: false,
110
        editRowRenderer: null,
111
        editRowClass: "jsgrid-edit-row",
112

113
        confirmDeleting: true,
114
        deleteConfirm: "Are you sure?",
115

116
        selecting: true,
117
        selectedRowClass: "jsgrid-selected-row",
118
        oddRowClass: "jsgrid-row",
119
        evenRowClass: "jsgrid-alt-row",
120
        cellClass: "jsgrid-cell",
121

122
        sorting: false,
123
        sortableClass: "jsgrid-header-sortable",
124
        sortAscClass: "jsgrid-header-sort jsgrid-header-sort-asc",
125
        sortDescClass: "jsgrid-header-sort jsgrid-header-sort-desc",
126

127
        paging: false,
128
        pagerContainer: null,
129
        pageIndex: 1,
130
        pageSize: 20,
131
        pageButtonCount: 15,
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",
145

146
        customLoading: false,
147
        pageLoading: false,
148

149
        autoload: false,
150
        controller: defaultController,
151

152
        loadIndication: true,
153
        loadIndicationDelay: 500,
154
        loadMessage: "Please, wait...",
155
        loadShading: true,
156

157
        invalidMessage: "Invalid data entered!",
158

159
        invalidNotify: function(args) {
160
            var messages = $.map(args.errors, function(error) {
161
                return error.message || null;
162
            });
163

164
            window.alert([this.invalidMessage].concat(messages).join("\n"));
165
        },
166

167
        onInit: $.noop,
168
        onRefreshing: $.noop,
169
        onRefreshed: $.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,
183
        onError: $.noop,
184

185
        invalidClass: "jsgrid-invalid",
186

187
        containerClass: "jsgrid",
188
        tableClass: "jsgrid-table",
189
        gridHeaderClass: "jsgrid-grid-header",
190
        gridBodyClass: "jsgrid-grid-body",
191

192
        _init: function(config) {
193
            $.extend(this, config);
194
            this._initLoadStrategy();
195
            this._initController();
196
            this._initFields();
197
            this._attachWindowLoadResize();
198
            this._attachWindowResizeCallback();
199
            this._callEventHandler(this.onInit)
200
        },
201

202
        loadStrategy: function() {
203
            return this.pageLoading
204
                ? new jsGrid.loadStrategies.PageLoadingStrategy(this)
205
                : new jsGrid.loadStrategies.DirectLoadingStrategy(this);
206
        },
207

208
        _initLoadStrategy: function() {
209
            this._loadStrategy = getOrApply(this.loadStrategy, this);
210
        },
211

212
        _initController: function() {
213
            this._controller = $.extend({}, defaultController, getOrApply(this.controller, this));
214
        },
215

216
        renderTemplate: function(source, context, config) {
217
            args = [];
218
            for(var key in config) {
219
                args.push(config[key]);
220
            }
221

222
            args.unshift(source, context);
223

224
            source = getOrApply.apply(null, args);
225
            return (source === undefined || source === null) ? "" : source;
226
        },
227

228
        loadIndicator: function(config) {
229
            return new jsGrid.LoadIndicator(config);
230
        },
231

232
        validation: function(config) {
233
            return jsGrid.Validation && new jsGrid.Validation(config);
234
        },
235

236
        _initFields: function() {
237
            var self = this;
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);
242
                }
243
                field._grid = self;
244
                return field;
245
            });
246
        },
247

248
        _attachWindowLoadResize: function() {
249
            $(window).on("load", $.proxy(this._refreshSize, this));
250
        },
251

252
        _attachWindowResizeCallback: function() {
253
            if(this.updateOnResize) {
254
                $(window).on("resize", $.proxy(this._refreshSize, this));
255
            }
256
        },
257

258
        _detachWindowResizeCallback: function() {
259
            $(window).off("resize", this._refreshSize);
260
        },
261

262
        option: function(key, value) {
263
            var optionChangingEventArgs,
264
                optionChangedEventArgs;
265

266
            if(arguments.length === 1)
267
                return this[key];
268

269
            optionChangingEventArgs = {
270
                option: key,
271
                oldValue: this[key],
272
                newValue: value
273
            };
274
            this._callEventHandler(this.onOptionChanging, optionChangingEventArgs);
275

276
            this._handleOptionChange(optionChangingEventArgs.option, optionChangingEventArgs.newValue);
277

278
            optionChangedEventArgs = {
279
                option: optionChangingEventArgs.option,
280
                value: optionChangingEventArgs.newValue
281
            };
282
            this._callEventHandler(this.onOptionChanged, optionChangedEventArgs);
283
        },
284

285
        fieldOption: function(field, key, value) {
286
            field = this._normalizeField(field);
287

288
            if(arguments.length === 2)
289
                return field[key];
290

291
            field[key] = value;
292
            this._renderGrid();
293
        },
294

295
        _handleOptionChange: function(name, value) {
296
            this[name] = value;
297

298
            switch(name) {
299
                case "width":
300
                case "height":
301
                    this._refreshSize();
302
                    break;
303
                case "rowClass":
304
                case "rowRenderer":
305
                case "rowClick":
306
                case "rowDoubleClick":
307
                case "noDataRowClass":
308
                case "noDataContent":
309
                case "selecting":
310
                case "selectedRowClass":
311
                case "oddRowClass":
312
                case "evenRowClass":
313
                    this._refreshContent();
314
                    break;
315
                case "pageButtonCount":
316
                case "pagerFormat":
317
                case "pagePrevText":
318
                case "pageNextText":
319
                case "pageFirstText":
320
                case "pageLastText":
321
                case "pageNavigatorNextText":
322
                case "pageNavigatorPrevText":
323
                case "pagerClass":
324
                case "pagerNavButtonClass":
325
                case "pageClass":
326
                case "currentPageClass":
327
                case "pagerRenderer":
328
                    this._refreshPager();
329
                    break;
330
                case "fields":
331
                    this._initFields();
332
                    this.render();
333
                    break;
334
                case "data":
335
                case "editing":
336
                case "heading":
337
                case "filtering":
338
                case "inserting":
339
                case "paging":
340
                    this.refresh();
341
                    break;
342
                case "loadStrategy":
343
                case "pageLoading":
344
                    this._initLoadStrategy();
345
                    this.search();
346
                    break;
347
                case "pageIndex":
348
                    this.openPage(value);
349
                    break;
350
                case "pageSize":
351
                    this.refresh();
352
                    this.search();
353
                    break;
354
                case "editRowRenderer":
355
                case "editRowClass":
356
                    this.cancelEdit();
357
                    break;
358
                case "updateOnResize":
359
                    this._detachWindowResizeCallback();
360
                    this._attachWindowResizeCallback();
361
                    break;
362
                case "invalidNotify":
363
                case "invalidMessage":
364
                    break;
365
                default:
366
                    this.render();
367
                    break;
368
            }
369
        },
370

371
        destroy: function() {
372
            this._detachWindowResizeCallback();
373
            this._clear();
374
            this._container.removeData(JSGRID_DATA_KEY);
375
        },
376

377
        render: function() {
378
            this._renderGrid();
379
            return this.autoload ? this.loadData() : $.Deferred().resolve().promise();
380
        },
381

382
        _renderGrid: function() {
383
            this._clear();
384

385
            this._container.addClass(this.containerClass)
386
                .css("position", "relative")
387
                .append(this._createHeader())
388
                .append(this._createBody());
389

390
            this._pagerContainer = this._createPagerContainer();
391
            this._loadIndicator = this._createLoadIndicator();
392
            this._validation = this._createValidation();
393

394
            this.refresh();
395
        },
396

397
        _createLoadIndicator: function() {
398
            return getOrApply(this.loadIndicator, this, {
399
                message: this.loadMessage,
400
                shading: this.loadShading,
401
                container: this._container
402
            });
403
        },
404

405
        _createValidation: function() {
406
            return getOrApply(this.validation, this);
407
        },
408

409
        _clear: function() {
410
            this.cancelEdit();
411

412
            clearTimeout(this._loadingTimer);
413

414
            this._pagerContainer && this._pagerContainer.empty();
415

416
            this._container.empty()
417
                .css({ position: "", width: "", height: "" });
418
        },
419

420
        _createHeader: function() {
421
            var $headerRow = this._headerRow = this._createHeaderRow(),
422
                $filterRow = this._filterRow = this._createFilterRow(),
423
                $insertRow = this._insertRow = this._createInsertRow();
424

425
            var $headerGrid = this._headerGrid = $("<table>").addClass(this.tableClass)
426
                .append($headerRow)
427
                .append($filterRow)
428
                .append($insertRow);
429

430
            var $header = this._header = $("<div>").addClass(this.gridHeaderClass)
431
                .addClass(this._scrollBarWidth() ? "jsgrid-header-scrollbar" : "")
432
                .append($headerGrid);
433

434
            return $header;
435
        },
436

437
        _createBody: function() {
438
            var $content = this._content = $("<tbody>");
439

440
            var $bodyGrid = this._bodyGrid = $("<table>").addClass(this.tableClass)
441
                .append($content);
442

443
            var $body = this._body = $("<div>").addClass(this.gridBodyClass)
444
                .append($bodyGrid)
445
                .on("scroll", $.proxy(function(e) {
446
                    this._header.scrollLeft(e.target.scrollLeft);
447
                }, this));
448

449
            return $body;
450
        },
451

452
        _createPagerContainer: function() {
453
            var pagerContainer = this.pagerContainer || $("<div>").appendTo(this._container);
454
            return $(pagerContainer).addClass(this.pagerContainerClass);
455
        },
456

457
        _eachField: function(callBack) {
458
            var self = this;
459
            $.each(this.fields, function(index, field) {
460
                if(field.visible) {
461
                    callBack.call(self, field, index);
462
                }
463
            });
464
        },
465

466
        _createHeaderRow: function() {
467
            if($.isFunction(this.headerRowRenderer))
468
                return $(this.renderTemplate(this.headerRowRenderer, this));
469

470
            var $result = $("<tr>").addClass(this.headerRowClass);
471

472
            this._eachField(function(field, index) {
473
                var $th = this._prepareCell("<th>", field, "headercss", this.headerCellClass)
474
                    .append(this.renderTemplate(field.headerTemplate, field))
475
                    .appendTo($result);
476

477
                if(this.sorting && field.sorting) {
478
                    $th.addClass(this.sortableClass)
479
                        .on("click", $.proxy(function() {
480
                            this.sort(index);
481
                        }, this));
482
                }
483
            });
484

485
            return $result;
486
        },
487

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) : "");
493
        },
494

495
        _createFilterRow: function() {
496
            if($.isFunction(this.filterRowRenderer))
497
                return $(this.renderTemplate(this.filterRowRenderer, this));
498

499
            var $result = $("<tr>").addClass(this.filterRowClass);
500

501
            this._eachField(function(field) {
502
                this._prepareCell("<td>", field, "filtercss")
503
                    .append(this.renderTemplate(field.filterTemplate, field))
504
                    .appendTo($result);
505
            });
506

507
            return $result;
508
        },
509

510
        _createInsertRow: function() {
511
            if($.isFunction(this.insertRowRenderer))
512
                return $(this.renderTemplate(this.insertRowRenderer, this));
513

514
            var $result = $("<tr>").addClass(this.insertRowClass);
515

516
            this._eachField(function(field) {
517
                this._prepareCell("<td>", field, "insertcss")
518
                    .append(this.renderTemplate(field.insertTemplate, field))
519
                    .appendTo($result);
520
            });
521

522
            return $result;
523
        },
524

525
        _callEventHandler: function(handler, eventParams) {
526
            handler.call(this, $.extend(eventParams, {
527
                grid: this
528
            }));
529

530
            return eventParams;
531
        },
532

533
        reset: function() {
534
            this._resetSorting();
535
            this._resetPager();
536
            return this._loadStrategy.reset();
537
        },
538

539
        _resetPager: function() {
540
            this._firstDisplayingPage = 1;
541
            this._setPage(1);
542
        },
543

544
        _resetSorting: function() {
545
            this._sortField = null;
546
            this._sortOrder = SORT_ORDER_ASC;
547
            this._clearSortingCss();
548
        },
549

550
        refresh: function() {
551
            this._callEventHandler(this.onRefreshing);
552

553
            this.cancelEdit();
554

555
            this._refreshHeading();
556
            this._refreshFiltering();
557
            this._refreshInserting();
558
            this._refreshContent();
559
            this._refreshPager();
560
            this._refreshSize();
561

562
            this._callEventHandler(this.onRefreshed);
563
        },
564

565
        _refreshHeading: function() {
566
            this._headerRow.toggle(this.heading);
567
        },
568

569
        _refreshFiltering: function() {
570
            this._filterRow.toggle(this.filtering);
571
        },
572

573
        _refreshInserting: function() {
574
            this._insertRow.toggle(this.inserting);
575
        },
576

577
        _refreshContent: function() {
578
            var $content = this._content;
579
            $content.empty();
580

581
            if(!this.data.length) {
582
                $content.append(this._createNoDataRow());
583
                return this;
584
            }
585

586
            var indexFrom = this._loadStrategy.firstDisplayIndex();
587
            var indexTo = this._loadStrategy.lastDisplayIndex();
588

589
            for(var itemIndex = indexFrom; itemIndex < indexTo; itemIndex++) {
590
                var item = this.data[itemIndex];
591
                $content.append(this._createRow(item, itemIndex));
592
            }
593
        },
594

595
        _createNoDataRow: function() {
596
            var amountOfFields = 0;
597
            this._eachField(function() {
598
                amountOfFields++;
599
            });
600

601
            return $("<tr>").addClass(this.noDataRowClass)
602
                .append($("<td>").addClass(this.cellClass).attr("colspan", amountOfFields)
603
                    .append(this.renderTemplate(this.noDataContent, this)));
604
        },
605

606
        _createRow: function(item, itemIndex) {
607
            var $result;
608

609
            if($.isFunction(this.rowRenderer)) {
610
                $result = this.renderTemplate(this.rowRenderer, this, { item: item, itemIndex: itemIndex });
611
            } else {
612
                $result = $("<tr>");
613
                this._renderCells($result, item);
614
            }
615

616
            $result.addClass(this._getRowClasses(item, itemIndex))
617
                .data(JSGRID_ROW_DATA_KEY, item)
618
                .on("click", $.proxy(function(e) {
619
                    this.rowClick({
620
                        item: item,
621
                        itemIndex: itemIndex,
622
                        event: e
623
                    });
624
                }, this))
625
                .on("dblclick", $.proxy(function(e) {
626
                    this.rowDoubleClick({
627
                        item: item,
628
                        itemIndex: itemIndex,
629
                        event: e
630
                    });
631
                }, this));
632

633
            if(this.selecting) {
634
                this._attachRowHover($result);
635
            }
636

637
            return $result;
638
        },
639

640
        _getRowClasses: function(item, itemIndex) {
641
            var classes = [];
642
            classes.push(((itemIndex + 1) % 2) ? this.oddRowClass : this.evenRowClass);
643
            classes.push(getOrApply(this.rowClass, this, item, itemIndex));
644
            return classes.join(" ");
645
        },
646

647
        _attachRowHover: function($row) {
648
            var selectedRowClass = this.selectedRowClass;
649
            $row.hover(function() {
650
                    $(this).addClass(selectedRowClass);
651
                },
652
                function() {
653
                    $(this).removeClass(selectedRowClass);
654
                }
655
            );
656
        },
657

658
        _renderCells: function($row, item) {
659
            this._eachField(function(field) {
660
                $row.append(this._createCell(item, field));
661
            });
662
            return this;
663
        },
664

665
        _createCell: function(item, field) {
666
            var $result;
667
            var fieldValue = this._getItemFieldValue(item, field);
668

669
            var args = { value: fieldValue, item : item };
670
            if($.isFunction(field.cellRenderer)) {
671
                $result = this.renderTemplate(field.cellRenderer, field, args);
672
            } else {
673
                $result = $("<td>").append(this.renderTemplate(field.itemTemplate || fieldValue, field, args));
674
            }
675

676
            return this._prepareCell($result, field);
677
        },
678

679
        _getItemFieldValue: function(item, field) {
680
            var props = field.name.split('.');
681
            var result = item[props.shift()];
682

683
            while(result && props.length) {
684
                result = result[props.shift()];
685
            }
686

687
            return result;
688
        },
689

690
        _setItemFieldValue: function(item, field, value) {
691
            var props = field.name.split('.');
692
            var current = item;
693
            var prop = props[0];
694

695
            while(current && props.length) {
696
                item = current;
697
                prop = props.shift();
698
                current = item[prop];
699
            }
700

701
            if(!current) {
702
                while(props.length) {
703
                    item = item[prop] = {};
704
                    prop = props.shift();
705
                }
706
            }
707

708
            item[prop] = value;
709
        },
710

711
        sort: function(field, order) {
712
            if($.isPlainObject(field)) {
713
                order = field.order;
714
                field = field.field;
715
            }
716

717
            this._clearSortingCss();
718
            this._setSortingParams(field, order);
719
            this._setSortingCss();
720
            return this._loadStrategy.sort();
721
        },
722

723
        _clearSortingCss: function() {
724
            this._headerRow.find("th")
725
                .removeClass(this.sortAscClass)
726
                .removeClass(this.sortDescClass);
727
        },
728

729
        _setSortingParams: function(field, order) {
730
            field = this._normalizeField(field);
731
            order = order || ((this._sortField === field) ? this._reversedSortOrder(this._sortOrder) : SORT_ORDER_ASC);
732

733
            this._sortField = field;
734
            this._sortOrder = order;
735
        },
736

737
        _normalizeField: function(field) {
738
            if($.isNumeric(field)) {
739
                return this.fields[field];
740
            }
741

742
            if(typeof field === "string") {
743
                return $.grep(this.fields, function(f) {
744
                    return f.name === field;
745
                })[0];
746
            }
747

748
            return field;
749
        },
750

751
        _reversedSortOrder: function(order) {
752
            return (order === SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC);
753
        },
754

755
        _setSortingCss: function() {
756
            var fieldIndex = this._visibleFieldIndex(this._sortField);
757

758
            this._headerRow.find("th").eq(fieldIndex)
759
                .addClass(this._sortOrder === SORT_ORDER_ASC ? this.sortAscClass : this.sortDescClass);
760
        },
761

762
        _visibleFieldIndex: function(field) {
763
            return $.inArray(field, $.grep(this.fields, function(f) { return f.visible; }));
764
        },
765

766
        _sortData: function() {
767
            var sortFactor = this._sortFactor(),
768
                sortField = this._sortField;
769

770
            if(sortField) {
771
                this.data.sort(function(item1, item2) {
772
                    return sortFactor * sortField.sortingFunc(item1[sortField.name], item2[sortField.name]);
773
                });
774
            }
775
        },
776

777
        _sortFactor: function() {
778
            return this._sortOrder === SORT_ORDER_ASC ? 1 : -1;
779
        },
780

781
        _itemsCount: function() {
782
            return this._loadStrategy.itemsCount();
783
        },
784

785
        _pagesCount: function() {
786
            var itemsCount = this._itemsCount(),
787
                pageSize = this.pageSize;
788
            return Math.floor(itemsCount / pageSize) + (itemsCount % pageSize ? 1 : 0);
789
        },
790

791
        _refreshPager: function() {
792
            var $pagerContainer = this._pagerContainer;
793
            $pagerContainer.empty();
794

795
            if(this.paging) {
796
                $pagerContainer.append(this._createPager());
797
            }
798

799
            var showPager = this.paging && this._pagesCount() > 1;
800
            $pagerContainer.toggle(showPager);
801
        },
802

803
        _createPager: function() {
804
            var $result;
805

806
            if($.isFunction(this.pagerRenderer)) {
807
                $result = $(this.pagerRenderer({
808
                    pageIndex: this.pageIndex,
809
                    pageCount: this._pagesCount()
810
                }));
811
            } else {
812
                $result = $("<div>").append(this._createPagerByFormat());
813
            }
814

815
            $result.addClass(this.pagerClass);
816

817
            return $result;
818
        },
819

820
        _createPagerByFormat: function() {
821
            var pageIndex = this.pageIndex,
822
                pageCount = this._pagesCount(),
823
                itemCount = this._itemsCount(),
824
                pagerParts = this.pagerFormat.split(" ");
825

826
            return $.map(pagerParts, $.proxy(function(pagerPart) {
827
                var result = pagerPart;
828

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) {
840
                    result = pageIndex;
841
                } else if(pagerPart === PAGE_COUNT_PLACEHOLDER) {
842
                    result = pageCount;
843
                } else if(pagerPart === ITEM_COUNT_PLACEHOLDER) {
844
                    result = itemCount;
845
                }
846

847
                return $.isArray(result) ? result.concat([" "]) : [result, " "];
848
            }, this));
849
        },
850

851
        _createPages: function() {
852
            var pageCount = this._pagesCount(),
853
                pageButtonCount = this.pageButtonCount,
854
                firstDisplayingPage = this._firstDisplayingPage,
855
                pages = [];
856

857
            if(firstDisplayingPage > 1) {
858
                pages.push(this._createPagerPageNavButton(this.pageNavigatorPrevText, this.showPrevPages));
859
            }
860

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));
865
            }
866

867
            if((firstDisplayingPage + pageButtonCount - 1) < pageCount) {
868
                pages.push(this._createPagerPageNavButton(this.pageNavigatorNextText, this.showNextPages));
869
            }
870

871
            return pages;
872
        },
873

874
        _createPagerNavButton: function(text, pageIndex, isActive) {
875
            return this._createPagerButton(text, this.pagerNavButtonClass + (isActive ? "" : " " + this.pagerNavButtonInactiveClass),
876
                isActive ? function() { this.openPage(pageIndex); } : $.noop);
877
        },
878

879
        _createPagerPageNavButton: function(text, handler) {
880
            return this._createPagerButton(text, this.pagerNavButtonClass, handler);
881
        },
882

883
        _createPagerPage: function(pageIndex) {
884
            return this._createPagerButton(pageIndex, this.pageClass, function() {
885
                this.openPage(pageIndex);
886
            });
887
        },
888

889
        _createPagerButton: function(text, css, handler) {
890
            var $link = $("<a>").attr("href", EMPTY_HREF)
891
                .html(text)
892
                .on("click", $.proxy(handler, this));
893

894
            return $("<span>").addClass(css).append($link);
895
        },
896

897
        _createPagerCurrentPage: function() {
898
            return $("<span>")
899
                .addClass(this.pageClass)
900
                .addClass(this.currentPageClass)
901
                .text(this.pageIndex);
902
        },
903

904
        _refreshSize: function() {
905
            this._refreshHeight();
906
            this._refreshWidth();
907
        },
908

909
        _refreshWidth: function() {
910
            var width = (this.width === "auto") ? this._getAutoWidth() : this.width;
911

912
            this._container.width(width);
913
        },
914

915
        _getAutoWidth: function() {
916
            var $headerGrid = this._headerGrid,
917
                $header = this._header;
918

919
            $headerGrid.width("auto");
920

921
            var contentWidth = $headerGrid.outerWidth();
922
            var borderWidth = $header.outerWidth() - $header.innerWidth();
923

924
            $headerGrid.width("");
925

926
            return contentWidth + borderWidth;
927
        },
928

929
        _scrollBarWidth: (function() {
930
            var result;
931

932
            return 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;
942
                }
943
                return result;
944
            };
945
        })(),
946

947
        _refreshHeight: function() {
948
            var container = this._container,
949
                pagerContainer = this._pagerContainer,
950
                height = this.height,
951
                nonBodyHeight;
952

953
            container.height(height);
954

955
            if(height !== "auto") {
956
                height = container.height();
957

958
                nonBodyHeight = this._header.outerHeight(true);
959
                if(pagerContainer.parents(container).length) {
960
                    nonBodyHeight += pagerContainer.outerHeight(true);
961
                }
962

963
                this._body.outerHeight(height - nonBodyHeight);
964
            }
965
        },
966

967
        showPrevPages: function() {
968
            var firstDisplayingPage = this._firstDisplayingPage,
969
                pageButtonCount = this.pageButtonCount;
970

971
            this._firstDisplayingPage = (firstDisplayingPage > pageButtonCount) ? firstDisplayingPage - pageButtonCount : 1;
972

973
            this._refreshPager();
974
        },
975

976
        showNextPages: function() {
977
            var firstDisplayingPage = this._firstDisplayingPage,
978
                pageButtonCount = this.pageButtonCount,
979
                pageCount = this._pagesCount();
980

981
            this._firstDisplayingPage = (firstDisplayingPage + 2 * pageButtonCount > pageCount)
982
                ? pageCount - pageButtonCount + 1
983
                : firstDisplayingPage + pageButtonCount;
984

985
            this._refreshPager();
986
        },
987

988
        openPage: function(pageIndex) {
989
            if(pageIndex < 1 || pageIndex > this._pagesCount())
990
                return;
991

992
            this._setPage(pageIndex);
993
            this._loadStrategy.openPage(pageIndex);
994
        },
995

996
        _setPage: function(pageIndex) {
997
            var firstDisplayingPage = this._firstDisplayingPage,
998
                pageButtonCount = this.pageButtonCount;
999

1000
            this.pageIndex = pageIndex;
1001

1002
            if(pageIndex < firstDisplayingPage) {
1003
                this._firstDisplayingPage = pageIndex;
1004
            }
1005

1006
            if(pageIndex > firstDisplayingPage + pageButtonCount - 1) {
1007
                this._firstDisplayingPage = pageIndex - pageButtonCount + 1;
1008
            }
1009

1010
            this._callEventHandler(this.onPageChanged, {
1011
                pageIndex: pageIndex
1012
            });
1013
        },
1014

1015
        _controllerCall: function(method, param, isCanceled, doneCallback) {
1016
            if(isCanceled)
1017
                return $.Deferred().reject().promise();
1018

1019
            this._showLoading();
1020

1021
            var controller = this._controller;
1022
            if(!controller || !controller[method]) {
1023
                throw Error("controller has no method '" + method + "'");
1024
            }
1025

1026
            return normalizePromise(controller[method](param))
1027
                .done($.proxy(doneCallback, this))
1028
                .fail($.proxy(this._errorHandler, this))
1029
                .always($.proxy(this._hideLoading, this));
1030
        },
1031

1032
        _errorHandler: function() {
1033
            this._callEventHandler(this.onError, {
1034
                args: $.makeArray(arguments)
1035
            });
1036
        },
1037

1038
        _showLoading: function() {
1039
            if(!this.loadIndication)
1040
                return;
1041

1042
            clearTimeout(this._loadingTimer);
1043

1044
            this._loadingTimer = setTimeout($.proxy(function() {
1045
                this._loadIndicator.show();
1046
            }, this), this.loadIndicationDelay);
1047
        },
1048

1049
        _hideLoading: function() {
1050
            if(!this.loadIndication)
1051
                return;
1052

1053
            clearTimeout(this._loadingTimer);
1054
            this._loadIndicator.hide();
1055
        },
1056

1057
        search: function(filter) {
1058
            this._resetSorting();
1059
            this._resetPager();
1060
            return this.loadData(filter);
1061
        },
1062

1063
        loadData: function(filter) {
1064
            filter = filter || (this.filtering ? this.getFilter() : {});
1065

1066
            $.extend(filter, this._loadStrategy.loadParams(), this._sortingParams());
1067

1068
            var args = this._callEventHandler(this.onDataLoading, {
1069
                filter: filter
1070
            });
1071

1072
            return this._controllerCall("loadData", filter, args.cancel, function(loadedData) {
1073
                if(!loadedData)
1074
                    return;
1075

1076
                this._loadStrategy.finishLoad(loadedData);
1077

1078
                this._callEventHandler(this.onDataLoaded, {
1079
                    data: loadedData
1080
                });
1081
            });
1082
        },
1083

1084
        getFilter: function() {
1085
            var result = {};
1086
            this._eachField(function(field) {
1087
                if(field.filtering) {
1088
                    this._setItemFieldValue(result, field, field.filterValue());
1089
                }
1090
            });
1091
            return result;
1092
        },
1093

1094
        _sortingParams: function() {
1095
            if(this.sorting && this._sortField) {
1096
                return {
1097
                    sortField: this._sortField.name,
1098
                    sortOrder: this._sortOrder
1099
                };
1100
            }
1101
            return {};
1102
        },
1103

1104
        getSorting: function() {
1105
            var sortingParams = this._sortingParams();
1106
            return {
1107
                field: sortingParams.sortField,
1108
                order: sortingParams.sortOrder
1109
            };
1110
        },
1111

1112
        clearFilter: function() {
1113
            var $filterRow = this._createFilterRow();
1114
            this._filterRow.replaceWith($filterRow);
1115
            this._filterRow = $filterRow;
1116
            return this.search();
1117
        },
1118

1119
        insertItem: function(item) {
1120
            var insertingItem = item || this._getValidatedInsertItem();
1121

1122
            if(!insertingItem)
1123
                return $.Deferred().reject().promise();
1124

1125
            var args = this._callEventHandler(this.onItemInserting, {
1126
                item: insertingItem
1127
            });
1128

1129
            return this._controllerCall("insertItem", insertingItem, args.cancel, function(insertedItem) {
1130
                insertedItem = insertedItem || insertingItem;
1131
                this._loadStrategy.finishInsert(insertedItem);
1132

1133
                this._callEventHandler(this.onItemInserted, {
1134
                    item: insertedItem
1135
                });
1136
            });
1137
        },
1138

1139
        _getValidatedInsertItem: function() {
1140
            var item = this._getInsertItem();
1141
            return this._validateItem(item, this._insertRow) ? item : null;
1142
        },
1143

1144
        _getInsertItem: function() {
1145
            var result = {};
1146
            this._eachField(function(field) {
1147
                if(field.inserting) {
1148
                    this._setItemFieldValue(result, field, field.insertValue());
1149
                }
1150
            });
1151
            return result;
1152
        },
1153

1154
        _validateItem: function(item, $row) {
1155
            var validationErrors = [];
1156

1157
            var args = {
1158
                item: item,
1159
                itemIndex: this._rowIndex($row),
1160
                row: $row
1161
            };
1162

1163
            this._eachField(function(field) {
1164
                if(!field.validate ||
1165
                   ($row === this._insertRow && !field.inserting) ||
1166
                   ($row === this._getEditRow() && !field.editing))
1167
                    return;
1168

1169
                var fieldValue = this._getItemFieldValue(item, field);
1170

1171
                var errors = this._validation.validate($.extend({
1172
                    value: fieldValue,
1173
                    rules: field.validate
1174
                }, args));
1175

1176
                this._setCellValidity($row.children().eq(this._visibleFieldIndex(field)), errors);
1177

1178
                if(!errors.length)
1179
                    return;
1180

1181
                validationErrors.push.apply(validationErrors,
1182
                    $.map(errors, function(message) {
1183
                        return { field: field, message: message };
1184
                    }));
1185
            });
1186

1187
            if(!validationErrors.length)
1188
                return true;
1189

1190
            var invalidArgs = $.extend({
1191
                errors: validationErrors
1192
            }, args);
1193
            this._callEventHandler(this.onItemInvalid, invalidArgs);
1194
            this.invalidNotify(invalidArgs);
1195

1196
            return false;
1197
        },
1198

1199
        _setCellValidity: function($cell, errors) {
1200
            $cell
1201
                .toggleClass(this.invalidClass, !!errors.length)
1202
                .attr("title", errors.join("\n"));
1203
        },
1204

1205
        clearInsert: function() {
1206
            var insertRow = this._createInsertRow();
1207
            this._insertRow.replaceWith(insertRow);
1208
            this._insertRow = insertRow;
1209
            this.refresh();
1210
        },
1211

1212
        editItem: function(item) {
1213
            var $row = this.rowByItem(item);
1214
            if($row.length) {
1215
                this._editRow($row);
1216
            }
1217
        },
1218

1219
        rowByItem: function(item) {
1220
            if(item.jquery || item.nodeType)
1221
                return $(item);
1222

1223
            return this._content.find("tr").filter(function() {
1224
                return $.data(this, JSGRID_ROW_DATA_KEY) === item;
1225
            });
1226
        },
1227

1228
        _editRow: function($row) {
1229
            if(!this.editing)
1230
                return;
1231

1232
            var item = $row.data(JSGRID_ROW_DATA_KEY);
1233

1234
            var args = this._callEventHandler(this.onItemEditing, {
1235
                row: $row,
1236
                item: item,
1237
                itemIndex: this._itemIndex(item)
1238
            });
1239

1240
            if(args.cancel)
1241
                return;
1242

1243
            if(this._editingRow) {
1244
                this.cancelEdit();
1245
            }
1246

1247
            var $editRow = this._createEditRow(item);
1248

1249
            this._editingRow = $row;
1250
            $row.hide();
1251
            $editRow.insertBefore($row);
1252
            $row.data(JSGRID_EDIT_ROW_DATA_KEY, $editRow);
1253
        },
1254

1255
        _createEditRow: function(item) {
1256
            if($.isFunction(this.editRowRenderer)) {
1257
                return $(this.renderTemplate(this.editRowRenderer, this, { item: item, itemIndex: this._itemIndex(item) }));
1258
            }
1259

1260
            var $result = $("<tr>").addClass(this.editRowClass);
1261

1262
            this._eachField(function(field) {
1263
                var fieldValue = this._getItemFieldValue(item, field);
1264

1265
                this._prepareCell("<td>", field, "editcss")
1266
                    .append(this.renderTemplate(field.editTemplate || "", field, { value: fieldValue, item: item }))
1267
                    .appendTo($result);
1268
            });
1269

1270
            return $result;
1271
        },
1272

1273
        updateItem: function(item, editedItem) {
1274
            if(arguments.length === 1) {
1275
                editedItem = item;
1276
            }
1277

1278
            var $row = item ? this.rowByItem(item) : this._editingRow;
1279
            editedItem = editedItem || this._getValidatedEditedItem();
1280

1281
            if(!editedItem)
1282
                return;
1283

1284
            return this._updateRow($row, editedItem);
1285
        },
1286

1287
        _getValidatedEditedItem: function() {
1288
            var item = this._getEditedItem();
1289
            return this._validateItem(item, this._getEditRow()) ? item : null;
1290
        },
1291

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);
1296

1297
            var args = this._callEventHandler(this.onItemUpdating, {
1298
                row: $updatingRow,
1299
                item: updatedItem,
1300
                itemIndex: updatingItemIndex,
1301
                previousItem: updatingItem
1302
            });
1303

1304
            return this._controllerCall("updateItem", updatedItem, args.cancel, function(loadedUpdatedItem) {
1305
                var previousItem = $.extend(true, {}, updatingItem);
1306
                updatedItem = loadedUpdatedItem || $.extend(true, updatingItem, editedItem);
1307

1308
                var $updatedRow = this._finishUpdate($updatingRow, updatedItem, updatingItemIndex);
1309

1310
                this._callEventHandler(this.onItemUpdated, {
1311
                    row: $updatedRow,
1312
                    item: updatedItem,
1313
                    itemIndex: updatingItemIndex,
1314
                    previousItem: previousItem
1315
                });
1316
            });
1317
        },
1318

1319
        _rowIndex: function(row) {
1320
            return this._content.children().index($(row));
1321
        },
1322

1323
        _itemIndex: function(item) {
1324
            return $.inArray(item, this.data);
1325
        },
1326

1327
        _finishUpdate: function($updatingRow, updatedItem, updatedItemIndex) {
1328
            this.cancelEdit();
1329
            this.data[updatedItemIndex] = updatedItem;
1330

1331
            var $updatedRow = this._createRow(updatedItem, updatedItemIndex);
1332
            $updatingRow.replaceWith($updatedRow);
1333
            return $updatedRow;
1334
        },
1335

1336
        _getEditedItem: function() {
1337
            var result = {};
1338
            this._eachField(function(field) {
1339
                if(field.editing) {
1340
                    this._setItemFieldValue(result, field, field.editValue());
1341
                }
1342
            });
1343
            return result;
1344
        },
1345

1346
        cancelEdit: function() {
1347
            if(!this._editingRow)
1348
                return;
1349

1350
            this._getEditRow().remove();
1351
            this._editingRow.show();
1352
            this._editingRow = null;
1353
        },
1354

1355
        _getEditRow: function() {
1356
            return this._editingRow && this._editingRow.data(JSGRID_EDIT_ROW_DATA_KEY);
1357
        },
1358

1359
        deleteItem: function(item) {
1360
            var $row = this.rowByItem(item);
1361

1362
            if(!$row.length)
1363
                return;
1364

1365
            if(this.confirmDeleting && !window.confirm(getOrApply(this.deleteConfirm, this, $row.data(JSGRID_ROW_DATA_KEY))))
1366
                return;
1367

1368
            return this._deleteRow($row);
1369
        },
1370

1371
        _deleteRow: function($row) {
1372
            var deletingItem = $row.data(JSGRID_ROW_DATA_KEY),
1373
                deletingItemIndex = this._itemIndex(deletingItem);
1374

1375
            var args = this._callEventHandler(this.onItemDeleting, {
1376
                row: $row,
1377
                item: deletingItem,
1378
                itemIndex: deletingItemIndex
1379
            });
1380

1381
            return this._controllerCall("deleteItem", deletingItem, args.cancel, function() {
1382
                this._loadStrategy.finishDelete(deletingItem, deletingItemIndex);
1383

1384
                this._callEventHandler(this.onItemDeleted, {
1385
                    row: $row,
1386
                    item: deletingItem,
1387
                    itemIndex: deletingItemIndex
1388
                });
1389
            });
1390
        }
1391
    };
1392

1393
    $.fn.jsGrid = function(config) {
1394
        var args = $.makeArray(arguments),
1395
            methodArgs = args.slice(1),
1396
            result = this;
1397

1398
        this.each(function() {
1399
            var $element = $(this),
1400
                instance = $element.data(JSGRID_DATA_KEY),
1401
                methodResult;
1402

1403
            if(instance) {
1404
                if(typeof config === "string") {
1405
                    methodResult = instance[config].apply(instance, methodArgs);
1406
                    if(methodResult !== undefined && methodResult !== instance) {
1407
                        result = methodResult;
1408
                        return false;
1409
                    }
1410
                } else {
1411
                    instance._detachWindowResizeCallback();
1412
                    instance._init(config);
1413
                    instance.render();
1414
                }
1415
            } else {
1416
                new Grid($element, config);
1417
            }
1418
        });
1419

1420
        return result;
1421
    };
1422

1423
    var fields = {};
1424

1425
    var setDefaults = function(config) {
1426
        var componentPrototype;
1427

1428
        if($.isPlainObject(config)) {
1429
            componentPrototype = Grid.prototype;
1430
        } else {
1431
            componentPrototype = fields[config].prototype;
1432
            config = arguments[1] || {};
1433
        }
1434

1435
        $.extend(componentPrototype, config);
1436
    };
1437

1438
    var locales = {};
1439

1440
    var locale = function(lang) {
1441
        var localeConfig = $.isPlainObject(lang) ? lang : locales[lang];
1442

1443
        if(!localeConfig)
1444
            throw Error("unknown locale " + lang);
1445

1446
        setLocale(jsGrid, localeConfig);
1447
    };
1448

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);
1453
                return;
1454
            }
1455

1456
            if(obj.hasOwnProperty(field)) {
1457
                obj[field] = value;
1458
            } else {
1459
                obj.prototype[field] = value;
1460
            }
1461
        });
1462
    };
1463

1464
    window.jsGrid = {
1465
        Grid: Grid,
1466
        fields: fields,
1467
        setDefaults: setDefaults,
1468
        locales: locales,
1469
        locale: locale,
1470
        version: '1.5.3'
1471
    };
1472

1473
}(window, jQuery));
1474

1475
(function(jsGrid, $, undefined) {
1476

1477
    function LoadIndicator(config) {
1478
        this._init(config);
1479
    }
1480

1481
    LoadIndicator.prototype = {
1482

1483
        container: "body",
1484
        message: "Loading...",
1485
        shading: true,
1486

1487
        zIndex: 1000,
1488
        shaderClass: "jsgrid-load-shader",
1489
        loadPanelClass: "jsgrid-load-panel",
1490

1491
        _init: function(config) {
1492
            $.extend(true, this, config);
1493

1494
            this._initContainer();
1495
            this._initShader();
1496
            this._initLoadPanel();
1497
        },
1498

1499
        _initContainer: function() {
1500
            this._container = $(this.container);
1501
        },
1502

1503
        _initShader: function() {
1504
            if(!this.shading)
1505
                return;
1506

1507
            this._shader = $("<div>").addClass(this.shaderClass)
1508
                .hide()
1509
                .css({
1510
                    position: "absolute",
1511
                    top: 0,
1512
                    right: 0,
1513
                    bottom: 0,
1514
                    left: 0,
1515
                    zIndex: this.zIndex
1516
                })
1517
                .appendTo(this._container);
1518
        },
1519

1520
        _initLoadPanel: function() {
1521
            this._loadPanel = $("<div>").addClass(this.loadPanelClass)
1522
                .text(this.message)
1523
                .hide()
1524
                .css({
1525
                    position: "absolute",
1526
                    top: "50%",
1527
                    left: "50%",
1528
                    zIndex: this.zIndex
1529
                })
1530
                .appendTo(this._container);
1531
        },
1532

1533
        show: function() {
1534
            var $loadPanel = this._loadPanel.show();
1535

1536
            var actualWidth = $loadPanel.outerWidth();
1537
            var actualHeight = $loadPanel.outerHeight();
1538

1539
            $loadPanel.css({
1540
                marginTop: -actualHeight / 2,
1541
                marginLeft: -actualWidth / 2
1542
            });
1543

1544
            this._shader.show();
1545
        },
1546

1547
        hide: function() {
1548
            this._loadPanel.hide();
1549
            this._shader.hide();
1550
        }
1551

1552
    };
1553

1554
    jsGrid.LoadIndicator = LoadIndicator;
1555

1556
}(jsGrid, jQuery));
1557

1558
(function(jsGrid, $, undefined) {
1559

1560
    function DirectLoadingStrategy(grid) {
1561
        this._grid = grid;
1562
    }
1563

1564
    DirectLoadingStrategy.prototype = {
1565

1566
        firstDisplayIndex: function() {
1567
            var grid = this._grid;
1568
            return grid.option("paging") ? (grid.option("pageIndex") - 1) * grid.option("pageSize") : 0;
1569
        },
1570

1571
        lastDisplayIndex: function() {
1572
            var grid = this._grid;
1573
            var itemsCount = grid.option("data").length;
1574

1575
            return grid.option("paging")
1576
                ? Math.min(grid.option("pageIndex") * grid.option("pageSize"), itemsCount)
1577
                : itemsCount;
1578
        },
1579

1580
        itemsCount: function() {
1581
            return this._grid.option("data").length;
1582
        },
1583

1584
        openPage: function(index) {
1585
            this._grid.refresh();
1586
        },
1587

1588
        loadParams: function() {
1589
            return {};
1590
        },
1591

1592
        sort: function() {
1593
            this._grid._sortData();
1594
            this._grid.refresh();
1595
            return $.Deferred().resolve().promise();
1596
        },
1597

1598
        reset: function() {
1599
            this._grid.refresh();
1600
            return $.Deferred().resolve().promise();
1601
        },
1602

1603
        finishLoad: function(loadedData) {
1604
            this._grid.option("data", loadedData);
1605
        },
1606

1607
        finishInsert: function(insertedItem) {
1608
            var grid = this._grid;
1609
            grid.option("data").push(insertedItem);
1610
            grid.refresh();
1611
        },
1612

1613
        finishDelete: function(deletedItem, deletedItemIndex) {
1614
            var grid = this._grid;
1615
            grid.option("data").splice(deletedItemIndex, 1);
1616
            grid.reset();
1617
        }
1618
    };
1619

1620

1621
    function PageLoadingStrategy(grid) {
1622
        this._grid = grid;
1623
        this._itemsCount = 0;
1624
    }
1625

1626
    PageLoadingStrategy.prototype = {
1627

1628
        firstDisplayIndex: function() {
1629
            return 0;
1630
        },
1631

1632
        lastDisplayIndex: function() {
1633
            return this._grid.option("data").length;
1634
        },
1635

1636
        itemsCount: function() {
1637
            return this._itemsCount;
1638
        },
1639

1640
        openPage: function(index) {
1641
            this._grid.loadData();
1642
        },
1643

1644
        loadParams: function() {
1645
            var grid = this._grid;
1646
            return {
1647
                pageIndex: grid.option("pageIndex"),
1648
                pageSize: grid.option("pageSize")
1649
            };
1650
        },
1651

1652
        reset: function() {
1653
            return this._grid.loadData();
1654
        },
1655

1656
        sort: function() {
1657
            return this._grid.loadData();
1658
        },
1659

1660
        finishLoad: function(loadedData) {
1661
            this._itemsCount = loadedData.itemsCount;
1662
            this._grid.option("data", loadedData.data);
1663
        },
1664

1665
        finishInsert: function(insertedItem) {
1666
            this._grid.search();
1667
        },
1668

1669
        finishDelete: function(deletedItem, deletedItemIndex) {
1670
            this._grid.search();
1671
        }
1672
    };
1673

1674
    jsGrid.loadStrategies = {
1675
        DirectLoadingStrategy: DirectLoadingStrategy,
1676
        PageLoadingStrategy: PageLoadingStrategy
1677
    };
1678

1679
}(jsGrid, jQuery));
1680

1681
(function(jsGrid, $, undefined) {
1682

1683
    var isDefined = function(val) {
1684
        return typeof(val) !== "undefined" && val !== null;
1685
    };
1686

1687
    var sortStrategies = {
1688
        string: function(str1, str2) {
1689
            if(!isDefined(str1) && !isDefined(str2))
1690
                return 0;
1691

1692
            if(!isDefined(str1))
1693
                return -1;
1694

1695
            if(!isDefined(str2))
1696
                return 1;
1697

1698
            return ("" + str1).localeCompare("" + str2);
1699
        },
1700

1701
        number: function(n1, n2) {
1702
            return n1 - n2;
1703
        },
1704

1705
        date: function(dt1, dt2) {
1706
            return dt1 - dt2;
1707
        },
1708

1709
        numberAsString: function(n1, n2) {
1710
            return parseFloat(n1) - parseFloat(n2);
1711
        }
1712
    };
1713

1714
    jsGrid.sortStrategies = sortStrategies;
1715

1716
}(jsGrid, jQuery));
1717

1718
(function(jsGrid, $, undefined) {
1719

1720
    function Validation(config) {
1721
        this._init(config);
1722
    }
1723

1724
    Validation.prototype = {
1725

1726
        _init: function(config) {
1727
            $.extend(true, this, config);
1728
        },
1729

1730
        validate: function(args) {
1731
            var errors = [];
1732

1733
            $.each(this._normalizeRules(args.rules), function(_, rule) {
1734
                if(rule.validator(args.value, args.item, rule.param))
1735
                    return;
1736

1737
                var errorMessage = $.isFunction(rule.message) ? rule.message(args.value, args.item) : rule.message;
1738
                errors.push(errorMessage);
1739
            });
1740

1741
            return errors;
1742
        },
1743

1744
        _normalizeRules: function(rules) {
1745
            if(!$.isArray(rules))
1746
                rules = [rules];
1747

1748
            return $.map(rules, $.proxy(function(rule) {
1749
                return this._normalizeRule(rule);
1750
            }, this));
1751
        },
1752

1753
        _normalizeRule: function(rule) {
1754
            if(typeof rule === "string")
1755
                rule = { validator: rule };
1756

1757
            if($.isFunction(rule))
1758
                rule = { validator: rule };
1759

1760
            if($.isPlainObject(rule))
1761
                rule = $.extend({}, rule);
1762
            else
1763
                throw Error("wrong validation config specified");
1764

1765
            if($.isFunction(rule.validator))
1766
                return rule;
1767

1768
            return this._applyNamedValidator(rule, rule.validator);
1769
        },
1770

1771
        _applyNamedValidator: function(rule, validatorName) {
1772
            delete rule.validator;
1773

1774
            var validator = validators[validatorName];
1775
            if(!validator)
1776
                throw Error("unknown validator \"" + validatorName + "\"");
1777

1778
            if($.isFunction(validator)) {
1779
                validator = { validator: validator };
1780
            }
1781

1782
            return $.extend({}, validator, rule);
1783
        }
1784
    };
1785

1786
    jsGrid.Validation = Validation;
1787

1788

1789
    var validators = {
1790
        required: {
1791
            message: "Field is required",
1792
            validator: function(value) {
1793
                return value !== undefined && value !== null && value !== "";
1794
            }
1795
        },
1796

1797
        rangeLength: {
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];
1801
            }
1802
        },
1803

1804
        minLength: {
1805
            message: "Field value is too short",
1806
            validator: function(value, _, param) {
1807
                return value.length >= param;
1808
            }
1809
        },
1810

1811
        maxLength: {
1812
            message: "Field value is too long",
1813
            validator: function(value, _, param) {
1814
                return value.length <= param;
1815
            }
1816
        },
1817

1818
        pattern: {
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 + ")$");
1823
                }
1824
                return param.test(value);
1825
            }
1826
        },
1827

1828
        range: {
1829
            message: "Field value is out of the defined range",
1830
            validator: function(value, _, param) {
1831
                return value >= param[0] && value <= param[1];
1832
            }
1833
        },
1834

1835
        min: {
1836
            message: "Field value is too small",
1837
            validator: function(value, _, param) {
1838
                return value >= param;
1839
            }
1840
        },
1841

1842
        max: {
1843
            message: "Field value is too large",
1844
            validator: function(value, _, param) {
1845
                return value <= param;
1846
            }
1847
        }
1848
    };
1849

1850
    jsGrid.validators = validators;
1851

1852
}(jsGrid, jQuery));
1853

1854
(function(jsGrid, $, undefined) {
1855

1856
    function Field(config) {
1857
        $.extend(true, this, config);
1858
        this.sortingFunc = this._getSortingFunc();
1859
    }
1860

1861
    Field.prototype = {
1862
        name: "",
1863
        title: null,
1864
        css: "",
1865
        align: "",
1866
        width: 100,
1867

1868
        visible: true,
1869
        filtering: true,
1870
        inserting: true,
1871
        editing: true,
1872
        sorting: true,
1873
        sorter: "string", // name of SortStrategy or function to compare elements
1874

1875
        headerTemplate: function() {
1876
            return (this.title === undefined || this.title === null) ? this.name : this.title;
1877
        },
1878

1879
        itemTemplate: function(value, item) {
1880
            return value;
1881
        },
1882

1883
        filterTemplate: function() {
1884
            return "";
1885
        },
1886

1887
        insertTemplate: function() {
1888
            return "";
1889
        },
1890

1891
        editTemplate: function(value, item) {
1892
            this._value = value;
1893
            return this.itemTemplate(value, item);
1894
        },
1895

1896
        filterValue: function() {
1897
            return "";
1898
        },
1899

1900
        insertValue: function() {
1901
            return "";
1902
        },
1903

1904
        editValue: function() {
1905
            return this._value;
1906
        },
1907

1908
        _getSortingFunc: function() {
1909
            var sorter = this.sorter;
1910

1911
            if($.isFunction(sorter)) {
1912
                return sorter;
1913
            }
1914

1915
            if(typeof sorter === "string") {
1916
                return jsGrid.sortStrategies[sorter];
1917
            }
1918

1919
            throw Error("wrong sorter for the field \"" + this.name + "\"!");
1920
        }
1921
    };
1922

1923
    jsGrid.Field = Field;
1924

1925
}(jsGrid, jQuery));
1926

1927
(function(jsGrid, $, undefined) {
1928

1929
    var Field = jsGrid.Field;
1930

1931
    function TextField(config) {
1932
        Field.call(this, config);
1933
    }
1934

1935
    TextField.prototype = new Field({
1936

1937
        autosearch: true,
1938
		readOnly: false,
1939

1940
        filterTemplate: function() {
1941
            if(!this.filtering)
1942
                return "";
1943

1944
            var grid = this._grid,
1945
                $result = this.filterControl = this._createTextBox();
1946

1947
            if(this.autosearch) {
1948
                $result.on("keypress", function(e) {
1949
                    if(e.which === 13) {
1950
                        grid.search();
1951
                        e.preventDefault();
1952
                    }
1953
                });
1954
            }
1955

1956
            return $result;
1957
        },
1958

1959
        insertTemplate: function() {
1960
            if(!this.inserting)
1961
                return "";
1962

1963
            return this.insertControl = this._createTextBox();
1964
        },
1965

1966
        editTemplate: function(value) {
1967
            if(!this.editing)
1968
                return this.itemTemplate.apply(this, arguments);
1969

1970
            var $result = this.editControl = this._createTextBox();
1971
            $result.val(value);
1972
            return $result;
1973
        },
1974

1975
        filterValue: function() {
1976
            return this.filterControl.val();
1977
        },
1978

1979
        insertValue: function() {
1980
            return this.insertControl.val();
1981
        },
1982

1983
        editValue: function() {
1984
            return this.editControl.val();
1985
        },
1986

1987
        _createTextBox: function() {
1988
            return $("<input>").attr("type", "text")
1989
                .prop("readonly", !!this.readOnly);
1990
        }
1991
    });
1992

1993
    jsGrid.fields.text = jsGrid.TextField = TextField;
1994

1995
}(jsGrid, jQuery));
1996

1997
(function(jsGrid, $, undefined) {
1998

1999
    var TextField = jsGrid.TextField;
2000

2001
    function NumberField(config) {
2002
        TextField.call(this, config);
2003
    }
2004

2005
    NumberField.prototype = new TextField({
2006

2007
        sorter: "number",
2008
        align: "right",
2009
		readOnly: false,
2010

2011
        filterValue: function() {
2012
            return this.filterControl.val()
2013
                ? parseInt(this.filterControl.val() || 0, 10)
2014
                : undefined;
2015
        },
2016

2017
        insertValue: function() {
2018
            return this.insertControl.val()
2019
                ? parseInt(this.insertControl.val() || 0, 10)
2020
                : undefined;
2021
        },
2022

2023
        editValue: function() {
2024
            return this.editControl.val()
2025
                ? parseInt(this.editControl.val() || 0, 10)
2026
                : undefined;
2027
        },
2028

2029
        _createTextBox: function() {
2030
			return $("<input>").attr("type", "number")
2031
                .prop("readonly", !!this.readOnly);
2032
        }
2033
    });
2034

2035
    jsGrid.fields.number = jsGrid.NumberField = NumberField;
2036

2037
}(jsGrid, jQuery));
2038

2039
(function(jsGrid, $, undefined) {
2040

2041
    var TextField = jsGrid.TextField;
2042

2043
    function TextAreaField(config) {
2044
        TextField.call(this, config);
2045
    }
2046

2047
    TextAreaField.prototype = new TextField({
2048

2049
        insertTemplate: function() {
2050
            if(!this.inserting)
2051
                return "";
2052

2053
            return this.insertControl = this._createTextArea();
2054
        },
2055

2056
        editTemplate: function(value) {
2057
            if(!this.editing)
2058
                return this.itemTemplate.apply(this, arguments);
2059

2060
            var $result = this.editControl = this._createTextArea();
2061
            $result.val(value);
2062
            return $result;
2063
        },
2064

2065
        _createTextArea: function() {
2066
            return $("<textarea>").prop("readonly", !!this.readOnly);
2067
        }
2068
    });
2069

2070
    jsGrid.fields.textarea = jsGrid.TextAreaField = TextAreaField;
2071

2072
}(jsGrid, jQuery));
2073

2074
(function(jsGrid, $, undefined) {
2075

2076
    var NumberField = jsGrid.NumberField;
2077
    var numberValueType = "number";
2078
    var stringValueType = "string";
2079

2080
    function SelectField(config) {
2081
        this.items = [];
2082
        this.selectedIndex = -1;
2083
        this.valueField = "";
2084
        this.textField = "";
2085

2086
        if(config.valueField && config.items.length) {
2087
            var firstItemValue = config.items[0][config.valueField];
2088
            this.valueType = (typeof firstItemValue) === numberValueType ? numberValueType : stringValueType;
2089
        }
2090

2091
        this.sorter = this.valueType;
2092

2093
        NumberField.call(this, config);
2094
    }
2095

2096
    SelectField.prototype = new NumberField({
2097

2098
        align: "center",
2099
        valueType: numberValueType,
2100

2101
        itemTemplate: function(value) {
2102
            var items = this.items,
2103
                valueField = this.valueField,
2104
                textField = this.textField,
2105
                resultItem;
2106

2107
            if(valueField) {
2108
                resultItem = $.grep(items, function(item, index) {
2109
                    return item[valueField] === value;
2110
                })[0] || {};
2111
            }
2112
            else {
2113
                resultItem = items[value];
2114
            }
2115

2116
            var result = (textField ? resultItem[textField] : resultItem);
2117

2118
            return (result === undefined || result === null) ? "" : result;
2119
        },
2120

2121
        filterTemplate: function() {
2122
            if(!this.filtering)
2123
                return "";
2124

2125
            var grid = this._grid,
2126
                $result = this.filterControl = this._createSelect();
2127

2128
            if(this.autosearch) {
2129
                $result.on("change", function(e) {
2130
                    grid.search();
2131
                });
2132
            }
2133

2134
            return $result;
2135
        },
2136

2137
        insertTemplate: function() {
2138
            if(!this.inserting)
2139
                return "";
2140

2141
            return this.insertControl = this._createSelect();
2142
        },
2143

2144
        editTemplate: function(value) {
2145
            if(!this.editing)
2146
                return this.itemTemplate.apply(this, arguments);
2147

2148
            var $result = this.editControl = this._createSelect();
2149
            (value !== undefined) && $result.val(value);
2150
            return $result;
2151
        },
2152

2153
        filterValue: function() {
2154
            var val = this.filterControl.val();
2155
            return this.valueType === numberValueType ? parseInt(val || 0, 10) : val;
2156
        },
2157

2158
        insertValue: function() {
2159
            var val = this.insertControl.val();
2160
            return this.valueType === numberValueType ? parseInt(val || 0, 10) : val;
2161
        },
2162

2163
        editValue: function() {
2164
            var val = this.editControl.val();
2165
            return this.valueType === numberValueType ? parseInt(val || 0, 10) : val;
2166
        },
2167

2168
        _createSelect: function() {
2169
            var $result = $("<select>"),
2170
                valueField = this.valueField,
2171
                textField = this.textField,
2172
                selectedIndex = this.selectedIndex;
2173

2174
            $.each(this.items, function(index, item) {
2175
                var value = valueField ? item[valueField] : index,
2176
                    text = textField ? item[textField] : item;
2177

2178
                var $option = $("<option>")
2179
                    .attr("value", value)
2180
                    .text(text)
2181
                    .appendTo($result);
2182

2183
                $option.prop("selected", (selectedIndex === index));
2184
            });
2185

2186
            $result.prop("disabled", !!this.readOnly);
2187

2188
            return $result;
2189
        }
2190
    });
2191

2192
    jsGrid.fields.select = jsGrid.SelectField = SelectField;
2193

2194
}(jsGrid, jQuery));
2195

2196
(function(jsGrid, $, undefined) {
2197

2198
    var Field = jsGrid.Field;
2199

2200
    function CheckboxField(config) {
2201
        Field.call(this, config);
2202
    }
2203

2204
    CheckboxField.prototype = new Field({
2205

2206
        sorter: "number",
2207
        align: "center",
2208
        autosearch: true,
2209

2210
        itemTemplate: function(value) {
2211
            return this._createCheckbox().prop({
2212
                checked: value,
2213
                disabled: true
2214
            });
2215
        },
2216

2217
        filterTemplate: function() {
2218
            if(!this.filtering)
2219
                return "";
2220

2221
            var grid = this._grid,
2222
                $result = this.filterControl = this._createCheckbox();
2223

2224
            $result.prop({
2225
                readOnly: true,
2226
                indeterminate: true
2227
            });
2228

2229
            $result.on("click", function() {
2230
                var $cb = $(this);
2231

2232
                if($cb.prop("readOnly")) {
2233
                    $cb.prop({
2234
                        checked: false,
2235
                        readOnly: false
2236
                    });
2237
                }
2238
                else if(!$cb.prop("checked")) {
2239
                    $cb.prop({
2240
                        readOnly: true,
2241
                        indeterminate: true
2242
                    });
2243
                }
2244
            });
2245

2246
            if(this.autosearch) {
2247
                $result.on("click", function() {
2248
                    grid.search();
2249
                });
2250
            }
2251

2252
            return $result;
2253
        },
2254

2255
        insertTemplate: function() {
2256
            if(!this.inserting)
2257
                return "";
2258

2259
            return this.insertControl = this._createCheckbox();
2260
        },
2261

2262
        editTemplate: function(value) {
2263
            if(!this.editing)
2264
                return this.itemTemplate.apply(this, arguments);
2265

2266
            var $result = this.editControl = this._createCheckbox();
2267
            $result.prop("checked", value);
2268
            return $result;
2269
        },
2270

2271
        filterValue: function() {
2272
            return this.filterControl.get(0).indeterminate
2273
                ? undefined
2274
                : this.filterControl.is(":checked");
2275
        },
2276

2277
        insertValue: function() {
2278
            return this.insertControl.is(":checked");
2279
        },
2280

2281
        editValue: function() {
2282
            return this.editControl.is(":checked");
2283
        },
2284

2285
        _createCheckbox: function() {
2286
            return $("<input>").attr("type", "checkbox");
2287
        }
2288
    });
2289

2290
    jsGrid.fields.checkbox = jsGrid.CheckboxField = CheckboxField;
2291

2292
}(jsGrid, jQuery));
2293

2294
(function(jsGrid, $, undefined) {
2295

2296
    var Field = jsGrid.Field;
2297

2298
    function ControlField(config) {
2299
        Field.call(this, config);
2300
        this._configInitialized = false;
2301
    }
2302

2303
    ControlField.prototype = new Field({
2304
        css: "jsgrid-control-field",
2305
        align: "center",
2306
        width: 50,
2307
        filtering: false,
2308
        inserting: false,
2309
        editing: false,
2310
        sorting: false,
2311

2312
        buttonClass: "jsgrid-button",
2313
        modeButtonClass: "jsgrid-mode-button",
2314

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",
2325

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",
2335

2336
        editButton: true,
2337
        deleteButton: true,
2338
        clearFilterButton: true,
2339
        modeSwitchButton: true,
2340

2341
        _initConfig: function() {
2342
            this._hasFiltering = this._grid.filtering;
2343
            this._hasInserting = this._grid.inserting;
2344

2345
            if(this._hasInserting && this.modeSwitchButton) {
2346
                this._grid.inserting = false;
2347
            }
2348

2349
            this._configInitialized = true;
2350
        },
2351

2352
        headerTemplate: function() {
2353
            if(!this._configInitialized) {
2354
                this._initConfig();
2355
            }
2356

2357
            var hasFiltering = this._hasFiltering;
2358
            var hasInserting = this._hasInserting;
2359

2360
            if(!this.modeSwitchButton || (!hasFiltering && !hasInserting))
2361
                return "";
2362

2363
            if(hasFiltering && !hasInserting)
2364
                return this._createFilterSwitchButton();
2365

2366
            if(hasInserting && !hasFiltering)
2367
                return this._createInsertSwitchButton();
2368

2369
            return this._createModeSwitchButton();
2370
        },
2371

2372
        itemTemplate: function(value, item) {
2373
            var $result = $([]);
2374

2375
            if(this.editButton) {
2376
                $result = $result.add(this._createEditButton(item));
2377
            }
2378

2379
            if(this.deleteButton) {
2380
                $result = $result.add(this._createDeleteButton(item));
2381
            }
2382

2383
            return $result;
2384
        },
2385

2386
        filterTemplate: function() {
2387
            var $result = this._createSearchButton();
2388
            return this.clearFilterButton ? $result.add(this._createClearFilterButton()) : $result;
2389
        },
2390

2391
        insertTemplate: function() {
2392
            return this._createInsertButton();
2393
        },
2394

2395
        editTemplate: function() {
2396
            return this._createUpdateButton().add(this._createCancelEditButton());
2397
        },
2398

2399
        _createFilterSwitchButton: function() {
2400
            return this._createOnOffSwitchButton("filtering", this.searchModeButtonClass, true);
2401
        },
2402

2403
        _createInsertSwitchButton: function() {
2404
            return this._createOnOffSwitchButton("inserting", this.insertModeButtonClass, false);
2405
        },
2406

2407
        _createOnOffSwitchButton: function(option, cssClass, isOnInitially) {
2408
            var isOn = isOnInitially;
2409

2410
            var updateButtonState = $.proxy(function() {
2411
                $button.toggleClass(this.modeOnButtonClass, isOn);
2412
            }, this);
2413

2414
            var $button = this._createGridButton(this.modeButtonClass + " " + cssClass, "", function(grid) {
2415
                isOn = !isOn;
2416
                grid.option(option, isOn);
2417
                updateButtonState();
2418
            });
2419

2420
            updateButtonState();
2421

2422
            return $button;
2423
        },
2424

2425
        _createModeSwitchButton: function() {
2426
            var isInserting = false;
2427

2428
            var updateButtonState = $.proxy(function() {
2429
                $button.attr("title", isInserting ? this.searchModeButtonTooltip : this.insertModeButtonTooltip)
2430
                    .toggleClass(this.insertModeButtonClass, !isInserting)
2431
                    .toggleClass(this.searchModeButtonClass, isInserting);
2432
            }, this);
2433

2434
            var $button = this._createGridButton(this.modeButtonClass, "", function(grid) {
2435
                isInserting = !isInserting;
2436
                grid.option("inserting", isInserting);
2437
                grid.option("filtering", !isInserting);
2438
                updateButtonState();
2439
            });
2440

2441
            updateButtonState();
2442

2443
            return $button;
2444
        },
2445

2446
        _createEditButton: function(item) {
2447
            return this._createGridButton(this.editButtonClass, this.editButtonTooltip, function(grid, e) {
2448
                grid.editItem(item);
2449
                e.stopPropagation();
2450
            });
2451
        },
2452

2453
        _createDeleteButton: function(item) {
2454
            return this._createGridButton(this.deleteButtonClass, this.deleteButtonTooltip, function(grid, e) {
2455
                grid.deleteItem(item);
2456
                e.stopPropagation();
2457
            });
2458
        },
2459

2460
        _createSearchButton: function() {
2461
            return this._createGridButton(this.searchButtonClass, this.searchButtonTooltip, function(grid) {
2462
                grid.search();
2463
            });
2464
        },
2465

2466
        _createClearFilterButton: function() {
2467
            return this._createGridButton(this.clearFilterButtonClass, this.clearFilterButtonTooltip, function(grid) {
2468
                grid.clearFilter();
2469
            });
2470
        },
2471

2472
        _createInsertButton: function() {
2473
            return this._createGridButton(this.insertButtonClass, this.insertButtonTooltip, function(grid) {
2474
                grid.insertItem().done(function() {
2475
                    grid.clearInsert();
2476
                });
2477
            });
2478
        },
2479

2480
        _createUpdateButton: function() {
2481
            return this._createGridButton(this.updateButtonClass, this.updateButtonTooltip, function(grid, e) {
2482
                grid.updateItem();
2483
                e.stopPropagation();
2484
            });
2485
        },
2486

2487
        _createCancelEditButton: function() {
2488
            return this._createGridButton(this.cancelEditButtonClass, this.cancelEditButtonTooltip, function(grid, e) {
2489
                grid.cancelEdit();
2490
                e.stopPropagation();
2491
            });
2492
        },
2493

2494
        _createGridButton: function(cls, tooltip, clickHandler) {
2495
            var grid = this._grid;
2496

2497
            return $("<input>").addClass(this.buttonClass)
2498
                .addClass(cls)
2499
                .attr({
2500
                    type: "button",
2501
                    title: tooltip
2502
                })
2503
                .on("click", function(e) {
2504
                    clickHandler(grid, e);
2505
                });
2506
        },
2507

2508
        editValue: function() {
2509
            return "";
2510
        }
2511

2512
    });
2513

2514
    jsGrid.fields.control = jsGrid.ControlField = ControlField;
2515

2516
}(jsGrid, jQuery));
2517

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.