/***
* Grid.Mvc
* Examples and documentation at: http://gridmvc.codeplex.com
* Version: 3.0.0
* Requires: window.jQuery v1.3+
* LGPL license: http://gridmvc.codeplex.com/license
*/
window.pageGrids = window.pageGrids || {};
$.fn.extend({
    gridmvc: function () {
        var aObj = [];
        $(this).each(function () {
            if (!$(this).data("gridmvc")) {
                var options = { lang: $(this).attr("data-lang"), selectable: $(this).attr("data-selectable") == "true", multiplefilters: $(this).attr("data-multiplefilters") == "true" };
                var grid = new GridMvc(this, options);
                var name = $(this).attr("data-gridname");
                if (name.length > 0)
                    window.pageGrids[$(this).attr("data-gridname")] = grid;

                aObj.push(grid);
                $(this).data("gridmvc", grid);
            } else {
                aObj.push($(this).data("gridmvc"));
            }
        });
        if (aObj.length == 1)
            return aObj[0];
        return aObj;
    }
});

GridMvc = (function ($) {
    function gridMvc(container, options) {
        this.jqContainer = $(container);
        options = options || {};
        this.options = $.extend({}, this.defaults(), options);
        this.init();
    }

    gridMvc.prototype.init = function () {
        //load current lang options:
        this.lang = GridMvc.lang[this.options.lang];
        if (typeof (this.lang) == 'undefined')
            this.lang = GridMvc.lang.en;
        this.events = [];
        if (this.options.selectable)
            this.initGridRowsEvents();
        //Register default filter widgets:
        this.filterWidgets = [];
        this.addFilterWidget(new TextFilterWidget());
        this.addFilterWidget(new NumberFilterWidget());
        this.addFilterWidget(new DateTimeFilterWidget());
        this.addFilterWidget(new BooleanFilterWidget());

        this.openedMenuBtn = null;
        this.initFilters();
    };
    /***
    * Handle Grid row events
    */
    gridMvc.prototype.initGridRowsEvents = function () {
        var $this = this;
        this.jqContainer.on("click", ".grid-row", function () {
            $this.rowClicked.call(this, $this);
        });
    };
    /***
    * Trigger on Grid row click
    */
    gridMvc.prototype.rowClicked = function ($context) {
        if (!$context.options.selectable)
            return;
        var row = $(this).closest(".grid-row");
        if (row.length <= 0)
            return;
        var gridRow = {};
        row.find(".grid-cell").each(function () {
            var columnName = $(this).attr("data-name");
            if (columnName.length > 0)
                gridRow[columnName] = $(this).text();
        });
        var evt = $.Event("RowClicked");
        $context.notifyOnRowSelect(gridRow, evt);
        if (!evt.isDefaultPrevented())
            $context.markRowSelected(row);
    };
    /***
    * Mark Grid row as selected
    */
    gridMvc.prototype.markRowSelected = function (row) {
        this.jqContainer.find(".grid-row.grid-row-selected").removeClass("grid-row-selected");
        row.addClass("grid-row-selected");
    };
    /***
    * Default Grid.Mvc options
    */
    gridMvc.prototype.defaults = function () {
        return {
            selectable: true,
            multiplefilters: false,
            lang: 'en'
        };
    };
    /***
    * ============= EVENTS =============
    * Methods that provides functionality for grid events
    */
    gridMvc.prototype.onRowSelect = function (func) {
        this.events.push({ name: "onRowSelect", callback: func });
    };

    gridMvc.prototype.notifyOnRowSelect = function (row, e) {
        e.row = row;
        this.notifyEvent("onRowSelect", e);
    };

    gridMvc.prototype.notifyEvent = function (eventName, e) {
        for (var i = 0; i < this.events.length; i++) {
            if (this.events[i].name == eventName)
                if (!this.events[i].callback(e)) break;
        }
    };
    /***
    * ============= FILTERS =============
    * Methods that provides functionality for filtering
    */
    /***
    * Method search all filter buttons and register 'onclick' event handlers:
    */
    gridMvc.prototype.initFilters = function () {
        var filterHtml = this.filterMenuHtml();
        var self = this;
        this.jqContainer.find(".grid-filter").each(function () {
            $(this).click(function () {
                return self.openFilterPopup.call(this, self, filterHtml);
            });
        });
    };
    /***
    * Shows filter popup window and render filter widget
    */
    gridMvc.prototype.openFilterPopup = function (self, html) {
        //retrive all column filter parameters from html attrs:
        var columnType = $(this).attr("data-type") || "";
        //determine widget
        var widget = self.getFilterWidgetForType(columnType);
        //if widget for specified column type not found - do nothing
        if (widget == null)
            return false;

        //if widget allready rendered - just open popup menu:
        if (this.hasAttribute("data-rendered")) {
            var or = self.openMenuOnClick.call(this, self);
            self.setupPopupInitialPosition($(this));
            if (!or && typeof (widget.onShow) != 'undefined')
                widget.onShow();
            return or;
        }

        var columnName = $(this).attr("data-name") || "";
        var filterData = $(this).attr("data-filterdata") || "";
        var widgetData = $(this).attr("data-widgetdata") || "{}";
        var filterDataObj = self.parseFilterValues(filterData) || {};
        var filterUrl = $(this).attr("data-url") || "";

        //mark filter as rendered
        $(this).attr("data-rendered", "1");
        //append base popup layout:
        $(this).append(html);

        //determine widget container:
        var widgetContainer = $(this).find(".grid-popup-widget");
        //onRender target widget
        if (typeof (widget.onRender) != 'undefined')
            widget.onRender(widgetContainer, self.lang, columnType, filterDataObj, function (values) {
                self.closeOpenedPopups();
                self.applyFilterValues(filterUrl, columnName, values, false);
            }, $.parseJSON(widgetData));
        //adding 'clear filter' button if needed:
        if ($(this).find(".grid-filter-btn").hasClass("filtered") && widget.showClearFilterButton()) {
            var inner = $(this).find(".grid-popup-additional");
            inner.append(self.getClearFilterButton(filterUrl));
            inner.find(".grid-filter-clear").click(function () {
                self.applyFilterValues(filterUrl, columnName, "", true);
            });
        }
        var openResult = self.openMenuOnClick.call(this, self);
        if (typeof (widget.onShow) != 'undefined')
            widget.onShow();
        self.setupPopupInitialPosition($(this));
        return openResult;
    };

    gridMvc.prototype.setupPopupInitialPosition = function (popup) {
        var drop = popup.find(".grid-dropdown");
        function getInfo() {
            var arrow = popup.find(".grid-dropdown-arrow");
            return { arrow: arrow, currentDropLeft: parseInt(drop.css("left")), currentArrowLeft: parseInt(arrow.css("left")) };
        }
        var dropLeft = drop.offset().left;
        if (dropLeft < 0) {
            var info = getInfo();
            info.arrow.css({ left: (info.currentArrowLeft + dropLeft - 10) + "px" });
            drop.css({ left: (info.currentDropLeft - dropLeft + 10) + "px" });
            return;
        }
        var dropWidth = drop.width();
        var offsetRight = $(window).width() - (dropLeft + dropWidth);
        if (offsetRight < 0) {
            var info = getInfo();
            info.arrow.css({ left: (info.currentArrowLeft - offsetRight + 10) + "px" });
            drop.css({ left: (info.currentDropLeft + offsetRight - 10) + "px" });
        }
    };
    /***
    * Returns layout of filter popup menu
    */
    gridMvc.prototype.filterMenuHtml = function () {
        return '<div class="dropdown dropdown-menu grid-dropdown" style="display: none;">\
                    <div class="grid-dropdown-arrow"></div>\
                    <div class="grid-dropdown-inner">\
                            <div class="grid-popup-widget"></div>\
                            <div class="grid-popup-additional"></div>\
                    </div>\
                </div>';
    };
    /***
    * Returns layout of 'clear filter' button
    */
    gridMvc.prototype.getClearFilterButton = function () {
        return '<ul class="menu-list">\
                    <li><a class="grid-filter-clear" href="javascript:void(0);">' + this.lang.clearFilterLabel + '</a></li>\
                </ul>';
    };
    /***
    * Register filter widget in widget collection
    */
    gridMvc.prototype.addFilterWidget = function (widget) {
        this.filterWidgets.push(widget);
    };
    /***
    * Parse filter settings from data attribute
    */
    gridMvc.prototype.parseFilterValues = function (filterData) {
        var opt = $.parseJSON(filterData);
        var filters = [];
        for (var i = 0; i < opt.length; i++) {
            filters.push({ filterValue: this.urldecode(opt[i].FilterValue), filterType: opt[i].FilterType, columnName: opt[i].ColumnName });
        }
        return filters;
    };

    gridMvc.prototype.urldecode = function (str) {
        return decodeURIComponent((str + '').replace(/\+/g, '%20'));
    };

    /***
    * Return registred widget for specific column type name
    */
    gridMvc.prototype.getFilterWidgetForType = function (typeName) {
        for (var i = 0; i < this.filterWidgets.length; i++) {
            if ($.inArray(typeName, this.filterWidgets[i].getAssociatedTypes()) >= 0)
                return this.filterWidgets[i];
        }
        return null;
    };
    /***
    * Replace existed filter widget
    */
    gridMvc.prototype.replaceFilterWidget = function (typeNameToReplace, widget) {
        for (var i = 0; i < this.filterWidgets.length; i++) {
            if ($.inArray(typeNameToReplace, this.filterWidgets[i].getAssociatedTypes()) >= 0) {
                this.filterWidgets.splice(i, 1);
                this.addFilterWidget(widget);
                return true;
            }
        }
        return false;
    };
    /***
    * Applies selected filter value by redirecting to another url:
    */
    gridMvc.prototype.applyFilterValues = function (initialUrl, columnName, values, skip) {
        var filters = this.jqContainer.find(".grid-filter");
        if (initialUrl.length > 0)
            initialUrl += "&";

        var url = "";
        if (!skip) {
            url += this.getFilterQueryData(columnName, values);
        }

        if (this.options.multiplefilters) { //multiple filters enabled
            for (var i = 0; i < filters.length; i++) {
                if ($(filters[i]).attr("data-name") != columnName) {
                    var filterData = this.parseFilterValues($(filters[i]).attr("data-filterdata"));
                    if (filterData.length == 0) continue;
                    if (url.length > 0) url += "&";
                    url += this.getFilterQueryData($(filters[i]).attr("data-name"), filterData);
                } else {
                    continue;
                }
            }
        }

        window.location.search = initialUrl + url;
    };
    gridMvc.prototype.getFilterQueryData = function (columnName, values) {
        var url = "";
        for (var i = 0; i < values.length; i++) {
            url += "grid-filter=" + encodeURIComponent(columnName) + "__" + values[i].filterType + "__" + encodeURIComponent(values[i].filterValue);
            if (i != values.length - 1)
                url += "&";
        }
        return url;
    };
    /***
    * ============= POPUP MENU =============
    * Methods that provides base functionality for popup menus
    */
    gridMvc.prototype.openMenuOnClick = function (self) {
        if ($(this).hasClass("clicked")) return true;
        self.closeOpenedPopups();
        $(this).addClass("clicked");
        var popup = $(this).find(".dropdown-menu");
        if (popup.length == 0) return true;
        popup.show();
        popup.addClass("opened");
        self.openedMenuBtn = $(this);
        $(document).bind("click.gridmvc", function (e) {
            self.documentCallback(e, self);
        });
        return false;
    };

    gridMvc.prototype.documentCallback = function (e, $context) {
        e = e || event;
        var target = e.target || e.srcElement;
        var box = $(".dropdown-menu.opened").get(0);
        var html = $("html").get(0);
        if (typeof box != "undefined") {
            do {
                if (box == target) {
                    // Click occured inside the box, do nothing.
                    return;
                }
                if (html == target) {
                    box.style.display = "none";
                    $(box).removeClass("opened");
                }
                target = target.parentNode;
            } while (target); // Click was outside the box, hide it.

        }
        if ($context.openedMenuBtn != null)
            $context.openedMenuBtn.removeClass("clicked");
        $(document).unbind("click.gridmvc");
    };

    gridMvc.prototype.closeOpenedPopups = function () {
        var openedPopup = $(".dropdown-menu.opened");
        openedPopup.hide();
        openedPopup.removeClass("opened");
        if (this.openedMenuBtn != null)
            this.openedMenuBtn.removeClass("clicked");
    };

    /* Grid.Mvc clients functions */

    gridMvc.prototype.selectable = function (enable) {
        this.options.selectable = enable;
    };

    return gridMvc;
})(window.jQuery);

