(function ($) {
    function contains(target, value) {
        var opts = $.data(target, "combobox").options;
        var data = $.data(target, "combobox").data;
        for (var i = 0; i < data.length; ++i) {
            if (data[i][opts.valueField] === value) {
                return true;
            }
        }
        return false;
    }

    var selectMethod = $.fn.combobox.methods.select;
    $.fn.combobox.methods.select = function (jq, value) {
        return jq.each(function () {
            var opts = $(this).combobox("options");
            if (opts.multiple) {
                value = value != null && typeof value === "object" && value.hasOwnProperty(opts.valueField) ? value[opts.valueField] : value;
                selectMethod.call($.fn.combobox.methods, $(this), value);
            } else {
                if (opts.editable && !opts.limitToList) {
                    $(this).combobox("reset").combobox("setValue", value);
                } else {
                    value = value != null && typeof value === "object" && value.hasOwnProperty(opts.valueField) ? value[opts.valueField] : value;
                    if (contains(this, value)) {
                        $(this).combobox("reset");
                        selectMethod.call($.fn.combobox.methods, $(this), value);
                    }
                }
            }
        });
    };

    $.fn.combobox.defaults = $.extend({}, $.fn.combobox.defaults, {
        loadFilter: function(data) {
            if (data && data.length) {
                if (typeof data[0] === "string" || typeof data[0] === "number") {
                    data = $.map(data, function (value) { return {text: value, value: value}; });
                    var values = $(this).combobox("getValues");
                    if (values.length == 0 || !values[0]) {
                        data[0]["selected"] = true;
                    }
                }
            }
            return data;
        },
        onLoadSuccess: function (data) {
            if (data.length && !$(this).combobox("getValue")) {
                var value = (typeof data[0] === "string" || typeof data[0] === "number") ? data[0] : data[0][$(this).combobox("options").valueField];
                $(this).combobox("select", value)
                        .combobox("panel")
                        .panel("body").css("max-height", "16em")
                        .panel("resize", {width: "auto", height:"auto"});
            }
        }
    });

    $.extend($.fn.combobox.methods, {
        "autocomplete": function(jq, enable) {
            var opts = jq.combobox("options");
            var comboOpts = jq.combo("options");
            var boxOpts = jq.combobox("textbox").validatebox("options");
            if (enable === false) {
                comboOpts.editable = opts.oldOptions.editable;
                opts.filter = opts.oldOptions.filter;
                opts.onLoadSuccess = opts.oldOptions.onLoadSuccess;
                boxOpts.validType = opts.oldOptions.validType;
                boxOpts.validParams = opts.oldOptions.validParams;
                opts.oldOptions = null;

                jq.combobox("readonly", false);
                jq.combobox("textbox").off(".autocomplete");
            } else {
                opts.oldOptions = {
                    editable: opts.editable,
                    filter: opts.filter,
                    onLoadSuccess: opts.onLoadSuccess,
                    validType: boxOpts.validType,
                    validParams: boxOpts.validParams
                };
                comboOpts.editable = true;
                jq.combobox("readonly", false);
                jq.combobox("textbox").on("focus.autocomplete", function () { $(this).select(); });

                opts.filter = function (query, row) {
                    var value = row[$(this).combobox("options").textField];
                    return value.toLowerCase().indexOf(query.toLowerCase()) != -1;
                };
                var onLoadSuccess = opts.onLoadSuccess;
                opts.onLoadSuccess = function(data) {
                    var t = $(this);
                    var textField = t.combobox("options").textField;
                    t.combobox("textbox").validatebox("options").validParams = $.map(data, function (row) { return typeof row === "object" ? row[textField] : row; });
                    t.combobox("getValue") && t.combobox("isValid");
                    onLoadSuccess && onLoadSuccess.apply(this, arguments);
                };
                boxOpts.validParams = $.map(jq.combobox("getData"), function(row) { return row[opts.textField]; });
                boxOpts.validType = $.isArray(boxOpts.validType)
                                    ? ["in_array"].concat(boxOpts.validType)
                                    : (typeof boxOpts.validType === "string" ? [boxOpts.validType, "in_array"] : "in_array");
            }
            return jq;
        }
    });
})(jQuery);

