| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516 | /* * jsGrid v1.5.3 (http://js-grid.com) * (c) 2016 Artem Tabalin * Licensed under MIT (https://github.com/tabalinas/jsgrid/blob/master/LICENSE) */(function(window, $, undefined) {    var JSGRID = "JSGrid",        JSGRID_DATA_KEY = JSGRID,        JSGRID_ROW_DATA_KEY = "JSGridItem",        JSGRID_EDIT_ROW_DATA_KEY = "JSGridEditRow",        SORT_ORDER_ASC = "asc",        SORT_ORDER_DESC = "desc",        FIRST_PAGE_PLACEHOLDER = "{first}",        PAGES_PLACEHOLDER = "{pages}",        PREV_PAGE_PLACEHOLDER = "{prev}",        NEXT_PAGE_PLACEHOLDER = "{next}",        LAST_PAGE_PLACEHOLDER = "{last}",        PAGE_INDEX_PLACEHOLDER = "{pageIndex}",        PAGE_COUNT_PLACEHOLDER = "{pageCount}",        ITEM_COUNT_PLACEHOLDER = "{itemCount}",        EMPTY_HREF = "javascript:void(0);";    var getOrApply = function(value, context) {        if($.isFunction(value)) {            return value.apply(context, $.makeArray(arguments).slice(2));        }        return value;    };    var normalizePromise = function(promise) {        var d = $.Deferred();        if(promise && promise.then) {            promise.then(function() {                d.resolve.apply(d, arguments);            }, function() {                d.reject.apply(d, arguments);            });        } else {            d.resolve(promise);        }        return d.promise();    };    var defaultController = {        loadData: $.noop,        insertItem: $.noop,        updateItem: $.noop,        deleteItem: $.noop    };    function Grid(element, config) {        var $element = $(element);        $element.data(JSGRID_DATA_KEY, this);        this._container = $element;        this.data = [];        this.fields = [];        this._editingRow = null;        this._sortField = null;        this._sortOrder = SORT_ORDER_ASC;        this._firstDisplayingPage = 1;        this._init(config);        this.render();    }    Grid.prototype = {        width: "auto",        height: "auto",        updateOnResize: true,        rowClass: $.noop,        rowRenderer: null,        rowClick: function(args) {            if(this.editing) {                this.editItem($(args.event.target).closest("tr"));            }        },        rowDoubleClick: $.noop,        noDataContent: "Not found",        noDataRowClass: "jsgrid-nodata-row",        heading: true,        headerRowRenderer: null,        headerRowClass: "jsgrid-header-row",        headerCellClass: "jsgrid-header-cell",        filtering: false,        filterRowRenderer: null,        filterRowClass: "jsgrid-filter-row",        inserting: false,        insertRowRenderer: null,        insertRowClass: "jsgrid-insert-row",        editing: false,        editRowRenderer: null,        editRowClass: "jsgrid-edit-row",        confirmDeleting: true,        deleteConfirm: "Are you sure?",        selecting: true,        selectedRowClass: "jsgrid-selected-row",        oddRowClass: "jsgrid-row",        evenRowClass: "jsgrid-alt-row",        cellClass: "jsgrid-cell",        sorting: false,        sortableClass: "jsgrid-header-sortable",        sortAscClass: "jsgrid-header-sort jsgrid-header-sort-asc",        sortDescClass: "jsgrid-header-sort jsgrid-header-sort-desc",        paging: false,        pagerContainer: null,        pageIndex: 1,        pageSize: 20,        pageButtonCount: 15,        pagerFormat: "Pages: {first} {prev} {pages} {next} {last}    {pageIndex} of {pageCount}",        pagePrevText: "Prev",        pageNextText: "Next",        pageFirstText: "First",        pageLastText: "Last",        pageNavigatorNextText: "...",        pageNavigatorPrevText: "...",        pagerContainerClass: "jsgrid-pager-container",        pagerClass: "jsgrid-pager",        pagerNavButtonClass: "jsgrid-pager-nav-button",        pagerNavButtonInactiveClass: "jsgrid-pager-nav-inactive-button",        pageClass: "jsgrid-pager-page",        currentPageClass: "jsgrid-pager-current-page",        customLoading: false,        pageLoading: false,        autoload: false,        controller: defaultController,        loadIndication: true,        loadIndicationDelay: 500,        loadMessage: "Please, wait...",        loadShading: true,        invalidMessage: "Invalid data entered!",        invalidNotify: function(args) {            var messages = $.map(args.errors, function(error) {                return error.message || null;            });            window.alert([this.invalidMessage].concat(messages).join("\n"));        },        onInit: $.noop,        onRefreshing: $.noop,        onRefreshed: $.noop,        onPageChanged: $.noop,        onItemDeleting: $.noop,        onItemDeleted: $.noop,        onItemInserting: $.noop,        onItemInserted: $.noop,        onItemEditing: $.noop,        onItemUpdating: $.noop,        onItemUpdated: $.noop,        onItemInvalid: $.noop,        onDataLoading: $.noop,        onDataLoaded: $.noop,        onOptionChanging: $.noop,        onOptionChanged: $.noop,        onError: $.noop,        invalidClass: "jsgrid-invalid",        containerClass: "jsgrid",        tableClass: "jsgrid-table",        gridHeaderClass: "jsgrid-grid-header",        gridBodyClass: "jsgrid-grid-body",        _init: function(config) {            $.extend(this, config);            this._initLoadStrategy();            this._initController();            this._initFields();            this._attachWindowLoadResize();            this._attachWindowResizeCallback();            this._callEventHandler(this.onInit)        },        loadStrategy: function() {            return this.pageLoading                ? new jsGrid.loadStrategies.PageLoadingStrategy(this)                : new jsGrid.loadStrategies.DirectLoadingStrategy(this);        },        _initLoadStrategy: function() {            this._loadStrategy = getOrApply(this.loadStrategy, this);        },        _initController: function() {            this._controller = $.extend({}, defaultController, getOrApply(this.controller, this));        },        renderTemplate: function(source, context, config) {            args = [];            for(var key in config) {                args.push(config[key]);            }            args.unshift(source, context);            source = getOrApply.apply(null, args);            return (source === undefined || source === null) ? "" : source;        },        loadIndicator: function(config) {            return new jsGrid.LoadIndicator(config);        },        validation: function(config) {            return jsGrid.Validation && new jsGrid.Validation(config);        },        _initFields: function() {            var self = this;            self.fields = $.map(self.fields, function(field) {                if($.isPlainObject(field)) {                    var fieldConstructor = (field.type && jsGrid.fields[field.type]) || jsGrid.Field;                    field = new fieldConstructor(field);                }                field._grid = self;                return field;            });        },        _attachWindowLoadResize: function() {            $(window).on("load", $.proxy(this._refreshSize, this));        },        _attachWindowResizeCallback: function() {            if(this.updateOnResize) {                $(window).on("resize", $.proxy(this._refreshSize, this));            }        },        _detachWindowResizeCallback: function() {            $(window).off("resize", this._refreshSize);        },        option: function(key, value) {            var optionChangingEventArgs,                optionChangedEventArgs;            if(arguments.length === 1)                return this[key];            optionChangingEventArgs = {                option: key,                oldValue: this[key],                newValue: value            };            this._callEventHandler(this.onOptionChanging, optionChangingEventArgs);            this._handleOptionChange(optionChangingEventArgs.option, optionChangingEventArgs.newValue);            optionChangedEventArgs = {                option: optionChangingEventArgs.option,                value: optionChangingEventArgs.newValue            };            this._callEventHandler(this.onOptionChanged, optionChangedEventArgs);        },        fieldOption: function(field, key, value) {            field = this._normalizeField(field);            if(arguments.length === 2)                return field[key];            field[key] = value;            this._renderGrid();        },        _handleOptionChange: function(name, value) {            this[name] = value;            switch(name) {                case "width":                case "height":                    this._refreshSize();                    break;                case "rowClass":                case "rowRenderer":                case "rowClick":                case "rowDoubleClick":                case "noDataRowClass":                case "noDataContent":                case "selecting":                case "selectedRowClass":                case "oddRowClass":                case "evenRowClass":                    this._refreshContent();                    break;                case "pageButtonCount":                case "pagerFormat":                case "pagePrevText":                case "pageNextText":                case "pageFirstText":                case "pageLastText":                case "pageNavigatorNextText":                case "pageNavigatorPrevText":                case "pagerClass":                case "pagerNavButtonClass":                case "pageClass":                case "currentPageClass":                case "pagerRenderer":                    this._refreshPager();                    break;                case "fields":                    this._initFields();                    this.render();                    break;                case "data":                case "editing":                case "heading":                case "filtering":                case "inserting":                case "paging":                    this.refresh();                    break;                case "loadStrategy":                case "pageLoading":                    this._initLoadStrategy();                    this.search();                    break;                case "pageIndex":                    this.openPage(value);                    break;                case "pageSize":                    this.refresh();                    this.search();                    break;                case "editRowRenderer":                case "editRowClass":                    this.cancelEdit();                    break;                case "updateOnResize":                    this._detachWindowResizeCallback();                    this._attachWindowResizeCallback();                    break;                case "invalidNotify":                case "invalidMessage":                    break;                default:                    this.render();                    break;            }        },        destroy: function() {            this._detachWindowResizeCallback();            this._clear();            this._container.removeData(JSGRID_DATA_KEY);        },        render: function() {            this._renderGrid();            return this.autoload ? this.loadData() : $.Deferred().resolve().promise();        },        _renderGrid: function() {            this._clear();            this._container.addClass(this.containerClass)                .css("position", "relative")                .append(this._createHeader())                .append(this._createBody());            this._pagerContainer = this._createPagerContainer();            this._loadIndicator = this._createLoadIndicator();            this._validation = this._createValidation();            this.refresh();        },        _createLoadIndicator: function() {            return getOrApply(this.loadIndicator, this, {                message: this.loadMessage,                shading: this.loadShading,                container: this._container            });        },        _createValidation: function() {            return getOrApply(this.validation, this);        },        _clear: function() {            this.cancelEdit();            clearTimeout(this._loadingTimer);            this._pagerContainer && this._pagerContainer.empty();            this._container.empty()                .css({ position: "", width: "", height: "" });        },        _createHeader: function() {            var $headerRow = this._headerRow = this._createHeaderRow(),                $filterRow = this._filterRow = this._createFilterRow(),                $insertRow = this._insertRow = this._createInsertRow();            var $headerGrid = this._headerGrid = $("<table>").addClass(this.tableClass)                .append($headerRow)                .append($filterRow)                .append($insertRow);            var $header = this._header = $("<div>").addClass(this.gridHeaderClass)                .addClass(this._scrollBarWidth() ? "jsgrid-header-scrollbar" : "")                .append($headerGrid);            return $header;        },        _createBody: function() {            var $content = this._content = $("<tbody>");            var $bodyGrid = this._bodyGrid = $("<table>").addClass(this.tableClass)                .append($content);            var $body = this._body = $("<div>").addClass(this.gridBodyClass)                .append($bodyGrid)                .on("scroll", $.proxy(function(e) {                    this._header.scrollLeft(e.target.scrollLeft);                }, this));            return $body;        },        _createPagerContainer: function() {            var pagerContainer = this.pagerContainer || $("<div>").appendTo(this._container);            return $(pagerContainer).addClass(this.pagerContainerClass);        },        _eachField: function(callBack) {            var self = this;            $.each(this.fields, function(index, field) {                if(field.visible) {                    callBack.call(self, field, index);                }            });        },        _createHeaderRow: function() {            if($.isFunction(this.headerRowRenderer))                return $(this.renderTemplate(this.headerRowRenderer, this));            var $result = $("<tr>").addClass(this.headerRowClass);            this._eachField(function(field, index) {                var $th = this._prepareCell("<th>", field, "headercss", this.headerCellClass)                    .append(this.renderTemplate(field.headerTemplate, field))                    .appendTo($result);                if(this.sorting && field.sorting) {                    $th.addClass(this.sortableClass)                        .on("click", $.proxy(function() {                            this.sort(index);                        }, this));                }            });            return $result;        },        _prepareCell: function(cell, field, cssprop, cellClass) {            return $(cell).css("width", field.width)                .addClass(cellClass || this.cellClass)                .addClass((cssprop && field[cssprop]) || field.css)                .addClass(field.align ? ("jsgrid-align-" + field.align) : "");        },        _createFilterRow: function() {            if($.isFunction(this.filterRowRenderer))                return $(this.renderTemplate(this.filterRowRenderer, this));            var $result = $("<tr>").addClass(this.filterRowClass);            this._eachField(function(field) {                this._prepareCell("<td>", field, "filtercss")                    .append(this.renderTemplate(field.filterTemplate, field))                    .appendTo($result);            });            return $result;        },        _createInsertRow: function() {            if($.isFunction(this.insertRowRenderer))                return $(this.renderTemplate(this.insertRowRenderer, this));            var $result = $("<tr>").addClass(this.insertRowClass);            this._eachField(function(field) {                this._prepareCell("<td>", field, "insertcss")                    .append(this.renderTemplate(field.insertTemplate, field))                    .appendTo($result);            });            return $result;        },        _callEventHandler: function(handler, eventParams) {            handler.call(this, $.extend(eventParams, {                grid: this            }));            return eventParams;        },        reset: function() {            this._resetSorting();            this._resetPager();            return this._loadStrategy.reset();        },        _resetPager: function() {            this._firstDisplayingPage = 1;            this._setPage(1);        },        _resetSorting: function() {            this._sortField = null;            this._sortOrder = SORT_ORDER_ASC;            this._clearSortingCss();        },        refresh: function() {            this._callEventHandler(this.onRefreshing);            this.cancelEdit();            this._refreshHeading();            this._refreshFiltering();            this._refreshInserting();            this._refreshContent();            this._refreshPager();            this._refreshSize();            this._callEventHandler(this.onRefreshed);        },        _refreshHeading: function() {            this._headerRow.toggle(this.heading);        },        _refreshFiltering: function() {            this._filterRow.toggle(this.filtering);        },        _refreshInserting: function() {            this._insertRow.toggle(this.inserting);        },        _refreshContent: function() {            var $content = this._content;            $content.empty();            if(!this.data.length) {                $content.append(this._createNoDataRow());                return this;            }            var indexFrom = this._loadStrategy.firstDisplayIndex();            var indexTo = this._loadStrategy.lastDisplayIndex();            for(var itemIndex = indexFrom; itemIndex < indexTo; itemIndex++) {                var item = this.data[itemIndex];                $content.append(this._createRow(item, itemIndex));            }        },        _createNoDataRow: function() {            var amountOfFields = 0;            this._eachField(function() {                amountOfFields++;            });            return $("<tr>").addClass(this.noDataRowClass)                .append($("<td>").addClass(this.cellClass).attr("colspan", amountOfFields)                    .append(this.renderTemplate(this.noDataContent, this)));        },        _createRow: function(item, itemIndex) {            var $result;            if($.isFunction(this.rowRenderer)) {                $result = this.renderTemplate(this.rowRenderer, this, { item: item, itemIndex: itemIndex });            } else {                $result = $("<tr>");                this._renderCells($result, item);            }            $result.addClass(this._getRowClasses(item, itemIndex))                .data(JSGRID_ROW_DATA_KEY, item)                .on("click", $.proxy(function(e) {                    this.rowClick({                        item: item,                        itemIndex: itemIndex,                        event: e                    });                }, this))                .on("dblclick", $.proxy(function(e) {                    this.rowDoubleClick({                        item: item,                        itemIndex: itemIndex,                        event: e                    });                }, this));            if(this.selecting) {                this._attachRowHover($result);            }            return $result;        },        _getRowClasses: function(item, itemIndex) {            var classes = [];            classes.push(((itemIndex + 1) % 2) ? this.oddRowClass : this.evenRowClass);            classes.push(getOrApply(this.rowClass, this, item, itemIndex));            return classes.join(" ");        },        _attachRowHover: function($row) {            var selectedRowClass = this.selectedRowClass;            $row.hover(function() {                    $(this).addClass(selectedRowClass);                },                function() {                    $(this).removeClass(selectedRowClass);                }            );        },        _renderCells: function($row, item) {            this._eachField(function(field) {                $row.append(this._createCell(item, field));            });            return this;        },        _createCell: function(item, field) {            var $result;            var fieldValue = this._getItemFieldValue(item, field);            var args = { value: fieldValue, item : item };            if($.isFunction(field.cellRenderer)) {                $result = this.renderTemplate(field.cellRenderer, field, args);            } else {                $result = $("<td>").append(this.renderTemplate(field.itemTemplate || fieldValue, field, args));            }            return this._prepareCell($result, field);        },        _getItemFieldValue: function(item, field) {            var props = field.name.split('.');            var result = item[props.shift()];            while(result && props.length) {                result = result[props.shift()];            }            return result;        },        _setItemFieldValue: function(item, field, value) {            var props = field.name.split('.');            var current = item;            var prop = props[0];            while(current && props.length) {                item = current;                prop = props.shift();                current = item[prop];            }            if(!current) {                while(props.length) {                    item = item[prop] = {};                    prop = props.shift();                }            }            item[prop] = value;        },        sort: function(field, order) {            if($.isPlainObject(field)) {                order = field.order;                field = field.field;            }            this._clearSortingCss();            this._setSortingParams(field, order);            this._setSortingCss();            return this._loadStrategy.sort();        },        _clearSortingCss: function() {            this._headerRow.find("th")                .removeClass(this.sortAscClass)                .removeClass(this.sortDescClass);        },        _setSortingParams: function(field, order) {            field = this._normalizeField(field);            order = order || ((this._sortField === field) ? this._reversedSortOrder(this._sortOrder) : SORT_ORDER_ASC);            this._sortField = field;            this._sortOrder = order;        },        _normalizeField: function(field) {            if($.isNumeric(field)) {                return this.fields[field];            }            if(typeof field === "string") {                return $.grep(this.fields, function(f) {                    return f.name === field;                })[0];            }            return field;        },        _reversedSortOrder: function(order) {            return (order === SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC);        },        _setSortingCss: function() {            var fieldIndex = this._visibleFieldIndex(this._sortField);            this._headerRow.find("th").eq(fieldIndex)                .addClass(this._sortOrder === SORT_ORDER_ASC ? this.sortAscClass : this.sortDescClass);        },        _visibleFieldIndex: function(field) {            return $.inArray(field, $.grep(this.fields, function(f) { return f.visible; }));        },        _sortData: function() {            var sortFactor = this._sortFactor(),                sortField = this._sortField;            if(sortField) {                this.data.sort(function(item1, item2) {                    return sortFactor * sortField.sortingFunc(item1[sortField.name], item2[sortField.name]);                });            }        },        _sortFactor: function() {            return this._sortOrder === SORT_ORDER_ASC ? 1 : -1;        },        _itemsCount: function() {            return this._loadStrategy.itemsCount();        },        _pagesCount: function() {            var itemsCount = this._itemsCount(),                pageSize = this.pageSize;            return Math.floor(itemsCount / pageSize) + (itemsCount % pageSize ? 1 : 0);        },        _refreshPager: function() {            var $pagerContainer = this._pagerContainer;            $pagerContainer.empty();            if(this.paging) {                $pagerContainer.append(this._createPager());            }            var showPager = this.paging && this._pagesCount() > 1;            $pagerContainer.toggle(showPager);        },        _createPager: function() {            var $result;            if($.isFunction(this.pagerRenderer)) {                $result = $(this.pagerRenderer({                    pageIndex: this.pageIndex,                    pageCount: this._pagesCount()                }));            } else {                $result = $("<div>").append(this._createPagerByFormat());            }            $result.addClass(this.pagerClass);            return $result;        },        _createPagerByFormat: function() {            var pageIndex = this.pageIndex,                pageCount = this._pagesCount(),                itemCount = this._itemsCount(),                pagerParts = this.pagerFormat.split(" ");            return $.map(pagerParts, $.proxy(function(pagerPart) {                var result = pagerPart;                if(pagerPart === PAGES_PLACEHOLDER) {                    result = this._createPages();                } else if(pagerPart === FIRST_PAGE_PLACEHOLDER) {                    result = this._createPagerNavButton(this.pageFirstText, 1, pageIndex > 1);                } else if(pagerPart === PREV_PAGE_PLACEHOLDER) {                    result = this._createPagerNavButton(this.pagePrevText, pageIndex - 1, pageIndex > 1);                } else if(pagerPart === NEXT_PAGE_PLACEHOLDER) {                    result = this._createPagerNavButton(this.pageNextText, pageIndex + 1, pageIndex < pageCount);                } else if(pagerPart === LAST_PAGE_PLACEHOLDER) {                    result = this._createPagerNavButton(this.pageLastText, pageCount, pageIndex < pageCount);                } else if(pagerPart === PAGE_INDEX_PLACEHOLDER) {                    result = pageIndex;                } else if(pagerPart === PAGE_COUNT_PLACEHOLDER) {                    result = pageCount;                } else if(pagerPart === ITEM_COUNT_PLACEHOLDER) {                    result = itemCount;                }                return $.isArray(result) ? result.concat([" "]) : [result, " "];            }, this));        },        _createPages: function() {            var pageCount = this._pagesCount(),                pageButtonCount = this.pageButtonCount,                firstDisplayingPage = this._firstDisplayingPage,                pages = [];            if(firstDisplayingPage > 1) {                pages.push(this._createPagerPageNavButton(this.pageNavigatorPrevText, this.showPrevPages));            }            for(var i = 0, pageNumber = firstDisplayingPage; i < pageButtonCount && pageNumber <= pageCount; i++, pageNumber++) {                pages.push(pageNumber === this.pageIndex                    ? this._createPagerCurrentPage()                    : this._createPagerPage(pageNumber));            }            if((firstDisplayingPage + pageButtonCount - 1) < pageCount) {                pages.push(this._createPagerPageNavButton(this.pageNavigatorNextText, this.showNextPages));            }            return pages;        },        _createPagerNavButton: function(text, pageIndex, isActive) {            return this._createPagerButton(text, this.pagerNavButtonClass + (isActive ? "" : " " + this.pagerNavButtonInactiveClass),                isActive ? function() { this.openPage(pageIndex); } : $.noop);        },        _createPagerPageNavButton: function(text, handler) {            return this._createPagerButton(text, this.pagerNavButtonClass, handler);        },        _createPagerPage: function(pageIndex) {            return this._createPagerButton(pageIndex, this.pageClass, function() {                this.openPage(pageIndex);            });        },        _createPagerButton: function(text, css, handler) {            var $link = $("<a>").attr("href", EMPTY_HREF)                .html(text)                .on("click", $.proxy(handler, this));            return $("<span>").addClass(css).append($link);        },        _createPagerCurrentPage: function() {            return $("<span>")                .addClass(this.pageClass)                .addClass(this.currentPageClass)                .text(this.pageIndex);        },        _refreshSize: function() {            this._refreshHeight();            this._refreshWidth();        },        _refreshWidth: function() {            var width = (this.width === "auto") ? this._getAutoWidth() : this.width;            this._container.width(width);        },        _getAutoWidth: function() {            var $headerGrid = this._headerGrid,                $header = this._header;            $headerGrid.width("auto");            var contentWidth = $headerGrid.outerWidth();            var borderWidth = $header.outerWidth() - $header.innerWidth();            $headerGrid.width("");            return contentWidth + borderWidth;        },        _scrollBarWidth: (function() {            var result;            return function() {                if(result === undefined) {                    var $ghostContainer = $("<div style='width:50px;height:50px;overflow:hidden;position:absolute;top:-10000px;left:-10000px;'></div>");                    var $ghostContent = $("<div style='height:100px;'></div>");                    $ghostContainer.append($ghostContent).appendTo("body");                    var width = $ghostContent.innerWidth();                    $ghostContainer.css("overflow-y", "auto");                    var widthExcludingScrollBar = $ghostContent.innerWidth();                    $ghostContainer.remove();                    result = width - widthExcludingScrollBar;                }                return result;            };        })(),        _refreshHeight: function() {            var container = this._container,                pagerContainer = this._pagerContainer,                height = this.height,                nonBodyHeight;            container.height(height);            if(height !== "auto") {                height = container.height();                nonBodyHeight = this._header.outerHeight(true);                if(pagerContainer.parents(container).length) {                    nonBodyHeight += pagerContainer.outerHeight(true);                }                this._body.outerHeight(height - nonBodyHeight);            }        },        showPrevPages: function() {            var firstDisplayingPage = this._firstDisplayingPage,                pageButtonCount = this.pageButtonCount;            this._firstDisplayingPage = (firstDisplayingPage > pageButtonCount) ? firstDisplayingPage - pageButtonCount : 1;            this._refreshPager();        },        showNextPages: function() {            var firstDisplayingPage = this._firstDisplayingPage,                pageButtonCount = this.pageButtonCount,                pageCount = this._pagesCount();            this._firstDisplayingPage = (firstDisplayingPage + 2 * pageButtonCount > pageCount)                ? pageCount - pageButtonCount + 1                : firstDisplayingPage + pageButtonCount;            this._refreshPager();        },        openPage: function(pageIndex) {            if(pageIndex < 1 || pageIndex > this._pagesCount())                return;            this._setPage(pageIndex);            this._loadStrategy.openPage(pageIndex);        },        _setPage: function(pageIndex) {            var firstDisplayingPage = this._firstDisplayingPage,                pageButtonCount = this.pageButtonCount;            this.pageIndex = pageIndex;            if(pageIndex < firstDisplayingPage) {                this._firstDisplayingPage = pageIndex;            }            if(pageIndex > firstDisplayingPage + pageButtonCount - 1) {                this._firstDisplayingPage = pageIndex - pageButtonCount + 1;            }            this._callEventHandler(this.onPageChanged, {                pageIndex: pageIndex            });        },        _controllerCall: function(method, param, isCanceled, doneCallback) {            if(isCanceled)                return $.Deferred().reject().promise();            this._showLoading();            var controller = this._controller;            if(!controller || !controller[method]) {                throw Error("controller has no method '" + method + "'");            }            return normalizePromise(controller[method](param))                .done($.proxy(doneCallback, this))                .fail($.proxy(this._errorHandler, this))                .always($.proxy(this._hideLoading, this));        },        _errorHandler: function() {            this._callEventHandler(this.onError, {                args: $.makeArray(arguments)            });        },        _showLoading: function() {            if(!this.loadIndication)                return;            clearTimeout(this._loadingTimer);            this._loadingTimer = setTimeout($.proxy(function() {                this._loadIndicator.show();            }, this), this.loadIndicationDelay);        },        _hideLoading: function() {            if(!this.loadIndication)                return;            clearTimeout(this._loadingTimer);            this._loadIndicator.hide();        },        search: function(filter) {            this._resetSorting();            this._resetPager();            return this.loadData(filter);        },        loadData: function(filter) {            filter = filter || (this.filtering ? this.getFilter() : {});            $.extend(filter, this._loadStrategy.loadParams(), this._sortingParams());            var args = this._callEventHandler(this.onDataLoading, {                filter: filter            });            return this._controllerCall("loadData", filter, args.cancel, function(loadedData) {                if(!loadedData)                    return;                this._loadStrategy.finishLoad(loadedData);                this._callEventHandler(this.onDataLoaded, {                    data: loadedData                });            });        },        getFilter: function() {            var result = {};            this._eachField(function(field) {                if(field.filtering) {                    this._setItemFieldValue(result, field, field.filterValue());                }            });            return result;        },        _sortingParams: function() {            if(this.sorting && this._sortField) {                return {                    sortField: this._sortField.name,                    sortOrder: this._sortOrder                };            }            return {};        },        getSorting: function() {            var sortingParams = this._sortingParams();            return {                field: sortingParams.sortField,                order: sortingParams.sortOrder            };        },        clearFilter: function() {            var $filterRow = this._createFilterRow();            this._filterRow.replaceWith($filterRow);            this._filterRow = $filterRow;            return this.search();        },        insertItem: function(item) {            var insertingItem = item || this._getValidatedInsertItem();            if(!insertingItem)                return $.Deferred().reject().promise();            var args = this._callEventHandler(this.onItemInserting, {                item: insertingItem            });            return this._controllerCall("insertItem", insertingItem, args.cancel, function(insertedItem) {                insertedItem = insertedItem || insertingItem;                this._loadStrategy.finishInsert(insertedItem);                this._callEventHandler(this.onItemInserted, {                    item: insertedItem                });            });        },        _getValidatedInsertItem: function() {            var item = this._getInsertItem();            return this._validateItem(item, this._insertRow) ? item : null;        },        _getInsertItem: function() {            var result = {};            this._eachField(function(field) {                if(field.inserting) {                    this._setItemFieldValue(result, field, field.insertValue());                }            });            return result;        },        _validateItem: function(item, $row) {            var validationErrors = [];            var args = {                item: item,                itemIndex: this._rowIndex($row),                row: $row            };            this._eachField(function(field) {                if(!field.validate ||                   ($row === this._insertRow && !field.inserting) ||                   ($row === this._getEditRow() && !field.editing))                    return;                var fieldValue = this._getItemFieldValue(item, field);                var errors = this._validation.validate($.extend({                    value: fieldValue,                    rules: field.validate                }, args));                this._setCellValidity($row.children().eq(this._visibleFieldIndex(field)), errors);                if(!errors.length)                    return;                validationErrors.push.apply(validationErrors,                    $.map(errors, function(message) {                        return { field: field, message: message };                    }));            });            if(!validationErrors.length)                return true;            var invalidArgs = $.extend({                errors: validationErrors            }, args);            this._callEventHandler(this.onItemInvalid, invalidArgs);            this.invalidNotify(invalidArgs);            return false;        },        _setCellValidity: function($cell, errors) {            $cell                .toggleClass(this.invalidClass, !!errors.length)                .attr("title", errors.join("\n"));        },        clearInsert: function() {            var insertRow = this._createInsertRow();            this._insertRow.replaceWith(insertRow);            this._insertRow = insertRow;            this.refresh();        },        editItem: function(item) {            var $row = this.rowByItem(item);            if($row.length) {                this._editRow($row);            }        },        rowByItem: function(item) {            if(item.jquery || item.nodeType)                return $(item);            return this._content.find("tr").filter(function() {                return $.data(this, JSGRID_ROW_DATA_KEY) === item;            });        },        _editRow: function($row) {            if(!this.editing)                return;            var item = $row.data(JSGRID_ROW_DATA_KEY);            var args = this._callEventHandler(this.onItemEditing, {                row: $row,                item: item,                itemIndex: this._itemIndex(item)            });            if(args.cancel)                return;            if(this._editingRow) {                this.cancelEdit();            }            var $editRow = this._createEditRow(item);            this._editingRow = $row;            $row.hide();            $editRow.insertBefore($row);            $row.data(JSGRID_EDIT_ROW_DATA_KEY, $editRow);        },        _createEditRow: function(item) {            if($.isFunction(this.editRowRenderer)) {                return $(this.renderTemplate(this.editRowRenderer, this, { item: item, itemIndex: this._itemIndex(item) }));            }            var $result = $("<tr>").addClass(this.editRowClass);            this._eachField(function(field) {                var fieldValue = this._getItemFieldValue(item, field);                this._prepareCell("<td>", field, "editcss")                    .append(this.renderTemplate(field.editTemplate || "", field, { value: fieldValue, item: item }))                    .appendTo($result);            });            return $result;        },        updateItem: function(item, editedItem) {            if(arguments.length === 1) {                editedItem = item;            }            var $row = item ? this.rowByItem(item) : this._editingRow;            editedItem = editedItem || this._getValidatedEditedItem();            if(!editedItem)                return;            return this._updateRow($row, editedItem);        },        _getValidatedEditedItem: function() {            var item = this._getEditedItem();            return this._validateItem(item, this._getEditRow()) ? item : null;        },        _updateRow: function($updatingRow, editedItem) {            var updatingItem = $updatingRow.data(JSGRID_ROW_DATA_KEY),                updatingItemIndex = this._itemIndex(updatingItem),                updatedItem = $.extend(true, {}, updatingItem, editedItem);            var args = this._callEventHandler(this.onItemUpdating, {                row: $updatingRow,                item: updatedItem,                itemIndex: updatingItemIndex,                previousItem: updatingItem            });            return this._controllerCall("updateItem", updatedItem, args.cancel, function(loadedUpdatedItem) {                var previousItem = $.extend(true, {}, updatingItem);                updatedItem = loadedUpdatedItem || $.extend(true, updatingItem, editedItem);                var $updatedRow = this._finishUpdate($updatingRow, updatedItem, updatingItemIndex);                this._callEventHandler(this.onItemUpdated, {                    row: $updatedRow,                    item: updatedItem,                    itemIndex: updatingItemIndex,                    previousItem: previousItem                });            });        },        _rowIndex: function(row) {            return this._content.children().index($(row));        },        _itemIndex: function(item) {            return $.inArray(item, this.data);        },        _finishUpdate: function($updatingRow, updatedItem, updatedItemIndex) {            this.cancelEdit();            this.data[updatedItemIndex] = updatedItem;            var $updatedRow = this._createRow(updatedItem, updatedItemIndex);            $updatingRow.replaceWith($updatedRow);            return $updatedRow;        },        _getEditedItem: function() {            var result = {};            this._eachField(function(field) {                if(field.editing) {                    this._setItemFieldValue(result, field, field.editValue());                }            });            return result;        },        cancelEdit: function() {            if(!this._editingRow)                return;            this._getEditRow().remove();            this._editingRow.show();            this._editingRow = null;        },        _getEditRow: function() {            return this._editingRow && this._editingRow.data(JSGRID_EDIT_ROW_DATA_KEY);        },        deleteItem: function(item) {            var $row = this.rowByItem(item);            if(!$row.length)                return;            if(this.confirmDeleting && !window.confirm(getOrApply(this.deleteConfirm, this, $row.data(JSGRID_ROW_DATA_KEY))))                return;            return this._deleteRow($row);        },        _deleteRow: function($row) {            var deletingItem = $row.data(JSGRID_ROW_DATA_KEY),                deletingItemIndex = this._itemIndex(deletingItem);            var args = this._callEventHandler(this.onItemDeleting, {                row: $row,                item: deletingItem,                itemIndex: deletingItemIndex            });            return this._controllerCall("deleteItem", deletingItem, args.cancel, function() {                this._loadStrategy.finishDelete(deletingItem, deletingItemIndex);                this._callEventHandler(this.onItemDeleted, {                    row: $row,                    item: deletingItem,                    itemIndex: deletingItemIndex                });            });        }    };    $.fn.jsGrid = function(config) {        var args = $.makeArray(arguments),            methodArgs = args.slice(1),            result = this;        this.each(function() {            var $element = $(this),                instance = $element.data(JSGRID_DATA_KEY),                methodResult;            if(instance) {                if(typeof config === "string") {                    methodResult = instance[config].apply(instance, methodArgs);                    if(methodResult !== undefined && methodResult !== instance) {                        result = methodResult;                        return false;                    }                } else {                    instance._detachWindowResizeCallback();                    instance._init(config);                    instance.render();                }            } else {                new Grid($element, config);            }        });        return result;    };    var fields = {};    var setDefaults = function(config) {        var componentPrototype;        if($.isPlainObject(config)) {            componentPrototype = Grid.prototype;        } else {            componentPrototype = fields[config].prototype;            config = arguments[1] || {};        }        $.extend(componentPrototype, config);    };    var locales = {};    var locale = function(lang) {        var localeConfig = $.isPlainObject(lang) ? lang : locales[lang];        if(!localeConfig)            throw Error("unknown locale " + lang);        setLocale(jsGrid, localeConfig);    };    var setLocale = function(obj, localeConfig) {        $.each(localeConfig, function(field, value) {            if($.isPlainObject(value)) {                setLocale(obj[field] || obj[field[0].toUpperCase() + field.slice(1)], value);                return;            }            if(obj.hasOwnProperty(field)) {                obj[field] = value;            } else {                obj.prototype[field] = value;            }        });    };    window.jsGrid = {        Grid: Grid,        fields: fields,        setDefaults: setDefaults,        locales: locales,        locale: locale,        version: '1.5.3'    };}(window, jQuery));(function(jsGrid, $, undefined) {    function LoadIndicator(config) {        this._init(config);    }    LoadIndicator.prototype = {        container: "body",        message: "Loading...",        shading: true,        zIndex: 1000,        shaderClass: "jsgrid-load-shader",        loadPanelClass: "jsgrid-load-panel",        _init: function(config) {            $.extend(true, this, config);            this._initContainer();            this._initShader();            this._initLoadPanel();        },        _initContainer: function() {            this._container = $(this.container);        },        _initShader: function() {            if(!this.shading)                return;            this._shader = $("<div>").addClass(this.shaderClass)                .hide()                .css({                    position: "absolute",                    top: 0,                    right: 0,                    bottom: 0,                    left: 0,                    zIndex: this.zIndex                })                .appendTo(this._container);        },        _initLoadPanel: function() {            this._loadPanel = $("<div>").addClass(this.loadPanelClass)                .text(this.message)                .hide()                .css({                    position: "absolute",                    top: "50%",                    left: "50%",                    zIndex: this.zIndex                })                .appendTo(this._container);        },        show: function() {            var $loadPanel = this._loadPanel.show();            var actualWidth = $loadPanel.outerWidth();            var actualHeight = $loadPanel.outerHeight();            $loadPanel.css({                marginTop: -actualHeight / 2,                marginLeft: -actualWidth / 2            });            this._shader.show();        },        hide: function() {            this._loadPanel.hide();            this._shader.hide();        }    };    jsGrid.LoadIndicator = LoadIndicator;}(jsGrid, jQuery));(function(jsGrid, $, undefined) {    function DirectLoadingStrategy(grid) {        this._grid = grid;    }    DirectLoadingStrategy.prototype = {        firstDisplayIndex: function() {            var grid = this._grid;            return grid.option("paging") ? (grid.option("pageIndex") - 1) * grid.option("pageSize") : 0;        },        lastDisplayIndex: function() {            var grid = this._grid;            var itemsCount = grid.option("data").length;            return grid.option("paging")                ? Math.min(grid.option("pageIndex") * grid.option("pageSize"), itemsCount)                : itemsCount;        },        itemsCount: function() {            return this._grid.option("data").length;        },        openPage: function(index) {            this._grid.refresh();        },        loadParams: function() {            return {};        },        sort: function() {            this._grid._sortData();            this._grid.refresh();            return $.Deferred().resolve().promise();        },        reset: function() {            this._grid.refresh();            return $.Deferred().resolve().promise();        },        finishLoad: function(loadedData) {            this._grid.option("data", loadedData);        },        finishInsert: function(insertedItem) {            var grid = this._grid;            grid.option("data").push(insertedItem);            grid.refresh();        },        finishDelete: function(deletedItem, deletedItemIndex) {            var grid = this._grid;            grid.option("data").splice(deletedItemIndex, 1);            grid.reset();        }    };    function PageLoadingStrategy(grid) {        this._grid = grid;        this._itemsCount = 0;    }    PageLoadingStrategy.prototype = {        firstDisplayIndex: function() {            return 0;        },        lastDisplayIndex: function() {            return this._grid.option("data").length;        },        itemsCount: function() {            return this._itemsCount;        },        openPage: function(index) {            this._grid.loadData();        },        loadParams: function() {            var grid = this._grid;            return {                pageIndex: grid.option("pageIndex"),                pageSize: grid.option("pageSize")            };        },        reset: function() {            return this._grid.loadData();        },        sort: function() {            return this._grid.loadData();        },        finishLoad: function(loadedData) {            this._itemsCount = loadedData.itemsCount;            this._grid.option("data", loadedData.data);        },        finishInsert: function(insertedItem) {            this._grid.search();        },        finishDelete: function(deletedItem, deletedItemIndex) {            this._grid.search();        }    };    jsGrid.loadStrategies = {        DirectLoadingStrategy: DirectLoadingStrategy,        PageLoadingStrategy: PageLoadingStrategy    };}(jsGrid, jQuery));(function(jsGrid, $, undefined) {    var isDefined = function(val) {        return typeof(val) !== "undefined" && val !== null;    };    var sortStrategies = {        string: function(str1, str2) {            if(!isDefined(str1) && !isDefined(str2))                return 0;            if(!isDefined(str1))                return -1;            if(!isDefined(str2))                return 1;            return ("" + str1).localeCompare("" + str2);        },        number: function(n1, n2) {            return n1 - n2;        },        date: function(dt1, dt2) {            return dt1 - dt2;        },        numberAsString: function(n1, n2) {            return parseFloat(n1) - parseFloat(n2);        }    };    jsGrid.sortStrategies = sortStrategies;}(jsGrid, jQuery));(function(jsGrid, $, undefined) {    function Validation(config) {        this._init(config);    }    Validation.prototype = {        _init: function(config) {            $.extend(true, this, config);        },        validate: function(args) {            var errors = [];            $.each(this._normalizeRules(args.rules), function(_, rule) {                if(rule.validator(args.value, args.item, rule.param))                    return;                var errorMessage = $.isFunction(rule.message) ? rule.message(args.value, args.item) : rule.message;                errors.push(errorMessage);            });            return errors;        },        _normalizeRules: function(rules) {            if(!$.isArray(rules))                rules = [rules];            return $.map(rules, $.proxy(function(rule) {                return this._normalizeRule(rule);            }, this));        },        _normalizeRule: function(rule) {            if(typeof rule === "string")                rule = { validator: rule };            if($.isFunction(rule))                rule = { validator: rule };            if($.isPlainObject(rule))                rule = $.extend({}, rule);            else                throw Error("wrong validation config specified");            if($.isFunction(rule.validator))                return rule;            return this._applyNamedValidator(rule, rule.validator);        },        _applyNamedValidator: function(rule, validatorName) {            delete rule.validator;            var validator = validators[validatorName];            if(!validator)                throw Error("unknown validator \"" + validatorName + "\"");            if($.isFunction(validator)) {                validator = { validator: validator };            }            return $.extend({}, validator, rule);        }    };    jsGrid.Validation = Validation;    var validators = {        required: {            message: "Field is required",            validator: function(value) {                return value !== undefined && value !== null && value !== "";            }        },        rangeLength: {            message: "Field value length is out of the defined range",            validator: function(value, _, param) {                return value.length >= param[0] && value.length <= param[1];            }        },        minLength: {            message: "Field value is too short",            validator: function(value, _, param) {                return value.length >= param;            }        },        maxLength: {            message: "Field value is too long",            validator: function(value, _, param) {                return value.length <= param;            }        },        pattern: {            message: "Field value is not matching the defined pattern",            validator: function(value, _, param) {                if(typeof param === "string") {                    param = new RegExp("^(?:" + param + ")$");                }                return param.test(value);            }        },        range: {            message: "Field value is out of the defined range",            validator: function(value, _, param) {                return value >= param[0] && value <= param[1];            }        },        min: {            message: "Field value is too small",            validator: function(value, _, param) {                return value >= param;            }        },        max: {            message: "Field value is too large",            validator: function(value, _, param) {                return value <= param;            }        }    };    jsGrid.validators = validators;}(jsGrid, jQuery));(function(jsGrid, $, undefined) {    function Field(config) {        $.extend(true, this, config);        this.sortingFunc = this._getSortingFunc();    }    Field.prototype = {        name: "",        title: null,        css: "",        align: "",        width: 100,        visible: true,        filtering: true,        inserting: true,        editing: true,        sorting: true,        sorter: "string", // name of SortStrategy or function to compare elements        headerTemplate: function() {            return (this.title === undefined || this.title === null) ? this.name : this.title;        },        itemTemplate: function(value, item) {            return value;        },        filterTemplate: function() {            return "";        },        insertTemplate: function() {            return "";        },        editTemplate: function(value, item) {            this._value = value;            return this.itemTemplate(value, item);        },        filterValue: function() {            return "";        },        insertValue: function() {            return "";        },        editValue: function() {            return this._value;        },        _getSortingFunc: function() {            var sorter = this.sorter;            if($.isFunction(sorter)) {                return sorter;            }            if(typeof sorter === "string") {                return jsGrid.sortStrategies[sorter];            }            throw Error("wrong sorter for the field \"" + this.name + "\"!");        }    };    jsGrid.Field = Field;}(jsGrid, jQuery));(function(jsGrid, $, undefined) {    var Field = jsGrid.Field;    function TextField(config) {        Field.call(this, config);    }    TextField.prototype = new Field({        autosearch: true,		readOnly: false,        filterTemplate: function() {            if(!this.filtering)                return "";            var grid = this._grid,                $result = this.filterControl = this._createTextBox();            if(this.autosearch) {                $result.on("keypress", function(e) {                    if(e.which === 13) {                        grid.search();                        e.preventDefault();                    }                });            }            return $result;        },        insertTemplate: function() {            if(!this.inserting)                return "";            return this.insertControl = this._createTextBox();        },        editTemplate: function(value) {            if(!this.editing)                return this.itemTemplate.apply(this, arguments);            var $result = this.editControl = this._createTextBox();            $result.val(value);            return $result;        },        filterValue: function() {            return this.filterControl.val();        },        insertValue: function() {            return this.insertControl.val();        },        editValue: function() {            return this.editControl.val();        },        _createTextBox: function() {            return $("<input>").attr("type", "text")                .prop("readonly", !!this.readOnly);        }    });    jsGrid.fields.text = jsGrid.TextField = TextField;}(jsGrid, jQuery));(function(jsGrid, $, undefined) {    var TextField = jsGrid.TextField;    function NumberField(config) {        TextField.call(this, config);    }    NumberField.prototype = new TextField({        sorter: "number",        align: "right",		readOnly: false,        filterValue: function() {            return this.filterControl.val()                ? parseInt(this.filterControl.val() || 0, 10)                : undefined;        },        insertValue: function() {            return this.insertControl.val()                ? parseInt(this.insertControl.val() || 0, 10)                : undefined;        },        editValue: function() {            return this.editControl.val()                ? parseInt(this.editControl.val() || 0, 10)                : undefined;        },        _createTextBox: function() {			return $("<input>").attr("type", "number")                .prop("readonly", !!this.readOnly);        }    });    jsGrid.fields.number = jsGrid.NumberField = NumberField;}(jsGrid, jQuery));(function(jsGrid, $, undefined) {    var TextField = jsGrid.TextField;    function TextAreaField(config) {        TextField.call(this, config);    }    TextAreaField.prototype = new TextField({        insertTemplate: function() {            if(!this.inserting)                return "";            return this.insertControl = this._createTextArea();        },        editTemplate: function(value) {            if(!this.editing)                return this.itemTemplate.apply(this, arguments);            var $result = this.editControl = this._createTextArea();            $result.val(value);            return $result;        },        _createTextArea: function() {            return $("<textarea>").prop("readonly", !!this.readOnly);        }    });    jsGrid.fields.textarea = jsGrid.TextAreaField = TextAreaField;}(jsGrid, jQuery));(function(jsGrid, $, undefined) {    var NumberField = jsGrid.NumberField;    var numberValueType = "number";    var stringValueType = "string";    function SelectField(config) {        this.items = [];        this.selectedIndex = -1;        this.valueField = "";        this.textField = "";        if(config.valueField && config.items.length) {            var firstItemValue = config.items[0][config.valueField];            this.valueType = (typeof firstItemValue) === numberValueType ? numberValueType : stringValueType;        }        this.sorter = this.valueType;        NumberField.call(this, config);    }    SelectField.prototype = new NumberField({        align: "center",        valueType: numberValueType,        itemTemplate: function(value) {            var items = this.items,                valueField = this.valueField,                textField = this.textField,                resultItem;            if(valueField) {                resultItem = $.grep(items, function(item, index) {                    return item[valueField] === value;                })[0] || {};            }            else {                resultItem = items[value];            }            var result = (textField ? resultItem[textField] : resultItem);            return (result === undefined || result === null) ? "" : result;        },        filterTemplate: function() {            if(!this.filtering)                return "";            var grid = this._grid,                $result = this.filterControl = this._createSelect();            if(this.autosearch) {                $result.on("change", function(e) {                    grid.search();                });            }            return $result;        },        insertTemplate: function() {            if(!this.inserting)                return "";            return this.insertControl = this._createSelect();        },        editTemplate: function(value) {            if(!this.editing)                return this.itemTemplate.apply(this, arguments);            var $result = this.editControl = this._createSelect();            (value !== undefined) && $result.val(value);            return $result;        },        filterValue: function() {            var val = this.filterControl.val();            return this.valueType === numberValueType ? parseInt(val || 0, 10) : val;        },        insertValue: function() {            var val = this.insertControl.val();            return this.valueType === numberValueType ? parseInt(val || 0, 10) : val;        },        editValue: function() {            var val = this.editControl.val();            return this.valueType === numberValueType ? parseInt(val || 0, 10) : val;        },        _createSelect: function() {            var $result = $("<select>"),                valueField = this.valueField,                textField = this.textField,                selectedIndex = this.selectedIndex;            $.each(this.items, function(index, item) {                var value = valueField ? item[valueField] : index,                    text = textField ? item[textField] : item;                var $option = $("<option>")                    .attr("value", value)                    .text(text)                    .appendTo($result);                $option.prop("selected", (selectedIndex === index));            });            $result.prop("disabled", !!this.readOnly);            return $result;        }    });    jsGrid.fields.select = jsGrid.SelectField = SelectField;}(jsGrid, jQuery));(function(jsGrid, $, undefined) {    var Field = jsGrid.Field;    function CheckboxField(config) {        Field.call(this, config);    }    CheckboxField.prototype = new Field({        sorter: "number",        align: "center",        autosearch: true,        itemTemplate: function(value) {            return this._createCheckbox().prop({                checked: value,                disabled: true            });        },        filterTemplate: function() {            if(!this.filtering)                return "";            var grid = this._grid,                $result = this.filterControl = this._createCheckbox();            $result.prop({                readOnly: true,                indeterminate: true            });            $result.on("click", function() {                var $cb = $(this);                if($cb.prop("readOnly")) {                    $cb.prop({                        checked: false,                        readOnly: false                    });                }                else if(!$cb.prop("checked")) {                    $cb.prop({                        readOnly: true,                        indeterminate: true                    });                }            });            if(this.autosearch) {                $result.on("click", function() {                    grid.search();                });            }            return $result;        },        insertTemplate: function() {            if(!this.inserting)                return "";            return this.insertControl = this._createCheckbox();        },        editTemplate: function(value) {            if(!this.editing)                return this.itemTemplate.apply(this, arguments);            var $result = this.editControl = this._createCheckbox();            $result.prop("checked", value);            return $result;        },        filterValue: function() {            return this.filterControl.get(0).indeterminate                ? undefined                : this.filterControl.is(":checked");        },        insertValue: function() {            return this.insertControl.is(":checked");        },        editValue: function() {            return this.editControl.is(":checked");        },        _createCheckbox: function() {            return $("<input>").attr("type", "checkbox");        }    });    jsGrid.fields.checkbox = jsGrid.CheckboxField = CheckboxField;}(jsGrid, jQuery));(function(jsGrid, $, undefined) {    var Field = jsGrid.Field;    function ControlField(config) {        Field.call(this, config);        this._configInitialized = false;    }    ControlField.prototype = new Field({        css: "jsgrid-control-field",        align: "center",        width: 50,        filtering: false,        inserting: false,        editing: false,        sorting: false,        buttonClass: "jsgrid-button",        modeButtonClass: "jsgrid-mode-button",        modeOnButtonClass: "jsgrid-mode-on-button",        searchModeButtonClass: "jsgrid-search-mode-button",        insertModeButtonClass: "jsgrid-insert-mode-button",        editButtonClass: "jsgrid-edit-button",        deleteButtonClass: "jsgrid-delete-button",        searchButtonClass: "jsgrid-search-button",        clearFilterButtonClass: "jsgrid-clear-filter-button",        insertButtonClass: "jsgrid-insert-button",        updateButtonClass: "jsgrid-update-button",        cancelEditButtonClass: "jsgrid-cancel-edit-button",        searchModeButtonTooltip: "Switch to searching",        insertModeButtonTooltip: "Switch to inserting",        editButtonTooltip: "Edit",        deleteButtonTooltip: "Delete",        searchButtonTooltip: "Search",        clearFilterButtonTooltip: "Clear filter",        insertButtonTooltip: "Insert",        updateButtonTooltip: "Update",        cancelEditButtonTooltip: "Cancel edit",        editButton: true,        deleteButton: true,        clearFilterButton: true,        modeSwitchButton: true,        _initConfig: function() {            this._hasFiltering = this._grid.filtering;            this._hasInserting = this._grid.inserting;            if(this._hasInserting && this.modeSwitchButton) {                this._grid.inserting = false;            }            this._configInitialized = true;        },        headerTemplate: function() {            if(!this._configInitialized) {                this._initConfig();            }            var hasFiltering = this._hasFiltering;            var hasInserting = this._hasInserting;            if(!this.modeSwitchButton || (!hasFiltering && !hasInserting))                return "";            if(hasFiltering && !hasInserting)                return this._createFilterSwitchButton();            if(hasInserting && !hasFiltering)                return this._createInsertSwitchButton();            return this._createModeSwitchButton();        },        itemTemplate: function(value, item) {            var $result = $([]);            if(this.editButton) {                $result = $result.add(this._createEditButton(item));            }            if(this.deleteButton) {                $result = $result.add(this._createDeleteButton(item));            }            return $result;        },        filterTemplate: function() {            var $result = this._createSearchButton();            return this.clearFilterButton ? $result.add(this._createClearFilterButton()) : $result;        },        insertTemplate: function() {            return this._createInsertButton();        },        editTemplate: function() {            return this._createUpdateButton().add(this._createCancelEditButton());        },        _createFilterSwitchButton: function() {            return this._createOnOffSwitchButton("filtering", this.searchModeButtonClass, true);        },        _createInsertSwitchButton: function() {            return this._createOnOffSwitchButton("inserting", this.insertModeButtonClass, false);        },        _createOnOffSwitchButton: function(option, cssClass, isOnInitially) {            var isOn = isOnInitially;            var updateButtonState = $.proxy(function() {                $button.toggleClass(this.modeOnButtonClass, isOn);            }, this);            var $button = this._createGridButton(this.modeButtonClass + " " + cssClass, "", function(grid) {                isOn = !isOn;                grid.option(option, isOn);                updateButtonState();            });            updateButtonState();            return $button;        },        _createModeSwitchButton: function() {            var isInserting = false;            var updateButtonState = $.proxy(function() {                $button.attr("title", isInserting ? this.searchModeButtonTooltip : this.insertModeButtonTooltip)                    .toggleClass(this.insertModeButtonClass, !isInserting)                    .toggleClass(this.searchModeButtonClass, isInserting);            }, this);            var $button = this._createGridButton(this.modeButtonClass, "", function(grid) {                isInserting = !isInserting;                grid.option("inserting", isInserting);                grid.option("filtering", !isInserting);                updateButtonState();            });            updateButtonState();            return $button;        },        _createEditButton: function(item) {            return this._createGridButton(this.editButtonClass, this.editButtonTooltip, function(grid, e) {                grid.editItem(item);                e.stopPropagation();            });        },        _createDeleteButton: function(item) {            return this._createGridButton(this.deleteButtonClass, this.deleteButtonTooltip, function(grid, e) {                grid.deleteItem(item);                e.stopPropagation();            });        },        _createSearchButton: function() {            return this._createGridButton(this.searchButtonClass, this.searchButtonTooltip, function(grid) {                grid.search();            });        },        _createClearFilterButton: function() {            return this._createGridButton(this.clearFilterButtonClass, this.clearFilterButtonTooltip, function(grid) {                grid.clearFilter();            });        },        _createInsertButton: function() {            return this._createGridButton(this.insertButtonClass, this.insertButtonTooltip, function(grid) {                grid.insertItem().done(function() {                    grid.clearInsert();                });            });        },        _createUpdateButton: function() {            return this._createGridButton(this.updateButtonClass, this.updateButtonTooltip, function(grid, e) {                grid.updateItem();                e.stopPropagation();            });        },        _createCancelEditButton: function() {            return this._createGridButton(this.cancelEditButtonClass, this.cancelEditButtonTooltip, function(grid, e) {                grid.cancelEdit();                e.stopPropagation();            });        },        _createGridButton: function(cls, tooltip, clickHandler) {            var grid = this._grid;            return $("<input>").addClass(this.buttonClass)                .addClass(cls)                .attr({                    type: "button",                    title: tooltip                })                .on("click", function(e) {                    clickHandler(grid, e);                });        },        editValue: function() {            return "";        }    });    jsGrid.fields.control = jsGrid.ControlField = ControlField;}(jsGrid, jQuery));
 |