/***
* ============= LOCALIZATION =============
* You can localize Grid.Mvc by creating localization files and include it on the page after this script file
* This script file provides localization only for english language.
* For more documentation see: http://gridmvc.codeplex.com/documentation
*/
if (typeof (GridMvc.lang) == 'undefined')
    GridMvc.lang = {};
GridMvc.lang.en = {
    filterTypeLabel: "Type: ",
    filterValueLabel: "Value:",
    applyFilterButtonText: "Apply",
    filterSelectTypes: {
        Equals: "Equals",
        StartsWith: "StartsWith",
        Contains: "Contains",
        EndsWith: "EndsWith",
        GreaterThan: "Greater than",
        LessThan: "Less than"
    },
    code: 'en',
    boolTrueLabel: "Yes",
    boolFalseLabel: "No",
    clearFilterLabel: "Clear filter"
};
/***
* ============= FILTER WIDGETS =============
* Filter widget allows onRender custom filter user interface for different columns. 
* For example if your added column is of type "DateTime" - widget can onRender calendar for picking filter value.
* This script provider base widget for default .Net types: System.String, System.Int32, System.Decimal etc.
* If you want to provide custom filter functionality - you can assign your own widget type for column and write widget for this types.
* For more documentation see: http://gridmvc.codeplex.com/documentation
*/