(function($) {
    function _hasUnitBox(jq) {
        var t = $(jq).numberbox("options").unitbox;
        return t && t.length && $.data(t[0], "combobox");
    }
    function _unitFactorFor(jq, value) {
        var factor = 1;
        if (_hasUnitBox(jq)) {
            var opts = $(jq).numberbox("options");
            var valueField = opts.unitbox.combobox("options").valueField;
            var units = $.map(opts.unitbox.combobox("getData"), function (entry) { return parseInt(entry[valueField]); });
            units.sort(function (a, b) { return a - b; });
            while (units.length) {
                factor = units.pop();
                if (value % factor == 0) {
                    break;
                }
            }
        }
        return factor;
    }

    var spinMethod = $.fn.numberspinner.defaults.spin;
    $.extend($.fn.numberspinner.defaults, {
        spin: function() {
            if (_hasUnitBox(this)) {
                var opts = $.data(this, "numberspinner").options;
                var step = opts.increment;
                try {
                    opts.increment *= parseInt(opts.unitbox.combobox("getValue"));
                    spinMethod.apply(this, arguments);
                } finally {
                    opts.increment = step;
                }
            } else {
                spinMethod.apply(this, arguments);
            }
        }
    });
    var fixMethod = $.fn.numberbox.methods.fix;
    var setValueMethod = $.fn.numberbox.methods.setValue;
    $.extend($.fn.numberbox.methods, {
        getValue: function(jq) {
            var value = $(jq).textbox("getValue");
            return _hasUnitBox(jq) ? parseFloat(value) * parseInt($(jq).numberbox("options").unitbox.combobox("getValue")) : $.isNumeric(value) ? parseFloat(value) : value;
        },
        setValue: function(jq, value) {
            return jq.each(function() {
                if (_hasUnitBox(this)) {
                    value = parseFloat(value);
                    var unitbox = $(this).numberbox("options").unitbox;
                    var factor = _unitFactorFor($(this), value);
                    setValueMethod.call(this, jq, value / factor);
                    unitbox.combobox("select", factor + "");
                } else {
                    setValueMethod.call(this, jq, value);
                }
            });
        },
        fix: function(jq) {
            return jq.each(function () {
                var opts = $(this).numberbox("options");
                if (_hasUnitBox(this) && typeof opts.max == "number") {
                    var max = opts.max;
                    try {
                        opts.max = null;
                        opts.value = null;
                        var value = parseFloat(opts.parser.call(this, $(this).numberbox("getText")));
                    } finally {
                        opts.max = max;
                    }
                    value *= parseInt(opts.unitbox.combobox("getValue"));
                    $(this).numberbox("setValue", value);
                } else {
                    fixMethod.call(this, jq);
                }
            });
        }
    });
    $.fn.numberbox.defaults.unitbox = null;
})(jQuery);

(function ($){
    var keydownHandler = $.fn.tagbox.defaults.inputEvents.keydown;
    var blurHandler = $.fn.tagbox.defaults.inputEvents.blur;
    var enterHandler = $.fn.tagbox.defaults.keyHandler.enter;
    var queryHander = $.fn.tagbox.defaults.keyHandler.query;

    $.fn.tagbox.defaults = $.extend({}, $.fn.tagbox.defaults, {
        loadFilter: function(data) {
            if (data && data.length && (typeof data[0] === "string" || typeof data[0] === "number")) {
                return $.map(data, function(value) { return {text: value, value: value}; });
            }
            return data;
        },
        keyHandler: $.extend({}, $.fn.tagbox.defaults.keyHandler, {
            query: function() {
                var opts = $(this).tagbox("options");
                if (opts.editable && !opts.limitToList) {
                    $.fn.combobox.defaults.keyHandler.query.apply(this, arguments);
                } else {
                    queryHander.apply(this, arguments);
                }
            },
            enter: function (e) {
                var t = $(this);
                var opts = t.tagbox("options");
                if (opts.editable && !opts.limitToList) {
                    var item = t.tagbox("panel").children("div.combobox-item-hover");
                    if (item.length) {
                        item.removeClass("combobox-item-hover");
                        var row = opts.finder.getRow(this, item);
                        t.tagbox(item.hasClass("combobox-item-selected") ? "unselect" : "select", row[opts.valueField]);
                    } else {
                        enterHandler.call(this, e);
                    }
                } else {
                    enterHandler.call(this, e);
                }
            }
        }),
        inputEvents: $.extend({}, $.fn.tagbox.defaults.inputEvents, {
            blur: function(e) {
                var t = e.data.target;
                var opts = $(t).tagbox("options");
                if (opts.editable && !opts.limitToList) {
                    opts.keyHandler.enter.call(t, e);
                } else {
                    blurHandler.call(this, e);
                }
            },
            keydown: function (e) {
                if (e.keyCode == 8) {
                    var t = $(e.data.target);
                    if (t.tagbox("options").editable && t.combobox("getText").length == 0) {
                        t.next().find(".tagbox-remove").last().click();
                        return false;
                    }
                } else {
                    keydownHandler.apply(this, arguments);
                }
            }
        })
    });
})(jQuery);