/***
* TextFilterWidget - Provides filter interface for text columns (of type "System.String")
* This widget onRenders filter interface with conditions, which specific for text types: contains, starts with etc.
*/
TextFilterWidget = (function ($) {
    function textFilterWidget() { }
    /***
    * This method must return type of columns that must be associated with current widget
    * If you not specify your own type name for column (see 'SetFilterWidgetType' method), GridMvc setup column type name from .Net type ("System.DateTime etc.)
    */
    textFilterWidget.prototype.getAssociatedTypes = function () { return ["System.String"]; };
    /***
    * This method invokes when filter widget was shown on the page
    */
    textFilterWidget.prototype.onShow = function () {
        var textBox = this.container.find(".grid-filter-input");
        if (textBox.length <= 0) return;
        textBox.focus();
    };
    /***
    * This method specify whether onRender 'Clear filter' button for this widget.
    */
    textFilterWidget.prototype.showClearFilterButton = function () { return true; };
    /***
    * This method will invoke when user first clicked on filter button.
    * container - html element, which must contain widget layout;
    * lang - current language settings;
    * typeName - current column type (if widget assign to multipile types, see: getAssociatedTypes);
    * filterType - current filter type (like equals, starts with etc);
    * filterValue - current filter value;
    * cb - callback function that must invoked when user want to filter this column. Widget must pass filter type and filter value.
    */
    textFilterWidget.prototype.onRender = function (container, lang, typeName, values, cb) {
        this.cb = cb;
        this.container = container;
        this.lang = lang;
        this.value = values.length > 0 ? values[0] : { filterType: 1, filterValue: "" };//support only one filter value
        this.renderWidget();
        this.registerEvents();
    };
    /***
    * Internal method that build widget layout and append it to the widget container
    */
    textFilterWidget.prototype.renderWidget = function () {
        var html = '<div class="form-group">\
                        <label>' + this.lang.filterTypeLabel + '</label>\
                        <select class="grid-filter-type form-control">\
                            <option value="1" ' + (this.value.filterType == "1" ? "selected=\"selected\"" : "") + '>' + this.lang.filterSelectTypes.Equals + '</option>\
                            <option value="2" ' + (this.value.filterType == "2" ? "selected=\"selected\"" : "") + '>' + this.lang.filterSelectTypes.Contains + '</option>\
                            <option value="3" ' + (this.value.filterType == "3" ? "selected=\"selected\"" : "") + '>' + this.lang.filterSelectTypes.StartsWith + '</option>\
                            <option value="4" ' + (this.value.filterType == "4" ? "selected=\"selected\"" : "") + '>' + this.lang.filterSelectTypes.EndsWith + '</option>\
                        </select>\
                    </div>\
                    <div class="form-group">\
                        <label>' + this.lang.filterValueLabel + '</label>\
                        <input type="text" class="grid-filter-input form-control" value="' + this.value.filterValue + '" />\
                    </div>\
                    <div class="grid-filter-buttons">\
                        <button type="button" class="btn btn-primary grid-apply" >' + this.lang.applyFilterButtonText + '</button>\
                    </div>';
        this.container.append(html);
    };
    /***
    * Internal method that register event handlers for 'apply' button.
    */
    textFilterWidget.prototype.registerEvents = function () {
        //get apply button from:
        var applyBtn = this.container.find(".grid-apply");
        //save current context:
        var $context = this;
        //register onclick event handler
        applyBtn.click(function () {
            //get selected filter type:
            var type = $context.container.find(".grid-filter-type").val();
            //get filter value:
            var value = $context.container.find(".grid-filter-input").val();
            //invoke callback with selected filter values:
            var filterValues = [{ filterType: type, filterValue: value }];
            $context.cb(filterValues);
        });
        //register onEnter event for filter text box:
        this.container.find(".grid-filter-input").keyup(function (event) {
            if (event.keyCode == 13) { applyBtn.click(); }
            if (event.keyCode == 27) { GridMvc.closeOpenedPopups(); }
        });
    };

    return textFilterWidget;
})(window.jQuery);

/***
* NumberFilterWidget - Provides filter interface for number columns
* This widget onRenders filter interface with conditions, which specific for number types: great than, less that etc.
* Also validates user's input for digits
*/
NumberFilterWidget = (function ($) {

    function numberFilterWidget() { }

    numberFilterWidget.prototype.showClearFilterButton = function () { return true; };

    numberFilterWidget.prototype.getAssociatedTypes = function () {
        return ["System.Int32", "System.Double", "System.Decimal", "System.Byte", "System.Single", "System.Float", "System.Int64"];
    };

    numberFilterWidget.prototype.onShow = function () {
        var textBox = this.container.find(".grid-filter-input");
        if (textBox.length <= 0) return;
        textBox.focus();
    };

    numberFilterWidget.prototype.onRender = function (container, lang, typeName, values, cb) {
        this.cb = cb;
        this.container = container;
        this.lang = lang;
        this.typeName = typeName;
        this.value = values.length > 0 ? values[0] : { filterType: 1, filterValue: "" };//support only one filter value
        this.renderWidget();
        this.registerEvents();
    };

    numberFilterWidget.prototype.renderWidget = function () {
        var html = '<div class="form-group">\
                        <label>' + this.lang.filterTypeLabel + '</label>\
                        <select class="grid-filter-type form-control">\
                            <option value="1" ' + (this.value.filterType == "1" ? "selected=\"selected\"" : "") + '>' + this.lang.filterSelectTypes.Equals + '</option>\
                            <option value="5" ' + (this.value.filterType == "5" ? "selected=\"selected\"" : "") + '>' + this.lang.filterSelectTypes.GreaterThan + '</option>\
                            <option value="6" ' + (this.value.filterType == "6" ? "selected=\"selected\"" : "") + '>' + this.lang.filterSelectTypes.LessThan + '</option>\
                        </select>\
                    </div>\
                    <div class="form-group">\
                        <label>' + this.lang.filterValueLabel + '</label>\
                        <input type="text" class="grid-filter-input form-control" value="' + this.value.filterValue + '" />\
                    </div>\
                    <div class="grid-filter-buttons">\
                        <button type="button" class="btn btn-primary grid-apply">' + this.lang.applyFilterButtonText + '</button>\
                    </div>';
        this.container.append(html);
    };

    numberFilterWidget.prototype.registerEvents = function () {
        var $context = this;
        var applyBtn = this.container.find(".grid-apply");
        applyBtn.click(function () {
            var type = $context.container.find(".grid-filter-type").val();
            var value = $context.container.find(".grid-filter-input").val();
            var filters = [{ filterType: type, filterValue: value }];
            $context.cb(filters);
        });
        var txt = this.container.find(".grid-filter-input");
        txt.keyup(function (event) {
            if (event.keyCode == 13) { applyBtn.click(); }
            if (event.keyCode == 27) { GridMvc.closeOpenedPopups(); }
        })
            .keypress(function (event) { return $context.validateInput.call($context, event); });
        if (this.typeName == "System.Byte")
            txt.attr("maxlength", "3");
    };

    numberFilterWidget.prototype.validateInput = function (evt) {
        var $event = evt || window.event;
        var key = $event.keyCode || $event.which;
        key = String.fromCharCode(key);
        var regex;
        switch (this.typeName) {
            case "System.Byte":
            case "System.Int32":
            case "System.Int64":
                regex = /[0-9]/;
                break;
            default:
                regex = /[0-9]|\.|\,/;
        }
        if (!regex.test(key)) {
            $event.returnValue = false;
            if ($event.preventDefault) $event.preventDefault();
        }
    };

    return numberFilterWidget;
})(window.jQuery);