(function($) {
    var emailRuleValidator = $.fn.validatebox.defaults.rules.email.validator;
    $.extend($.fn.validatebox.defaults.rules, {
        "email": {
            validator: function(value) {
                value = /^(.+)?[ \t]*<(.+)>[ \t]*$/.test(value) ? RegExp.$2 : value;
                return emailRuleValidator.call(this, value);
            },
            message: $.fn.validatebox.defaults.rules.email.message
        },
        "pattern": {
            validator: function(value, params) {
                var pattern = params && params.length ? params[0] : "";
                var flags = params && params.length > 1 ? params[1] : "";
                try { return new RegExp(pattern, flags).test(value);} catch (e) { return false }
            },
            message: $.fn.validatebox.defaults.missingMessage || ""
        },
        "variable": {
            validator: function(value) {
                return /^%[^%]+%$/.test(value.trim());
            },
            message: $.fn.validatebox.defaults.missingMessage || ""
        },
        "in_array": {
            validator: function(value, params) {
                return $.inArray(value, params || []) != -1;
            },
            message: $.fn.validatebox.defaults.missingMessage || ""
        },
        "in_range": {
            validator: function(value, params) {
                var lowerBound = params && params.length ? parseInt(params[0], 10) : NaN;
                var upperBound = params && params.length > 1 ? parseInt(params[1], 10) : NaN;
                value = parseInt(value, 10);
                return !isNaN(lowerBound) && !isNaN(upperBound) && !isNaN(value) && isFinite(value) ? lowerBound <= value && value <= upperBound : true;
            },
            message: "Please enter a value between {0} and {1}."
        },
        "multi_value": {
            validator: function (value, params) {
                var rule = $(this).validatebox("options").rules[params[0]];
                var separator = params.length > 1 ? params[1] : " ";
                var values = value.split(separator);
                for (var i = 0; i < values.length; i++) {
                    var str = values[i].trim();
                    if (str.length && !rule["validator"].call(this, str)) {
                        return false;
                    }
                }
                return true;
            },
            message: ""
        },
        "any": {
            validator: function(value, params) {
                var opts = $(this).validatebox("options");
                var validType = opts.validType;
                try {
                    for (var i = 0; i < params.length; ++i) {
                        opts.validType = params[i];
                        if ($(this).validatebox("isValid")) {
                            return true;
                        }
                    }
                    return false;
                } finally {
                    opts.validType = validType;
                }
            },
            message: ""
        },
        "non_empty": {
            validator: function(value) {
                return value && value.trim().length != 0;
            },
            message: ""
        }
    });
})(jQuery);