/***
* DateTimeFilterWidget - Provides filter interface for date columns (of type "System.DateTime").
* If datepicker script included, this widget will render calendar for pick filter values
* In other case he onRender textbox field for specifing date value (more info at http://window.jQueryui.com/)
*/
DateTimeFilterWidget = (function ($) {

    function dateTimeFilterWidget() { }

    dateTimeFilterWidget.prototype.getAssociatedTypes = function () { return ["System.DateTime"]; };

    dateTimeFilterWidget.prototype.showClearFilterButton = function () { return true; };

    dateTimeFilterWidget.prototype.onRender = function (container, lang, typeName, values, applycb, data) {
        this.datePickerIncluded = typeof ($.fn.datepicker) != 'undefined';
        this.cb = applycb;
        this.data = data;
        this.container = container;
        this.lang = lang;
        this.value = values.length > 0 ? values[0] : { filterType: 1, filterValue: "" };//support only one filter value
        this.renderWidget();
        this.registerEvents();
    };

    dateTimeFilterWidget.prototype.renderWidget = function () {
        var html = '<div class="form-group">\
                        <label>' + this.lang.filterTypeLabel + '</label>\
                        <select class="grid-filter-type form-control">\
                            <option value="1" ' + (this.value.filterType == "1" ? "selected=\"selected\"" : "") + '>' + this.lang.filterSelectTypes.Equals + '</option>\
                            <option value="5" ' + (this.value.filterType == "5" ? "selected=\"selected\"" : "") + '>' + this.lang.filterSelectTypes.GreaterThan + '</option>\
                            <option value="6" ' + (this.value.filterType == "6" ? "selected=\"selected\"" : "") + '>' + this.lang.filterSelectTypes.LessThan + '</option>\
                        </select>\
                    </div>' +
                        (this.datePickerIncluded ?
                            '<div class="grid-filter-datepicker"></div>'
                            :
                            '<div class="form-group">\
                                <label>' + this.lang.filterValueLabel + '</label>\
                                <input type="text" class="grid-filter-input form-control" value="' + this.value.filterValue + '" />\
                             </div>\
                             <div class="grid-filter-buttons">\
                                <input type="button" class="btn btn-primary grid-apply" value="' + this.lang.applyFilterButtonText + '" />\
                             </div>');
        this.container.append(html);
        //if window.jQueryUi included:
        if (this.datePickerIncluded) {
            var datePickerOptions = this.data || {};
            datePickerOptions.format = datePickerOptions.format || "yyyy-mm-dd";
            datePickerOptions.language = datePickerOptions.language || this.lang.code;

            var $context = this;
            var dateContainer = this.container.find(".grid-filter-datepicker");
            dateContainer.datepicker(datePickerOptions).on('changeDate', function (ev) {
                var type = $context.container.find(".grid-filter-type").val();
                var filterValues = [{ filterType: type, filterValue: ev.format() }];
                $context.cb(filterValues);
            });
            if (this.value.filterValue)
                dateContainer.datepicker('update', this.value.filterValue);
        }
    };

    dateTimeFilterWidget.prototype.registerEvents = function () {
        var $context = this;
        var applyBtn = this.container.find(".grid-apply");
        applyBtn.click(function () {
            var type = $context.container.find(".grid-filter-type").val();
            var value = $context.container.find(".grid-filter-input").val();
            var filterValues = [{ filterType: type, filterValue: value }];
            $context.cb(filterValues);
        });
        this.container.find(".grid-filter-input").keyup(function (event) {
            if (event.keyCode == 13) {
                applyBtn.click();
            }
        });
    };

    return dateTimeFilterWidget;
})(window.jQuery);