(function ($) {
    function escapeXml(value) {
        var entities = {"<": "&lt;", ">": "&gt;", "&": "&amp;", '"': "&quot;"};
        return value.replace(/[<>&"]/g, function (entry) { return entities[entry]});
    }
    function xssFilter(value) {
        return typeof value === "string" ? escapeXml(value) : value;
    }
    var getColumnOptionMethod = $.fn.datagrid.methods.getColumnOption;
    $.fn.datagrid.methods.getColumnOption = function (jq, field) {
        var col = getColumnOptionMethod.call(this, jq, field);
        if (col && !$.isFunction(col.formatter)) {
            col.formatter = xssFilter;
        }
        return col;
    };

    var treeFormatter = $.fn.tree.defaults.formatter;
    $.fn.tree.defaults.formatter = function() { return xssFilter(treeFormatter.apply(this, arguments)); };

    var comboFormatter = $.fn.combobox.defaults.formatter;
    $.fn.combobox.defaults.formatter = function() { return xssFilter(comboFormatter.apply(this, arguments)); };

    var tagboxFormatter = $.fn.tagbox.defaults.formatter;
    var tagboxTagFormatter = $.fn.tagbox.defaults.tagFormatter;
    $.fn.tagbox.defaults.formatter = function() { return xssFilter(tagboxFormatter.apply(this, arguments)); };
    $.fn.tagbox.defaults.tagFormatter = function() { return xssFilter(tagboxTagFormatter.apply(this, arguments)); };

    $.each($.messager, function(key) {
        var fn = $.messager[key];
        if ($.isFunction(fn)) {
            $.messager[key] = function() {
                function filter(data, force) {
                    if (typeof data === "string") {
                        return force ? xssFilter(data) : data;
                    } else if ($.isArray(data)) {
                        for (var i = 0; i < data.length; ++i) {
                            data[i] = filter(data[i], !!force);
                        }
                    } else if (typeof data == "object") {
                        var escape = force || (data.hasOwnProperty("escapeXml") && data["escapeXml"] !== false);
                        for (key in data) {
                            if (data.hasOwnProperty(key)) {
                                data[key] = filter(data[key], escape);
                            }
                        }
                        delete data.escapeXml;
                    }
                    return data;
                }
                return fn.apply(this, filter(arguments, false));
            }
        }
    });
})(jQuery);

(function ($) {
    function pageFilter(data) {
        var total = $.isArray(data) ? data.length : data.total;
        var rows = $.isArray(data) ? data : data.rows;
        var opts = $.data(this, "datagrid").options;
        var pl = pageList(opts.pageList, total);
        opts.pageSize = pageSize(opts.pageSize || $(this).datagrid("getPager").pagination("options").pageSize, pl);
        opts.pageNumber = Math.max(1, opts.pageNumber);
        var startIndex = (opts.pageNumber - 1) * opts.pageSize;
        rows = startIndex < rows.length
                ? rows.slice(startIndex, startIndex + opts.pageSize)
                : rows.splice(0, opts.pageSize);

        var dg = $(this);
        $(this).datagrid("getPager")
                .pagination({
                pageNumber: opts.pageNumber,
                pageSize: opts.pageSize,
                pageList: pl,
                        total: total,
                        buttons: pageButtons(dg),
                        onSelectPage: function (pageNumber, pageSize) {
                            opts.pageNumber = pageNumber;
                            opts.pageSize = pageSize;
                            $(this).pagination("refresh", {pageNumber: pageNumber, pageSize: pageSize});
                            if (opts.remoteSort) {
                                dg.datagrid("reload");
                            } else {
                                dg.datagrid("loadData", dg.datagrid("getData"));
                            }
                        },
                        onBeforeRefresh: function () {
                            dg.datagrid("reload");
                            return false;
                        }
                });
        return {total: total, rows: rows};
    }

    function pageList(list, total) {
        list = (total > 0 ? [total] : []).concat(list).sort(function (a, b) { return a - b; });
        var index = total > 0 ? $.inArray(total, list) : -1;
        return index == 0 ? list.splice(1, 1) : index > 0 ? list.slice(0, index + 1) : list;
    }

    function pageSize(value, values) {
        var candidate = values.length ? values[0] : value;
        for (var i = 0; i < values.length; ++i) {
            if (Math.abs(value - values[i]) < Math.abs(value - candidate)) {
                candidate = values[i];
            }
        }
        return candidate;
    }

    function pageButtons(jq) {
        var opts = jq.datagrid("options");
        return opts.search ? [{iconCls: "icon-search", handler: function () { jq.datagrid("getFindbar").findbar("show"); }}] : [];
    }

    var loadFilter = $.fn.datagrid.defaults.loadFilter;
    var onBeforeSortColumn = $.fn.datagrid.defaults.onBeforeSortColumn;
    var onSortColumn = $.fn.datagrid.defaults.onSortColumn;
    $.extend($.fn.datagrid.defaults, {
        loadFilter: function(data) {
            data = loadFilter.call(this, data);
            var options = $.data(this, "datagrid").options;
            return options.pagination ? pageFilter.call(this, data) : data;
        },
        onBeforeSortColumn: function() {
            var ret = onBeforeSortColumn.apply(this, arguments);
            if (ret != false) { $.data(this, "datagrid").sorting = true; }
            return ret;
        },
        onSortColumn: function() {
            onSortColumn.apply(this, arguments);
            $.data(this, "datagrid").sorting = false;
        }
    });
    var getData = $.fn.datagrid.methods.getData;
    var loadData = $.fn.datagrid.methods.loadData;
    $.extend($.fn.datagrid.methods, {
        getData: function(jq) {
            var data = $.data(jq[0], "datagrid").originalData;
            return data || getData.apply(this, arguments);
        },
        loadData: function(jq, data) {
            var state = $.data(jq[0], "datagrid");
            if (!state.sorting) { state.originalData = data; }
            return loadData.apply(this, arguments);
        }
    });
    var reloadMethod = $.fn.datagrid.methods.reload;
    $.extend($.fn.datagrid.methods, {
        reload: function(jq, callback) {
            var opts = $(jq).datagrid("options");
            if (opts.pagination && opts.pageSize === $(jq).datagrid("getData").total) {
                opts.pageSize = undefined;
            }
            if ($.isFunction(callback)) {
                var loadSuccess = opts.onLoadSuccess;
                var loadError = opts.onLoadError;
                opts.onLoadSuccess = function () {
                    opts.onLoadSuccess = loadSuccess;
                    opts.onLoadError = loadError;
                    opts.onLoadSuccess.apply(this, arguments);
                    callback();
                    opts = loadSuccess = loadError = null;
                };
                opts.onLoadError = function () {
                    opts.onLoadSuccess = loadSuccess;
                    opts.onLoadError = loadError;
                    opts.onLoadError.apply(this, arguments);
                    opts = loadSuccess = loadError = null;
                };
                return reloadMethod.call(this, jq);
            }
            return reloadMethod.apply(this, arguments);
        }
    });
})(jQuery);