/***
* BooleanFilterWidget - Provides filter interface for boolean columns (of type "System.Boolean").
* Renders two button for filter - true and false
*/
BooleanFilterWidget = (function ($) {

    function booleanFilterWidget() { }

    booleanFilterWidget.prototype.getAssociatedTypes = function () { return ["System.Boolean"]; };

    booleanFilterWidget.prototype.showClearFilterButton = function () { return true; };

    booleanFilterWidget.prototype.onRender = function (container, lang, typeName, values, cb) {
        this.cb = cb;
        this.container = container;
        this.lang = lang;
        this.value = values.length > 0 ? values[0] : { filterType: 1, filterValue: "" };//support only one filter value
        this.renderWidget();
        this.registerEvents();
    };

    booleanFilterWidget.prototype.renderWidget = function () {
        var html = '<label>' + this.lang.filterValueLabel + '</label>\
                    <ul class="menu-list">\
                        <li><a class="grid-filter-choose ' + (this.value.filterValue == "true" ? "choose-selected" : "") + '" data-value="true" href="javascript:void(0);">' + this.lang.boolTrueLabel + '</a></li>\
                        <li><a class="grid-filter-choose ' + (this.value.filterValue == "false" ? "choose-selected" : "") + '" data-value="false" href="javascript:void(0);">' + this.lang.boolFalseLabel + '</a></li>\
                    </ul>';
        this.container.append(html);
    };

    booleanFilterWidget.prototype.registerEvents = function () {
        var $context = this;
        var applyBtn = this.container.find(".grid-filter-choose");
        applyBtn.click(function () {
            var filterValues = [{ filterType: "1", filterValue: $(this).attr("data-value") }];
            $context.cb(filterValues);
        });
    };

    return booleanFilterWidget;
})(window.jQuery);

//startup init:
(function ($) {
    if (!$) return;//jquery not referenced
    $(function () {
        $(".grid-mvc").each(function () {
            $(".grid-mvc").gridmvc();
        });
    });
})(window.jQuery);