All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.wicket.kendo.ui.resource.kendo.web.js Maven / Gradle / Ivy

/*
* Kendo UI Web v2013.3.1119 (http://kendoui.com)
* Copyright 2013 Telerik AD. All rights reserved.
*
* Kendo UI Web commercial licenses may be obtained at
* https://www.kendoui.com/purchase/license-agreement/kendo-ui-web-commercial.aspx
* If you do not own a commercial license, this file shall be governed by the
* GNU General Public License (GPL) version 3.
* For GPL requirements, please review: http://www.gnu.org/copyleft/gpl.html
*/
(function($, evil, undefined) {
    var kendo = window.kendo = window.kendo || { cultures: {} },
        extend = $.extend,
        each = $.each,
        isArray = $.isArray,
        proxy = $.proxy,
        noop = $.noop,
        math = Math,
        Template,
        JSON = window.JSON || {},
        support = {},
        percentRegExp = /%/,
        formatRegExp = /\{(\d+)(:[^\}]+)?\}/g,
        boxShadowRegExp = /(\d+?)px\s*(\d+?)px\s*(\d+?)px\s*(\d+?)?/i,
        numberRegExp = /^(\+|-?)\d+(\.?)\d*$/,
        FUNCTION = "function",
        STRING = "string",
        NUMBER = "number",
        OBJECT = "object",
        NULL = "null",
        BOOLEAN = "boolean",
        UNDEFINED = "undefined",
        getterCache = {},
        setterCache = {},
        slice = [].slice,
        globalize = window.Globalize;

    kendo.version = "2013.3.1119";

    function Class() {}

    Class.extend = function(proto) {
        var base = function() {},
            member,
            that = this,
            subclass = proto && proto.init ? proto.init : function () {
                that.apply(this, arguments);
            },
            fn;

        base.prototype = that.prototype;
        fn = subclass.fn = subclass.prototype = new base();

        for (member in proto) {
            if (typeof proto[member] === OBJECT && !(proto[member] instanceof Array) && proto[member] !== null) {
                // Merge object members
                fn[member] = extend(true, {}, base.prototype[member], proto[member]);
            } else {
                fn[member] = proto[member];
            }
        }

        fn.constructor = subclass;
        subclass.extend = that.extend;

        return subclass;
    };

    Class.prototype._initOptions = function(options) {
        this.options = deepExtend({}, this.options, options);
    };

    var isFunction = kendo.isFunction = function(fn) {
        return typeof fn === "function";
    };

    var preventDefault = function() {
        this._defaultPrevented = true;
    };

    var isDefaultPrevented = function() {
        return this._defaultPrevented === true;
    };

    var Observable = Class.extend({
        init: function() {
            this._events = {};
        },

        bind: function(eventName, handlers, one) {
            var that = this,
                idx,
                eventNames = typeof eventName === STRING ? [eventName] : eventName,
                length,
                original,
                handler,
                handlersIsFunction = typeof handlers === FUNCTION,
                events;

            if (handlers === undefined) {
                for (idx in eventName) {
                    that.bind(idx, eventName[idx]);
                }
                return that;
            }

            for (idx = 0, length = eventNames.length; idx < length; idx++) {
                eventName = eventNames[idx];

                handler = handlersIsFunction ? handlers : handlers[eventName];

                if (handler) {
                    if (one) {
                        original = handler;
                        handler = function() {
                            that.unbind(eventName, handler);
                            original.apply(that, arguments);
                        };
                    }
                    events = that._events[eventName] = that._events[eventName] || [];
                    events.push(handler);
                }
            }

            return that;
        },

        one: function(eventNames, handlers) {
            return this.bind(eventNames, handlers, true);
        },

        first: function(eventName, handlers) {
            var that = this,
                idx,
                eventNames = typeof eventName === STRING ? [eventName] : eventName,
                length,
                handler,
                handlersIsFunction = typeof handlers === FUNCTION,
                events;

            for (idx = 0, length = eventNames.length; idx < length; idx++) {
                eventName = eventNames[idx];

                handler = handlersIsFunction ? handlers : handlers[eventName];

                if (handler) {
                    events = that._events[eventName] = that._events[eventName] || [];
                    events.unshift(handler);
                }
            }

            return that;
        },

        trigger: function(eventName, e) {
            var that = this,
                events = that._events[eventName],
                idx,
                length;

            if (events) {
                e = e || {};

                e.sender = that;

                e._defaultPrevented = false;

                e.preventDefault = preventDefault;

                e.isDefaultPrevented = isDefaultPrevented;

                events = events.slice();

                for (idx = 0, length = events.length; idx < length; idx++) {
                    events[idx].call(that, e);
                }

                return e._defaultPrevented === true;
            }

            return false;
        },

        unbind: function(eventName, handler) {
            var that = this,
                events = that._events[eventName],
                idx;

            if (eventName === undefined) {
                that._events = {};
            } else if (events) {
                if (handler) {
                    for (idx = events.length - 1; idx >= 0; idx--) {
                        if (events[idx] === handler) {
                            events.splice(idx, 1);
                        }
                    }
                } else {
                    that._events[eventName] = [];
                }
            }

            return that;
        }
    });


     function compilePart(part, stringPart) {
         if (stringPart) {
             return "'" +
                 part.split("'").join("\\'")
                     .split('\\"').join('\\\\\\"')
                     .replace(/\n/g, "\\n")
                     .replace(/\r/g, "\\r")
                     .replace(/\t/g, "\\t") + "'";
         } else {
             var first = part.charAt(0),
                 rest = part.substring(1);

             if (first === "=") {
                 return "+(" + rest + ")+";
             } else if (first === ":") {
                 return "+e(" + rest + ")+";
             } else {
                 return ";" + part + ";o+=";
             }
         }
     }

    var argumentNameRegExp = /^\w+/,
        encodeRegExp = /\$\{([^}]*)\}/g,
        escapedCurlyRegExp = /\\\}/g,
        curlyRegExp = /__CURLY__/g,
        escapedSharpRegExp = /\\#/g,
        sharpRegExp = /__SHARP__/g,
        zeros = ["", "0", "00", "000", "0000"];

    Template = {
        paramName: "data", // name of the parameter of the generated template
        useWithBlock: true, // whether to wrap the template in a with() block
        render: function(template, data) {
            var idx,
                length,
                html = "";

            for (idx = 0, length = data.length; idx < length; idx++) {
                html += template(data[idx]);
            }

            return html;
        },
        compile: function(template, options) {
            var settings = extend({}, this, options),
                paramName = settings.paramName,
                argumentName = paramName.match(argumentNameRegExp)[0],
                useWithBlock = settings.useWithBlock,
                functionBody = "var o,e=kendo.htmlEncode;",
                fn,
                parts,
                idx;

            if (isFunction(template)) {
                if (template.length === 2) {
                    //looks like jQuery.template
                    return function(d) {
                        return template($, { data: d }).join("");
                    };
                }
                return template;
            }

            functionBody += useWithBlock ? "with(" + paramName + "){" : "";

            functionBody += "o=";

            parts = template
                .replace(escapedCurlyRegExp, "__CURLY__")
                .replace(encodeRegExp, "#=e($1)#")
                .replace(curlyRegExp, "}")
                .replace(escapedSharpRegExp, "__SHARP__")
                .split("#");

            for (idx = 0; idx < parts.length; idx ++) {
                functionBody += compilePart(parts[idx], idx % 2 === 0);
            }

            functionBody += useWithBlock ? ";}" : ";";

            functionBody += "return o;";

            functionBody = functionBody.replace(sharpRegExp, "#");

            try {
                fn = new Function(argumentName, functionBody);
                fn._slotCount = Math.floor(parts.length / 2);
                return fn;
            } catch(e) {
                throw new Error(kendo.format("Invalid template:'{0}' Generated code:'{1}'", template, functionBody));
            }
        }
    };

function pad(number, digits, end) {
    number = number + "";
    digits = digits || 2;
    end = digits - number.length;

    if (end) {
        return zeros[digits].substring(0, end) + number;
    }

    return number;
}

    //JSON stringify
(function() {
    var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {
            "\b": "\\b",
            "\t": "\\t",
            "\n": "\\n",
            "\f": "\\f",
            "\r": "\\r",
            "\"" : '\\"',
            "\\": "\\\\"
        },
        rep,
        toString = {}.toString;

    if (typeof Date.prototype.toJSON !== FUNCTION) {

        Date.prototype.toJSON = function () {
            var that = this;

            return isFinite(that.valueOf()) ?
                pad(that.getUTCFullYear(), 4) + "-" +
                pad(that.getUTCMonth() + 1)   + "-" +
                pad(that.getUTCDate())        + "T" +
                pad(that.getUTCHours())       + ":" +
                pad(that.getUTCMinutes())     + ":" +
                pad(that.getUTCSeconds())     + "Z" : null;
        };

        String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function () {
            return this.valueOf();
        };
    }

    function quote(string) {
        escapable.lastIndex = 0;
        return escapable.test(string) ? "\"" + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === STRING ? c :
                "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
        }) + "\"" : "\"" + string + "\"";
    }

    function str(key, holder) {
        var i,
            k,
            v,
            length,
            mind = gap,
            partial,
            value = holder[key],
            type;

        if (value && typeof value === OBJECT && typeof value.toJSON === FUNCTION) {
            value = value.toJSON(key);
        }

        if (typeof rep === FUNCTION) {
            value = rep.call(holder, key, value);
        }

        type = typeof value;
        if (type === STRING) {
            return quote(value);
        } else if (type === NUMBER) {
            return isFinite(value) ? String(value) : NULL;
        } else if (type === BOOLEAN || type === NULL) {
            return String(value);
        } else if (type === OBJECT) {
            if (!value) {
                return NULL;
            }
            gap += indent;
            partial = [];
            if (toString.apply(value) === "[object Array]") {
                length = value.length;
                for (i = 0; i < length; i++) {
                    partial[i] = str(i, value) || NULL;
                }
                v = partial.length === 0 ? "[]" : gap ?
                    "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" :
                    "[" + partial.join(",") + "]";
                gap = mind;
                return v;
            }
            if (rep && typeof rep === OBJECT) {
                length = rep.length;
                for (i = 0; i < length; i++) {
                    if (typeof rep[i] === STRING) {
                        k = rep[i];
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ": " : ":") + v);
                        }
                    }
                }
            } else {
                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ": " : ":") + v);
                        }
                    }
                }
            }

            v = partial.length === 0 ? "{}" : gap ?
                "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" :
                "{" + partial.join(",") + "}";
            gap = mind;
            return v;
        }
    }

    if (typeof JSON.stringify !== FUNCTION) {
        JSON.stringify = function (value, replacer, space) {
            var i;
            gap = "";
            indent = "";

            if (typeof space === NUMBER) {
                for (i = 0; i < space; i += 1) {
                    indent += " ";
                }

            } else if (typeof space === STRING) {
                indent = space;
            }

            rep = replacer;
            if (replacer && typeof replacer !== FUNCTION && (typeof replacer !== OBJECT || typeof replacer.length !== NUMBER)) {
                throw new Error("JSON.stringify");
            }

            return str("", {"": value});
        };
    }
})();

// Date and Number formatting
(function() {
    var dateFormatRegExp = /dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|HH|H|hh|h|mm|m|fff|ff|f|tt|ss|s|"[^"]*"|'[^']*'/g,
        standardFormatRegExp =  /^(n|c|p|e)(\d*)$/i,
        literalRegExp = /(\\.)|(['][^']*[']?)|(["][^"]*["]?)/g,
        commaRegExp = /\,/g,
        EMPTY = "",
        POINT = ".",
        COMMA = ",",
        SHARP = "#",
        ZERO = "0",
        PLACEHOLDER = "??",
        EN = "en-US",
        objectToString = {}.toString;

    //cultures
    kendo.cultures["en-US"] = {
        name: EN,
        numberFormat: {
            pattern: ["-n"],
            decimals: 2,
            ",": ",",
            ".": ".",
            groupSize: [3],
            percent: {
                pattern: ["-n %", "n %"],
                decimals: 2,
                ",": ",",
                ".": ".",
                groupSize: [3],
                symbol: "%"
            },
            currency: {
                pattern: ["($n)", "$n"],
                decimals: 2,
                ",": ",",
                ".": ".",
                groupSize: [3],
                symbol: "$"
            }
        },
        calendars: {
            standard: {
                days: {
                    names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
                    namesAbbr: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
                    namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ]
                },
                months: {
                    names: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
                    namesAbbr: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
                },
                AM: [ "AM", "am", "AM" ],
                PM: [ "PM", "pm", "PM" ],
                patterns: {
                    d: "M/d/yyyy",
                    D: "dddd, MMMM dd, yyyy",
                    F: "dddd, MMMM dd, yyyy h:mm:ss tt",
                    g: "M/d/yyyy h:mm tt",
                    G: "M/d/yyyy h:mm:ss tt",
                    m: "MMMM dd",
                    M: "MMMM dd",
                    s: "yyyy'-'MM'-'ddTHH':'mm':'ss",
                    t: "h:mm tt",
                    T: "h:mm:ss tt",
                    u: "yyyy'-'MM'-'dd HH':'mm':'ss'Z'",
                    y: "MMMM, yyyy",
                    Y: "MMMM, yyyy"
                },
                "/": "/",
                ":": ":",
                firstDay: 0,
                twoDigitYearMax: 2029
            }
        }
    };


     function findCulture(culture) {
        if (culture) {
            if (culture.numberFormat) {
                return culture;
            }

            if (typeof culture === STRING) {
                var cultures = kendo.cultures;
                return cultures[culture] || cultures[culture.split("-")[0]] || null;
            }

            return null;
        }

        return null;
    }

    function getCulture(culture) {
        if (culture) {
            culture = findCulture(culture);
        }

        return culture || kendo.cultures.current;
    }

    function expandNumberFormat(numberFormat) {
        numberFormat.groupSizes = numberFormat.groupSize;
        numberFormat.percent.groupSizes = numberFormat.percent.groupSize;
        numberFormat.currency.groupSizes = numberFormat.currency.groupSize;
    }

    kendo.culture = function(cultureName) {
        var cultures = kendo.cultures, culture;

        if (cultureName !== undefined) {
            culture = findCulture(cultureName) || cultures[EN];
            culture.calendar = culture.calendars.standard;
            cultures.current = culture;

            if (globalize) {
                expandNumberFormat(culture.numberFormat);
            }

        } else {
            return cultures.current;
        }
    };

    kendo.findCulture = findCulture;
    kendo.getCulture = getCulture;

    //set current culture to en-US.
    kendo.culture(EN);

    function formatDate(date, format, culture) {
        culture = getCulture(culture);

        var calendar = culture.calendars.standard,
            days = calendar.days,
            months = calendar.months;

        format = calendar.patterns[format] || format;

        return format.replace(dateFormatRegExp, function (match) {
            var result;

            if (match === "d") {
                result = date.getDate();
            } else if (match === "dd") {
                result = pad(date.getDate());
            } else if (match === "ddd") {
                result = days.namesAbbr[date.getDay()];
            } else if (match === "dddd") {
                result = days.names[date.getDay()];
            } else if (match === "M") {
                result = date.getMonth() + 1;
            } else if (match === "MM") {
                result = pad(date.getMonth() + 1);
            } else if (match === "MMM") {
                result = months.namesAbbr[date.getMonth()];
            } else if (match === "MMMM") {
                result = months.names[date.getMonth()];
            } else if (match === "yy") {
                result = pad(date.getFullYear() % 100);
            } else if (match === "yyyy") {
                result = pad(date.getFullYear(), 4);
            } else if (match === "h" ) {
                result = date.getHours() % 12 || 12;
            } else if (match === "hh") {
                result = pad(date.getHours() % 12 || 12);
            } else if (match === "H") {
                result = date.getHours();
            } else if (match === "HH") {
                result = pad(date.getHours());
            } else if (match === "m") {
                result = date.getMinutes();
            } else if (match === "mm") {
                result = pad(date.getMinutes());
            } else if (match === "s") {
                result = date.getSeconds();
            } else if (match === "ss") {
                result = pad(date.getSeconds());
            } else if (match === "f") {
                result = math.floor(date.getMilliseconds() / 100);
            } else if (match === "ff") {
                result = math.floor(date.getMilliseconds() / 10);
            } else if (match === "fff") {
                result = date.getMilliseconds();
            } else if (match === "tt") {
                result = date.getHours() < 12 ? calendar.AM[0] : calendar.PM[0];
            }

            return result !== undefined ? result : match.slice(1, match.length - 1);
        });
    }

    //number formatting
    function formatNumber(number, format, culture) {
        culture = getCulture(culture);

        var numberFormat = culture.numberFormat,
            groupSize = numberFormat.groupSize[0],
            groupSeparator = numberFormat[COMMA],
            decimal = numberFormat[POINT],
            precision = numberFormat.decimals,
            pattern = numberFormat.pattern[0],
            literals = [],
            symbol,
            isCurrency, isPercent,
            customPrecision,
            formatAndPrecision,
            negative = number < 0,
            integer,
            fraction,
            integerLength,
            fractionLength,
            replacement = EMPTY,
            value = EMPTY,
            idx,
            length,
            ch,
            hasGroup,
            hasNegativeFormat,
            decimalIndex,
            sharpIndex,
            zeroIndex,
            hasZero, hasSharp,
            percentIndex,
            currencyIndex,
            startZeroIndex,
            start = -1,
            end;

        //return empty string if no number
        if (number === undefined) {
            return EMPTY;
        }

        if (!isFinite(number)) {
            return number;
        }

        //if no format then return number.toString() or number.toLocaleString() if culture.name is not defined
        if (!format) {
            return culture.name.length ? number.toLocaleString() : number.toString();
        }

        formatAndPrecision = standardFormatRegExp.exec(format);

        // standard formatting
        if (formatAndPrecision) {
            format = formatAndPrecision[1].toLowerCase();

            isCurrency = format === "c";
            isPercent = format === "p";

            if (isCurrency || isPercent) {
                //get specific number format information if format is currency or percent
                numberFormat = isCurrency ? numberFormat.currency : numberFormat.percent;
                groupSize = numberFormat.groupSize[0];
                groupSeparator = numberFormat[COMMA];
                decimal = numberFormat[POINT];
                precision = numberFormat.decimals;
                symbol = numberFormat.symbol;
                pattern = numberFormat.pattern[negative ? 0 : 1];
            }

            customPrecision = formatAndPrecision[2];

            if (customPrecision) {
                precision = +customPrecision;
            }

            //return number in exponential format
            if (format === "e") {
                return customPrecision ? number.toExponential(precision) : number.toExponential(); // toExponential() and toExponential(undefined) differ in FF #653438.
            }

            // multiply if format is percent
            if (isPercent) {
                number *= 100;
            }

            number = round(number, precision);
            number = number.split(POINT);

            integer = number[0];
            fraction = number[1];

            //exclude "-" if number is negative.
            if (negative) {
                integer = integer.substring(1);
            }

            value = integer;
            integerLength = integer.length;

            //add group separator to the number if it is longer enough
            if (integerLength >= groupSize) {
                value = EMPTY;
                for (idx = 0; idx < integerLength; idx++) {
                    if (idx > 0 && (integerLength - idx) % groupSize === 0) {
                        value += groupSeparator;
                    }
                    value += integer.charAt(idx);
                }
            }

            if (fraction) {
                value += decimal + fraction;
            }

            if (format === "n" && !negative) {
                return value;
            }

            number = EMPTY;

            for (idx = 0, length = pattern.length; idx < length; idx++) {
                ch = pattern.charAt(idx);

                if (ch === "n") {
                    number += value;
                } else if (ch === "$" || ch === "%") {
                    number += symbol;
                } else {
                    number += ch;
                }
            }

            return number;
        }

        //custom formatting
        //
        //separate format by sections.

        //make number positive
        if (negative) {
            number = -number;
        }

        if (format.indexOf("'") > -1 || format.indexOf("\"") > -1 || format.indexOf("\\") > -1) {
            format = format.replace(literalRegExp, function (match) {
                var quoteChar = match.charAt(0).replace("\\", ""),
                    literal = match.slice(1).replace(quoteChar, "");

                literals.push(literal);

                return PLACEHOLDER;
            });
        }

        format = format.split(";");
        if (negative && format[1]) {
            //get negative format
            format = format[1];
            hasNegativeFormat = true;
        } else if (number === 0) {
            //format for zeros
            format = format[2] || format[0];
            if (format.indexOf(SHARP) == -1 && format.indexOf(ZERO) == -1) {
                //return format if it is string constant.
                return format;
            }
        } else {
            format = format[0];
        }

        percentIndex = format.indexOf("%");
        currencyIndex = format.indexOf("$");

        isPercent = percentIndex != -1;
        isCurrency = currencyIndex != -1;

        //multiply number if the format has percent
        if (isPercent) {
            number *= 100;
        }

        if (isCurrency && format[currencyIndex - 1] === "\\") {
            format = format.split("\\").join("");
            isCurrency = false;
        }

        if (isCurrency || isPercent) {
            //get specific number format information if format is currency or percent
            numberFormat = isCurrency ? numberFormat.currency : numberFormat.percent;
            groupSize = numberFormat.groupSize[0];
            groupSeparator = numberFormat[COMMA];
            decimal = numberFormat[POINT];
            precision = numberFormat.decimals;
            symbol = numberFormat.symbol;
        }

        hasGroup = format.indexOf(COMMA) > -1;
        if (hasGroup) {
            format = format.replace(commaRegExp, EMPTY);
        }

        decimalIndex = format.indexOf(POINT);
        length = format.length;

        if (decimalIndex != -1) {
            fraction = number.toString().split("e");
            if (fraction[1]) {
                fraction = round(number, Math.abs(fraction[1]));
            } else {
                fraction = fraction[0];
            }
            fraction = fraction.split(POINT)[1] || EMPTY;
            zeroIndex = format.lastIndexOf(ZERO) - decimalIndex;
            sharpIndex = format.lastIndexOf(SHARP) - decimalIndex;
            hasZero = zeroIndex > -1;
            hasSharp = sharpIndex > -1;
            idx = fraction.length;

            if (!hasZero && !hasSharp) {
                format = format.substring(0, decimalIndex) + format.substring(decimalIndex + 1);
                length = format.length;
                decimalIndex = -1;
                idx = 0;
            } if (hasZero && zeroIndex > sharpIndex) {
                idx = zeroIndex;
            } else if (sharpIndex > zeroIndex) {
                if (hasSharp && idx > sharpIndex) {
                    idx = sharpIndex;
                } else if (hasZero && idx < zeroIndex) {
                    idx = zeroIndex;
                }
            }

            if (idx > -1) {
                number = round(number, idx);
            }
        } else {
            number = round(number);
        }

        sharpIndex = format.indexOf(SHARP);
        startZeroIndex = zeroIndex = format.indexOf(ZERO);

        //define the index of the first digit placeholder
        if (sharpIndex == -1 && zeroIndex != -1) {
            start = zeroIndex;
        } else if (sharpIndex != -1 && zeroIndex == -1) {
            start = sharpIndex;
        } else {
            start = sharpIndex > zeroIndex ? zeroIndex : sharpIndex;
        }

        sharpIndex = format.lastIndexOf(SHARP);
        zeroIndex = format.lastIndexOf(ZERO);

        //define the index of the last digit placeholder
        if (sharpIndex == -1 && zeroIndex != -1) {
            end = zeroIndex;
        } else if (sharpIndex != -1 && zeroIndex == -1) {
            end = sharpIndex;
        } else {
            end = sharpIndex > zeroIndex ? sharpIndex : zeroIndex;
        }

        if (start == length) {
            end = start;
        }

        if (start != -1) {
            value = number.toString().split(POINT);
            integer = value[0];
            fraction = value[1] || EMPTY;

            integerLength = integer.length;
            fractionLength = fraction.length;

            //add group separator to the number if it is longer enough
            if (hasGroup) {
                if (integerLength === groupSize && integerLength < decimalIndex - startZeroIndex) {
                    integer = groupSeparator + integer;
                } else if (integerLength > groupSize) {
                    value = EMPTY;
                    for (idx = 0; idx < integerLength; idx++) {
                        if (idx > 0 && (integerLength - idx) % groupSize === 0) {
                            value += groupSeparator;
                        }
                        value += integer.charAt(idx);
                    }

                    integer = value;
                }
            }

            number = format.substring(0, start);

            if (negative && !hasNegativeFormat) {
                number += "-";
            }

            for (idx = start; idx < length; idx++) {
                ch = format.charAt(idx);

                if (decimalIndex == -1) {
                    if (end - idx < integerLength) {
                        number += integer;
                        break;
                    }
                } else {
                    if (zeroIndex != -1 && zeroIndex < idx) {
                        replacement = EMPTY;
                    }

                    if ((decimalIndex - idx) <= integerLength && decimalIndex - idx > -1) {
                        number += integer;
                        idx = decimalIndex;
                    }

                    if (decimalIndex === idx) {
                        number += (fraction ? decimal : EMPTY) + fraction;
                        idx += end - decimalIndex + 1;
                        continue;
                    }
                }

                if (ch === ZERO) {
                    number += ch;
                    replacement = ch;
                } else if (ch === SHARP) {
                    number += replacement;
                }
            }

            if (end >= start) {
                number += format.substring(end + 1);
            }

            //replace symbol placeholders
            if (isCurrency || isPercent) {
                value = EMPTY;
                for (idx = 0, length = number.length; idx < length; idx++) {
                    ch = number.charAt(idx);
                    value += (ch === "$" || ch === "%") ? symbol : ch;
                }
                number = value;
            }

            length = literals.length;

            if (length) {
                for (idx = 0; idx < length; idx++) {
                    number = number.replace(PLACEHOLDER, literals[idx]);
                }
            }
        }

        return number;
    }

    var round = function(value, precision) {
        var power = Math.pow(10, precision || 0);
        return (Math.round(value * power) / power).toFixed(precision);
    };

    var toString = function(value, fmt, culture) {
        if (fmt) {
            if (objectToString.call(value) === "[object Date]") {
                return formatDate(value, fmt, culture);
            } else if (typeof value === NUMBER) {
                return formatNumber(value, fmt, culture);
            }
        }

        return value !== undefined ? value : "";
    };

    if (globalize) {
        toString = proxy(globalize.format, globalize);
    }

    kendo.format = function(fmt) {
        var values = arguments;

        return fmt.replace(formatRegExp, function(match, index, placeholderFormat) {
            var value = values[parseInt(index, 10) + 1];

            return toString(value, placeholderFormat ? placeholderFormat.substring(1) : "");
        });
    };

    kendo._extractFormat = function (format) {
        if (format.slice(0,3) === "{0:") {
            format = format.slice(3, format.length - 1);
        }

        return format;
    };

    kendo._activeElement = function() {
        try {
            return document.activeElement;
        } catch(e) {
            return document.documentElement.activeElement;
        }
    };

    kendo._round = round;
    kendo.toString = toString;
})();


(function() {
    var nonBreakingSpaceRegExp = /\u00A0/g,
        exponentRegExp = /[eE][\-+]?[0-9]+/,
        shortTimeZoneRegExp = /[+|\-]\d{1,2}/,
        longTimeZoneRegExp = /[+|\-]\d{1,2}:\d{2}/,
        dateRegExp = /^\/Date\((.*?)\)\/$/,
        formatsSequence = ["G", "g", "d", "F", "D", "y", "m", "T", "t"],
        numberRegExp = {
            2: /^\d{1,2}/,
            4: /^\d{4}/
        },
        objectToString = {}.toString;

    function outOfRange(value, start, end) {
        return !(value >= start && value <= end);
    }

    function designatorPredicate(designator) {
        return designator.charAt(0);
    }

    function mapDesignators(designators) {
        return $.map(designators, designatorPredicate);
    }

    //if date's day is different than the typed one - adjust
    function adjustDST(date, hours) {
        if (!hours && date.getHours() === 23) {
            date.setHours(date.getHours() + 2);
        }
    }

    function lowerArray(data) {
        var idx = 0,
            length = data.length,
            array = [];

        for (; idx < length; idx++) {
            array[idx] = (data[idx] + "").toLowerCase();
        }

        return array;
    }

    function lowerLocalInfo(localInfo) {
        var newLocalInfo = {}, property;

        for (property in localInfo) {
            newLocalInfo[property] = lowerArray(localInfo[property]);
        }

        return newLocalInfo;
    }

    function parseExact(value, format, culture) {
        if (!value) {
            return null;
        }

        var lookAhead = function (match) {
                var i = 0;
                while (format[idx] === match) {
                    i++;
                    idx++;
                }
                if (i > 0) {
                    idx -= 1;
                }
                return i;
            },
            getNumber = function(size) {
                var rg = numberRegExp[size] || new RegExp('^\\d{1,' + size + '}'),
                    match = value.substr(valueIdx, size).match(rg);

                if (match) {
                    match = match[0];
                    valueIdx += match.length;
                    return parseInt(match, 10);
                }
                return null;
            },
            getIndexByName = function (names, lower) {
                var i = 0,
                    length = names.length,
                    name, nameLength,
                    subValue;

                for (; i < length; i++) {
                    name = names[i];
                    nameLength = name.length;
                    subValue = value.substr(valueIdx, nameLength);

                    if (lower) {
                        subValue = subValue.toLowerCase();
                    }

                    if (subValue == name) {
                        valueIdx += nameLength;
                        return i + 1;
                    }
                }
                return null;
            },
            checkLiteral = function() {
                var result = false;
                if (value.charAt(valueIdx) === format[idx]) {
                    valueIdx++;
                    result = true;
                }
                return result;
            },
            calendar = culture.calendars.standard,
            year = null,
            month = null,
            day = null,
            hours = null,
            minutes = null,
            seconds = null,
            milliseconds = null,
            idx = 0,
            valueIdx = 0,
            literal = false,
            date = new Date(),
            twoDigitYearMax = calendar.twoDigitYearMax || 2029,
            defaultYear = date.getFullYear(),
            ch, count, length, pattern,
            pmHour, UTC, ISO8601, matches,
            amDesignators, pmDesignators,
            hoursOffset, minutesOffset,
            hasTime;

        if (!format) {
            format = "d"; //shord date format
        }

        //if format is part of the patterns get real format
        pattern = calendar.patterns[format];
        if (pattern) {
            format = pattern;
        }

        format = format.split("");
        length = format.length;

        for (; idx < length; idx++) {
            ch = format[idx];

            if (literal) {
                if (ch === "'") {
                    literal = false;
                } else {
                    checkLiteral();
                }
            } else {
                if (ch === "d") {
                    count = lookAhead("d");
                    if (!calendar._lowerDays) {
                        calendar._lowerDays = lowerLocalInfo(calendar.days);
                    }

                    day = count < 3 ? getNumber(2) : getIndexByName(calendar._lowerDays[count == 3 ? "namesAbbr" : "names"], true);

                    if (day === null || outOfRange(day, 1, 31)) {
                        return null;
                    }
                } else if (ch === "M") {
                    count = lookAhead("M");
                    if (!calendar._lowerMonths) {
                        calendar._lowerMonths = lowerLocalInfo(calendar.months);
                    }
                    month = count < 3 ? getNumber(2) : getIndexByName(calendar._lowerMonths[count == 3 ? 'namesAbbr' : 'names'], true);

                    if (month === null || outOfRange(month, 1, 12)) {
                        return null;
                    }
                    month -= 1; //because month is zero based
                } else if (ch === "y") {
                    count = lookAhead("y");
                    year = getNumber(count);

                    if (year === null) {
                        return null;
                    }

                    if (count == 2) {
                        if (typeof twoDigitYearMax === "string") {
                            twoDigitYearMax = defaultYear + parseInt(twoDigitYearMax, 10);
                        }

                        year = (defaultYear - defaultYear % 100) + year;
                        if (year > twoDigitYearMax) {
                            year -= 100;
                        }
                    }
                } else if (ch === "h" ) {
                    lookAhead("h");
                    hours = getNumber(2);
                    if (hours == 12) {
                        hours = 0;
                    }
                    if (hours === null || outOfRange(hours, 0, 11)) {
                        return null;
                    }
                } else if (ch === "H") {
                    lookAhead("H");
                    hours = getNumber(2);
                    if (hours === null || outOfRange(hours, 0, 23)) {
                        return null;
                    }
                } else if (ch === "m") {
                    lookAhead("m");
                    minutes = getNumber(2);
                    if (minutes === null || outOfRange(minutes, 0, 59)) {
                        return null;
                    }
                } else if (ch === "s") {
                    lookAhead("s");
                    seconds = getNumber(2);
                    if (seconds === null || outOfRange(seconds, 0, 59)) {
                        return null;
                    }
                } else if (ch === "f") {
                    count = lookAhead("f");
                    milliseconds = getNumber(count);

                    if (milliseconds !== null && count > 3) {
                        milliseconds = parseInt(milliseconds.toString().substring(0, 3), 10);
                    }

                    if (milliseconds === null || outOfRange(milliseconds, 0, 999)) {
                        return null;
                    }

                } else if (ch === "t") {
                    count = lookAhead("t");
                    amDesignators = calendar.AM;
                    pmDesignators = calendar.PM;

                    if (count === 1) {
                        amDesignators = mapDesignators(amDesignators);
                        pmDesignators = mapDesignators(pmDesignators);
                    }

                    pmHour = getIndexByName(pmDesignators);
                    if (!pmHour && !getIndexByName(amDesignators)) {
                        return null;
                    }
                }
                else if (ch === "z") {
                    UTC = true;
                    count = lookAhead("z");

                    if (value.substr(valueIdx, 1) === "Z") {
                        if (!ISO8601) {
                            return null;
                        }

                        checkLiteral();
                        continue;
                    }

                    matches = value.substr(valueIdx, 6)
                                   .match(count > 2 ? longTimeZoneRegExp : shortTimeZoneRegExp);

                    if (!matches) {
                        return null;
                    }

                    matches = matches[0];
                    valueIdx = matches.length;
                    matches = matches.split(":");

                    hoursOffset = parseInt(matches[0], 10);
                    if (outOfRange(hoursOffset, -12, 13)) {
                        return null;
                    }

                    if (count > 2) {
                        minutesOffset = parseInt(matches[1], 10);
                        if (isNaN(minutesOffset) || outOfRange(minutesOffset, 0, 59)) {
                            return null;
                        }
                    }
                } else if (ch === "T") {
                    ISO8601 = checkLiteral();
                } else if (ch === "'") {
                    literal = true;
                    checkLiteral();
                } else if (!checkLiteral()) {
                    return null;
                }
            }
        }

        hasTime = hours !== null || minutes !== null || seconds || null;

        if (year === null && month === null && day === null && hasTime) {
            year = defaultYear;
            month = date.getMonth();
            day = date.getDate();
        } else {
            if (year === null) {
                year = defaultYear;
            }

            if (day === null) {
                day = 1;
            }
        }

        if (pmHour && hours < 12) {
            hours += 12;
        }

        if (UTC) {
            if (hoursOffset) {
                hours += -hoursOffset;
            }

            if (minutesOffset) {
                minutes += -minutesOffset;
            }

            value = new Date(Date.UTC(year, month, day, hours, minutes, seconds, milliseconds));
        } else {
            value = new Date(year, month, day, hours, minutes, seconds, milliseconds);
            adjustDST(value, hours);
        }

        if (year < 100) {
            value.setFullYear(year);
        }

        if (value.getDate() !== day && UTC === undefined) {
            return null;
        }

        return value;
    }

    kendo.parseDate = function(value, formats, culture) {
        if (objectToString.call(value) === "[object Date]") {
            return value;
        }

        var idx = 0,
            date = null,
            length, patterns;

        if (value && value.indexOf("/D") === 0) {
            date = dateRegExp.exec(value);
            if (date) {
                return new Date(parseInt(date[1], 10));
            }
        }

        culture = kendo.getCulture(culture);

        if (!formats) {
            formats = [];
            patterns = culture.calendar.patterns;
            length = formatsSequence.length;

            for (; idx < length; idx++) {
                formats[idx] = patterns[formatsSequence[idx]];
            }

            idx = 0;

            formats.push(
                "yyyy/MM/dd HH:mm:ss",
                "yyyy/MM/dd HH:mm",
                "yyyy/MM/dd",
                "ddd MMM dd yyyy HH:mm:ss",
                "yyyy-MM-ddTHH:mm:ss.fffffffzzz",
                "yyyy-MM-ddTHH:mm:ss.fffzzz",
                "yyyy-MM-ddTHH:mm:sszzz",
                "yyyy-MM-ddTHH:mmzzz",
                "yyyy-MM-ddTHH:mmzz",
                "yyyy-MM-ddTHH:mm:ss",
                "yyyy-MM-ddTHH:mm",
                "yyyy-MM-dd HH:mm:ss",
                "yyyy-MM-dd HH:mm",
                "yyyy-MM-dd"
            );
        }

        formats = isArray(formats) ? formats: [formats];
        length = formats.length;

        for (; idx < length; idx++) {
            date = parseExact(value, formats[idx], culture);
            if (date) {
                return date;
            }
        }

        return date;
    };

    kendo.parseInt = function(value, culture) {
        var result = kendo.parseFloat(value, culture);
        if (result) {
            result = result | 0;
        }
        return result;
    };

    kendo.parseFloat = function(value, culture, format) {
        if (!value && value !== 0) {
           return null;
        }

        if (typeof value === NUMBER) {
           return value;
        }

        value = value.toString();
        culture = kendo.getCulture(culture);

        var number = culture.numberFormat,
            percent = number.percent,
            currency = number.currency,
            symbol = currency.symbol,
            percentSymbol = percent.symbol,
            negative = value.indexOf("-"),
            parts, isPercent;

        //handle exponential number
        if (exponentRegExp.test(value)) {
            value = parseFloat(value.replace(number["."], "."));
            if (isNaN(value)) {
                value = null;
            }
            return value;
        }

        if (negative > 0) {
            return null;
        } else {
            negative = negative > -1;
        }

        if (value.indexOf(symbol) > -1 || (format && format.toLowerCase().indexOf("c") > -1)) {
            number = currency;
            parts = number.pattern[0].replace("$", symbol).split("n");
            if (value.indexOf(parts[0]) > -1 && value.indexOf(parts[1]) > -1) {
                value = value.replace(parts[0], "").replace(parts[1], "");
                negative = true;
            }
        } else if (value.indexOf(percentSymbol) > -1) {
            isPercent = true;
            number = percent;
            symbol = percentSymbol;
        }

        value = value.replace("-", "")
                     .replace(symbol, "")
                     .replace(nonBreakingSpaceRegExp, " ")
                     .split(number[","].replace(nonBreakingSpaceRegExp, " ")).join("")
                     .replace(number["."], ".");

        value = parseFloat(value);

        if (isNaN(value)) {
            value = null;
        } else if (negative) {
            value *= -1;
        }

        if (value && isPercent) {
            value /= 100;
        }

        return value;
    };

    if (globalize) {
        kendo.parseDate = function (value, format, culture) {
            if (objectToString.call(value) === "[object Date]") {
                return value;
            }

            return globalize.parseDate(value, format, culture);
        };

        kendo.parseFloat = function (value, culture) {
            if (typeof value === NUMBER) {
                return value;
            }

            if (value === undefined || value === null) {
               return null;
            }

            value = globalize.parseFloat(value, culture);

            return isNaN(value) ? null : value;
        };
    }
})();

    function wrap(element, autosize) {
        var browser = support.browser,
            percentage,
            isRtl = element.css("direction") == "rtl";

        if (!element.parent().hasClass("k-animation-container")) {
            var shadow = element.css(kendo.support.transitions.css + "box-shadow") || element.css("box-shadow"),
                radius = shadow ? shadow.match(boxShadowRegExp) || [ 0, 0, 0, 0, 0 ] : [ 0, 0, 0, 0, 0 ],
                blur = math.max((+radius[3]), +(radius[4] || 0)),
                left = (-radius[1]) + blur,
                right = (+radius[1]) + blur,
                bottom = (+radius[2]) + blur,
                width = element[0].style.width,
                height = element[0].style.height,
                percentWidth = percentRegExp.test(width),
                percentHeight = percentRegExp.test(height);

            if (browser.opera) { // Box shadow can't be retrieved in Opera
                left = right = bottom = 5;
            }

            percentage = percentWidth || percentHeight;

            if (!percentWidth && (!autosize || (autosize && width))) { width = element.outerWidth(); }
            if (!percentHeight && (!autosize || (autosize && height))) { height = element.outerHeight(); }

            element.wrap(
                         $("
") .addClass("k-animation-container") .css({ width: width, height: height, marginLeft: left * (isRtl ? 1 : -1), paddingLeft: left, paddingRight: right, paddingBottom: bottom })); if (percentage) { element.css({ width: "100%", height: "100%", boxSizing: "border-box", mozBoxSizing: "border-box", webkitBoxSizing: "border-box" }); } } else { var wrapper = element.parent(".k-animation-container"), wrapperStyle = wrapper[0].style; if (wrapper.is(":hidden")) { wrapper.show(); } percentage = percentRegExp.test(wrapperStyle.width) || percentRegExp.test(wrapperStyle.height); if (!percentage) { wrapper.css({ width: element.outerWidth(), height: element.outerHeight(), boxSizing: "content-box", mozBoxSizing: "content-box", webkitBoxSizing: "content-box" }); } } if (browser.msie && math.floor(browser.version) <= 7) { element.css({ zoom: 1 }); element.children(".k-menu").width(element.width()); } return element.parent(); } function deepExtend(destination) { var i = 1, length = arguments.length; for (i = 1; i < length; i++) { deepExtendOne(destination, arguments[i]); } return destination; } function deepExtendOne(destination, source) { var ObservableArray = kendo.data.ObservableArray, DataSource = kendo.data.DataSource, property, propValue, propType, destProp; for (property in source) { propValue = source[property]; propType = typeof propValue; if (propType === OBJECT && propValue !== null && propValue.constructor !== Array && propValue.constructor !== ObservableArray && propValue.constructor !== DataSource) { if (propValue instanceof Date) { destination[property] = new Date(propValue.getTime()); } else { destProp = destination[property]; if (typeof (destProp) === OBJECT) { destination[property] = destProp || {}; } else { destination[property] = {}; } deepExtendOne(destination[property], propValue); } } else if (propType !== UNDEFINED) { destination[property] = propValue; } } return destination; } function testRx(agent, rxs, dflt) { for (var rx in rxs) { if (rxs.hasOwnProperty(rx) && rxs[rx].test(agent)) { return rx; } } return dflt !== undefined ? dflt : agent; } function toHyphens(str) { return str.replace(/([a-z][A-Z])/g, function (g) { return g.charAt(0) + '-' + g.charAt(1).toLowerCase(); }); } function toCamelCase(str) { return str.replace(/\-(\w)/g, function (strMatch, g1) { return g1.toUpperCase(); }); } function getComputedStyles(element, properties) { var styles = {}, computedStyle; if (document.defaultView && document.defaultView.getComputedStyle) { computedStyle = document.defaultView.getComputedStyle(element, ""); if (properties) { $.each(properties, function(idx, value) { styles[value] = computedStyle.getPropertyValue(value); }); } } else { computedStyle = element.currentStyle; if (properties) { $.each(properties, function(idx, value) { styles[value] = computedStyle[toCamelCase(value)]; }); } } if (!kendo.size(styles)) { styles = computedStyle; } return styles; } (function() { support.scrollbar = function() { var div = document.createElement("div"), result; div.style.cssText = "overflow:scroll;overflow-x:hidden;zoom:1;clear:both"; div.innerHTML = " "; document.body.appendChild(div); result = div.offsetWidth - div.scrollWidth; document.body.removeChild(div); return result; }; support.isRtl = function(element) { return $(element).closest(".k-rtl").length > 0; }; var table = document.createElement("table"); // Internet Explorer does not support setting the innerHTML of TBODY and TABLE elements try { table.innerHTML = ""; support.tbodyInnerHtml = true; } catch (e) { support.tbodyInnerHtml = false; } support.touch = "ontouchstart" in window; support.msPointers = navigator.msPointerEnabled; support.pointers = navigator.pointerEnabled; var transitions = support.transitions = false, transforms = support.transforms = false, elementProto = "HTMLElement" in window ? HTMLElement.prototype : []; support.hasHW3D = ("WebKitCSSMatrix" in window && "m11" in new window.WebKitCSSMatrix()) || "MozPerspective" in document.documentElement.style || "msPerspective" in document.documentElement.style; each([ "Moz", "webkit", "O", "ms" ], function () { var prefix = this.toString(), hasTransitions = typeof table.style[prefix + "Transition"] === STRING; if (hasTransitions || typeof table.style[prefix + "Transform"] === STRING) { var lowPrefix = prefix.toLowerCase(); transforms = { css: (lowPrefix != "ms") ? "-" + lowPrefix + "-" : "", prefix: prefix, event: (lowPrefix === "o" || lowPrefix === "webkit") ? lowPrefix : "" }; if (hasTransitions) { transitions = transforms; transitions.event = transitions.event ? transitions.event + "TransitionEnd" : "transitionend"; } return false; } }); support.transforms = transforms; support.transitions = transitions; support.devicePixelRatio = window.devicePixelRatio === undefined ? 1 : window.devicePixelRatio; try { support.screenWidth = window.outerWidth || window.screen ? window.screen.availWidth : window.innerWidth; support.screenHeight = window.outerHeight || window.screen ? window.screen.availHeight : window.innerHeight; } catch(e) { //window.outerWidth throws error when in IE showModalDialog. support.screenWidth = window.screen.availWidth; support.screenHeight = window.screen.availHeight; } support.detectOS = function (ua) { var os = false, minorVersion, match = [], notAndroidPhone = !/mobile safari/i.test(ua), agentRxs = { fire: /(Silk)\/(\d+)\.(\d+(\.\d+)?)/, android: /(Android|Android.*(?:Opera|Firefox).*?\/)\s*(\d+)\.(\d+(\.\d+)?)/, iphone: /(iPhone|iPod).*OS\s+(\d+)[\._]([\d\._]+)/, ipad: /(iPad).*OS\s+(\d+)[\._]([\d_]+)/, meego: /(MeeGo).+NokiaBrowser\/(\d+)\.([\d\._]+)/, webos: /(webOS)\/(\d+)\.(\d+(\.\d+)?)/, blackberry: /(BlackBerry|BB10).*?Version\/(\d+)\.(\d+(\.\d+)?)/, playbook: /(PlayBook).*?Tablet\s*OS\s*(\d+)\.(\d+(\.\d+)?)/, wp: /(Windows Phone(?: OS)?)\s(\d+)\.(\d+(\.\d+)?)/, windows: /(MSIE)\s+(\d+)\.(\d+(\.\d+)?)/, ffos: /(Mobile).*rv:(\d+)\.(\d+(\.\d+)?).*Firefox/ }, osRxs = { ios: /^i(phone|pad|pod)$/i, android: /^android|fire$/i, blackberry: /^blackberry|playbook/i, windows: /windows/, wp: /wp/, meego: /meego|ffos/ }, formFactorRxs = { tablet: /playbook|ipad|fire/i }, browserRxs = { omini: /Opera\sMini/i, omobile: /Opera\sMobi/i, firefox: /Firefox|Fennec/i, mobilesafari: /version\/.*safari/i, chrome: /chrome/i, webkit: /webkit/i, ie: /MSIE|Windows\sPhone/i }; for (var agent in agentRxs) { if (agentRxs.hasOwnProperty(agent)) { match = ua.match(agentRxs[agent]); if (match) { if (agent == "windows" && "plugins" in navigator) { return false; } // Break if not Metro/Mobile Windows os = {}; os.device = agent; os.tablet = testRx(agent, formFactorRxs, false); os.browser = testRx(ua, browserRxs, "default"); os.name = testRx(agent, osRxs); os[os.name] = true; os.majorVersion = match[2]; os.minorVersion = match[3].replace("_", "."); minorVersion = os.minorVersion.replace(".", "").substr(0, 2); os.flatVersion = os.majorVersion + minorVersion + (new Array(3 - (minorVersion.length < 3 ? minorVersion.length : 2)).join("0")); os.appMode = window.navigator.standalone || (/file|local|wmapp/).test(window.location.protocol) || typeof window.PhoneGap !== UNDEFINED || typeof window.cordova !== UNDEFINED; // Use file protocol to detect appModes. if (os.android && (support.devicePixelRatio < 1.5 && os.flatVersion < 400 || notAndroidPhone) && (support.screenWidth > 800 || support.screenHeight > 800)) { os.tablet = agent; } break; } } } return os; }; var mobileOS = support.mobileOS = support.detectOS(navigator.userAgent); support.wpDevicePixelRatio = mobileOS.wp ? screen.width / 320 : 0; support.kineticScrollNeeded = mobileOS && (support.touch || support.msPointers || support.pointers); support.hasNativeScrolling = false; if ((mobileOS.ios && mobileOS.majorVersion > 4) || (mobileOS.android && mobileOS.majorVersion > 2) || mobileOS.wp) { support.hasNativeScrolling = mobileOS; } support.mouseAndTouchPresent = support.touch && !(support.mobileOS.ios || support.mobileOS.android); support.detectBrowser = function(ua) { var browser = false, match = [], browserRxs = { webkit: /(chrome)[ \/]([\w.]+)/i, safari: /(webkit)[ \/]([\w.]+)/i, opera: /(opera)(?:.*version|)[ \/]([\w.]+)/i, msie: /(msie\s|trident.*? rv:)([\w.]+)/i, mozilla: /(mozilla)(?:.*? rv:([\w.]+)|)/i }; for (var agent in browserRxs) { if (browserRxs.hasOwnProperty(agent)) { match = ua.match(browserRxs[agent]); if (match) { browser = {}; browser[agent] = true; browser[match[1].toLowerCase()] = true; browser.version = parseInt(document.documentMode || match[2], 10); break; } } } return browser; }; support.browser = support.detectBrowser(navigator.userAgent); support.zoomLevel = function() { try { return support.touch ? (document.documentElement.clientWidth / window.innerWidth) : support.browser.msie && support.browser.version >= 10 ? ((top || window).outerWidth / (top || window).innerWidth) : 1; } catch(e) { return 1; } }; support.cssBorderSpacing = typeof document.documentElement.style.borderSpacing != "undefined" && !(support.browser.msie && support.browser.version < 8); (function(browser) { // add browser-specific CSS class var cssClass, majorVersion = parseInt(browser.version, 10); if (browser.msie) { cssClass = "ie"; } else if (browser.mozilla) { cssClass = "ff"; } else if (browser.safari) { cssClass = "safari"; } else if (browser.webkit) { cssClass = "webkit"; } else if (browser.opera) { cssClass = "opera"; } if (cssClass) { $(document.documentElement).addClass("k-" + cssClass + " k-" + cssClass + majorVersion); } })(support.browser); support.eventCapture = document.documentElement.addEventListener; var input = document.createElement("input"); support.placeholder = "placeholder" in input; support.input = (function() { var types = ["number", "date", "time", "month", "week", "datetime", "datetime-local"]; var length = types.length; var value = "test"; var result = {}; var idx = 0; var type; for (;idx < length; idx++) { type = types[idx]; input.setAttribute("type", type); input.value = value; result[type.replace("-", "")] = input.type !== "text" && input.value !== value; } return result; })(); support.stableSort = (function() { var sorted = [0,1,2,3,4,5,6,7,8,9,10,11,12].sort(function() { return 0; } ); return sorted[0] === 0 && sorted[1] === 1 && sorted[2] === 2 && sorted[3] === 3 && sorted[4] === 4 && sorted[5] === 5 && sorted[6] === 6 && sorted[7] === 7 && sorted[8] === 8 && sorted[9] === 9 && sorted[10] === 10 && sorted[11] === 11 && sorted[12] === 12; })(); support.matchesSelector = elementProto.webkitMatchesSelector || elementProto.mozMatchesSelector || elementProto.msMatchesSelector || elementProto.oMatchesSelector || elementProto.matchesSelector || function( selector ) { var nodeList = document.querySelectorAll ? ( this.parentNode || document ).querySelectorAll( selector ) || [] : $(selector), i = nodeList.length; while (i--) { if (nodeList[i] == this) { return true; } } return false; }; support.pushState = window.history && window.history.pushState; var documentMode = document.documentMode; support.hashChange = ("onhashchange" in window) && !(support.browser.msie && (!documentMode || documentMode <= 8)); // old IE detection })(); function size(obj) { var result = 0, key; for (key in obj) { if (obj.hasOwnProperty(key) && key != "toJSON") { // Ignore fake IE7 toJSON. result++; } } return result; } function getOffset(element, type, positioned) { if (!type) { type = "offset"; } var result = element[type](), mobileOS = support.mobileOS; if (support.touch && mobileOS.ios && mobileOS.flatVersion < 410) { // Extra processing only in broken iOS' var offset = type == "offset" ? result : element.offset(), position = (result.left == offset.left && result.top == offset.top); if (position) { return { top: result.top - window.scrollY, left: result.left - window.scrollX }; } } // TODO: Switch to browser detection here if ((kendo.support.pointers || kendo.support.msPointers) && !positioned) { // IE10 touch zoom is living in a separate viewport. result.top -= (window.pageYOffset - document.documentElement.scrollTop); result.left -= (window.pageXOffset - document.documentElement.scrollLeft); } return result; } var directions = { left: { reverse: "right" }, right: { reverse: "left" }, down: { reverse: "up" }, up: { reverse: "down" }, top: { reverse: "bottom" }, bottom: { reverse: "top" }, "in": { reverse: "out" }, out: { reverse: "in" } }; function parseEffects(input) { var effects = {}; each((typeof input === "string" ? input.split(" ") : input), function(idx) { effects[idx] = this; }); return effects; } function fx(element) { return new kendo.effects.Element(element); } var effects = {}; $.extend(effects, { Element: function(element) { this.element = $(element); }, promise: function (element, options) { if (!element.is(":visible")) { element.css({ display: element.data("olddisplay") || "block" }).css("display"); } if (options.hide) { element.data("olddisplay", element.css("display")).hide(); } if (options.init) { options.init(); } if (options.completeCallback) { options.completeCallback(element); // call the external complete callback with the element } element.dequeue(); }, transitionPromise: function(element, destination, options) { var container = kendo.wrap(element); container.append(destination); element.hide(); destination.show(); if (options.completeCallback) { options.completeCallback(element); // call the external complete callback with the element } return element; } }); function prepareAnimationOptions(options, duration, reverse, complete) { if (typeof options === STRING) { // options is the list of effect names separated by space e.g. animate(element, "fadeIn slideDown") // only callback is provided e.g. animate(element, options, function() {}); if (isFunction(duration)) { complete = duration; duration = 400; reverse = false; } if (isFunction(reverse)) { complete = reverse; reverse = false; } if (typeof duration === BOOLEAN){ reverse = duration; duration = 400; } options = { effects: options, duration: duration, reverse: reverse, complete: complete }; } return extend({ //default options effects: {}, duration: 400, //jQuery default duration reverse: false, init: noop, teardown: noop, hide: false }, options, { completeCallback: options.complete, complete: noop }); // Move external complete callback, so deferred.resolve can be always executed. } function animate(element, options, duration, reverse, complete) { var idx = 0, length = element.length, instance; for (; idx < length; idx ++) { instance = $(element[idx]); instance.queue(function() { effects.promise(instance, prepareAnimationOptions(options, duration, reverse, complete)); }); } return element; } function animateTo(element, destination, options, duration, reverse, complete) { return effects.transitionPromise(element, destination, prepareAnimationOptions(options, duration, reverse, complete)); } function toggleClass(element, classes, options, add) { if (classes) { classes = classes.split(" "); each(classes, function(idx, value) { element.toggleClass(value, add); }); } return element; } if (!("kendoAnimate" in $.fn)) { extend($.fn, { kendoStop: function(clearQueue, gotoEnd) { return this.stop(clearQueue, gotoEnd); }, kendoAnimate: function(options, duration, reverse, complete) { return animate(this, options, duration, reverse, complete); }, kendoAnimateTo: function(destination, options, duration, reverse, complete) { return animateTo(this, destination, options, duration, reverse, complete); }, kendoAddClass: function(classes, options){ return kendo.toggleClass(this, classes, options, true); }, kendoRemoveClass: function(classes, options){ return kendo.toggleClass(this, classes, options, false); }, kendoToggleClass: function(classes, options, toggle){ return kendo.toggleClass(this, classes, options, toggle); } }); } var ampRegExp = /&/g, ltRegExp = //g; function htmlEncode(value) { return ("" + value).replace(ampRegExp, "&").replace(ltRegExp, "<").replace(gtRegExp, ">"); } var eventTarget = function (e) { return e.target; }; if (support.touch) { eventTarget = function(e) { var touches = "originalEvent" in e ? e.originalEvent.changedTouches : "changedTouches" in e ? e.changedTouches : null; return touches ? document.elementFromPoint(touches[0].clientX, touches[0].clientY) : e.target; }; each(["swipe", "swipeLeft", "swipeRight", "swipeUp", "swipeDown", "doubleTap", "tap"], function(m, value) { $.fn[value] = function(callback) { return this.bind(value, callback); }; }); } if (support.touch) { if (!support.mobileOS) { support.mousedown = "mousedown touchstart"; support.mouseup = "mouseup touchend"; support.mousemove = "mousemove touchmove"; support.mousecancel = "mouseleave touchcancel"; support.click = "click"; support.resize = "resize"; } else { support.mousedown = "touchstart"; support.mouseup = "touchend"; support.mousemove = "touchmove"; support.mousecancel = "touchcancel"; support.click = "touchend"; support.resize = "orientationchange"; } } else if (support.pointers) { support.mousemove = "pointermove"; support.mousedown = "pointerdown"; support.mouseup = "pointerup"; support.mousecancel = "pointercancel"; support.click = "pointerup"; support.resize = "orientationchange resize"; } else if (support.msPointers) { support.mousemove = "MSPointerMove"; support.mousedown = "MSPointerDown"; support.mouseup = "MSPointerUp"; support.mousecancel = "MSPointerCancel"; support.click = "MSPointerUp"; support.resize = "orientationchange resize"; } else { support.mousemove = "mousemove"; support.mousedown = "mousedown"; support.mouseup = "mouseup"; support.mousecancel = "mouseleave"; support.click = "click"; support.resize = "resize"; } var wrapExpression = function(members, paramName) { var result = paramName || "d", index, idx, length, member, count = 1; for (idx = 0, length = members.length; idx < length; idx++) { member = members[idx]; if (member !== "") { index = member.indexOf("["); if (index !== 0) { if (index == -1) { member = "." + member; } else { count++; member = "." + member.substring(0, index) + " || {})" + member.substring(index); } } count++; result += member + ((idx < length - 1) ? " || {})" : ")"); } } return new Array(count).join("(") + result; }, localUrlRe = /^([a-z]+:)?\/\//i; extend(kendo, { ui: kendo.ui || {}, fx: kendo.fx || fx, effects: kendo.effects || effects, mobile: kendo.mobile || {}, data: kendo.data || {}, dataviz: kendo.dataviz || {ui: { roles: {}}}, keys: { INSERT: 45, DELETE: 46, BACKSPACE: 8, TAB: 9, ENTER: 13, ESC: 27, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, END: 35, HOME: 36, SPACEBAR: 32, PAGEUP: 33, PAGEDOWN: 34, F2: 113, F10: 121, F12: 123 }, support: kendo.support || support, animate: kendo.animate || animate, ns: "", attr: function(value) { return "data-" + kendo.ns + value; }, wrap: wrap, deepExtend: deepExtend, getComputedStyles: getComputedStyles, size: size, toCamelCase: toCamelCase, toHyphens: toHyphens, getOffset: kendo.getOffset || getOffset, parseEffects: kendo.parseEffects || parseEffects, toggleClass: kendo.toggleClass || toggleClass, directions: kendo.directions || directions, Observable: Observable, Class: Class, Template: Template, template: proxy(Template.compile, Template), render: proxy(Template.render, Template), stringify: proxy(JSON.stringify, JSON), eventTarget: eventTarget, htmlEncode: htmlEncode, isLocalUrl: function(url) { return url && !localUrlRe.test(url); }, expr: function(expression, safe, paramName) { expression = expression || ""; if (typeof safe == STRING) { paramName = safe; safe = false; } paramName = paramName || "d"; if (expression && expression.charAt(0) !== "[") { expression = "." + expression; } if (safe) { expression = wrapExpression(expression.split("."), paramName); } else { expression = paramName + expression; } return expression; }, getter: function(expression, safe) { return getterCache[expression] = getterCache[expression] || new Function("d", "return " + kendo.expr(expression, safe)); }, setter: function(expression) { return setterCache[expression] = setterCache[expression] || new Function("d,value", kendo.expr(expression) + "=value"); }, accessor: function(expression) { return { get: kendo.getter(expression), set: kendo.setter(expression) }; }, guid: function() { var id = "", i, random; for (i = 0; i < 32; i++) { random = math.random() * 16 | 0; if (i == 8 || i == 12 || i == 16 || i == 20) { id += "-"; } id += (i == 12 ? 4 : (i == 16 ? (random & 3 | 8) : random)).toString(16); } return id; }, roleSelector: function(role) { return role.replace(/(\S+)/g, "[" + kendo.attr("role") + "=$1],").slice(0, -1); }, triggeredByInput: function(e) { return (/^(label|input|textarea|select)$/i).test(e.target.tagName); }, logToConsole: function(message) { var console = window.console; if (typeof(console) != "undefined" && console.log) { console.log(message); } } }); var Widget = Observable.extend( { init: function(element, options) { var that = this; that.element = kendo.jQuery(element).handler(that); Observable.fn.init.call(that); options = that.options = extend(true, {}, that.options, options); if (!that.element.attr(kendo.attr("role"))) { that.element.attr(kendo.attr("role"), (options.name || "").toLowerCase()); } that.element.data("kendo" + options.prefix + options.name, that); that.bind(that.events, options); }, events: [], options: { prefix: "" }, _hasBindingTarget: function() { return !!this.element[0].kendoBindingTarget; }, _tabindex: function(target) { target = target || this.wrapper; var element = this.element, TABINDEX = "tabindex", tabindex = target.attr(TABINDEX) || element.attr(TABINDEX); element.removeAttr(TABINDEX); target.attr(TABINDEX, !isNaN(tabindex) ? tabindex : 0); }, setOptions: function(options) { var that = this, idx = 0, length = that.events.length, e; for (; idx < length; idx ++) { e = that.events[idx]; if (that.options[e] && options[e]) { that.unbind(e, that.options[e]); } } $.extend(that.options, options); that.bind(that.events, options); }, resize: function(force) { var size = this.getSize(), currentSize = this._size; if (force || !currentSize || size.width !== currentSize.width || size.height !== currentSize.height) { this._resize(size); this.trigger("resize", size); this._size = size; } }, getSize: function() { return kendo.dimensions(this.element); }, size: function(size) { if (!size) { return this.getSize(); } else { this.setSize(size); } }, setSize: $.noop, _resize: $.noop, destroy: function() { var that = this; that.element.removeData("kendo" + that.options.prefix + that.options.name); that.element.removeData("handler"); that.unbind(); } }); kendo.dimensions = function(element, dimensions) { var domElement = element[0]; if (dimensions) { element.css(dimensions); } return { width: domElement.offsetWidth, height: domElement.offsetHeight }; }; kendo.notify = noop; var templateRegExp = /template$/i, jsonRegExp = /^\s*(?:\{(?:.|\r\n|\n)*\}|\[(?:.|\r\n|\n)*\])\s*$/, jsonFormatRegExp = /^\{(\d+)(:[^\}]+)?\}/, dashRegExp = /([A-Z])/g; function parseOption(element, option) { var value; if (option.indexOf("data") === 0) { option = option.substring(4); option = option.charAt(0).toLowerCase() + option.substring(1); } option = option.replace(dashRegExp, "-$1"); value = element.getAttribute("data-" + kendo.ns + option); if (value === null) { value = undefined; } else if (value === "null") { value = null; } else if (value === "true") { value = true; } else if (value === "false") { value = false; } else if (numberRegExp.test(value)) { value = parseFloat(value); } else if (jsonRegExp.test(value) && !jsonFormatRegExp.test(value)) { value = evil("(" + value + ")"); } return value; } function parseOptions(element, options) { var result = {}, option, value; for (option in options) { value = parseOption(element, option); if (value !== undefined) { if (templateRegExp.test(option)) { value = kendo.template($("#" + value).html()); } result[option] = value; } } return result; } kendo.initWidget = function(element, options, roles) { var result, option, widget, idx, length, role, value, dataSource; // Preserve backwards compatibility with (element, options, namespace) signature, where namespace was kendo.ui if (!roles) { roles = kendo.ui.roles; } else if (roles.roles) { roles = roles.roles; } element = element.nodeType ? element : element[0]; role = element.getAttribute("data-" + kendo.ns + "role"); if (!role) { return; } if (role.indexOf(".") === -1) { widget = roles[role]; } else { // full namespace path - like kendo.ui.Widget widget = kendo.getter(role)(window); } if (!widget) { return; } dataSource = parseOption(element, "dataSource"); options = $.extend({}, parseOptions(element, widget.fn.options), options); if (dataSource) { if (typeof dataSource === STRING) { options.dataSource = kendo.getter(dataSource)(window); } else { options.dataSource = dataSource; } } for (idx = 0, length = widget.fn.events.length; idx < length; idx++) { option = widget.fn.events[idx]; value = parseOption(element, option); if (value !== undefined) { options[option] = kendo.getter(value)(window); } } result = $(element).data("kendo" + widget.fn.options.prefix + widget.fn.options.name); if (!result) { result = new widget(element, options); } else { result.setOptions(options); } return result; }; kendo.rolesFromNamespaces = function(namespaces) { var roles = [], idx, length; if (!namespaces[0]) { namespaces = [kendo.ui, kendo.dataviz.ui]; } for (idx = 0, length = namespaces.length; idx < length; idx ++) { roles[idx] = namespaces[idx].roles; } return extend.apply(null, [{}].concat(roles.reverse())); }; kendo.init = function(element) { var roles = kendo.rolesFromNamespaces(slice.call(arguments, 1)); $(element).find("[data-" + kendo.ns + "role]").addBack().each(function(){ kendo.initWidget(this, {}, roles); }); }; kendo.destroy = function(element) { $(element).find("[data-" + kendo.ns + "role]").addBack().each(function(){ var widget = kendo.widgetInstance($(this)); if (widget) { widget.destroy(); } }); }; kendo.resize = function(element) { $(element).each(function() { var child = $(this), widget; if (!child.is(":visible") && $.inArray(child.attr("data-role"), ["slider", "rangeslider"]) === -1) { return; } if (child.is("[data-" + kendo.ns + "role]")) { widget = kendo.widgetInstance(child); if (widget) { widget.resize(); } } kendo.resize(child.children()); }); }; kendo.parseOptions = parseOptions; extend(kendo.ui, { Widget: Widget, roles: {}, progress: function(container, toggle) { var mask = container.find(".k-loading-mask"), support = kendo.support, browser = support.browser, isRtl, leftRight, webkitCorrection, containerScrollLeft; if (toggle) { if (!mask.length) { isRtl = support.isRtl(container); leftRight = isRtl ? "right" : "left"; containerScrollLeft = container.scrollLeft(); webkitCorrection = browser.webkit ? (!isRtl ? 0 : container[0].scrollWidth - container.width() - 2 * containerScrollLeft) : 0; mask = $("
Loading...
") .width("100%").height("100%") .css("top", container.scrollTop()) .css(leftRight, Math.abs(containerScrollLeft) + webkitCorrection) .prependTo(container); } } else if (mask) { mask.remove(); } }, plugin: function(widget, register, prefix) { var name = widget.fn.options.name, getter; register = register || kendo.ui; prefix = prefix || ""; register[name] = widget; register.roles[name.toLowerCase()] = widget; getter = "getKendo" + prefix + name; name = "kendo" + prefix + name; $.fn[name] = function(options) { var value = this, args; if (typeof options === STRING) { args = slice.call(arguments, 1); this.each(function(){ var widget = $.data(this, name), method, result; if (!widget) { throw new Error(kendo.format("Cannot call method '{0}' of {1} before it is initialized", options, name)); } method = widget[options]; if (typeof method !== FUNCTION) { throw new Error(kendo.format("Cannot find method '{0}' of {1}", options, name)); } result = method.apply(widget, args); if (result !== undefined) { value = result; return false; } }); } else { this.each(function() { new widget(this, options); }); } return value; }; $.fn[getter] = function() { return this.data(name); }; } }); var ContainerNullObject = { bind: function () { return this; } }; var MobileWidget = Widget.extend({ init: function(element, options) { Widget.fn.init.call(this, element, options); this.element.autoApplyNS(); this.wrapper = this.element; this.element.addClass("km-widget"); }, destroy: function() { Widget.fn.destroy.call(this); this.element.kendoDestroy(); }, options: { prefix: "Mobile" }, events: [], view: function() { var viewElement = this.element.closest(kendo.roleSelector("view splitview modalview drawer")); return kendo.widgetInstance(viewElement, kendo.mobile.ui); }, container: function() { var element = this.element.closest(kendo.roleSelector("view layout modalview drawer")); return kendo.widgetInstance(element, kendo.mobile.ui) || ContainerNullObject; } }); extend(kendo.mobile, { init: function(element) { kendo.init(element, kendo.mobile.ui, kendo.ui, kendo.dataviz.ui); }, ui: { Widget: MobileWidget, roles: {}, plugin: function(widget) { kendo.ui.plugin(widget, kendo.mobile.ui, "Mobile"); } } }); kendo.touchScroller = function(elements, options) { // return the first touch scroller return $(elements).map(function(idx, element) { element = $(element); if (support.kineticScrollNeeded && kendo.mobile.ui.Scroller && !element.data("kendoMobileScroller")) { element.kendoMobileScroller(options); return element.data("kendoMobileScroller"); } else { return false; } })[0]; }; kendo.preventDefault = function(e) { e.preventDefault(); }; kendo.widgetInstance = function(element, suite) { var role = element.data(kendo.ns + "role"); // HACK!!! mobile view scroller widgets are instantiated on data-role="content" elements. We need to discover them when resizing. if (role === "content") { role = "scroller"; } if (!suite) { suite = { roles: $.extend({}, kendo.mobile.ui.roles, kendo.dataviz.ui.roles, kendo.ui.roles) }; } var widget = suite.roles[role]; if (widget) { return element.data("kendo" + widget.fn.options.prefix + widget.fn.options.name); } }; kendo.onResize = function(callback) { var handler = callback; if (support.mobileOS.android) { handler = function() { setTimeout(callback, 600); }; } $(window).on(support.resize, handler); return handler; }; kendo.unbindResize = function(callback) { $(window).off(support.resize, callback); }; kendo.attrValue = function(element, key) { return element.data(kendo.ns + key); }; kendo.days = { Sunday: 0, Monday: 1, Tuesday: 2, Wednesday: 3, Thursday: 4, Friday: 5, Saturday: 6 }; function focusable(element, isTabIndexNotNaN) { var nodeName = element.nodeName.toLowerCase(); return (/input|select|textarea|button|object/.test(nodeName) ? !element.disabled : "a" === nodeName ? element.href || isTabIndexNotNaN : isTabIndexNotNaN ) && visible(element); } function visible(element) { return !$(element).parents().addBack().filter(function() { return $.css(this,"visibility") === "hidden" || $.expr.filters.hidden(this); }).length; } $.extend($.expr[ ":" ], { kendoFocusable: function(element) { var idx = $.attr(element, "tabindex"); return focusable(element, !isNaN(idx) && idx > -1); } }); var MOUSE_EVENTS = ["mousedown", "mousemove", "mouseenter", "mouseleave", "mouseover", "mouseout", "mouseup", "click"]; var EXCLUDE_BUST_CLICK_SELECTOR = "label, input, [data-rel=external]"; var MouseEventNormalizer = { setupMouseMute: function() { var idx = 0, length = MOUSE_EVENTS.length, element = document.documentElement; if (MouseEventNormalizer.mouseTrap || !support.eventCapture) { return; } MouseEventNormalizer.mouseTrap = true; MouseEventNormalizer.bustClick = false; MouseEventNormalizer.captureMouse = false; var handler = function(e) { if (MouseEventNormalizer.captureMouse) { if (e.type === "click") { if (MouseEventNormalizer.bustClick && !$(e.target).is(EXCLUDE_BUST_CLICK_SELECTOR)) { e.preventDefault(); e.stopPropagation(); } } else { e.stopPropagation(); } } }; for (; idx < length; idx++) { element.addEventListener(MOUSE_EVENTS[idx], handler, true); } }, muteMouse: function(e) { MouseEventNormalizer.captureMouse = true; if (e.data.bustClick) { MouseEventNormalizer.bustClick = true; } clearTimeout(MouseEventNormalizer.mouseTrapTimeoutID); }, unMuteMouse: function() { clearTimeout(MouseEventNormalizer.mouseTrapTimeoutID); MouseEventNormalizer.mouseTrapTimeoutID = setTimeout(function() { MouseEventNormalizer.captureMouse = false; MouseEventNormalizer.bustClick = false; }, 400); } }; var eventMap = { down: "touchstart mousedown", move: "mousemove touchmove", up: "mouseup touchend touchcancel", cancel: "mouseleave touchcancel" }; if (support.touch && (support.mobileOS.ios || support.mobileOS.android)) { eventMap = { down: "touchstart", move: "touchmove", up: "touchend touchcancel", cancel: "touchcancel" }; } else if (support.pointers) { eventMap = { down: "pointerdown", move: "pointermove", up: "pointerup", cancel: "pointercancel pointerleave" }; } else if (support.msPointers) { eventMap = { down: "MSPointerDown", move: "MSPointerMove", up: "MSPointerUp", cancel: "MSPointerCancel MSPointerLeave" }; } if (support.msPointers && !("onmspointerenter" in window)) { // IE10 // Create MSPointerEnter/MSPointerLeave events using mouseover/out and event-time checks $.each({ MSPointerEnter: "MSPointerOver", MSPointerLeave: "MSPointerOut" }, function( orig, fix ) { $.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function( event ) { var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj; // For mousenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || (related !== target && !$.contains( target, related )) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; }); } var getEventMap = function(e) { return (eventMap[e] || e); }, eventRegEx = /([^ ]+)/g; kendo.applyEventMap = function(events, ns) { events = events.replace(eventRegEx, getEventMap); if (ns) { events = events.replace(eventRegEx, "$1." + ns); } return events; }; var on = $.fn.on; function kendoJQuery(selector, context) { return new kendoJQuery.fn.init(selector, context); } extend(true, kendoJQuery, $); kendoJQuery.fn = kendoJQuery.prototype = new $(); kendoJQuery.fn.constructor = kendoJQuery; kendoJQuery.fn.init = function(selector, context) { if (context && context instanceof $ && !(context instanceof kendoJQuery)) { context = kendoJQuery(context); } return $.fn.init.call(this, selector, context, rootjQuery); }; kendoJQuery.fn.init.prototype = kendoJQuery.fn; var rootjQuery = kendoJQuery(document); extend(kendoJQuery.fn, { handler: function(handler) { this.data("handler", handler); return this; }, autoApplyNS: function(ns) { this.data("kendoNS", ns || kendo.guid()); return this; }, on: function() { var that = this, ns = that.data("kendoNS"); // support for event map signature if (arguments.length === 1) { return on.call(that, arguments[0]); } var context = that, args = slice.call(arguments); if (typeof args[args.length -1] === UNDEFINED) { args.pop(); } var callback = args[args.length - 1], events = kendo.applyEventMap(args[0], ns); // setup mouse trap if (support.mouseAndTouchPresent && events.search(/mouse|click/) > -1 && this[0] !== document.documentElement) { MouseEventNormalizer.setupMouseMute(); var selector = args.length === 2 ? null : args[1], bustClick = events.indexOf("click") > -1 && events.indexOf("touchend") > -1; on.call(this, { touchstart: MouseEventNormalizer.muteMouse, touchend: MouseEventNormalizer.unMuteMouse }, selector, { bustClick: bustClick }); } if (typeof callback === STRING) { context = that.data("handler"); callback = context[callback]; args[args.length - 1] = function(e) { callback.call(context, e); }; } args[0] = events; on.apply(that, args); return that; }, kendoDestroy: function(ns) { ns = ns || this.data("kendoNS"); if (ns) { this.off("." + ns); } return this; } }); kendo.jQuery = kendoJQuery; kendo.eventMap = eventMap; kendo.timezone = (function(){ var months = { Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11 }; var days = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 }; function ruleToDate(year, rule) { var date; var targetDay; var ourDay; var month = rule[3]; var on = rule[4]; var time = rule[5]; var cache = rule[8]; if (!cache) { rule[8] = cache = {}; } if (cache[year]) { return cache[year]; } if (!isNaN(on)) { date = new Date(Date.UTC(year, months[month], on, time[0], time[1], time[2], 0)); } else if (on.indexOf("last") === 0) { date = new Date(Date.UTC(year, months[month] + 1, 1, time[0] - 24, time[1], time[2], 0)); targetDay = days[on.substr(4, 3)]; ourDay = date.getUTCDay(); date.setUTCDate(date.getUTCDate() + targetDay - ourDay - (targetDay > ourDay ? 7 : 0)); } else if (on.indexOf(">=") >= 0) { date = new Date(Date.UTC(year, months[month], on.substr(5), time[0], time[1], time[2], 0)); targetDay = days[on.substr(0, 3)]; ourDay = date.getUTCDay(); date.setUTCDate(date.getUTCDate() + targetDay - ourDay + (targetDay < ourDay ? 7 : 0)); } return cache[year] = date; } function findRule(utcTime, rules, zone) { rules = rules[zone]; if (!rules) { var time = zone.split(":"); var offset = 0; if (time.length > 1) { offset = time[0] * 60 + Number(time[1]); } return [-1000000, 'max', '-', 'Jan', 1, [0, 0, 0], offset, '-']; } var year = new Date(utcTime).getUTCFullYear(); rules = jQuery.grep(rules, function(rule) { var from = rule[0]; var to = rule[1]; return from <= year && (to >= year || (from == year && to == "only") || to == "max"); }); rules.push(utcTime); rules.sort(function(a, b) { if (typeof a != "number") { a = Number(ruleToDate(year, a)); } if (typeof b != "number") { b = Number(ruleToDate(year, b)); } return a - b; }); return rules[jQuery.inArray(utcTime, rules) - 1]; } function findZone(utcTime, zones, timezone) { var zoneRules = zones[timezone]; if (typeof zoneRules === "string") { zoneRules = zones[zoneRules]; } if (!zoneRules) { throw new Error('Timezone "' + timezone + '" is either incorrect, or kendo.timezones.min.js is not included.'); } for (var idx = zoneRules.length - 1; idx >= 0; idx--) { var until = zoneRules[idx][3]; if (until && utcTime > until) { break; } } var zone = zoneRules[idx + 1]; if (!zone) { throw new Error('Timezone "' + timezone + '" not found on ' + utcTime + "."); } return zone; } function zoneAndRule(utcTime, zones, rules, timezone) { if (typeof utcTime != NUMBER) { utcTime = Date.UTC(utcTime.getFullYear(), utcTime.getMonth(), utcTime.getDate(), utcTime.getHours(), utcTime.getMinutes(), utcTime.getSeconds(), utcTime.getMilliseconds()); } var zone = findZone(utcTime, zones, timezone); return { zone: zone, rule: findRule(utcTime, rules, zone[1]) }; } function offset(utcTime, timezone) { if (timezone == "Etc/UTC" || timezone == "Etc/GMT") { return 0; } var info = zoneAndRule(utcTime, this.zones, this.rules, timezone); var zone = info.zone; var rule = info.rule; return rule? zone[0] - rule[6] : zone[0]; } function abbr(utcTime, timezone) { var info = zoneAndRule(utcTime, this.zones, this.rules, timezone); var zone = info.zone; var rule = info.rule; var base = zone[2]; if (base.indexOf("/") >= 0) { return base.split("/")[rule && rule[6] ? 1 : 0]; } else if (base.indexOf("%s") >= 0) { return base.replace("%s", (!rule || rule[7] == "-") ? '' : rule[7]); } return base; } function convert(date, fromOffset, toOffset) { if (typeof fromOffset == STRING) { fromOffset = this.offset(date, fromOffset); } if (typeof toOffset == STRING) { toOffset = this.offset(date, toOffset); } var fromLocalOffset = date.getTimezoneOffset(); date = new Date(date.getTime() + (fromOffset - toOffset) * 60000); var toLocalOffset = date.getTimezoneOffset(); return new Date(date.getTime() + (toLocalOffset - fromLocalOffset) * 60000); } function apply(date, timezone) { return this.convert(date, date.getTimezoneOffset(), timezone); } function remove(date, timezone) { return this.convert(date, timezone, date.getTimezoneOffset()); } function toLocalDate(time) { return this.apply(new Date(time), "Etc/UTC"); } return { zones: {}, rules: {}, offset: offset, convert: convert, apply: apply, remove: remove, abbr: abbr, toLocalDate: toLocalDate }; })(); kendo.date = (function(){ var MS_PER_MINUTE = 60000, MS_PER_DAY = 86400000; function adjustDST(date, hours) { if (hours === 0 && date.getHours() === 23) { date.setHours(date.getHours() + 2); return true; } return false; } function setDayOfWeek(date, day, dir) { var hours = date.getHours(); dir = dir || 1; day = ((day - date.getDay()) + (7 * dir)) % 7; date.setDate(date.getDate() + day); adjustDST(date, hours); } function dayOfWeek(date, day, dir) { date = new Date(date); setDayOfWeek(date, day, dir); return date; } function firstDayOfMonth(date) { return new Date( date.getFullYear(), date.getMonth(), 1 ); } function lastDayOfMonth(date) { var last = new Date(date.getFullYear(), date.getMonth() + 1, 0), first = firstDayOfMonth(date), timeOffset = Math.abs(last.getTimezoneOffset() - first.getTimezoneOffset()); if (timeOffset) { last.setHours(first.getHours() + (timeOffset / 60)); } return last; } function getDate(date) { date = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); adjustDST(date, 0); return date; } function toUtcTime(date) { return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()); } function getMilliseconds(date) { return date.getTime() - getDate(date); } function isInTimeRange(value, min, max) { var msMin = getMilliseconds(min), msMax = getMilliseconds(max), msValue; if (!value || msMin == msMax) { return true; } if (min >= max) { max += MS_PER_DAY; } msValue = getMilliseconds(value); if (msMin > msValue) { msValue += MS_PER_DAY; } if (msMax < msMin) { msMax += MS_PER_DAY; } return msValue >= msMin && msValue <= msMax; } function isInDateRange(value, min, max) { var msMin = min.getTime(), msMax = max.getTime(), msValue; if (msMin >= msMax) { msMax += MS_PER_DAY; } msValue = value.getTime(); return msValue >= msMin && msValue <= msMax; } function addDays(date, offset) { var hours = date.getHours(); date = new Date(date); setTime(date, offset * MS_PER_DAY); adjustDST(date, hours); return date; } function setTime(date, milliseconds, ignoreDST) { var offset = date.getTimezoneOffset(); var difference; date.setTime(date.getTime() + milliseconds); if (!ignoreDST) { difference = date.getTimezoneOffset() - offset; date.setTime(date.getTime() + difference * MS_PER_MINUTE); } } function today() { return getDate(new Date()); } function isToday(date) { return getDate(date).getTime() == today().getTime(); } function toInvariantTime(date) { var staticDate = new Date(1980, 1, 1, 0, 0, 0); if (date) { staticDate.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()); } return staticDate; } return { adjustDST: adjustDST, dayOfWeek: dayOfWeek, setDayOfWeek: setDayOfWeek, getDate: getDate, isInDateRange: isInDateRange, isInTimeRange: isInTimeRange, isToday: isToday, nextDay: function(date) { return addDays(date, 1); }, previousDay: function(date) { return addDays(date, -1); }, toUtcTime: toUtcTime, MS_PER_DAY: MS_PER_DAY, MS_PER_MINUTE: MS_PER_MINUTE, setTime: setTime, addDays: addDays, today: today, toInvariantTime: toInvariantTime, firstDayOfMonth: firstDayOfMonth, lastDayOfMonth: lastDayOfMonth, getMilliseconds: getMilliseconds //TODO methods: combine date portion and time portion from arguments - date1, date 2 }; })(); kendo.stripWhitespace = function(element) { var iterator = document.createNodeIterator(element, NodeFilter.SHOW_TEXT, function(node) { return node.parentNode == element ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; }, false); while (iterator.nextNode()) { if (iterator.referenceNode && !iterator.referenceNode.textContent.trim()) { iterator.referenceNode.parentNode.removeChild(iterator.referenceNode); } } }; var animationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback){ setTimeout(callback, 1000 / 60); }; kendo.animationFrame = function(callback) { animationFrame.call(window, callback); }; var animationQueue = []; kendo.queueAnimation = function(callback) { animationQueue[animationQueue.length] = callback; if (animationQueue.length === 1) { kendo.runNextAnimation(); } }; kendo.runNextAnimation = function() { var callback = animationQueue.shift(); kendo.animationFrame(function() { callback(); if (animationFrame[0]) { kendo.runNextAnimation(); } }); }; kendo.parseQueryStringParams = function(url) { var queryString = url.split('?')[1] || "", params = {}, paramParts = queryString.split(/&|=/), length = paramParts.length, idx = 0; for (; idx < length; idx += 2) { if(paramParts[idx] !== "") { params[decodeURIComponent(paramParts[idx])] = decodeURIComponent(paramParts[idx + 1]); } } return params; }; })(jQuery, eval); /*global kendo_module:true */ if (typeof kendo_module === "undefined") { kendo_module = function(){}; } kendo_module({ id: "core", name: "Core", category: "framework", description: "The core of the Kendo framework." }); kendo_module({ id: "router", name: "Router", category: "framework", description: "The Router class is responsible for tracking the application state and navigating between the application states.", depends: [ "core" ], hidden: false }); (function($, undefined) { var kendo = window.kendo, CHANGE = "change", BACK = "back", support = kendo.support, location = window.location, history = window.history, CHECK_URL_INTERVAL = 50, hashStrip = /^#*/, document = window.document; function absoluteURL(path, pathPrefix) { if (!pathPrefix) { return path; } if (path + "/" === pathPrefix) { path = pathPrefix; } var regEx = new RegExp("^" + pathPrefix, "i"); if (!regEx.test(path)) { path = pathPrefix + "/" + path; } return location.protocol + '//' + (location.host + "/" + path).replace(/\/\/+/g, '/'); } function stripRoot(root, url) { if (url.indexOf(root) === 0) { return (url.substr(root.length)).replace(/\/\//g, '/'); } else { return root; } } var PushStateAdapter = kendo.Class.extend({ init: function(root) { this.root = root; }, navigate: function(to) { history.pushState({}, document.title, absoluteURL(to, this.root)); return this.current(); }, current: function() { var current = location.pathname; if (location.search) { current += location.search; } return stripRoot(this.root, current); }, change: function(callback) { $(window).bind("popstate.kendo", callback); }, stop: function() { $(window).unbind("popstate.kendo"); } }); var HashAdapter = kendo.Class.extend({ navigate: function(to) { location.hash = to; return to; }, change: function(callback) { if (support.hashChange) { $(window).bind("hashchange.kendo", callback); } else { this._interval = setInterval(callback, CHECK_URL_INTERVAL); } }, stop: function() { $(window).unbind("popstate.kendo"); clearInterval(this._interval); }, current: function() { return location.hash.replace(hashStrip, ''); } }); var History = kendo.Observable.extend({ start: function(options) { options = options || {}; this.bind([CHANGE, BACK], options); if (this._started) { return; } this._started = true; var pathname = location.pathname, hash = location.hash, pushState = support.pushState && options.pushState, root = options.root || "/", atRoot = root === pathname; this.adapter = pushState ? new PushStateAdapter(root) : new HashAdapter(); if (options.pushState && !support.pushState && !atRoot) { location.replace(root + '#' + stripRoot(root, pathname)); return true; // browser will reload at this point. } if (pushState) { var fixedUrl; if (root === pathname + "/") { fixedUrl = root; } if (atRoot && hash) { fixedUrl = absoluteURL(hash.replace(hashStrip, ''), root); } if (fixedUrl) { history.replaceState({}, document.title, fixedUrl); } } this.root = root; this.current = this.adapter.current(); this.locations = [this.current]; this.adapter.change($.proxy(this, "_checkUrl")); }, stop: function() { if (!this._started) { return; } this.adapter.stop(); this.unbind(CHANGE); this._started = false; }, change: function(callback) { this.bind(CHANGE, callback); }, navigate: function(to, silent) { if (to === "#:back") { history.back(); return; } to = to.replace(hashStrip, ''); if (this.current === to || this.current === decodeURIComponent(to)) { return; } if (!silent) { if (this.trigger(CHANGE, { url: to })) { return; } } this.current = this.adapter.navigate(to); this.locations.push(this.current); }, _checkUrl: function() { var current = this.adapter.current(), back = current === this.locations[this.locations.length - 2], prev = this.current; if (this.current === current || this.current === decodeURIComponent(current)) { return; } this.current = current; if (back && this.trigger("back", { url: prev, to: current })) { history.forward(); this.current = prev; return; } if (this.trigger(CHANGE, { url: current })) { if (back) { history.forward(); } else { history.back(); } this.current = prev; return; } if (back) { this.locations.pop(); } else { this.locations.push(current); } } }); kendo.absoluteURL = absoluteURL; kendo.history = new History(); })(window.kendo.jQuery); (function() { var kendo = window.kendo, history = kendo.history, Observable = kendo.Observable, INIT = "init", ROUTE_MISSING = "routeMissing", CHANGE = "change", BACK = "back", optionalParam = /\((.*?)\)/g, namedParam = /(\(\?)?:\w+/g, splatParam = /\*\w+/g, escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; function namedParamReplace(match, optional) { return optional ? match : '([^\/]+)'; } function routeToRegExp(route) { return new RegExp('^' + route .replace(escapeRegExp, '\\$&') .replace(optionalParam, '(?:$1)?') .replace(namedParam, namedParamReplace) .replace(splatParam, '(.*?)') + '$'); } function stripUrl(url) { return url.replace(/(\?.*)|(#.*)/g, ""); } var Route = kendo.Class.extend({ init: function(route, callback) { if (!(route instanceof RegExp)) { route = routeToRegExp(route); } this.route = route; this._callback = callback; }, callback: function(url) { var params, idx = 0, length, queryStringParams = kendo.parseQueryStringParams(url); url = stripUrl(url); params = this.route.exec(url).slice(1); length = params.length; for (; idx < length; idx ++) { if (typeof params[idx] !== 'undefined') { params[idx] = decodeURIComponent(params[idx]); } } params.push(queryStringParams); this._callback.apply(null, params); }, worksWith: function(url) { if (this.route.test(url)) { this.callback(url); return true; } else { return false; } } }); var Router = Observable.extend({ init: function(options) { Observable.fn.init.call(this); this.routes = []; this.pushState = options ? options.pushState : false; if (options && options.root) { this.root = options.root; } this.bind([INIT, ROUTE_MISSING, CHANGE], options); }, destroy: function() { history.unbind(CHANGE, this._urlChangedProxy); history.unbind(BACK, this._backProxy); this.unbind(); }, start: function() { var that = this, backProxy = function(e) { that._back(e); }, urlChangedProxy = function(e) { that._urlChanged(e); }; history.start({ change: urlChangedProxy, back: backProxy, pushState: that.pushState, root: that.root }); var initEventObject = { url: history.current || "/" }; if (!that.trigger(INIT, initEventObject)) { that._urlChanged(initEventObject); } this._urlChangedProxy = urlChangedProxy; this._backProxy = backProxy; }, route: function(route, callback) { this.routes.push(new Route(route, callback)); }, navigate: function(url, silent) { kendo.history.navigate(url, silent); }, _back: function(e) { if (this.trigger(BACK, { url: e.url, to: e.to })) { e.preventDefault(); } }, _urlChanged: function(e) { var url = e.url; if (!url) { url = "/"; } if (this.trigger(CHANGE, { url: e.url, params: kendo.parseQueryStringParams(e.url) })) { e.preventDefault(); return; } var idx = 0, routes = this.routes, route, length = routes.length; for (; idx < length; idx ++) { route = routes[idx]; if (route.worksWith(url)) { return; } } if (this.trigger(ROUTE_MISSING, { url: url, params: kendo.parseQueryStringParams(url) })) { e.preventDefault(); } } }); kendo.Router = Router; })(); kendo_module({ id: "view", name: "View", category: "framework", description: "The View class instantiates and handles the events of a certain screen from the application.", depends: [ "core", "binder" ], hidden: false }); (function($, undefined) { var kendo = window.kendo, Observable = kendo.Observable, SCRIPT = "SCRIPT", INIT = "init", SHOW = "show", HIDE = "hide", sizzleErrorRegExp = /unrecognized expression/; var View = Observable.extend({ init: function(content, options) { var that = this; options = options || {}; Observable.fn.init.call(that); that.content = content; that.tagName = options.tagName || "div"; that.model = options.model; that._wrap = options.wrap !== false; that.bind([ INIT, SHOW, HIDE ], options); }, render: function(container) { var that = this, notInitialized = !that.element; // The order below matters - kendo.bind should be happen when the element is in the DOM, and SHOW should be triggered after INIT. if (notInitialized) { that.element = that._createElement(); } if (container) { $(container).append(that.element); } if (notInitialized) { kendo.bind(that.element, that.model); that.trigger(INIT); } if (container) { that.trigger(SHOW); } return that.element; }, hide: function() { this.element.detach(); this.trigger(HIDE); }, destroy: function() { var element = this.element; if (element) { kendo.unbind(element); kendo.destroy(element); element.remove(); } }, _createElement: function() { var that = this, element, content; try { content = $(document.getElementById(that.content) || that.content); // support passing id without # } catch(e) { if (sizzleErrorRegExp.test(e.message)) { content = that.content; } } element = $("<" + that.tagName + " />").append(content[0].tagName === SCRIPT ? content.html() : content); // drop the wrapper if asked - this seems like the easiest (although not very intuitive) way to avoid messing up templates with questionable content, like the one below // if (!that._wrap) { element = element.contents(); } return element; } }); var Layout = View.extend({ init: function(content, options) { View.fn.init.call(this, content, options); this.regions = {}; }, showIn: function(container, view) { var previousView = this.regions[container]; if (previousView) { previousView.hide(); } view.render(this.render().find(container), previousView); this.regions[container] = view; } }); kendo.Layout = Layout; kendo.View = View; })(window.kendo.jQuery); kendo_module({ id: "fx", name: "Effects", category: "framework", description: "Required for animation effects in all Kendo UI widgets.", depends: [ "core" ] }); (function($, undefined) { var kendo = window.kendo, fx = kendo.effects, each = $.each, extend = $.extend, proxy = $.proxy, support = kendo.support, browser = support.browser, transforms = support.transforms, transitions = support.transitions, scaleProperties = { scale: 0, scalex: 0, scaley: 0, scale3d: 0 }, translateProperties = { translate: 0, translatex: 0, translatey: 0, translate3d: 0 }, hasZoom = (typeof document.documentElement.style.zoom !== "undefined") && !transforms, matrix3dRegExp = /matrix3?d?\s*\(.*,\s*([\d\.\-]+)\w*?,\s*([\d\.\-]+)\w*?,\s*([\d\.\-]+)\w*?,\s*([\d\.\-]+)\w*?/i, cssParamsRegExp = /^(-?[\d\.\-]+)?[\w\s]*,?\s*(-?[\d\.\-]+)?[\w\s]*/i, translateXRegExp = /translatex?$/i, oldEffectsRegExp = /(zoom|fade|expand)(\w+)/, singleEffectRegExp = /(zoom|fade|expand)/, unitRegExp = /[xy]$/i, transformProps = ["perspective", "rotate", "rotatex", "rotatey", "rotatez", "rotate3d", "scale", "scalex", "scaley", "scalez", "scale3d", "skew", "skewx", "skewy", "translate", "translatex", "translatey", "translatez", "translate3d", "matrix", "matrix3d"], transform2d = ["rotate", "scale", "scalex", "scaley", "skew", "skewx", "skewy", "translate", "translatex", "translatey", "matrix"], transform2units = { "rotate": "deg", scale: "", skew: "px", translate: "px" }, cssPrefix = transforms.css, round = Math.round, BLANK = "", PX = "px", NONE = "none", AUTO = "auto", WIDTH = "width", HEIGHT = "height", HIDDEN = "hidden", ORIGIN = "origin", ABORT_ID = "abortId", OVERFLOW = "overflow", TRANSLATE = "translate", POSITION = "position", COMPLETE_CALLBACK = "completeCallback", TRANSITION = cssPrefix + "transition", TRANSFORM = cssPrefix + "transform", BACKFACE = cssPrefix + "backface-visibility", PERSPECTIVE = cssPrefix + "perspective", DEFAULT_PERSPECTIVE = "1500px", TRANSFORM_PERSPECTIVE = "perspective(" + DEFAULT_PERSPECTIVE + ")", ios7 = support.mobileOS && support.mobileOS.majorVersion == 7, directions = { left: { reverse: "right", property: "left", transition: "translatex", vertical: false, modifier: -1 }, right: { reverse: "left", property: "left", transition: "translatex", vertical: false, modifier: 1 }, down: { reverse: "up", property: "top", transition: "translatey", vertical: true, modifier: 1 }, up: { reverse: "down", property: "top", transition: "translatey", vertical: true, modifier: -1 }, top: { reverse: "bottom" }, bottom: { reverse: "top" }, "in": { reverse: "out", modifier: -1 }, out: { reverse: "in", modifier: 1 }, vertical: { reverse: "vertical" }, horizontal: { reverse: "horizontal" } }; kendo.directions = directions; extend($.fn, { kendoStop: function(clearQueue, gotoEnd) { if (transitions) { return fx.stopQueue(this, clearQueue || false, gotoEnd || false); } else { return this.stop(clearQueue, gotoEnd); } } }); /* jQuery support for all transform animations (FF 3.5/3.6, Opera 10.x, IE9 */ if (transforms && !transitions) { each(transform2d, function(idx, value) { $.fn[value] = function(val) { if (typeof val == "undefined") { return animationProperty(this, value); } else { var that = $(this)[0], transformValue = value + "(" + val + transform2units[value.replace(unitRegExp, "")] + ")"; if (that.style.cssText.indexOf(TRANSFORM) == -1) { $(this).css(TRANSFORM, transformValue); } else { that.style.cssText = that.style.cssText.replace(new RegExp(value + "\\(.*?\\)", "i"), transformValue); } } return this; }; $.fx.step[value] = function (fx) { $(fx.elem)[value](fx.now); }; }); var curProxy = $.fx.prototype.cur; $.fx.prototype.cur = function () { if (transform2d.indexOf(this.prop) != -1) { return parseFloat($(this.elem)[this.prop]()); } return curProxy.apply(this, arguments); }; } kendo.toggleClass = function(element, classes, options, add) { if (classes) { classes = classes.split(" "); if (transitions) { options = extend({ exclusive: "all", duration: 400, ease: "ease-out" }, options); element.css(TRANSITION, options.exclusive + " " + options.duration + "ms " + options.ease); setTimeout(function() { element.css(TRANSITION, "").css(HEIGHT); }, options.duration); // TODO: this should fire a kendoAnimate session instead. } each(classes, function(idx, value) { element.toggleClass(value, add); }); } return element; }; kendo.parseEffects = function(input, mirror) { var effects = {}; if (typeof input === "string") { each(input.split(" "), function(idx, value) { var redirectedEffect = !singleEffectRegExp.test(value), resolved = value.replace(oldEffectsRegExp, function(match, $1, $2) { return $1 + ":" + $2.toLowerCase(); }), // Support for old zoomIn/fadeOut style, now deprecated. effect = resolved.split(":"), direction = effect[1], effectBody = {}; if (effect.length > 1) { effectBody.direction = (mirror && redirectedEffect ? directions[direction].reverse : direction); } effects[effect[0]] = effectBody; }); } else { each(input, function(idx) { var direction = this.direction; if (direction && mirror && !singleEffectRegExp.test(idx)) { this.direction = directions[direction].reverse; } effects[idx] = this; }); } return effects; }; function parseInteger(value) { return parseInt(value, 10); } function parseCSS(element, property) { return parseInteger(element.css(property)); } function parseTransitionEffects(options) { var effects = options.effects; if (effects === "zoom") { effects = "zoom:in fade:in"; } if (effects === "fade") { effects = "fade:in"; } if (effects === "slide") { effects = "tile:left"; } if (/^slide:(.+)$/.test(effects)) { effects = "tile:" + RegExp.$1; } if (effects === "overlay") { effects = "slideIn:left"; } if (/^overlay:(.+)$/.test(effects)) { effects = "slideIn:" + RegExp.$1; } options.effects = kendo.parseEffects(effects); if (ios7 && effects == "tile:left") { options.previousDivisor = 3; } return options; } function keys(obj) { var acc = []; for (var propertyName in obj) { acc.push(propertyName); } return acc; } function strip3DTransforms(properties) { for (var key in properties) { if (transformProps.indexOf(key) != -1 && transform2d.indexOf(key) == -1) { delete properties[key]; } } return properties; } function normalizeCSS(element, properties) { var transformation = [], cssValues = {}, lowerKey, key, value, isTransformed; for (key in properties) { lowerKey = key.toLowerCase(); isTransformed = transforms && transformProps.indexOf(lowerKey) != -1; if (!support.hasHW3D && isTransformed && transform2d.indexOf(lowerKey) == -1) { delete properties[key]; } else { value = properties[key]; if (isTransformed) { transformation.push(key + "(" + value + ")"); } else { cssValues[key] = value; } } } if (transformation.length) { cssValues[TRANSFORM] = transformation.join(" "); } return cssValues; } if (transitions) { extend(fx, { transition: function(element, properties, options) { var css, delay = 0, oldKeys = element.data("keys") || [], timeoutID; options = extend({ duration: 200, ease: "ease-out", complete: null, exclusive: "all" }, options ); var stopTransitionCalled = false; var stopTransition = function() { if (!stopTransitionCalled) { stopTransitionCalled = true; if (timeoutID) { clearTimeout(timeoutID); timeoutID = null; } element .removeData(ABORT_ID) .dequeue() .css(TRANSITION, "") .css(TRANSITION); options.complete.call(element); } }; options.duration = $.fx ? $.fx.speeds[options.duration] || options.duration : options.duration; css = normalizeCSS(element, properties); $.merge(oldKeys, keys(css)); element .data("keys", $.unique(oldKeys)) .height(); element.css(TRANSITION, options.exclusive + " " + options.duration + "ms " + options.ease).css(TRANSITION); element.css(css).css(TRANSFORM); /** * Use transitionEnd event for browsers who support it - but duplicate it with setTimeout, as the transitionEnd event will not be triggered if no CSS properties change. * This should be cleaned up at some point (widget by widget), and refactored to widgets not relying on the complete callback if no transition occurs. * * For IE9 and below, resort to setTimeout. */ if (transitions.event) { element.one(transitions.event, stopTransition); if (options.duration !== 0) { delay = 500; } } timeoutID = setTimeout(stopTransition, options.duration + delay); element.data(ABORT_ID, timeoutID); element.data(COMPLETE_CALLBACK, stopTransition); }, stopQueue: function(element, clearQueue, gotoEnd) { var cssValues, taskKeys = element.data("keys"), retainPosition = (!gotoEnd && taskKeys), completeCallback = element.data(COMPLETE_CALLBACK); if (retainPosition) { cssValues = kendo.getComputedStyles(element[0], taskKeys); } if (completeCallback) { completeCallback(); } if (retainPosition) { element.css(cssValues); } return element .removeData("keys") .stop(clearQueue); } }); } function animationProperty(element, property) { if (transforms) { var transform = element.css(TRANSFORM); if (transform == NONE) { return property == "scale" ? 1 : 0; } var match = transform.match(new RegExp(property + "\\s*\\(([\\d\\w\\.]+)")), computed = 0; if (match) { computed = parseInteger(match[1]); } else { match = transform.match(matrix3dRegExp) || [0, 0, 0, 0, 0]; property = property.toLowerCase(); if (translateXRegExp.test(property)) { computed = parseFloat(match[3] / match[2]); } else if (property == "translatey") { computed = parseFloat(match[4] / match[2]); } else if (property == "scale") { computed = parseFloat(match[2]); } else if (property == "rotate") { computed = parseFloat(Math.atan2(match[2], match[1])); } } return computed; } else { return parseFloat(element.css(property)); } } var EffectSet = kendo.Class.extend({ init: function(element, options) { var that = this; that.element = element; that.effects = []; that.options = options; that.restore = []; }, run: function(effects) { var that = this, effect, idx, jdx, length = effects.length, element = that.element, options = that.options, deferred = $.Deferred(), start = {}, end = {}, target, children, childrenLength; that.effects = effects; deferred.then($.proxy(that, "complete")); element.data("animating", true); for (idx = 0; idx < length; idx ++) { effect = effects[idx]; effect.setReverse(options.reverse); effect.setOptions(options); that.addRestoreProperties(effect.restore); effect.prepare(start, end); children = effect.children(); for (jdx = 0, childrenLength = children.length; jdx < childrenLength; jdx ++) { children[jdx].duration(options.duration).run(); } } // legacy support for options.properties for (var effectName in options.effects) { extend(end, options.effects[effectName].properties); } // Show the element initially if (!element.is(":visible")) { extend(start, { display: element.data("olddisplay") || "block" }); } if (transforms && !options.reset) { target = element.data("targetTransform"); if (target) { start = extend(target, start); } } start = normalizeCSS(element, start); if (transforms && !transitions) { start = strip3DTransforms(start); } element.css(start) .css(TRANSFORM); // Nudge for (idx = 0; idx < length; idx ++) { effects[idx].setup(); } if (options.init) { options.init(); } element.data("targetTransform", end); fx.animate(element, end, extend({}, options, { complete: deferred.resolve })); return deferred.promise(); }, stop: function() { $(this.element).kendoStop(true, true); }, addRestoreProperties: function(restore) { var element = this.element, value, i = 0, length = restore.length; for (; i < length; i ++) { value = restore[i]; this.restore.push(value); if (!element.data(value)) { element.data(value, element.css(value)); } } }, restoreCallback: function() { var element = this.element; for (var i = 0, length = this.restore.length; i < length; i ++) { var value = this.restore[i]; element.css(value, element.data(value)); } }, complete: function() { var that = this, idx = 0, element = that.element, options = that.options, effects = that.effects, length = effects.length; element .removeData("animating") .dequeue(); // call next animation from the queue if (options.hide) { element.data("olddisplay", element.css("display")).hide(); } this.restoreCallback(); if (hasZoom && !transforms) { setTimeout($.proxy(this, "restoreCallback"), 0); // Again jQuery callback in IE8- } for (; idx < length; idx ++) { effects[idx].teardown(); } if (options.completeCallback) { options.completeCallback(element); } } }); fx.promise = function(element, options) { var effects = [], effectClass, effectSet = new EffectSet(element, options), parsedEffects = kendo.parseEffects(options.effects), effect; options.effects = parsedEffects; for (var effectName in parsedEffects) { effectClass = fx[capitalize(effectName)]; if (effectClass) { effect = new effectClass(element, parsedEffects[effectName].direction); effects.push(effect); } } if (effects[0]) { effectSet.run(effects); } else { // Not sure how would an fx promise reach this state - means that you call kendoAnimate with no valid effects? Why? if (!element.is(":visible")) { element.css({ display: element.data("olddisplay") || "block" }).css("display"); } if (options.init) { options.init(); } element.dequeue(); effectSet.complete(); } }; fx.transitionPromise = function(element, destination, options) { fx.animateTo(element, destination, options); return element; }; extend(fx, { animate: function(elements, properties, options) { var useTransition = options.transition !== false; delete options.transition; if (transitions && "transition" in fx && useTransition) { fx.transition(elements, properties, options); } else { if (transforms) { elements.animate(strip3DTransforms(properties), { queue: false, show: false, hide: false, duration: options.duration, complete: options.complete }); // Stop animate from showing/hiding the element to be able to hide it later on. } else { elements.each(function() { var element = $(this), multiple = {}; each(transformProps, function(idx, value) { // remove transforms to avoid IE and older browsers confusion var params, currentValue = properties ? properties[value]+ " " : null; // We need to match if (currentValue) { var single = properties; if (value in scaleProperties && properties[value] !== undefined) { params = currentValue.match(cssParamsRegExp); if (transforms) { extend(single, { scale: +params[0] }); } } else { if (value in translateProperties && properties[value] !== undefined) { var position = element.css(POSITION), isFixed = (position == "absolute" || position == "fixed"); if (!element.data(TRANSLATE)) { if (isFixed) { element.data(TRANSLATE, { top: parseCSS(element, "top") || 0, left: parseCSS(element, "left") || 0, bottom: parseCSS(element, "bottom"), right: parseCSS(element, "right") }); } else { element.data(TRANSLATE, { top: parseCSS(element, "marginTop") || 0, left: parseCSS(element, "marginLeft") || 0 }); } } var originalPosition = element.data(TRANSLATE); params = currentValue.match(cssParamsRegExp); if (params) { var dX = value == TRANSLATE + "y" ? +null : +params[1], dY = value == TRANSLATE + "y" ? +params[1] : +params[2]; if (isFixed) { if (!isNaN(originalPosition.right)) { if (!isNaN(dX)) { extend(single, { right: originalPosition.right - dX }); } } else { if (!isNaN(dX)) { extend(single, { left: originalPosition.left + dX }); } } if (!isNaN(originalPosition.bottom)) { if (!isNaN(dY)) { extend(single, { bottom: originalPosition.bottom - dY }); } } else { if (!isNaN(dY)) { extend(single, { top: originalPosition.top + dY }); } } } else { if (!isNaN(dX)) { extend(single, { marginLeft: originalPosition.left + dX }); } if (!isNaN(dY)) { extend(single, { marginTop: originalPosition.top + dY }); } } } } } if (!transforms && value != "scale" && value in single) { delete single[value]; } if (single) { extend(multiple, single); } } }); if (browser.msie) { delete multiple.scale; } element.animate(multiple, { queue: false, show: false, hide: false, duration: options.duration, complete: options.complete }); // Stop animate from showing/hiding the element to be able to hide it later on. }); } } }, animateTo: function(element, destination, options) { var direction, commonParent = element.parents().filter(destination.parents()).first(), both = $().add(element.parent()).add(destination.parent()), isAbsolute = element.css(POSITION) == "absolute", originalOverflow, originalPosition; if (!isAbsolute) { originalPosition = both.css(POSITION); both.css(POSITION, "absolute"); } options = parseTransitionEffects(options); if (!support.mobileOS.android) { originalOverflow = commonParent.css(OVERFLOW); commonParent.css(OVERFLOW, "hidden"); } $.each(options.effects, function(name, definition) { direction = direction || definition.direction; }); function complete(animatedElement) { destination[0].style.cssText = ""; element.each(function() { this.style.cssText = ""; }); if (!support.mobileOS.android) { commonParent.css(OVERFLOW, originalOverflow); } if (!isAbsolute) { both.css(POSITION, originalPosition); } if (options.completeCallback) { options.completeCallback.call(element, animatedElement); } } options.complete = browser.msie ? function() { setTimeout(complete, 0); } : complete; options.previous = (options.reverse ? destination : element); options.reset = true; // Reset transforms if there are any. // execute callback only once, and hook up derived animations to previous view only once. (options.reverse ? element : destination).each(function() { $(this).kendoAnimate(extend(true, {}, options)); options.complete = null; options.previous = null; }); } }); var Effect = kendo.Class.extend({ init: function(element, direction) { var that = this; that.element = element; that._direction = direction; that.options = {}; that._additionalEffects = []; if (!that.restore) { that.restore = []; } }, // Public API reverse: function() { this._reverse = true; return this.run(); }, play: function() { this._reverse = false; return this.run(); }, add: function(additional) { this._additionalEffects.push(additional); return this; }, direction: function(value) { this._direction = value; return this; }, duration: function(duration) { this._duration = duration; return this; }, compositeRun: function() { var that = this, effectSet = new EffectSet(that.element, { reverse: that._reverse, duration: that._duration }), effects = that._additionalEffects.concat([ that ]); return effectSet.run(effects); }, run: function() { if (this._additionalEffects && this._additionalEffects[0]) { return this.compositeRun(); } var that = this, element = that.element, idx = 0, restore = that.restore, length = restore.length, value, deferred = $.Deferred(), start = {}, end = {}, target, children = that.children(), childrenLength = children.length; deferred.then($.proxy(that, "_complete")); element.data("animating", true); for (idx = 0; idx < length; idx ++) { value = restore[idx]; if (!element.data(value)) { element.data(value, element.css(value)); } } for (idx = 0; idx < childrenLength; idx ++) { children[idx].duration(that._duration).run(); } that.prepare(start, end); if (!element.is(":visible")) { extend(start, { display: element.data("olddisplay") || "block" }); } if (transforms) { target = element.data("targetTransform"); if (target) { start = extend(target, start); } } start = normalizeCSS(element, start); if (transforms && !transitions) { start = strip3DTransforms(start); } element.css(start).css(TRANSFORM); // Nudge that.setup(); element.data("targetTransform", end); fx.animate(element, end, { duration: that._duration, complete: deferred.resolve }); return deferred.promise(); }, stop: function() { var idx = 0, children = this.children(), childrenLength = children.length; for (idx = 0; idx < childrenLength; idx ++) { children[idx].stop(); } $(this.element).kendoStop(true, true); return this; }, restoreCallback: function() { var element = this.element; for (var i = 0, length = this.restore.length; i < length; i ++) { var value = this.restore[i]; element.css(value, element.data(value)); } }, _complete: function() { var that = this, element = that.element; element .removeData("animating") .dequeue(); // call next animation from the queue that.restoreCallback(); if (that.shouldHide()) { element.data("olddisplay", element.css("display")).hide(); } if (hasZoom && !transforms) { setTimeout($.proxy(that, "restoreCallback"), 0); // Again jQuery callback in IE8- } that.teardown(); }, /////////////////////////// Support for kendo.animate; setOptions: function(options) { extend(true, this.options, options); }, children: function() { return []; }, shouldHide: $.noop, setup: $.noop, prepare: $.noop, teardown: $.noop, directions: [], setReverse: function(reverse) { this._reverse = reverse; return this; } }); function capitalize(word) { return word.charAt(0).toUpperCase() + word.substring(1); } function createEffect(name, definition) { var effectClass = Effect.extend(definition), directions = effectClass.prototype.directions; fx[capitalize(name)] = effectClass; fx.Element.prototype[name] = function(direction, opt1, opt2, opt3) { return new effectClass(this.element, direction, opt1, opt2, opt3); }; each(directions, function(idx, theDirection) { fx.Element.prototype[name + capitalize(theDirection)] = function(opt1, opt2, opt3) { return new effectClass(this.element, theDirection, opt1, opt2, opt3); }; }); } var FOUR_DIRECTIONS = ["left", "right", "up", "down"], IN_OUT = ["in", "out"]; createEffect("slideIn", { directions: FOUR_DIRECTIONS, divisor: function(value) { this.options.divisor = value; return this; }, prepare: function(start, end) { var that = this, tmp, element = that.element, direction = directions[that._direction], offset = -direction.modifier * (direction.vertical ? element.outerHeight() : element.outerWidth()), startValue = offset / (that.options && that.options.divisor || 1) + PX, endValue = "0px"; if (that._reverse) { tmp = start; start = end; end = tmp; } if (transforms) { start[direction.transition] = startValue; end[direction.transition] = endValue; } else { start[direction.property] = startValue; end[direction.property] = endValue; } } }); createEffect("tile", { directions: FOUR_DIRECTIONS, init: function(element, direction, previous) { Effect.prototype.init.call(this, element, direction); this.options = { previous: previous }; }, previousDivisor: function(value) { this.options.previousDivisor = value; return this; }, children: function() { var that = this, reverse = that._reverse, previous = that.options.previous, divisor = that.options.previousDivisor || 1, dir = that._direction; var children = [ kendo.fx(that.element).slideIn(dir).setReverse(reverse) ]; if (previous) { children.push( kendo.fx(previous).slideIn(directions[dir].reverse).divisor(divisor).setReverse(!reverse) ); } return children; } }); function createToggleEffect(name, property, defaultStart, defaultEnd) { createEffect(name, { directions: IN_OUT, startValue: function(value) { this._startValue = value; return this; }, endValue: function(value) { this._endValue = value; return this; }, shouldHide: function() { return this._shouldHide; }, prepare: function(start, end) { var that = this, startValue, endValue, out = this._direction === "out", startDataValue = that.element.data(property), startDataValueIsSet = !(isNaN(startDataValue) || startDataValue == defaultStart); if (startDataValueIsSet) { startValue = startDataValue; } else if (typeof this._startValue !== "undefined") { startValue = this._startValue; } else { startValue = out ? defaultStart : defaultEnd; } if (typeof this._endValue !== "undefined") { endValue = this._endValue; } else { endValue = out ? defaultEnd : defaultStart; } if (this._reverse) { start[property] = endValue; end[property] = startValue; } else { start[property] = startValue; end[property] = endValue; } that._shouldHide = end[property] === defaultEnd; } }); } createToggleEffect("fade", "opacity", 1, 0); createToggleEffect("zoom", "scale", 1, 0.01); createEffect("slideMargin", { prepare: function(start, end) { var that = this, element = that.element, options = that.options, origin = element.data(ORIGIN), offset = options.offset, margin, reverse = that._reverse; if (!reverse && origin === null) { element.data(ORIGIN, parseFloat(element.css("margin-" + options.axis))); } margin = (element.data(ORIGIN) || 0); end["margin-" + options.axis] = !reverse ? margin + offset : margin; } }); createEffect("slideTo", { prepare: function(start, end) { var that = this, element = that.element, options = that.options, offset = options.offset.split(","), reverse = that._reverse; if (transforms) { end.translatex = !reverse ? offset[0] : 0; end.translatey = !reverse ? offset[1] : 0; } else { end.left = !reverse ? offset[0] : 0; end.top = !reverse ? offset[1] : 0; } element.css("left"); } }); createEffect("expand", { directions: ["horizontal", "vertical"], restore: [ OVERFLOW ], prepare: function(start, end) { var that = this, element = that.element, options = that.options, reverse = that._reverse, property = that._direction === "vertical" ? HEIGHT : WIDTH, setLength = element[0].style[property], oldLength = element.data(property), length = parseFloat(oldLength || setLength), realLength = round(element.css(property, AUTO)[property]()); start.overflow = HIDDEN; length = (options && options.reset) ? realLength || length : length || realLength; end[property] = (reverse ? 0 : length) + PX; start[property] = (reverse ? length : 0) + PX; if (oldLength === undefined) { element.data(property, setLength); } }, shouldHide: function() { return this._reverse; }, teardown: function() { var that = this, element = that.element, property = that._direction === "vertical" ? HEIGHT : WIDTH, length = element.data(property); if (length == AUTO || length === BLANK) { setTimeout(function() { element.css(property, AUTO).css(property); }, 0); // jQuery animate complete callback in IE is called before the last animation step! } } }); var TRANSFER_START_STATE = { position: "absolute", marginLeft: 0, marginTop: 0, scale: 1 }; /** * Intersection point formulas are taken from here - http://zonalandeducation.com/mmts/intersections/intersectionOfTwoLines1/intersectionOfTwoLines1.html * Formula for a linear function from two points from here - http://demo.activemath.org/ActiveMath2/search/show.cmd?id=mbase://AC_UK_calculus/functions/ex_linear_equation_two_points * The transform origin point is the intersection point of the two lines from the top left corners/top right corners of the element and target. * The math and variables below MAY BE SIMPLIFIED (zeroes removed), but this would make the formula too cryptic. */ createEffect("transfer", { init: function(element, target) { this.element = element; this.options = { target: target }; this.restore = []; }, setup: function() { this.element.appendTo(document.body); }, prepare: function(start, end) { var that = this, element = that.element, options = that.options, reverse = that._reverse, target = options.target, offset, currentScale = animationProperty(element, "scale"), targetOffset = target.offset(), scale = target.outerHeight() / element.outerHeight(); extend(start, TRANSFER_START_STATE); end.scale = 1; element.css(TRANSFORM, "scale(1)").css(TRANSFORM); offset = element.offset(); element.css(TRANSFORM, "scale(" + currentScale + ")"); var x1 = 0, y1 = 0, x2 = targetOffset.left - offset.left, y2 = targetOffset.top - offset.top, x3 = x1 + element.outerWidth(), y3 = y1, x4 = x2 + target.outerWidth(), y4 = y2, Z1 = (y2 - y1) / (x2 - x1), Z2 = (y4 - y3) / (x4 - x3), X = (y1 - y3 - Z1 * x1 + Z2 * x3) / (Z2 - Z1), Y = y1 + Z1 * (X - x1); start.top = offset.top; start.left = offset.left; start.transformOrigin = X + PX + " " + Y + PX; if (reverse) { start.scale = scale; } else { end.scale = scale; } } }); var CLIPS = { top: "rect(auto auto $size auto)", bottom: "rect($size auto auto auto)", left: "rect(auto $size auto auto)", right: "rect(auto auto auto $size)" }; var ROTATIONS = { top: { start: "rotatex(0deg)", end: "rotatex(180deg)" }, bottom: { start: "rotatex(-180deg)", end: "rotatex(0deg)" }, left: { start: "rotatey(0deg)", end: "rotatey(-180deg)" }, right: { start: "rotatey(180deg)", end: "rotatey(0deg)" } }; function clipInHalf(container, direction) { var vertical = kendo.directions[direction].vertical, size = (container[vertical ? HEIGHT : WIDTH]() / 2) + "px"; return CLIPS[direction].replace("$size", size); } createEffect("turningPage", { directions: FOUR_DIRECTIONS, init: function(element, direction, container) { Effect.prototype.init.call(this, element, direction); this._container = container; }, prepare: function(start, end) { var that = this, reverse = that._reverse, direction = reverse ? directions[that._direction].reverse : that._direction, rotation = ROTATIONS[direction]; start.zIndex = 1; if (that._clipInHalf) { start.clip = clipInHalf(that._container, kendo.directions[direction].reverse); } start[BACKFACE] = HIDDEN; end[TRANSFORM] = TRANSFORM_PERSPECTIVE + (reverse ? rotation.start : rotation.end); start[TRANSFORM] = TRANSFORM_PERSPECTIVE + (reverse ? rotation.end : rotation.start); }, setup: function() { this._container.append(this.element); }, face: function(value) { this._face = value; return this; }, shouldHide: function() { var that = this, reverse = that._reverse, face = that._face; return (reverse && !face) || (!reverse && face); }, clipInHalf: function(value) { this._clipInHalf = value; return this; }, temporary: function() { this.element.addClass('temp-page'); return this; } }); createEffect("staticPage", { directions: FOUR_DIRECTIONS, init: function(element, direction, container) { Effect.prototype.init.call(this, element, direction); this._container = container; }, restore: ["clip"], prepare: function(start, end) { var that = this, direction = that._reverse ? directions[that._direction].reverse : that._direction; start.clip = clipInHalf(that._container, direction); start.opacity = 0.999; end.opacity = 1; }, shouldHide: function() { var that = this, reverse = that._reverse, face = that._face; return (reverse && !face) || (!reverse && face); }, face: function(value) { this._face = value; return this; } }); createEffect("pageturn", { directions: ["horizontal", "vertical"], init: function(element, direction, face, back) { Effect.prototype.init.call(this, element, direction); this.options = {}; this.options.face = face; this.options.back = back; }, children: function() { var that = this, options = that.options, direction = that._direction === "horizontal" ? "left" : "top", reverseDirection = kendo.directions[direction].reverse, reverse = that._reverse, temp, faceClone = options.face.clone(true).removeAttr("id"), backClone = options.back.clone(true).removeAttr("id"), element = that.element; if (reverse) { temp = direction; direction = reverseDirection; reverseDirection = temp; } return [ kendo.fx(options.face).staticPage(direction, element).face(true).setReverse(reverse), kendo.fx(options.back).staticPage(reverseDirection, element).setReverse(reverse), kendo.fx(faceClone).turningPage(direction, element).face(true).clipInHalf(true).temporary().setReverse(reverse), kendo.fx(backClone).turningPage(reverseDirection, element).clipInHalf(true).temporary().setReverse(reverse) ]; }, prepare: function(start, end) { start[PERSPECTIVE] = DEFAULT_PERSPECTIVE; start.transformStyle = "preserve-3d"; // hack to trigger transition end. start.opacity = 0.999; end.opacity = 1; }, teardown: function() { this.element.find(".temp-page").remove(); } }); createEffect("flip", { directions: ["horizontal", "vertical"], init: function(element, direction, face, back) { Effect.prototype.init.call(this, element, direction); this.options = {}; this.options.face = face; this.options.back = back; }, children: function() { var that = this, options = that.options, direction = that._direction === "horizontal" ? "left" : "top", reverseDirection = kendo.directions[direction].reverse, reverse = that._reverse, temp, element = that.element; if (reverse) { temp = direction; direction = reverseDirection; reverseDirection = temp; } return [ kendo.fx(options.face).turningPage(direction, element).face(true).setReverse(reverse), kendo.fx(options.back).turningPage(reverseDirection, element).setReverse(reverse) ]; }, prepare: function(start) { start[PERSPECTIVE] = DEFAULT_PERSPECTIVE; start.transformStyle = "preserve-3d"; } }); var Animation = kendo.Class.extend({ init: function() { var that = this; that._tickProxy = proxy(that._tick, that); that._started = false; }, tick: $.noop, done: $.noop, onEnd: $.noop, onCancel: $.noop, start: function() { if (!this.done()) { this._started = true; kendo.animationFrame(this._tickProxy); } }, cancel: function() { this._started = false; this.onCancel(); }, _tick: function() { var that = this; if (!that._started) { return; } that.tick(); if (!that.done()) { kendo.queueAnimation(that._tickProxy); } else { that._started = false; that.onEnd(); } } }); var Transition = Animation.extend({ init: function(options) { var that = this; extend(that, options); Animation.fn.init.call(that); }, done: function() { return this.timePassed() >= this.duration; }, timePassed: function() { return Math.min(this.duration, (new Date()) - this.startDate); }, moveTo: function(options) { var that = this, movable = that.movable; that.initial = movable[that.axis]; that.delta = options.location - that.initial; that.duration = typeof options.duration == "number" ? options.duration : 300; that.tick = that._easeProxy(options.ease); that.startDate = new Date(); that.start(); }, _easeProxy: function(ease) { var that = this; return function() { that.movable.moveAxis(that.axis, ease(that.timePassed(), that.initial, that.delta, that.duration)); }; } }); extend(Transition, { easeOutExpo: function (t, b, c, d) { return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; }, easeOutBack: function (t, b, c, d, s) { s = 1.70158; return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; } }); fx.Animation = Animation; fx.Transition = Transition; fx.createEffect = createEffect; })(window.kendo.jQuery); kendo_module({ id: "data.odata", name: "OData", category: "framework", depends: [ "core" ], hidden: true }); (function($, undefined) { var kendo = window.kendo, extend = $.extend, odataFilters = { eq: "eq", neq: "ne", gt: "gt", gte: "ge", lt: "lt", lte: "le", contains : "substringof", doesnotcontain: "substringof", endswith: "endswith", startswith: "startswith" }, mappers = { pageSize: $.noop, page: $.noop, filter: function(params, filter) { if (filter) { params.$filter = toOdataFilter(filter); } }, sort: function(params, orderby) { var expr = $.map(orderby, function(value) { var order = value.field.replace(/\./g, "/"); if (value.dir === "desc") { order += " desc"; } return order; }).join(","); if (expr) { params.$orderby = expr; } }, skip: function(params, skip) { if (skip) { params.$skip = skip; } }, take: function(params, take) { if (take) { params.$top = take; } } }, defaultDataType = { read: { dataType: "jsonp" } }; function toOdataFilter(filter) { var result = [], logic = filter.logic || "and", idx, length, field, type, format, operator, value, ignoreCase, filters = filter.filters; for (idx = 0, length = filters.length; idx < length; idx++) { filter = filters[idx]; field = filter.field; value = filter.value; operator = filter.operator; if (filter.filters) { filter = toOdataFilter(filter); } else { ignoreCase = filter.ignoreCase; field = field.replace(/\./g, "/"); filter = odataFilters[operator]; if (filter && value !== undefined) { type = $.type(value); if (type === "string") { format = "'{1}'"; value = value.replace(/'/g, "''"); if (ignoreCase === true) { field = "tolower(" + field + ")"; } } else if (type === "date") { format = "datetime'{1:yyyy-MM-ddTHH:mm:ss}'"; } else { format = "{1}"; } if (filter.length > 3) { if (filter !== "substringof") { format = "{0}({2}," + format + ")"; } else { format = "{0}(" + format + ",{2})"; if (operator === "doesnotcontain") { format += " eq false"; } } } else { format = "{2} {0} " + format; } filter = kendo.format(format, filter, value, field); } } result.push(filter); } filter = result.join(" " + logic + " "); if (result.length > 1) { filter = "(" + filter + ")"; } return filter; } extend(true, kendo.data, { schemas: { odata: { type: "json", data: function(data) { return data.d.results || [data.d]; }, total: "d.__count" } }, transports: { odata: { read: { cache: true, // to prevent jQuery from adding cache buster dataType: "jsonp", jsonp: "$callback" }, update: { cache: true, dataType: "json", contentType: "application/json", // to inform the server the the request body is JSON encoded type: "PUT" // can be PUT or MERGE }, create: { cache: true, dataType: "json", contentType: "application/json", type: "POST" // must be POST to create new entity }, destroy: { cache: true, dataType: "json", type: "DELETE" }, parameterMap: function(options, type) { var params, value, option, dataType; options = options || {}; type = type || "read"; dataType = (this.options || defaultDataType)[type]; dataType = dataType ? dataType.dataType : "json"; if (type === "read") { params = { $inlinecount: "allpages" }; if (dataType != "json") { params.$format = "json"; } for (option in options) { if (mappers[option]) { mappers[option](params, options[option]); } else { params[option] = options[option]; } } } else { if (dataType !== "json") { throw new Error("Only json dataType can be used for " + type + " operation."); } if (type !== "destroy") { for (option in options) { value = options[option]; if (typeof value === "number") { options[option] = value + ""; } } params = kendo.stringify(options); } } return params; } } } }); })(window.kendo.jQuery); kendo_module({ id: "data.xml", name: "XML", category: "framework", depends: [ "core" ], hidden: true }); (function($, undefined) { var kendo = window.kendo, isArray = $.isArray, isPlainObject = $.isPlainObject, map = $.map, each = $.each, extend = $.extend, getter = kendo.getter, Class = kendo.Class; var XmlDataReader = Class.extend({ init: function(options) { var that = this, total = options.total, model = options.model, parse = options.parse, errors = options.errors, serialize = options.serialize, data = options.data; if (model) { if (isPlainObject(model)) { if (model.fields) { each(model.fields, function(field, value) { if (isPlainObject(value) && value.field) { value = extend(value, { field: that.getter(value.field) }); } else { value = { field: that.getter(value) }; } model.fields[field] = value; }); } var id = model.id; if (id) { var idField = {}; idField[that.xpathToMember(id, true)] = { field : that.getter(id) }; model.fields = extend(idField, model.fields); model.id = that.xpathToMember(id); } model = kendo.data.Model.define(model); } that.model = model; } if (total) { if (typeof total == "string") { total = that.getter(total); that.total = function(data) { return parseInt(total(data), 10); }; } else if (typeof total == "function"){ that.total = total; } } if (errors) { if (typeof errors == "string") { errors = that.getter(errors); that.errors = function(data) { return errors(data) || null; }; } else if (typeof errors == "function"){ that.errors = errors; } } if (data) { if (typeof data == "string") { data = that.xpathToMember(data); that.data = function(value) { var result = that.evaluate(value, data), modelInstance; result = isArray(result) ? result : [result]; if (that.model && model.fields) { modelInstance = new that.model(); return map(result, function(value) { if (value) { var record = {}, field; for (field in model.fields) { record[field] = modelInstance._parse(field, model.fields[field].field(value)); } return record; } }); } return result; }; } else if (typeof data == "function") { that.data = data; } } if (typeof parse == "function") { var xmlParse = that.parse; that.parse = function(data) { var xml = parse.call(that, data); return xmlParse.call(that, xml); }; } if (typeof serialize == "function") { that.serialize = serialize; } }, total: function(result) { return this.data(result).length; }, errors: function(data) { return data ? data.errors : null; }, serialize: function(data) { return data; }, parseDOM: function(element) { var result = {}, parsedNode, node, nodeType, nodeName, member, attribute, attributes = element.attributes, attributeCount = attributes.length, idx; for (idx = 0; idx < attributeCount; idx++) { attribute = attributes[idx]; result["@" + attribute.nodeName] = attribute.nodeValue; } for (node = element.firstChild; node; node = node.nextSibling) { nodeType = node.nodeType; if (nodeType === 3 || nodeType === 4) { // text nodes or CDATA are stored as #text field result["#text"] = node.nodeValue; } else if (nodeType === 1) { // elements are stored as fields parsedNode = this.parseDOM(node); nodeName = node.nodeName; member = result[nodeName]; if (isArray(member)) { // elements of same nodeName are stored as array member.push(parsedNode); } else if (member !== undefined) { member = [member, parsedNode]; } else { member = parsedNode; } result[nodeName] = member; } } return result; }, evaluate: function(value, expression) { var members = expression.split("."), member, result, length, intermediateResult, idx; while (member = members.shift()) { value = value[member]; if (isArray(value)) { result = []; expression = members.join("."); for (idx = 0, length = value.length; idx < length; idx++) { intermediateResult = this.evaluate(value[idx], expression); intermediateResult = isArray(intermediateResult) ? intermediateResult : [intermediateResult]; result.push.apply(result, intermediateResult); } return result; } } return value; }, parse: function(xml) { var documentElement, tree, result = {}; documentElement = xml.documentElement || $.parseXML(xml).documentElement; tree = this.parseDOM(documentElement); result[documentElement.nodeName] = tree; return result; }, xpathToMember: function(member, raw) { if (!member) { return ""; } member = member.replace(/^\//, "") // remove the first "/" .replace(/\//g, "."); // replace all "/" with "." if (member.indexOf("@") >= 0) { // replace @attribute with '["@attribute"]' return member.replace(/\.?(@.*)/, raw? '$1':'["$1"]'); } if (member.indexOf("text()") >= 0) { // replace ".text()" with '["#text"]' return member.replace(/(\.?text\(\))/, raw? '#text':'["#text"]'); } return member; }, getter: function(member) { return getter(this.xpathToMember(member), true); } }); $.extend(true, kendo.data, { XmlDataReader: XmlDataReader, readers: { xml: XmlDataReader } }); })(window.kendo.jQuery); kendo_module({ id: "data", name: "Data source", category: "framework", description: "Powerful component for using local and remote data.Fully supports CRUD, Sorting, Paging, Filtering, Grouping, and Aggregates.", depends: [ "core" ], features: [ { id: "data-odata", name: "OData", description: "Support for accessing Open Data Protocol (OData) services.", depends: [ "data.odata" ] }, { id: "data-XML", name: "XML", description: "Support for binding to XML.", depends: [ "data.xml" ] } ] }); (function($, undefined) { var extend = $.extend, proxy = $.proxy, isPlainObject = $.isPlainObject, isEmptyObject = $.isEmptyObject, isArray = $.isArray, grep = $.grep, ajax = $.ajax, map, each = $.each, noop = $.noop, kendo = window.kendo, isFunction = kendo.isFunction, Observable = kendo.Observable, Class = kendo.Class, STRING = "string", FUNCTION = "function", CREATE = "create", READ = "read", UPDATE = "update", DESTROY = "destroy", CHANGE = "change", SYNC = "sync", GET = "get", ERROR = "error", REQUESTSTART = "requestStart", PROGRESS = "progress", REQUESTEND = "requestEnd", crud = [CREATE, READ, UPDATE, DESTROY], identity = function(o) { return o; }, getter = kendo.getter, stringify = kendo.stringify, math = Math, push = [].push, join = [].join, pop = [].pop, splice = [].splice, shift = [].shift, slice = [].slice, unshift = [].unshift, toString = {}.toString, stableSort = kendo.support.stableSort, dateRegExp = /^\/Date\((.*?)\)\/$/, newLineRegExp = /(\r+|\n+)/g, quoteRegExp = /(?=['\\])/g; var ObservableArray = Observable.extend({ init: function(array, type) { var that = this; that.type = type || ObservableObject; Observable.fn.init.call(that); that.length = array.length; that.wrapAll(array, that); }, toJSON: function() { var idx, length = this.length, value, json = new Array(length); for (idx = 0; idx < length; idx++){ value = this[idx]; if (value instanceof ObservableObject) { value = value.toJSON(); } json[idx] = value; } return json; }, parent: noop, wrapAll: function(source, target) { var that = this, idx, length, parent = function() { return that; }; target = target || []; for (idx = 0, length = source.length; idx < length; idx++) { target[idx] = that.wrap(source[idx], parent); } return target; }, wrap: function(object, parent) { var that = this, observable; if (object !== null && toString.call(object) === "[object Object]") { observable = object instanceof that.type || object instanceof Model; if (!observable) { object = object instanceof ObservableObject ? object.toJSON() : object; object = new that.type(object); } object.parent = parent; object.bind(CHANGE, function(e) { that.trigger(CHANGE, { field: e.field, node: e.node, index: e.index, items: e.items || [this], action: e.node ? (e.action || "itemchange") : "itemchange" }); }); } return object; }, push: function() { var index = this.length, items = this.wrapAll(arguments), result; result = push.apply(this, items); this.trigger(CHANGE, { action: "add", index: index, items: items }); return result; }, slice: slice, join: join, pop: function() { var length = this.length, result = pop.apply(this); if (length) { this.trigger(CHANGE, { action: "remove", index: length - 1, items:[result] }); } return result; }, splice: function(index, howMany, item) { var items = this.wrapAll(slice.call(arguments, 2)), result, i, len; result = splice.apply(this, [index, howMany].concat(items)); if (result.length) { this.trigger(CHANGE, { action: "remove", index: index, items: result }); for (i = 0, len = result.length; i < len; i++) { if (result[i].children) { result[i].unbind(CHANGE); } } } if (item) { this.trigger(CHANGE, { action: "add", index: index, items: items }); } return result; }, shift: function() { var length = this.length, result = shift.apply(this); if (length) { this.trigger(CHANGE, { action: "remove", index: 0, items:[result] }); } return result; }, unshift: function() { var items = this.wrapAll(arguments), result; result = unshift.apply(this, items); this.trigger(CHANGE, { action: "add", index: 0, items: items }); return result; }, indexOf: function(item) { var that = this, idx, length; for (idx = 0, length = that.length; idx < length; idx++) { if (that[idx] === item) { return idx; } } return -1; }, forEach: function(callback) { var idx = 0, length = this.length; for (; idx < length; idx++) { callback(this[idx], idx, this); } }, map: function(callback) { var idx = 0, result = [], length = this.length; for (; idx < length; idx++) { result[idx] = callback(this[idx], idx, this); } return result; }, filter: function(callback) { var idx = 0, result = [], item, length = this.length; for (; idx < length; idx++) { item = this[idx]; if (callback(item, idx, this)) { result[result.length] = item; } } return result; }, find: function(callback) { var idx = 0, item, length = this.length; for (; idx < length; idx++) { item = this[idx]; if (callback(item, idx, this)) { return item; } } }, every: function(callback) { var idx = 0, item, length = this.length; for (; idx < length; idx++) { item = this[idx]; if (!callback(item, idx, this)) { return false; } } return true; }, some: function(callback) { var idx = 0, item, length = this.length; for (; idx < length; idx++) { item = this[idx]; if (callback(item, idx, this)) { return true; } } return false; }, // non-standard collection methods remove: function(item) { this.splice(this.indexOf(item), 1); }, empty: function() { this.splice(0, this.length); } }); function eventHandler(context, type, field, prefix) { return function(e) { var event = {}, key; for (key in e) { event[key] = e[key]; } if (prefix) { event.field = field + "." + e.field; } else { event.field = field; } if (type == CHANGE && context._notifyChange) { context._notifyChange(event); } context.trigger(type, event); }; } var ObservableObject = Observable.extend({ init: function(value) { var that = this, member, field, parent = function() { return that; }; Observable.fn.init.call(this); for (field in value) { member = value[field]; if (field.charAt(0) != "_") { member = that.wrap(member, field, parent); } that[field] = member; } that.uid = kendo.guid(); }, shouldSerialize: function(field) { return this.hasOwnProperty(field) && field !== "_events" && typeof this[field] !== FUNCTION && field !== "uid"; }, forEach: function(f) { for (var i in this) { if (this.shouldSerialize(i)) { f(this[i], i); } } }, toJSON: function() { var result = {}, value, field; for (field in this) { if (this.shouldSerialize(field)) { value = this[field]; if (value instanceof ObservableObject || value instanceof ObservableArray) { value = value.toJSON(); } result[field] = value; } } return result; }, get: function(field) { var that = this, result; that.trigger(GET, { field: field }); if (field === "this") { result = that; } else { result = kendo.getter(field, true)(that); } return result; }, _set: function(field, value) { var that = this; var composite = field.indexOf(".") >= 0; if (composite) { var paths = field.split("."), path = ""; while (paths.length > 1) { path += paths.shift(); var obj = kendo.getter(path, true)(that); if (obj instanceof ObservableObject) { obj.set(paths.join("."), value); return composite; } path += "."; } } kendo.setter(field)(that, value); return composite; }, set: function(field, value) { var that = this, current = kendo.getter(field, true)(that); if (current !== value) { if (!that.trigger("set", { field: field, value: value })) { if (!that._set(field, that.wrap(value, field, function() { return that; })) || field.indexOf("(") >= 0 || field.indexOf("[") >= 0) { that.trigger(CHANGE, { field: field }); } } } }, parent: noop, wrap: function(object, field, parent) { var that = this, type = toString.call(object); if (object != null && (type === "[object Object]" || type === "[object Array]")) { var isObservableArray = object instanceof ObservableArray; var isDataSource = object instanceof DataSource; if (type === "[object Object]" && !isDataSource && !isObservableArray) { if (!(object instanceof ObservableObject)) { object = new ObservableObject(object); } if (object.parent() != parent()) { object.bind(GET, eventHandler(that, GET, field, true)); object.bind(CHANGE, eventHandler(that, CHANGE, field, true)); } } else if (type === "[object Array]" || isObservableArray || isDataSource) { if (!isObservableArray && !isDataSource) { object = new ObservableArray(object); } if (object.parent() != parent()) { object.bind(CHANGE, eventHandler(that, CHANGE, field, false)); } } object.parent = parent; } return object; } }); function equal(x, y) { if (x === y) { return true; } var xtype = $.type(x), ytype = $.type(y), field; if (xtype !== ytype) { return false; } if (xtype === "date") { return x.getTime() === y.getTime(); } if (xtype !== "object" && xtype !== "array") { return false; } for (field in x) { if (!equal(x[field], y[field])) { return false; } } return true; } var parsers = { "number": function(value) { return kendo.parseFloat(value); }, "date": function(value) { return kendo.parseDate(value); }, "boolean": function(value) { if (typeof value === STRING) { return value.toLowerCase() === "true"; } return value != null ? !!value : value; }, "string": function(value) { return value != null ? (value + "") : value; }, "default": function(value) { return value; } }; var defaultValues = { "string": "", "number": 0, "date": new Date(), "boolean": false, "default": "" }; function getFieldByName(obj, name) { var field, fieldName; for (fieldName in obj) { field = obj[fieldName]; if (isPlainObject(field) && field.field && field.field === name) { return field; } else if (field === name) { return field; } } return null; } var Model = ObservableObject.extend({ init: function(data) { var that = this; if (!data || $.isEmptyObject(data)) { data = $.extend({}, that.defaults, data); } ObservableObject.fn.init.call(that, data); that.dirty = false; if (that.idField) { that.id = that.get(that.idField); if (that.id === undefined) { that.id = that._defaultId; } } }, shouldSerialize: function(field) { return ObservableObject.fn.shouldSerialize.call(this, field) && field !== "uid" && !(this.idField !== "id" && field === "id") && field !== "dirty" && field !== "_accessors"; }, _parse: function(field, value) { var that = this, fieldName = field, fields = (that.fields || {}), parse; field = fields[field]; if (!field) { field = getFieldByName(fields, fieldName); } if (field) { parse = field.parse; if (!parse && field.type) { parse = parsers[field.type.toLowerCase()]; } } return parse ? parse(value) : value; }, _notifyChange: function(e) { var action = e.action; if (action == "add" || action == "remove") { this.dirty = true; } }, editable: function(field) { field = (this.fields || {})[field]; return field ? field.editable !== false : true; }, set: function(field, value, initiator) { var that = this; if (that.editable(field)) { value = that._parse(field, value); if (!equal(value, that.get(field))) { that.dirty = true; ObservableObject.fn.set.call(that, field, value, initiator); } } }, accept: function(data) { var that = this, parent = function() { return that; }, field; for (field in data) { var value = data[field]; if (field.charAt(0) != "_") { value = that.wrap(data[field], field, parent); } that._set(field, value); } if (that.idField) { that.id = that.get(that.idField); } that.dirty = false; }, isNew: function() { return this.id === this._defaultId; } }); Model.define = function(base, options) { if (options === undefined) { options = base; base = Model; } var model, proto = extend({ defaults: {} }, options), name, field, type, value, idx, length, fields = {}, originalName, id = proto.id; if (id) { proto.idField = id; } if (proto.id) { delete proto.id; } if (id) { proto.defaults[id] = proto._defaultId = ""; } if (toString.call(proto.fields) === "[object Array]") { for (idx = 0, length = proto.fields.length; idx < length; idx++) { field = proto.fields[idx]; if (typeof field === STRING) { fields[field] = {}; } else if (field.field) { fields[field.field] = field; } } proto.fields = fields; } for (name in proto.fields) { field = proto.fields[name]; type = field.type || "default"; value = null; originalName = name; name = typeof (field.field) === STRING ? field.field : name; if (!field.nullable) { value = proto.defaults[originalName !== name ? originalName : name] = field.defaultValue !== undefined ? field.defaultValue : defaultValues[type.toLowerCase()]; } if (options.id === name) { proto._defaultId = value; } proto.defaults[originalName !== name ? originalName : name] = value; field.parse = field.parse || parsers[type]; } model = base.extend(proto); model.define = function(options) { return Model.define(model, options); }; if (proto.fields) { model.fields = proto.fields; model.idField = proto.idField; } return model; }; var Comparer = { selector: function(field) { return isFunction(field) ? field : getter(field); }, compare: function(field) { var selector = this.selector(field); return function (a, b) { a = selector(a); b = selector(b); if (a == null && b == null) { return 0; } if (a == null) { return -1; } if (b == null) { return 1; } if (a.localeCompare) { return a.localeCompare(b); } return a > b ? 1 : (a < b ? -1 : 0); }; }, create: function(sort) { var compare = sort.compare || this.compare(sort.field); if (sort.dir == "desc") { return function(a, b) { return compare(b, a, true); }; } return compare; }, combine: function(comparers) { return function(a, b) { var result = comparers[0](a, b), idx, length; for (idx = 1, length = comparers.length; idx < length; idx ++) { result = result || comparers[idx](a, b); } return result; }; } }; var StableComparer = extend({}, Comparer, { asc: function(field) { var selector = this.selector(field); return function (a, b) { var valueA = selector(a); var valueB = selector(b); if (valueA && valueA.getTime && valueB && valueB.getTime) { valueA = valueA.getTime(); valueB = valueB.getTime(); } if (valueA === valueB) { return a.__position - b.__position; } if (valueA == null) { return -1; } if (valueB == null) { return 1; } if (valueA.localeCompare) { return valueA.localeCompare(valueB); } return valueA > valueB ? 1 : -1; }; }, desc: function(field) { var selector = this.selector(field); return function (a, b) { var valueA = selector(a); var valueB = selector(b); if (valueA && valueA.getTime && valueB && valueB.getTime) { valueA = valueA.getTime(); valueB = valueB.getTime(); } if (valueA === valueB) { return a.__position - b.__position; } if (valueA == null) { return 1; } if (valueB == null) { return -1; } if (valueB.localeCompare) { return valueB.localeCompare(valueA); } return valueA < valueB ? 1 : -1; }; }, create: function(sort) { return this[sort.dir](sort.field); } }); map = function (array, callback) { var idx, length = array.length, result = new Array(length); for (idx = 0; idx < length; idx++) { result[idx] = callback(array[idx], idx, array); } return result; }; var operators = (function(){ function quote(value) { return value.replace(quoteRegExp, "\\").replace(newLineRegExp, ""); } function operator(op, a, b, ignore) { var date; if (b != null) { if (typeof b === STRING) { b = quote(b); date = dateRegExp.exec(b); if (date) { b = new Date(+date[1]); } else if (ignore) { b = "'" + b.toLowerCase() + "'"; a = "(" + a + " || '').toLowerCase()"; } else { b = "'" + b + "'"; } } if (b.getTime) { //b looks like a Date a = "(" + a + "?" + a + ".getTime():" + a + ")"; b = b.getTime(); } } return a + " " + op + " " + b; } return { eq: function(a, b, ignore) { return operator("==", a, b, ignore); }, neq: function(a, b, ignore) { return operator("!=", a, b, ignore); }, gt: function(a, b, ignore) { return operator(">", a, b, ignore); }, gte: function(a, b, ignore) { return operator(">=", a, b, ignore); }, lt: function(a, b, ignore) { return operator("<", a, b, ignore); }, lte: function(a, b, ignore) { return operator("<=", a, b, ignore); }, startswith: function(a, b, ignore) { if (ignore) { a = "(" + a + " || '').toLowerCase()"; if (b) { b = b.toLowerCase(); } } if (b) { b = quote(b); } return a + ".lastIndexOf('" + b + "', 0) == 0"; }, endswith: function(a, b, ignore) { if (ignore) { a = "(" + a + " || '').toLowerCase()"; if (b) { b = b.toLowerCase(); } } if (b) { b = quote(b); } return a + ".indexOf('" + b + "', " + a + ".length - " + (b || "").length + ") >= 0"; }, contains: function(a, b, ignore) { if (ignore) { a = "(" + a + " || '').toLowerCase()"; if (b) { b = b.toLowerCase(); } } if (b) { b = quote(b); } return a + ".indexOf('" + b + "') >= 0"; }, doesnotcontain: function(a, b, ignore) { if (ignore) { a = "(" + a + " || '').toLowerCase()"; if (b) { b = b.toLowerCase(); } } if (b) { b = quote(b); } return a + ".indexOf('" + b + "') == -1"; } }; })(); function Query(data) { this.data = data || []; } Query.filterExpr = function(expression) { var expressions = [], logic = { and: " && ", or: " || " }, idx, length, filter, expr, fieldFunctions = [], operatorFunctions = [], field, operator, filters = expression.filters; for (idx = 0, length = filters.length; idx < length; idx++) { filter = filters[idx]; field = filter.field; operator = filter.operator; if (filter.filters) { expr = Query.filterExpr(filter); //Nested function fields or operators - update their index e.g. __o[0] -> __o[1] filter = expr.expression .replace(/__o\[(\d+)\]/g, function(match, index) { index = +index; return "__o[" + (operatorFunctions.length + index) + "]"; }) .replace(/__f\[(\d+)\]/g, function(match, index) { index = +index; return "__f[" + (fieldFunctions.length + index) + "]"; }); operatorFunctions.push.apply(operatorFunctions, expr.operators); fieldFunctions.push.apply(fieldFunctions, expr.fields); } else { if (typeof field === FUNCTION) { expr = "__f[" + fieldFunctions.length +"](d)"; fieldFunctions.push(field); } else { expr = kendo.expr(field); } if (typeof operator === FUNCTION) { filter = "__o[" + operatorFunctions.length + "](" + expr + ", " + filter.value + ")"; operatorFunctions.push(operator); } else { filter = operators[(operator || "eq").toLowerCase()](expr, filter.value, filter.ignoreCase !== undefined? filter.ignoreCase : true); } } expressions.push(filter); } return { expression: "(" + expressions.join(logic[expression.logic]) + ")", fields: fieldFunctions, operators: operatorFunctions }; }; function normalizeSort(field, dir) { if (field) { var descriptor = typeof field === STRING ? { field: field, dir: dir } : field, descriptors = isArray(descriptor) ? descriptor : (descriptor !== undefined ? [descriptor] : []); return grep(descriptors, function(d) { return !!d.dir; }); } } var operatorMap = { "==": "eq", equals: "eq", isequalto: "eq", equalto: "eq", equal: "eq", "!=": "neq", ne: "neq", notequals: "neq", isnotequalto: "neq", notequalto: "neq", notequal: "neq", "<": "lt", islessthan: "lt", lessthan: "lt", less: "lt", "<=": "lte", le: "lte", islessthanorequalto: "lte", lessthanequal: "lte", ">": "gt", isgreaterthan: "gt", greaterthan: "gt", greater: "gt", ">=": "gte", isgreaterthanorequalto: "gte", greaterthanequal: "gte", ge: "gte", notsubstringof: "doesnotcontain" }; function normalizeOperator(expression) { var idx, length, filter, operator, filters = expression.filters; if (filters) { for (idx = 0, length = filters.length; idx < length; idx++) { filter = filters[idx]; operator = filter.operator; if (operator && typeof operator === STRING) { filter.operator = operatorMap[operator.toLowerCase()] || operator; } normalizeOperator(filter); } } } function normalizeFilter(expression) { if (expression && !isEmptyObject(expression)) { if (isArray(expression) || !expression.filters) { expression = { logic: "and", filters: isArray(expression) ? expression : [expression] }; } normalizeOperator(expression); return expression; } } Query.normalizeFilter = normalizeFilter; function normalizeAggregate(expressions) { return isArray(expressions) ? expressions : [expressions]; } function normalizeGroup(field, dir) { var descriptor = typeof field === STRING ? { field: field, dir: dir } : field, descriptors = isArray(descriptor) ? descriptor : (descriptor !== undefined ? [descriptor] : []); return map(descriptors, function(d) { return { field: d.field, dir: d.dir || "asc", aggregates: d.aggregates }; }); } Query.prototype = { toArray: function () { return this.data; }, range: function(index, count) { return new Query(this.data.slice(index, index + count)); }, skip: function (count) { return new Query(this.data.slice(count)); }, take: function (count) { return new Query(this.data.slice(0, count)); }, select: function (selector) { return new Query(map(this.data, selector)); }, order: function(selector, dir) { var sort = { dir: dir }; if (selector) { if (selector.compare) { sort.compare = selector.compare; } else { sort.field = selector; } } return new Query(this.data.slice(0).sort(Comparer.create(sort))); }, orderBy: function(selector) { return this.order(selector, "asc"); }, orderByDescending: function(selector) { return this.order(selector, "desc"); }, sort: function(field, dir, comparer) { var idx, length, descriptors = normalizeSort(field, dir), comparers = []; comparer = comparer || Comparer; if (descriptors.length) { for (idx = 0, length = descriptors.length; idx < length; idx++) { comparers.push(comparer.create(descriptors[idx])); } return this.orderBy({ compare: comparer.combine(comparers) }); } return this; }, filter: function(expressions) { var idx, current, length, compiled, predicate, data = this.data, fields, operators, result = [], filter; expressions = normalizeFilter(expressions); if (!expressions || expressions.filters.length === 0) { return this; } compiled = Query.filterExpr(expressions); fields = compiled.fields; operators = compiled.operators; predicate = filter = new Function("d, __f, __o", "return " + compiled.expression); if (fields.length || operators.length) { filter = function(d) { return predicate(d, fields, operators); }; } for (idx = 0, length = data.length; idx < length; idx++) { current = data[idx]; if (filter(current)) { result.push(current); } } return new Query(result); }, group: function(descriptors, allData) { descriptors = normalizeGroup(descriptors || []); allData = allData || this.data; var that = this, result = new Query(that.data), descriptor; if (descriptors.length > 0) { descriptor = descriptors[0]; result = result.groupBy(descriptor).select(function(group) { var data = new Query(allData).filter([ { field: group.field, operator: "eq", value: group.value, ignoreCase: false } ]); return { field: group.field, value: group.value, items: descriptors.length > 1 ? new Query(group.items).group(descriptors.slice(1), data.toArray()).toArray() : group.items, hasSubgroups: descriptors.length > 1, aggregates: data.aggregate(descriptor.aggregates) }; }); } return result; }, groupBy: function(descriptor) { if (isEmptyObject(descriptor) || !this.data.length) { return new Query([]); } var field = descriptor.field, sorted = this._sortForGrouping(field, descriptor.dir || "asc"), accessor = kendo.accessor(field), item, groupValue = accessor.get(sorted[0], field), group = { field: field, value: groupValue, items: [] }, currentValue, idx, len, result = [group]; for(idx = 0, len = sorted.length; idx < len; idx++) { item = sorted[idx]; currentValue = accessor.get(item, field); if(!groupValueComparer(groupValue, currentValue)) { groupValue = currentValue; group = { field: field, value: groupValue, items: [] }; result.push(group); } group.items.push(item); } return new Query(result); }, _sortForGrouping: function(field, dir) { var idx, length, data = this.data; if (!stableSort) { for (idx = 0, length = data.length; idx < length; idx++) { data[idx].__position = idx; } data = new Query(data).sort(field, dir, StableComparer).toArray(); for (idx = 0, length = data.length; idx < length; idx++) { delete data[idx].__position; } return data; } return this.sort(field, dir).toArray(); }, aggregate: function (aggregates) { var idx, len, result = {}; if (aggregates && aggregates.length) { for(idx = 0, len = this.data.length; idx < len; idx++) { calculateAggregate(result, aggregates, this.data[idx], idx, len); } } return result; } }; function groupValueComparer(a, b) { if (a && a.getTime && b && b.getTime) { return a.getTime() === b.getTime(); } return a === b; } function calculateAggregate(accumulator, aggregates, item, index, length) { aggregates = aggregates || []; var idx, aggr, functionName, len = aggregates.length; for (idx = 0; idx < len; idx++) { aggr = aggregates[idx]; functionName = aggr.aggregate; var field = aggr.field; accumulator[field] = accumulator[field] || {}; accumulator[field][functionName] = functions[functionName.toLowerCase()](accumulator[field][functionName], item, kendo.accessor(field), index, length); } } var functions = { sum: function(accumulator, item, accessor) { return (accumulator || 0) + accessor.get(item); }, count: function(accumulator) { return (accumulator || 0) + 1; }, average: function(accumulator, item, accessor, index, length) { accumulator = (accumulator || 0) + accessor.get(item); if(index == length - 1) { accumulator = accumulator / length; } return accumulator; }, max: function(accumulator, item, accessor) { var value = accessor.get(item); accumulator = accumulator || 0; if(accumulator < value) { accumulator = value; } return accumulator; }, min: function(accumulator, item, accessor) { var value = accessor.get(item); if (!isNumber(accumulator)) { accumulator = value; } if(accumulator > value && isNumber(value)) { accumulator = value; } return accumulator; } }; function isNumber(val) { return typeof val === "number" && !isNaN(val); } function toJSON(array) { var idx, length = array.length, result = new Array(length); for (idx = 0; idx < length; idx++) { result[idx] = array[idx].toJSON(); } return result; } Query.process = function(data, options) { options = options || {}; var query = new Query(data), group = options.group, sort = normalizeGroup(group || []).concat(normalizeSort(options.sort || [])), total, filter = options.filter, skip = options.skip, take = options.take; if (filter) { query = query.filter(filter); total = query.toArray().length; } if (sort) { query = query.sort(sort); if (group) { data = query.toArray(); } } if (skip !== undefined && take !== undefined) { query = query.range(skip, take); } if (group) { query = query.group(group, data); } return { total: total, data: query.toArray() }; }; function calculateAggregates(data, options) { options = options || {}; var query = new Query(data), aggregates = options.aggregate, filter = options.filter; if(filter) { query = query.filter(filter); } return query.aggregate(aggregates); } var LocalTransport = Class.extend({ init: function(options) { this.data = options.data; }, read: function(options) { options.success(this.data); }, update: function(options) { options.success(options.data); }, create: function(options) { options.success(options.data); }, destroy: function(options) { options.success(options.data); } }); var RemoteTransport = Class.extend( { init: function(options) { var that = this, parameterMap; options = that.options = extend({}, that.options, options); each(crud, function(index, type) { if (typeof options[type] === STRING) { options[type] = { url: options[type] }; } }); that.cache = options.cache? Cache.create(options.cache) : { find: noop, add: noop }; parameterMap = options.parameterMap; that.parameterMap = isFunction(parameterMap) ? parameterMap : function(options) { var result = {}; each(options, function(option, value) { if (option in parameterMap) { option = parameterMap[option]; if (isPlainObject(option)) { value = option.value(value); option = option.key; } } result[option] = value; }); return result; }; }, options: { parameterMap: identity }, create: function(options) { return ajax(this.setup(options, CREATE)); }, read: function(options) { var that = this, success, error, result, cache = that.cache; options = that.setup(options, READ); success = options.success || noop; error = options.error || noop; result = cache.find(options.data); if(result !== undefined) { success(result); } else { options.success = function(result) { cache.add(options.data, result); success(result); }; $.ajax(options); } }, update: function(options) { return ajax(this.setup(options, UPDATE)); }, destroy: function(options) { return ajax(this.setup(options, DESTROY)); }, setup: function(options, type) { options = options || {}; var that = this, parameters, operation = that.options[type], data = isFunction(operation.data) ? operation.data(options.data) : operation.data; options = extend(true, {}, operation, options); parameters = extend(true, {}, data, options.data); options.data = that.parameterMap(parameters, type); if (isFunction(options.url)) { options.url = options.url(parameters); } return options; } }); var Cache = Class.extend({ init: function() { this._store = {}; }, add: function(key, data) { if(key !== undefined) { this._store[stringify(key)] = data; } }, find: function(key) { return this._store[stringify(key)]; }, clear: function() { this._store = {}; }, remove: function(key) { delete this._store[stringify(key)]; } }); Cache.create = function(options) { var store = { "inmemory": function() { return new Cache(); } }; if (isPlainObject(options) && isFunction(options.find)) { return options; } if (options === true) { return new Cache(); } return store[options](); }; function serializeRecords(data, getters, modelInstance, originalFieldNames, fieldNames) { var record, getter, originalName, idx, length; for (idx = 0, length = data.length; idx < length; idx++) { record = data[idx]; for (getter in getters) { originalName = fieldNames[getter]; if (originalName && originalName !== getter) { record[originalName] = getters[getter](record); delete record[getter]; } } } } function convertRecords(data, getters, modelInstance, originalFieldNames, fieldNames) { var record, getter, originalName, idx, length; for (idx = 0, length = data.length; idx < length; idx++) { record = data[idx]; for (getter in getters) { record[getter] = modelInstance._parse(getter, getters[getter](record)); originalName = fieldNames[getter]; if (originalName && originalName !== getter) { delete record[originalName]; } } } } function convertGroup(data, getters, modelInstance, originalFieldNames, fieldNames) { var record, idx, fieldName, length; for (idx = 0, length = data.length; idx < length; idx++) { record = data[idx]; fieldName = originalFieldNames[record.field]; if (fieldName && fieldName != record.field) { record.field = fieldName; } record.value = modelInstance._parse(record.field, record.value); if (record.hasSubgroups) { convertGroup(record.items, getters, modelInstance, originalFieldNames, fieldNames); } else { convertRecords(record.items, getters, modelInstance, originalFieldNames, fieldNames); } } } function wrapDataAccess(originalFunction, model, converter, getters, originalFieldNames, fieldNames) { return function(data) { data = originalFunction(data); if (data && !isEmptyObject(getters)) { if (toString.call(data) !== "[object Array]" && !(data instanceof ObservableArray)) { data = [data]; } converter(data, getters, new model(), originalFieldNames, fieldNames); } return data || []; }; } var DataReader = Class.extend({ init: function(schema) { var that = this, member, get, model, base; schema = schema || {}; for (member in schema) { get = schema[member]; that[member] = typeof get === STRING ? getter(get) : get; } base = schema.modelBase || Model; if (isPlainObject(that.model)) { that.model = model = base.define(that.model); } if (that.model) { var dataFunction = proxy(that.data, that), groupsFunction = proxy(that.groups, that), serializeFunction = proxy(that.serialize, that), originalFieldNames = {}, getters = {}, serializeGetters = {}, fieldNames = {}, shouldSerialize = false, fieldName; model = that.model; if (model.fields) { each(model.fields, function(field, value) { var fromName; fieldName = field; if (isPlainObject(value) && value.field) { fieldName = value.field; } else if (typeof value === STRING) { fieldName = value; } if (isPlainObject(value) && value.from) { fromName = value.from; } shouldSerialize = shouldSerialize || (fromName && fromName !== field) || fieldName !== field; getters[field] = getter(fromName || fieldName); serializeGetters[field] = getter(field); originalFieldNames[fromName || fieldName] = field; fieldNames[field] = fromName || fieldName; }); if (!schema.serialize && shouldSerialize) { that.serialize = wrapDataAccess(serializeFunction, model, serializeRecords, serializeGetters, originalFieldNames, fieldNames); } } that.data = wrapDataAccess(dataFunction, model, convertRecords, getters, originalFieldNames, fieldNames); that.groups = wrapDataAccess(groupsFunction, model, convertGroup, getters, originalFieldNames, fieldNames); } }, errors: function(data) { return data ? data.errors : null; }, parse: identity, data: identity, total: function(data) { return data.length; }, groups: identity, aggregates: function() { return {}; }, serialize: function(data) { return data; } }); function mergeGroups(target, dest, start, count) { var group, idx = 0, items; while (dest.length && count) { group = dest[idx]; items = group.items; if (target && target.field === group.field && target.value === group.value) { if (target.hasSubgroups && target.items.length) { mergeGroups(target.items[target.items.length - 1], group.items, start, count); } else { items = items.slice(start, count); count -= items.length; target.items = target.items.concat(items); } dest.splice(idx--, 1); } else { items = items.slice(start, count); count -= items.length; group.items = items; if (!group.items.length) { dest.splice(idx--, 1); count -= start; } } start = 0; if (++idx >= dest.length) { break; } } if (idx < dest.length) { dest.splice(idx, dest.length - idx); } } function flattenGroups(data) { var idx, length, result = []; for (idx = 0, length = data.length; idx < length; idx++) { if (data[idx].hasSubgroups) { result = result.concat(flattenGroups(data[idx].items)); } else { result = result.concat(data[idx].items.slice()); } } return result; } function wrapGroupItems(data, model) { var idx, length, group, items; if (model) { for (idx = 0, length = data.length; idx < length; idx++) { group = data[idx]; items = group.items; if (group.hasSubgroups) { wrapGroupItems(items, model); } else if (items.length && !(items[0] instanceof model)) { items.type = model; items.wrapAll(items, items); } } } } function eachGroupItems(data, func) { var idx, length; for (idx = 0, length = data.length; idx < length; idx++) { if (data[idx].hasSubgroups) { if (eachGroupItems(data[idx].items, func)) { return true; } } else if (func(data[idx].items, data[idx])) { return true; } } } function removeModel(data, model) { var idx, length; for (idx = 0, length = data.length; idx < length; idx++) { if (data[idx].uid == model.uid) { model = data[idx]; data.splice(idx, 1); return model; } } } function wrapInEmptyGroup(groups, model) { var parent, group, idx, length; for (idx = groups.length-1, length = 0; idx >= length; idx--) { group = groups[idx]; parent = { value: model.get(group.field), field: group.field, items: parent ? [parent] : [model], hasSubgroups: !!parent, aggregates: {} }; } return parent; } function indexOfPristineModel(data, model) { if (model) { return indexOf(data, function(item) { return item[model.idField] === model.id; }); } return -1; } function indexOfModel(data, model) { if (model) { return indexOf(data, function(item) { return item.uid == model.uid; }); } return -1; } function indexOf(data, comparer) { var idx, length; for (idx = 0, length = data.length; idx < length; idx++) { if (comparer(data[idx])) { return idx; } } return -1; } function fieldNameFromModel(fields, name) { if (fields && !isEmptyObject(fields)) { var descriptor = fields[name]; var fieldName; if (isPlainObject(descriptor)) { fieldName = descriptor.from || descriptor.field || name; } else { fieldName = fields[name] || name; } if (isFunction(fieldName)) { return name; } return fieldName; } return name; } function convertFilterDescriptorsField(descriptor, model) { var idx, length, target = {}; for (var field in descriptor) { if (field !== "filters") { target[field] = descriptor[field]; } } if (descriptor.filters) { target.filters = []; for (idx = 0, length = descriptor.filters.length; idx < length; idx++) { target.filters[idx] = convertFilterDescriptorsField(descriptor.filters[idx], model); } } else { target.field = fieldNameFromModel(model.fields, target.field); } return target; } function convertDescriptorsField(descriptors, model) { var idx, length, result = [], target, descriptor; for (idx = 0, length = descriptors.length; idx < length; idx ++) { target = {}; descriptor = descriptors[idx]; for (var field in descriptor) { target[field] = descriptor[field]; } target.field = fieldNameFromModel(model.fields, target.field); if (target.aggregates && isArray(target.aggregates)) { target.aggregates = convertDescriptorsField(target.aggregates, model); } result.push(target); } return result; } var DataSource = Observable.extend({ init: function(options) { var that = this, model, data; if (options) { data = options.data; } options = that.options = extend({}, that.options, options); that._map = {}; that._prefetch = {}; that._data = []; that._pristineData = []; that._ranges = []; that._view = []; that._pristine = []; that._destroyed = []; that._pageSize = options.pageSize; that._page = options.page || (options.pageSize ? 1 : undefined); that._sort = normalizeSort(options.sort); that._filter = normalizeFilter(options.filter); that._group = normalizeGroup(options.group); that._aggregate = options.aggregate; that._total = options.total; Observable.fn.init.call(that); that.transport = Transport.create(options, data); that.reader = new kendo.data.readers[options.schema.type || "json" ](options.schema); model = that.reader.model || {}; that._data = that._observe(that._data); that.bind([ERROR, CHANGE, REQUESTSTART, SYNC, REQUESTEND, PROGRESS], options); }, options: { data: [], schema: { modelBase: Model }, serverSorting: false, serverPaging: false, serverFiltering: false, serverGrouping: false, serverAggregates: false, batch: false }, _isServerGrouped: function() { var group = this.group() || []; return this.options.serverGrouping && group.length; }, _flatData: function(data) { if (this._isServerGrouped()) { return flattenGroups(data); } return data; }, parent: noop, get: function(id) { var idx, length, data = this._flatData(this._data); for (idx = 0, length = data.length; idx < length; idx++) { if (data[idx].id == id) { return data[idx]; } } }, getByUid: function(id) { var idx, length, data = this._flatData(this._data); if (!data) { return; } for (idx = 0, length = data.length; idx < length; idx++) { if (data[idx].uid == id) { return data[idx]; } } }, indexOf: function(model) { return indexOfModel(this._data, model); }, at: function(index) { return this._data[index]; }, data: function(value) { var that = this; if (value !== undefined) { that._data = this._observe(value); that._ranges = []; that._addRange(that._data); that._total = that._data.length; that._process(that._data); } else { return that._data; } }, view: function() { return this._view; }, add: function(model) { return this.insert(this._data.length, model); }, _createNewModel: function(model) { if (this.reader.model) { return new this.reader.model(model); } return new ObservableObject(model); }, insert: function(index, model) { if (!model) { model = index; index = 0; } if (!(model instanceof Model)) { model = this._createNewModel(model); } if (this._isServerGrouped()) { this._data.splice(index, 0, wrapInEmptyGroup(this.group(), model)); } else { this._data.splice(index, 0, model); } return model; }, remove: function(model) { var result, that = this, hasGroups = that._isServerGrouped(); this._eachItem(that._data, function(items) { result = removeModel(items, model); if (result && hasGroups) { if (!result.isNew || !result.isNew()) { that._destroyed.push(result); } return true; } }); return model; }, sync: function() { var that = this, idx, length, created = [], updated = [], destroyed = that._destroyed, data = that._flatData(that._data); if (!that.reader.model) { return; } for (idx = 0, length = data.length; idx < length; idx++) { if (data[idx].isNew()) { created.push(data[idx]); } else if (data[idx].dirty) { updated.push(data[idx]); } } var promises = that._send("create", created); promises.push.apply(promises ,that._send("update", updated)); promises.push.apply(promises ,that._send("destroy", destroyed)); $.when.apply(null, promises) .then(function() { var idx, length; for (idx = 0, length = arguments.length; idx < length; idx++){ that._accept(arguments[idx]); } that._change({ action: "sync" }); that.trigger(SYNC); }); }, cancelChanges: function(model) { var that = this; if (model instanceof kendo.data.Model) { that._cancelModel(model); } else { that._destroyed = []; that._data = that._observe(that._pristineData); if (that.options.serverPaging) { that._total = that.reader.total(that._pristine); } that._change(); } }, hasChanges: function() { var idx, length, data = this._data; if (this._destroyed.length) { return true; } for (idx = 0, length = data.length; idx < length; idx++) { if (data[idx].isNew() || data[idx].dirty) { return true; } } return false; }, _accept: function(result) { var that = this, models = result.models, response = result.response, idx = 0, serverGroup = that._isServerGrouped(), pristine = that._pristineData, type = result.type, length; that.trigger(REQUESTEND, { response: response, type: type }); if (response && !isEmptyObject(response)) { response = that.reader.parse(response); if (that._handleCustomErrors(response)) { return; } response = that.reader.data(response); if (!$.isArray(response)) { response = [response]; } } else { response = $.map(models, function(model) { return model.toJSON(); } ); } if (type === "destroy") { that._destroyed = []; } for (idx = 0, length = models.length; idx < length; idx++) { if (type !== "destroy") { models[idx].accept(response[idx]); if (type === "create") { pristine.push(serverGroup ? wrapInEmptyGroup(that.group(), models[idx]) : response[idx]); } else if (type === "update") { that._updatePristineForModel(models[idx], response[idx]); } } else { that._removePristineForModel(models[idx]); } } }, _updatePristineForModel: function(model, values) { this._executeOnPristineForModel(model, function(index, items) { kendo.deepExtend(items[index], values); }); }, _executeOnPristineForModel: function(model, callback) { this._eachPristineItem( function(items) { var index = indexOfPristineModel(items, model); if (index > -1) { callback(index, items); return true; } }); }, _removePristineForModel: function(model) { this._executeOnPristineForModel(model, function(index, items) { items.splice(index, 1); }); }, _readData: function(data) { var read = !this._isServerGrouped() ? this.reader.data : this.reader.groups; return read(data); }, _eachPristineItem: function(callback) { this._eachItem(this._pristineData, callback); }, _eachItem: function(data, callback) { if (data && data.length) { if (this._isServerGrouped()) { eachGroupItems(data, callback); } else { callback(data); } } }, _pristineForModel: function(model) { var pristine, idx, callback = function(items) { idx = indexOfPristineModel(items, model); if (idx > -1) { pristine = items[idx]; return true; } }; this._eachPristineItem(callback); return pristine; }, _cancelModel: function(model) { var pristine = this._pristineForModel(model), idx; this._eachItem(this._data, function(items) { idx = indexOfModel(items, model); if (idx != -1) { if (!model.isNew() && pristine) { items[idx].accept(pristine); } else { items.splice(idx, 1); } } }); }, _promise: function(data, models, type) { var that = this, transport = that.transport; return $.Deferred(function(deferred) { that.trigger(REQUESTSTART, { type: type }); transport[type].call(transport, extend({ success: function(response) { deferred.resolve({ response: response, models: models, type: type }); }, error: function(response, status, error) { deferred.reject(response); that.error(response, status, error); } }, data) ); }).promise(); }, _send: function(method, data) { var that = this, idx, length, promises = [], converted = that.reader.serialize(toJSON(data)); if (that.options.batch) { if (data.length) { promises.push(that._promise( { data: { models: converted } }, data , method)); } } else { for (idx = 0, length = data.length; idx < length; idx++) { promises.push(that._promise( { data: converted[idx] }, [ data[idx] ], method)); } } return promises; }, read: function(data) { var that = this, params = that._params(data); that._queueRequest(params, function() { if (!that.trigger(REQUESTSTART, { type: "read" })) { that.trigger(PROGRESS); that._ranges = []; that.transport.read({ data: params, success: proxy(that.success, that), error: proxy(that.error, that) }); } else { that._dequeueRequest(); } }); }, success: function(data) { var that = this, options = that.options; that.trigger(REQUESTEND, { response: data, type: "read" }); data = that.reader.parse(data); if (that._handleCustomErrors(data)) { that._dequeueRequest(); return; } that._pristine = isPlainObject(data) ? $.extend(true, {}, data) : data.slice ? data.slice(0) : data; that._total = that.reader.total(data); if (that._aggregate && options.serverAggregates) { that._aggregateResult = that.reader.aggregates(data); } data = that._readData(data); that._pristineData = data.slice(0); that._data = that._observe(data); that._addRange(that._data); that._process(that._data); that._dequeueRequest(); }, _addRange: function(data) { var that = this, start = that._skip || 0, end = start + that._flatData(data).length; that._ranges.push({ start: start, end: end, data: data }); that._ranges.sort( function(x, y) { return x.start - y.start; } ); }, error: function(xhr, status, errorThrown) { this._dequeueRequest(); this.trigger(REQUESTEND, { }); this.trigger(ERROR, { xhr: xhr, status: status, errorThrown: errorThrown }); }, _params: function(data) { var that = this, options = extend({ take: that.take(), skip: that.skip(), page: that.page(), pageSize: that.pageSize(), sort: that._sort, filter: that._filter, group: that._group, aggregate: that._aggregate }, data); if (!that.options.serverPaging) { delete options.take; delete options.skip; delete options.page; delete options.pageSize; } if (!that.options.serverGrouping) { delete options.group; } else if (that.reader.model && options.group) { options.group = convertDescriptorsField(options.group, that.reader.model); } if (!that.options.serverFiltering) { delete options.filter; } else if (that.reader.model && options.filter) { options.filter = convertFilterDescriptorsField(options.filter, that.reader.model); } if (!that.options.serverSorting) { delete options.sort; } else if (that.reader.model && options.sort) { options.sort = convertDescriptorsField(options.sort, that.reader.model); } if (!that.options.serverAggregates) { delete options.aggregate; } else if (that.reader.model && options.aggregate) { options.aggregate = convertDescriptorsField(options.aggregate, that.reader.model); } return options; }, _queueRequest: function(options, callback) { var that = this; if (!that._requestInProgress) { that._requestInProgress = true; that._pending = undefined; callback(); } else { that._pending = { callback: proxy(callback, that), options: options }; } }, _dequeueRequest: function() { var that = this; that._requestInProgress = false; if (that._pending) { that._queueRequest(that._pending.options, that._pending.callback); } }, _handleCustomErrors: function(response) { if (this.reader.errors) { var errors = this.reader.errors(response); if (errors) { this.trigger(ERROR, { xhr: null, status: "customerror", errorThrown: "custom error", errors: errors }); return true; } } return false; }, _observe: function(data) { var that = this, model = that.reader.model, wrap = false; if (model && data.length) { wrap = !(data[0] instanceof model); } if (data instanceof ObservableArray) { if (wrap) { data.type = that.reader.model; data.wrapAll(data, data); } } else { data = new ObservableArray(data, that.reader.model); data.parent = function() { return that.parent(); }; } if (that._isServerGrouped()) { wrapGroupItems(data, model); } if (that._changeHandler && that._data && that._data instanceof ObservableArray) { that._data.unbind(CHANGE, that._changeHandler); } else { that._changeHandler = proxy(that._change, that); } return data.bind(CHANGE, that._changeHandler); }, _change: function(e) { var that = this, idx, length, action = e ? e.action : ""; if (action === "remove") { for (idx = 0, length = e.items.length; idx < length; idx++) { if (!e.items[idx].isNew || !e.items[idx].isNew()) { that._destroyed.push(e.items[idx]); } } } if (that.options.autoSync && (action === "add" || action === "remove" || action === "itemchange")) { that.sync(); } else { var total = parseInt(that._total || that.reader.total(that._pristine), 10); if (action === "add") { total += e.items.length; } else if (action === "remove") { total -= e.items.length; } else if (action !== "itemchange" && action !== "sync" && !that.options.serverPaging) { total = that.reader.total(that._pristine); } that._total = total; that._process(that._data, e); } }, _process: function (data, e) { var that = this, options = {}, result; if (that.options.serverPaging !== true) { options.skip = that._skip; options.take = that._take || that._pageSize; if(options.skip === undefined && that._page !== undefined && that._pageSize !== undefined) { options.skip = (that._page - 1) * that._pageSize; } } if (that.options.serverSorting !== true) { options.sort = that._sort; } if (that.options.serverFiltering !== true) { options.filter = that._filter; } if (that.options.serverGrouping !== true) { options.group = that._group; } if (that.options.serverAggregates !== true) { options.aggregate = that._aggregate; that._aggregateResult = calculateAggregates(data, options); } result = Query.process(data, options); that._view = result.data; if (result.total !== undefined && !that.options.serverFiltering) { that._total = result.total; } e = e || {}; e.items = e.items || that._view; that.trigger(CHANGE, e); }, _mergeState: function(options) { var that = this; if (options !== undefined) { that._pageSize = options.pageSize; that._page = options.page; that._sort = options.sort; that._filter = options.filter; that._group = options.group; that._aggregate = options.aggregate; that._skip = options.skip; that._take = options.take; if(that._skip === undefined) { that._skip = that.skip(); options.skip = that.skip(); } if(that._take === undefined && that._pageSize !== undefined) { that._take = that._pageSize; options.take = that._take; } if (options.sort) { that._sort = options.sort = normalizeSort(options.sort); } if (options.filter) { that._filter = options.filter = normalizeFilter(options.filter); } if (options.group) { that._group = options.group = normalizeGroup(options.group); } if (options.aggregate) { that._aggregate = options.aggregate = normalizeAggregate(options.aggregate); } } return options; }, query: function(options) { var that = this, result, remote = that.options.serverSorting || that.options.serverPaging || that.options.serverFiltering || that.options.serverGrouping || that.options.serverAggregates; if (remote || ((that._data === undefined || that._data.length === 0) && !that._destroyed.length)) { that.read(that._mergeState(options)); } else { if (!that.trigger(REQUESTSTART, { type: "read" })) { that.trigger(PROGRESS); result = Query.process(that._data, that._mergeState(options)); if (!that.options.serverFiltering) { if (result.total !== undefined) { that._total = result.total; } else { that._total = that._data.length; } } that._view = result.data; that._aggregateResult = calculateAggregates(that._data, options); that.trigger(REQUESTEND, { }); that.trigger(CHANGE, { items: result.data }); } } }, fetch: function(callback) { var that = this; return $.Deferred(function(deferred) { var success = function(e) { that.unbind(ERROR, error); deferred.resolve(); if (callback) { callback.call(that, e); } }; var error = function(e) { deferred.reject(e); }; that.one(CHANGE, success); that.one(ERROR, error); that._query(); }).promise(); }, _query: function(options) { var that = this; that.query(extend({}, { page: that.page(), pageSize: that.pageSize(), sort: that.sort(), filter: that.filter(), group: that.group(), aggregate: that.aggregate() }, options)); }, next: function(options) { var that = this, page = that.page(), total = that.total(); options = options || {}; if (!page || (total && page + 1 > that.totalPages())) { return; } that._skip = page * that.take(); page += 1; options.page = page; that._query(options); return page; }, prev: function(options) { var that = this, page = that.page(); options = options || {}; if (!page || page === 1) { return; } that._skip = that._skip - that.take(); page -= 1; options.page = page; that._query(options); return page; }, page: function(val) { var that = this, skip; if(val !== undefined) { val = math.max(math.min(math.max(val, 1), that.totalPages()), 1); that._query({ page: val }); return; } skip = that.skip(); return skip !== undefined ? math.round((skip || 0) / (that.take() || 1)) + 1 : undefined; }, pageSize: function(val) { var that = this; if(val !== undefined) { that._query({ pageSize: val, page: 1 }); return; } return that.take(); }, sort: function(val) { var that = this; if(val !== undefined) { that._query({ sort: val }); return; } return that._sort; }, filter: function(val) { var that = this; if (val === undefined) { return that._filter; } that._query({ filter: val, page: 1 }); }, group: function(val) { var that = this; if(val !== undefined) { that._query({ group: val }); return; } return that._group; }, total: function() { return parseInt(this._total || 0, 10); }, aggregate: function(val) { var that = this; if(val !== undefined) { that._query({ aggregate: val }); return; } return that._aggregate; }, aggregates: function() { return this._aggregateResult; }, totalPages: function() { var that = this, pageSize = that.pageSize() || that.total(); return math.ceil((that.total() || 0) / pageSize); }, inRange: function(skip, take) { var that = this, end = math.min(skip + take, that.total()); if (!that.options.serverPaging && that.data.length > 0) { return true; } return that._findRange(skip, end).length > 0; }, lastRange: function() { var ranges = this._ranges; return ranges[ranges.length - 1] || { start: 0, end: 0, data: [] }; }, firstItemUid: function() { var ranges = this._ranges; return ranges.length && ranges[0].data.length && ranges[0].data[0].uid; }, range: function(skip, take) { skip = math.min(skip || 0, this.total()); var that = this, pageSkip = math.max(math.floor(skip / take), 0) * take, size = math.min(pageSkip + take, that.total()), data; data = that._findRange(skip, math.min(skip + take, that.total())); if (data.length) { that._skip = skip > that.skip() ? math.min(size, (that.totalPages() - 1) * that.take()) : pageSkip; that._take = take; var paging = that.options.serverPaging; var sorting = that.options.serverSorting; var filtering = that.options.serverFiltering; try { that.options.serverPaging = true; if (!that._isServerGrouped() && !(that.group() && that.group().length)) { that.options.serverSorting = true; } that.options.serverFiltering = true; if (paging) { that._data = data = that._observe(data); } that._process(data); } finally { that.options.serverPaging = paging; that.options.serverSorting = sorting; that.options.serverFiltering = filtering; } return; } if (take !== undefined) { if (!that._rangeExists(pageSkip, size)) { that.prefetch(pageSkip, take, function() { if (skip > pageSkip && size < that.total() && !that._rangeExists(size, math.min(size + take, that.total()))) { that.prefetch(size, take, function() { that.range(skip, take); }); } else { that.range(skip, take); } }); } else if (pageSkip < skip) { that.prefetch(size, take, function() { that.range(skip, take); }); } } }, _findRange: function(start, end) { var that = this, ranges = that._ranges, range, data = [], skipIdx, takeIdx, startIndex, endIndex, rangeData, rangeEnd, processed, options = that.options, remote = options.serverSorting || options.serverPaging || options.serverFiltering || options.serverGrouping || options.serverAggregates, flatData, count, length; for (skipIdx = 0, length = ranges.length; skipIdx < length; skipIdx++) { range = ranges[skipIdx]; if (start >= range.start && start <= range.end) { count = 0; for (takeIdx = skipIdx; takeIdx < length; takeIdx++) { range = ranges[takeIdx]; flatData = that._flatData(range.data); if (flatData.length && start + count >= range.start) { rangeData = range.data; rangeEnd = range.end; if (!remote) { var sort = normalizeGroup(that.group() || []).concat(normalizeSort(that.sort() || [])); processed = Query.process(range.data, { sort: sort, filter: that.filter() }); flatData = rangeData = processed.data; if (processed.total !== undefined) { rangeEnd = processed.total; } } startIndex = 0; if (start + count > range.start) { startIndex = (start + count) - range.start; } endIndex = flatData.length; if (rangeEnd > end) { endIndex = endIndex - (rangeEnd - end); } count += endIndex - startIndex; data = that._mergeGroups(data, rangeData, startIndex, endIndex); if (end <= range.end && count == end - start) { return data; } } } break; } } return []; }, _mergeGroups: function(data, range, startIndex, endIndex) { if (this._isServerGrouped()) { var temp = range.toJSON(), prevGroup; if (data.length) { prevGroup = data[data.length - 1]; } mergeGroups(prevGroup, temp, startIndex, endIndex); return data.concat(temp); } return data.concat(range.slice(startIndex, endIndex)); }, skip: function() { var that = this; if (that._skip === undefined) { return (that._page !== undefined ? (that._page - 1) * (that.take() || 1) : undefined); } return that._skip; }, take: function() { return this._take || this._pageSize; }, _prefetchSuccessHandler: function (skip, size, callback) { var that = this; return function(data) { var found = false, range = { start: skip, end: size, data: [] }, idx, length; that._dequeueRequest(); for (idx = 0, length = that._ranges.length; idx < length; idx++) { if (that._ranges[idx].start === skip) { found = true; range = that._ranges[idx]; break; } } if (!found) { that._ranges.push(range); } that.trigger(REQUESTEND, { response: data, type: "read" }); data = that.reader.parse(data); range.data = that._observe(that._readData(data)); range.end = range.start + that._flatData(range.data).length; that._ranges.sort( function(x, y) { return x.start - y.start; } ); that._total = that.reader.total(data); if (callback) { callback(); } }; }, prefetch: function(skip, take, callback) { var that = this, size = math.min(skip + take, that.total()), options = { take: take, skip: skip, page: skip / take + 1, pageSize: take, sort: that._sort, filter: that._filter, group: that._group, aggregate: that._aggregate }; if (!that._rangeExists(skip, size)) { clearTimeout(that._timeout); that._timeout = setTimeout(function() { that._queueRequest(options, function() { if (!that.trigger(REQUESTSTART, { type: "read" })) { that.transport.read({ data: that._params(options), success: that._prefetchSuccessHandler(skip, size, callback) }); } else { that._dequeueRequest(); } }); }, 100); } else if (callback) { callback(); } }, _rangeExists: function(start, end) { var that = this, ranges = that._ranges, idx, length; for (idx = 0, length = ranges.length; idx < length; idx++) { if (ranges[idx].start <= start && ranges[idx].end >= end) { return true; } } return false; } }); var Transport = {}; Transport.create = function(options, data) { var transport, transportOptions = options.transport; if (transportOptions) { transportOptions.read = typeof transportOptions.read === STRING ? { url: transportOptions.read } : transportOptions.read; if (options.type) { if (kendo.data.transports[options.type] && !isPlainObject(kendo.data.transports[options.type])) { transport = new kendo.data.transports[options.type](extend(transportOptions, { data: data })); } else { transportOptions = extend(true, {}, kendo.data.transports[options.type], transportOptions); } options.schema = extend(true, {}, kendo.data.schemas[options.type], options.schema); } if (!transport) { transport = isFunction(transportOptions.read) ? transportOptions : new RemoteTransport(transportOptions); } } else { transport = new LocalTransport({ data: options.data }); } return transport; }; DataSource.create = function(options) { options = options && options.push ? { data: options } : options; var dataSource = options || {}, data = dataSource.data, fields = dataSource.fields, table = dataSource.table, select = dataSource.select, idx, length, model = {}, field; if (!data && fields && !dataSource.transport) { if (table) { data = inferTable(table, fields); } else if (select) { data = inferSelect(select, fields); } } if (kendo.data.Model && fields && (!dataSource.schema || !dataSource.schema.model)) { for (idx = 0, length = fields.length; idx < length; idx++) { field = fields[idx]; if (field.type) { model[field.field] = field; } } if (!isEmptyObject(model)) { dataSource.schema = extend(true, dataSource.schema, { model: { fields: model } }); } } dataSource.data = data; return dataSource instanceof DataSource ? dataSource : new DataSource(dataSource); }; function inferSelect(select, fields) { var options = $(select)[0].children, idx, length, data = [], record, firstField = fields[0], secondField = fields[1], value, option; for (idx = 0, length = options.length; idx < length; idx++) { record = {}; option = options[idx]; if (option.disabled) { continue; } record[firstField.field] = option.text; value = option.attributes.value; if (value && value.specified) { value = option.value; } else { value = option.text; } record[secondField.field] = value; data.push(record); } return data; } function inferTable(table, fields) { var tbody = $(table)[0].tBodies[0], rows = tbody ? tbody.rows : [], idx, length, fieldIndex, fieldCount = fields.length, data = [], cells, record, cell, empty; for (idx = 0, length = rows.length; idx < length; idx++) { record = {}; empty = true; cells = rows[idx].cells; for (fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { cell = cells[fieldIndex]; if(cell.nodeName.toLowerCase() !== "th") { empty = false; record[fields[fieldIndex].field] = cell.innerHTML; } } if(!empty) { data.push(record); } } return data; } var Node = Model.define({ init: function(value) { var that = this, hasChildren = that.hasChildren || value && value.hasChildren, childrenField = "items", childrenOptions = {}; kendo.data.Model.fn.init.call(that, value); if (typeof that.children === STRING) { childrenField = that.children; } childrenOptions = { schema: { data: childrenField, model: { hasChildren: hasChildren, id: that.idField } } }; if (typeof that.children !== STRING) { extend(childrenOptions, that.children); } childrenOptions.data = value; if (!hasChildren) { hasChildren = childrenOptions.schema.data; } if (typeof hasChildren === STRING) { hasChildren = kendo.getter(hasChildren); } if (isFunction(hasChildren)) { that.hasChildren = !!hasChildren.call(that, that); } that._childrenOptions = childrenOptions; if (that.hasChildren) { that._initChildren(); } that._loaded = !!(value && (value[childrenField] || value._loaded)); }, _initChildren: function() { var that = this; var children, transport, parameterMap; if (!(that.children instanceof HierarchicalDataSource)) { children = that.children = new HierarchicalDataSource(that._childrenOptions); transport = children.transport; parameterMap = transport.parameterMap; transport.parameterMap = function(data) { data[that.idField || "id"] = that.id; if (parameterMap) { data = parameterMap(data); } return data; }; children.parent = function(){ return that; }; children.bind(CHANGE, function(e){ e.node = e.node || that; that.trigger(CHANGE, e); }); children.bind(ERROR, function(e){ var collection = that.parent(); if (collection) { e.node = e.node || that; collection.trigger(ERROR, e); } }); that._updateChildrenField(); } }, append: function(model) { this._initChildren(); this.loaded(true); this.children.add(model); }, hasChildren: false, level: function() { var parentNode = this.parentNode(), level = 0; while (parentNode && parentNode.parentNode) { level++; parentNode = parentNode.parentNode ? parentNode.parentNode() : null; } return level; }, _updateChildrenField: function() { var fieldName = this._childrenOptions.schema.data; this[fieldName || "items"] = this.children.data(); }, load: function() { var that = this, options = {}; if (that.hasChildren) { that._initChildren(); options[that.idField || "id"] = that.id; if (!that._loaded) { that.children._data = undefined; } that.children.one(CHANGE, function() { that._loaded = true; that._updateChildrenField(); }) ._query(options); } else { that.loaded(true); } }, parentNode: function() { var array = this.parent(); return array.parent(); }, loaded: function(value) { if (value !== undefined) { this._loaded = value; } else { return this._loaded; } }, shouldSerialize: function(field) { return Model.fn.shouldSerialize.call(this, field) && field !== "children" && field !== "_loaded" && field !== "hasChildren" && field !== "_childrenOptions"; } }); function dataMethod(name) { return function() { var data = this._data, result = DataSource.fn[name].apply(this, slice.call(arguments)); if (this._data != data) { this._attachBubbleHandlers(); } return result; }; } var HierarchicalDataSource = DataSource.extend({ init: function(options) { var node = Node.define({ children: options }); DataSource.fn.init.call(this, extend(true, {}, { schema: { modelBase: node, model: node } }, options)); this._attachBubbleHandlers(); }, _attachBubbleHandlers: function() { var that = this; that._data.bind(ERROR, function(e) { that.trigger(ERROR, e); }); }, remove: function(node){ var parentNode = node.parentNode(), dataSource = this, result; if (parentNode && parentNode._initChildren) { dataSource = parentNode.children; } result = DataSource.fn.remove.call(dataSource, node); if (parentNode && !dataSource.data().length) { parentNode.hasChildren = false; } return result; }, success: dataMethod("success"), data: dataMethod("data"), insert: function(index, model) { var parentNode = this.parent(); if (parentNode && parentNode._initChildren) { parentNode.hasChildren = true; parentNode._initChildren(); } return DataSource.fn.insert.call(this, index, model); }, _find: function(method, value) { var idx, length, node, data, children; node = DataSource.fn[method].call(this, value); if (node) { return node; } data = this._flatData(this.data()); if (!data) { return; } for (idx = 0, length = data.length; idx < length; idx++) { children = data[idx].children; if (!(children instanceof HierarchicalDataSource)) { continue; } node = children[method](value); if (node) { return node; } } }, get: function(id) { return this._find("get", id); }, getByUid: function(uid) { return this._find("getByUid", uid); } }); function inferList(list, fields) { var items = $(list).children(), idx, length, data = [], record, textField = fields[0].field, urlField = fields[1] && fields[1].field, spriteCssClassField = fields[2] && fields[2].field, imageUrlField = fields[3] && fields[3].field, item, id, textChild, className, children; function elements(collection, tagName) { return collection.filter(tagName).add(collection.find(tagName)); } for (idx = 0, length = items.length; idx < length; idx++) { record = { _loaded: true }; item = items.eq(idx); textChild = item[0].firstChild; children = item.children(); list = children.filter("ul"); children = children.filter(":not(ul)"); id = item.attr("data-id"); if (id) { record.id = id; } if (textChild) { record[textField] = textChild.nodeType == 3 ? textChild.nodeValue : children.text(); } if (urlField) { record[urlField] = elements(children, "a").attr("href"); } if (imageUrlField) { record[imageUrlField] = elements(children, "img").attr("src"); } if (spriteCssClassField) { className = elements(children, ".k-sprite").prop("className"); record[spriteCssClassField] = className && $.trim(className.replace("k-sprite", "")); } if (list.length) { record.items = inferList(list.eq(0), fields); } if (item.attr("data-hasChildren") == "true") { record.hasChildren = true; } data.push(record); } return data; } HierarchicalDataSource.create = function(options) { options = options && options.push ? { data: options } : options; var dataSource = options || {}, data = dataSource.data, fields = dataSource.fields, list = dataSource.list; if (data && data._dataSource) { return data._dataSource; } if (!data && fields && !dataSource.transport) { if (list) { data = inferList(list, fields); } } dataSource.data = data; return dataSource instanceof HierarchicalDataSource ? dataSource : new HierarchicalDataSource(dataSource); }; var Buffer = kendo.Observable.extend({ init: function(dataSource, viewSize, disablePrefetch) { kendo.Observable.fn.init.call(this); this._prefetching = false; this.dataSource = dataSource; this.prefetch = !disablePrefetch; var buffer = this; dataSource.bind("change", function() { buffer._change(); }); this._syncWithDataSource(); this.setViewSize(viewSize); }, setViewSize: function(viewSize) { this.viewSize = viewSize; this._recalculate(); }, at: function(index) { var pageSize = this.pageSize, item; if (index >= this.total()) { this.trigger("endreached", {index: index }); return; } if (!this.useRanges) { return this.dataSource.view()[index]; } if (this.useRanges) { // out of range request if (index < this.dataOffset || index > this.skip + pageSize) { var offset = Math.floor(index / pageSize) * pageSize; this.range(offset); } // prefetch if (index === this.prefetchThreshold) { this._prefetch(); } // mid-range jump - prefetchThreshold and nextPageThreshold may be equal, do not change to else if if (index === this.midPageThreshold) { this.range(this.nextMidRange); } // next range jump else if (index === this.nextPageThreshold) { this.range(this.nextFullRange); } // pull-back else if (index === this.pullBackThreshold) { if (this.offset === this.skip) { // from full range to mid range this.range(this.previousMidRange); } else { // from mid range to full range this.range(this.previousFullRange); } } item = this.dataSource.at(index - this.dataOffset); } if (item === undefined) { this.trigger("endreached", { index: index }); } return item; }, indexOf: function(item) { return this.dataSource.data().indexOf(item) + this.dataOffset; }, total: function() { return parseInt(this.dataSource.total(), 10); }, next: function() { var buffer = this, pageSize = buffer.pageSize, offset = buffer.skip - buffer.viewSize, // this calculation relies that the buffer has already jumped into the mid range segment pageSkip = math.max(math.floor(offset / pageSize), 0) * pageSize + pageSize; this.offset = offset; this.dataSource.prefetch(pageSkip, pageSize, function() { buffer._goToRange(offset, true); }); }, range: function(offset) { if (this.offset === offset) { return; } var buffer = this, pageSize = this.pageSize, pageSkip = math.max(math.floor(offset / pageSize), 0) * pageSize + pageSize, dataSource = this.dataSource; this.offset = offset; this._recalculate(); if (dataSource.inRange(offset, pageSize)) { this._goToRange(offset); } else if (this.prefetch) { dataSource.prefetch(pageSkip, pageSize, function() { buffer._goToRange(offset, true); }); } }, syncDataSource: function() { var offset = this.offset; this.offset = null; this.range(offset); }, destroy: function() { this.unbind(); }, _prefetch: function() { var buffer = this, pageSize = this.pageSize, prefetchOffset = this.skip + pageSize, dataSource = this.dataSource; if (!dataSource.inRange(prefetchOffset, pageSize) && !this._prefetching && this.prefetch) { this._prefetching = true; this.trigger("prefetching", { skip: prefetchOffset, take: pageSize }); dataSource.prefetch(prefetchOffset, pageSize, function() { buffer._prefetching = false; buffer.trigger("prefetched", { skip: prefetchOffset, take: pageSize }); }); } }, _goToRange: function(offset, expanding) { if (this.offset !== offset) { return; } this.dataOffset = offset; this._expanding = expanding; this.dataSource.range(offset, this.pageSize); }, _change: function() { var dataSource = this.dataSource, firstItemUid = dataSource.firstItemUid(); this.length = this.useRanges ? dataSource.lastRange().end : dataSource.view().length; if (this._firstItemUid !== firstItemUid || !this.useRanges) { this._syncWithDataSource(); this._recalculate(); this.trigger("reset", { offset: this.offset }); } this.trigger("resize"); if (this._expanding) { this.trigger("expand"); } delete this._expanding; }, _syncWithDataSource: function() { var dataSource = this.dataSource; this._firstItemUid = dataSource.firstItemUid(); this.dataOffset = this.offset = dataSource.skip() || 0; this.pageSize = dataSource.pageSize(); this.useRanges = dataSource.options.serverPaging; }, _recalculate: function() { var pageSize = this.pageSize, offset = this.offset, viewSize = this.viewSize, skip = Math.ceil(offset / pageSize) * pageSize; this.skip = skip; this.midPageThreshold = skip + pageSize - 1; this.nextPageThreshold = skip + viewSize - 1; this.prefetchThreshold = skip + Math.floor(pageSize / 3 * 2); this.pullBackThreshold = this.offset - 1; this.nextMidRange = skip + pageSize - viewSize; this.nextFullRange = skip; this.previousMidRange = offset - viewSize; this.previousFullRange = skip - pageSize; } }); var BatchBuffer = kendo.Observable.extend({ init: function(dataSource, batchSize) { var batchBuffer = this; kendo.Observable.fn.init.call(batchBuffer); this.dataSource = dataSource; this.batchSize = batchSize; this._total = 0; this.buffer = new Buffer(dataSource, batchSize * 3); this.buffer.bind({ "endreached": function (e) { batchBuffer.trigger("endreached", { index: e.index }); }, "prefetching": function (e) { batchBuffer.trigger("prefetching", { skip: e.skip, take: e.take }); }, "prefetched": function (e) { batchBuffer.trigger("prefetched", { skip: e.skip, take: e.take }); }, "reset": function () { batchBuffer._total = 0; batchBuffer.trigger("reset"); }, "resize": function () { batchBuffer._total = Math.ceil(this.length / batchBuffer.batchSize); batchBuffer.trigger("resize", { total: batchBuffer.total(), offset: this.offset }); } }); }, syncDataSource: function() { this.buffer.syncDataSource(); }, at: function(index) { var buffer = this.buffer, skip = index * this.batchSize, take = this.batchSize, view = [], item; if (buffer.offset > skip) { buffer.at(buffer.offset - 1); } for (var i = 0; i < take; i++) { item = buffer.at(skip + i); if (item === undefined) { break; } view.push(item); } return view; }, total: function() { return this._total; }, destroy: function() { this.buffer.destroy(); this.unbind(); } }); extend(true, kendo.data, { readers: { json: DataReader }, Query: Query, DataSource: DataSource, HierarchicalDataSource: HierarchicalDataSource, Node: Node, ObservableObject: ObservableObject, ObservableArray: ObservableArray, LocalTransport: LocalTransport, RemoteTransport: RemoteTransport, Cache: Cache, DataReader: DataReader, Model: Model, Buffer: Buffer, BatchBuffer: BatchBuffer }); })(window.kendo.jQuery); kendo_module({ id: "binder", name: "MVVM", category: "framework", description: "Model View ViewModel (MVVM) is a design pattern which helps developers separate the Model (the data) from the View (the UI).", depends: [ "core", "data" ] }); (function ($, undefined) { var kendo = window.kendo, Observable = kendo.Observable, ObservableObject = kendo.data.ObservableObject, ObservableArray = kendo.data.ObservableArray, toString = {}.toString, binders = {}, splice = Array.prototype.splice, Class = kendo.Class, innerText, proxy = $.proxy, VALUE = "value", SOURCE = "source", EVENTS = "events", CHECKED = "checked", CHANGE = "change"; (function() { var a = document.createElement("a"); if (a.innerText !== undefined) { innerText = "innerText"; } else if (a.textContent !== undefined) { innerText = "textContent"; } })(); var Binding = Observable.extend( { init: function(parents, path) { var that = this; Observable.fn.init.call(that); that.source = parents[0]; that.parents = parents; that.path = path; that.dependencies = {}; that.dependencies[path] = true; that.observable = that.source instanceof Observable; that._access = function(e) { that.dependencies[e.field] = true; }; if (that.observable) { that._change = function(e) { that.change(e); }; that.source.bind(CHANGE, that._change); } }, _parents: function() { var parents = this.parents; var value = this.get(); if (value && typeof value.parent == "function") { var parent = value.parent(); if ($.inArray(parent, parents) < 0) { parents = [parent].concat(parents); } } return parents; }, change: function(e) { var dependency, ch, field = e.field, that = this; if (that.path === "this") { that.trigger(CHANGE, e); } else { for (dependency in that.dependencies) { if (dependency.indexOf(field) === 0) { ch = dependency.charAt(field.length); if (!ch || ch === "." || ch === "[") { that.trigger(CHANGE, e); break; } } } } }, start: function(source) { source.bind("get", this._access); }, stop: function(source) { source.unbind("get", this._access); }, get: function() { var that = this, source = that.source, index = 0, path = that.path, result = source; if (!that.observable) { return result; } that.start(that.source); result = source.get(path); // Traverse the observable hierarchy if the binding is not resolved at the current level. while (result === undefined && source) { source = that.parents[++index]; if (source instanceof ObservableObject) { result = source.get(path); } } // second pass try to get the parent from the object hierarchy if (result === undefined) { source = that.source; //get the initial source while (result === undefined && source) { source = source.parent(); if (source instanceof ObservableObject) { result = source.get(path); } } } // If the result is a function - invoke it if (typeof result === "function") { index = path.lastIndexOf("."); // If the function is a member of a nested observable object make that nested observable the context (this) of the function if (index > 0) { source = source.get(path.substring(0, index)); } // Invoke the function that.start(source); result = result.call(source, that.source); that.stop(source); } // If the binding is resolved by a parent object if (source && source !== that.source) { that.currentSource = source; // save parent object // Listen for changes in the parent object source.unbind(CHANGE, that._change) .bind(CHANGE, that._change); } that.stop(that.source); return result; }, set: function(value) { var that = this, source = that.currentSource || that.source; source.set(that.path, value); }, destroy: function() { if (this.observable) { this.source.unbind(CHANGE, this._change); } } }); var EventBinding = Binding.extend( { get: function() { var source = this.source, path = this.path, index = 0, handler; handler = source.get(path); while (!handler && source) { source = this.parents[++index]; if (source instanceof ObservableObject) { handler = source.get(path); } } return proxy(handler, source); } }); var TemplateBinding = Binding.extend( { init: function(source, path, template) { var that = this; Binding.fn.init.call(that, source, path); that.template = template; }, render: function(value) { var html; this.start(this.source); html = kendo.render(this.template, value); this.stop(this.source); return html; } }); var Binder = Class.extend({ init: function(element, bindings, options) { this.element = element; this.bindings = bindings; this.options = options; }, bind: function(binding, attribute) { var that = this; binding = attribute ? binding[attribute] : binding; binding.bind(CHANGE, function(e) { that.refresh(attribute || e); }); that.refresh(attribute); }, destroy: function() { } }); binders.attr = Binder.extend({ refresh: function(key) { this.element.setAttribute(key, this.bindings.attr[key].get()); } }); binders.style = Binder.extend({ refresh: function(key) { this.element.style[key] = this.bindings.style[key].get() || ""; } }); binders.enabled = Binder.extend({ refresh: function() { if (this.bindings.enabled.get()) { this.element.removeAttribute("disabled"); } else { this.element.setAttribute("disabled", "disabled"); } } }); binders.readonly = Binder.extend({ refresh: function() { if (this.bindings.readonly.get()) { this.element.setAttribute("readonly", "readonly"); } else { this.element.removeAttribute("readonly"); } } }); binders.disabled = Binder.extend({ refresh: function() { if (this.bindings.disabled.get()) { this.element.setAttribute("disabled", "disabled"); } else { this.element.removeAttribute("disabled"); } } }); binders.events = Binder.extend({ init: function(element, bindings, options) { Binder.fn.init.call(this, element, bindings, options); this.handlers = {}; }, refresh: function(key) { var element = $(this.element), binding = this.bindings.events[key], handler = this.handlers[key]; if (handler) { element.off(key, handler); } handler = this.handlers[key] = binding.get(); element.on(key, binding.source, handler); }, destroy: function() { var element = $(this.element), handler; for (handler in this.handlers) { element.off(handler, this.handlers[handler]); } } }); binders.text = Binder.extend({ refresh: function() { var text = this.bindings.text.get(); if (text == null) { text = ""; } this.element[innerText] = text; } }); binders.visible = Binder.extend({ refresh: function() { if (this.bindings.visible.get()) { this.element.style.display = ""; } else { this.element.style.display = "none"; } } }); binders.invisible = Binder.extend({ refresh: function() { if (!this.bindings.invisible.get()) { this.element.style.display = ""; } else { this.element.style.display = "none"; } } }); binders.html = Binder.extend({ refresh: function() { this.element.innerHTML = this.bindings.html.get(); } }); binders.value = Binder.extend({ init: function(element, bindings, options) { Binder.fn.init.call(this, element, bindings, options); this._change = proxy(this.change, this); this.eventName = options.valueUpdate || CHANGE; $(this.element).on(this.eventName, this._change); this._initChange = false; }, change: function() { this._initChange = this.eventName != CHANGE; var value = this.element.value; var type = this.element.type; if (type == "date") { value = kendo.parseDate(value, "yyyy-MM-dd"); } else if (type == "datetime-local") { value = kendo.parseDate(value, ["yyyy-MM-ddTHH:mm:ss", "yyyy-MM-ddTHH:mm"] ); } else if (type == "number") { value = kendo.parseFloat(value); } this.bindings[VALUE].set(value); this._initChange = false; }, refresh: function() { if (!this._initChange) { var value = this.bindings[VALUE].get(); if (value == null) { value = ""; } var type = this.element.type; if (type == "date") { value = kendo.toString(value, "yyyy-MM-dd"); } else if (type == "datetime-local") { value = kendo.toString(value, "yyyy-MM-ddTHH:mm:ss"); } this.element.value = value; } this._initChange = false; }, destroy: function() { $(this.element).off(this.eventName, this._change); } }); binders.source = Binder.extend({ init: function(element, bindings, options) { Binder.fn.init.call(this, element, bindings, options); var source = this.bindings.source.get(); if (source instanceof kendo.data.DataSource && options.autoBind !== false) { source.fetch(); } }, refresh: function(e) { var that = this, source = that.bindings.source.get(); if (source instanceof ObservableArray || source instanceof kendo.data.DataSource) { e = e || {}; if (e.action == "add") { that.add(e.index, e.items); } else if (e.action == "remove") { that.remove(e.index, e.items); } else if (e.action != "itemchange") { that.render(); } } else { that.render(); } }, container: function() { var element = this.element; if (element.nodeName.toLowerCase() == "table") { if (!element.tBodies[0]) { element.appendChild(document.createElement("tbody")); } element = element.tBodies[0]; } return element; }, template: function() { var options = this.options, template = options.template, nodeName = this.container().nodeName.toLowerCase(); if (!template) { if (nodeName == "select") { if (options.valueField || options.textField) { template = kendo.format('', options.valueField || options.textField, options.textField || options.valueField); } else { template = ""; } } else if (nodeName == "tbody") { template = "#:data#"; } else if (nodeName == "ul" || nodeName == "ol") { template = "
  • #:data#
  • "; } else { template = "#:data#"; } template = kendo.template(template); } return template; }, destroy: function() { var source = this.bindings.source.get(); source.unbind(CHANGE, this._change); }, add: function(index, items) { var element = this.container(), parents, idx, length, child, clone = element.cloneNode(false), reference = element.children[index]; $(clone).html(kendo.render(this.template(), items)); if (clone.children.length) { parents = this.bindings.source._parents(); for (idx = 0, length = items.length; idx < length; idx++) { child = clone.children[0]; element.insertBefore(child, reference || null); bindElement(child, items[idx], this.options.roles, [items[idx]].concat(parents)); } } }, remove: function(index, items) { var idx, element = this.container(); for (idx = 0; idx < items.length; idx++) { var child = element.children[index]; unbindElementTree(child); element.removeChild(child); } }, render: function() { var source = this.bindings.source.get(), parents, idx, length, element = this.container(), template = this.template(), parent; if (source instanceof kendo.data.DataSource) { source = source.view(); } if (!(source instanceof ObservableArray) && toString.call(source) !== "[object Array]") { if (source.parent) { parent = source.parent; } source = new ObservableArray([source]); if (source.parent) { source.parent = parent; } } if (this.bindings.template) { unbindElementChildren(element); $(element).html(this.bindings.template.render(source)); if (element.children.length) { parents = this.bindings.source._parents(); for (idx = 0, length = source.length; idx < length; idx++) { bindElement(element.children[idx], source[idx], this.options.roles, [source[idx]].concat(parents)); } } } else { $(element).html(kendo.render(template, source)); } } }); binders.input = { checked: Binder.extend({ init: function(element, bindings, options) { Binder.fn.init.call(this, element, bindings, options); this._change = proxy(this.change, this); $(this.element).change(this._change); }, change: function() { var element = this.element; var value = this.value(); if (element.type == "radio") { this.bindings[CHECKED].set(value); } else if (element.type == "checkbox") { var source = this.bindings[CHECKED].get(); var index; if (source instanceof ObservableArray) { value = this.element.value; if (value !== "on" && value !== "off") { index = source.indexOf(value); if (index > -1) { source.splice(index, 1); } else { source.push(value); } } } else { this.bindings[CHECKED].set(value); } } }, refresh: function() { var value = this.bindings[CHECKED].get(), source = value, element = this.element; if (element.type == "checkbox") { if (source instanceof ObservableArray) { value = this.element.value; if (source.indexOf(value) >= 0) { value = true; } } element.checked = value === true; } else if (element.type == "radio" && value != null) { if (element.value === value.toString()) { element.checked = true; } } }, value: function() { var element = this.element, value = element.value; if (element.type == "checkbox") { value = element.checked; } return value; }, destroy: function() { $(this.element).off(CHANGE, this._change); } }) }; binders.select = { value: Binder.extend({ init: function(target, bindings, options) { Binder.fn.init.call(this, target, bindings, options); this._change = proxy(this.change, this); $(this.element).change(this._change); }, change: function() { var values = [], element = this.element, source, field = this.options.valueField || this.options.textField, valuePrimitive = this.options.valuePrimitive, option, valueIndex, value, idx, length; for (idx = 0, length = element.options.length; idx < length; idx++) { option = element.options[idx]; if (option.selected) { value = option.attributes.value; if (value && value.specified) { value = option.value; } else { value = option.text; } values.push(value); } } if (field) { source = this.bindings.source.get(); for (valueIndex = 0; valueIndex < values.length; valueIndex++) { for (idx = 0, length = source.length; idx < length; idx++) { if (source[idx].get(field) == values[valueIndex]) { values[valueIndex] = source[idx]; break; } } } } value = this.bindings[VALUE].get(); if (value instanceof ObservableArray) { value.splice.apply(value, [0, value.length].concat(values)); } else if (!valuePrimitive && (value instanceof ObservableObject || !field)) { this.bindings[VALUE].set(values[0]); } else { this.bindings[VALUE].set(values[0].get(field)); } }, refresh: function() { var optionIndex, element = this.element, options = element.options, value = this.bindings[VALUE].get(), values = value, field = this.options.valueField || this.options.textField, found = false, optionValue; if (!(values instanceof ObservableArray)) { values = new ObservableArray([value]); } element.selectedIndex = -1; for (var valueIndex = 0; valueIndex < values.length; valueIndex++) { value = values[valueIndex]; if (field && value instanceof ObservableObject) { value = value.get(field); } for (optionIndex = 0; optionIndex < options.length; optionIndex++) { optionValue = options[optionIndex].value; if (optionValue === "" && value !== "") { optionValue = options[optionIndex].text; } if (optionValue == value) { options[optionIndex].selected = true; found = true; } } } }, destroy: function() { $(this.element).off(CHANGE, this._change); } }) }; binders.widget = { events : Binder.extend({ init: function(widget, bindings, options) { Binder.fn.init.call(this, widget.element[0], bindings, options); this.widget = widget; this.handlers = {}; }, refresh: function(key) { var binding = this.bindings.events[key], handler = this.handlers[key]; if (handler) { this.widget.unbind(key, handler); } handler = binding.get(); this.handlers[key] = function(e) { e.data = binding.source; handler(e); if (e.data === binding.source) { delete e.data; } }; this.widget.bind(key, this.handlers[key]); }, destroy: function() { var handler; for (handler in this.handlers) { this.widget.unbind(handler, this.handlers[handler]); } } }), checked: Binder.extend({ init: function(widget, bindings, options) { Binder.fn.init.call(this, widget.element[0], bindings, options); this.widget = widget; this._change = proxy(this.change, this); this.widget.bind(CHANGE, this._change); }, change: function() { this.bindings[CHECKED].set(this.value()); }, refresh: function() { this.widget.check(this.bindings[CHECKED].get() === true); }, value: function() { var element = this.element, value = element.value; if (value == "on" || value == "off") { value = element.checked; } return value; }, destroy: function() { this.widget.unbind(CHANGE, this._change); } }), visible: Binder.extend({ init: function(widget, bindings, options) { Binder.fn.init.call(this, widget.element[0], bindings, options); this.widget = widget; }, refresh: function() { var visible = this.bindings.visible.get(); this.widget.wrapper[0].style.display = visible ? "" : "none"; } }), invisible: Binder.extend({ init: function(widget, bindings, options) { Binder.fn.init.call(this, widget.element[0], bindings, options); this.widget = widget; }, refresh: function() { var invisible = this.bindings.invisible.get(); this.widget.wrapper[0].style.display = invisible ? "none" : ""; } }), enabled: Binder.extend({ init: function(widget, bindings, options) { Binder.fn.init.call(this, widget.element[0], bindings, options); this.widget = widget; }, refresh: function() { if (this.widget.enable) { this.widget.enable(this.bindings.enabled.get()); } } }), disabled: Binder.extend({ init: function(widget, bindings, options) { Binder.fn.init.call(this, widget.element[0], bindings, options); this.widget = widget; }, refresh: function() { if (this.widget.enable) { this.widget.enable(!this.bindings.disabled.get()); } } }), source: Binder.extend({ init: function(widget, bindings, options) { var that = this; Binder.fn.init.call(that, widget.element[0], bindings, options); that.widget = widget; that._dataBinding = proxy(that.dataBinding, that); that._dataBound = proxy(that.dataBound, that); that._itemChange = proxy(that.itemChange, that); }, itemChange: function(e) { bindElement(e.item[0], e.data, this._ns(e.ns), [e.data].concat(this.bindings.source._parents())); }, dataBinding: function() { var idx, length, widget = this.widget, items = widget.items(); for (idx = 0, length = items.length; idx < length; idx++) { unbindElementTree(items[idx]); } }, _ns: function(ns) { ns = ns || kendo.ui; var all = [ kendo.ui, kendo.dataviz.ui, kendo.mobile.ui ]; all.splice($.inArray(ns, all), 1); all.unshift(ns); return kendo.rolesFromNamespaces(all); }, dataBound: function(e) { var idx, length, widget = this.widget, items = widget.items(), dataSource = widget.dataSource, view = dataSource.view(), parents, groups = dataSource.group() || []; if (items.length) { if (groups.length) { view = flattenGroups(view); } parents = this.bindings.source._parents(); for (idx = 0, length = view.length; idx < length; idx++) { bindElement(items[idx], view[idx], this._ns(e.ns), [view[idx]].concat(parents)); } } }, refresh: function(e) { var that = this, source, widget = that.widget; e = e || {}; if (!e.action) { that.destroy(); widget.bind("dataBinding", that._dataBinding); widget.bind("dataBound", that._dataBound); widget.bind("itemChange", that._itemChange); source = that.bindings.source.get(); if (widget.dataSource instanceof kendo.data.DataSource && widget.dataSource != source) { if (source instanceof kendo.data.DataSource) { widget.setDataSource(source); } else if (source && source._dataSource) { widget.setDataSource(source._dataSource); } else { widget.dataSource.data(source); } } } }, destroy: function() { var widget = this.widget; widget.unbind("dataBinding", this._dataBinding); widget.unbind("dataBound", this._dataBound); widget.unbind("itemChange", this._itemChange); } }), value: Binder.extend({ init: function(widget, bindings, options) { Binder.fn.init.call(this, widget.element[0], bindings, options); this.widget = widget; this._change = $.proxy(this.change, this); this.widget.first(CHANGE, this._change); var value = this.bindings.value.get(); this._valueIsObservableObject = !options.valuePrimitive && (value == null || value instanceof ObservableObject); this._valueIsObservableArray = value instanceof ObservableArray; this._initChange = false; }, change: function() { var value = this.widget.value(), field = this.options.dataValueField || this.options.dataTextField, isArray = toString.call(value) === "[object Array]", isObservableObject = this._valueIsObservableObject, valueIndex, valueLength, values = [], sourceItem, sourceValue, idx, length, source; this._initChange = true; if (field) { if (this.bindings.source) { source = this.bindings.source.get(); } if (value === "" && (isObservableObject || this.options.valuePrimitive)) { value = null; } else { if (!source || source instanceof kendo.data.DataSource) { source = this.widget.dataSource.view(); } if (isArray) { valueLength = value.length; values = value.slice(0); } for (idx = 0, length = source.length; idx < length; idx++) { sourceItem = source[idx]; sourceValue = sourceItem.get(field); if (isArray) { for (valueIndex = 0; valueIndex < valueLength; valueIndex++) { if (sourceValue == values[valueIndex]) { values[valueIndex] = sourceItem; break; } } } else if (sourceValue == value) { value = isObservableObject ? sourceItem : sourceValue; break; } } if (values[0]) { if (this._valueIsObservableArray) { value = values; } else if (isObservableObject || !field) { value = values[0]; } else { value = values[0].get(field); } } } } this.bindings.value.set(value); this._initChange = false; }, refresh: function() { if (!this._initChange) { var field = this.options.dataValueField || this.options.dataTextField, value = this.bindings.value.get(), idx = 0, length, values = []; if (field) { if (value instanceof ObservableArray) { for (length = value.length; idx < length; idx++) { values[idx] = value[idx].get(field); } value = values; } else if (value instanceof ObservableObject) { value = value.get(field); } } this.widget.value(value); } this._initChange = false; }, destroy: function() { this.widget.unbind(CHANGE, this._change); } }), multiselect: { value: Binder.extend({ init: function(widget, bindings, options) { Binder.fn.init.call(this, widget.element[0], bindings, options); this.widget = widget; this._change = $.proxy(this.change, this); this.widget.first(CHANGE, this._change); this._initChange = false; }, change: function() { var that = this, oldValues = that.bindings[VALUE].get(), valuePrimitive = that.options.valuePrimitive, newValues = valuePrimitive ? that.widget.value() : that.widget.dataItems(); var field = this.options.dataValueField || this.options.dataTextField; newValues = newValues.slice(0); that._initChange = true; if (oldValues instanceof ObservableArray) { var remove = []; var newLength = newValues.length; var i = 0, j = 0; var old = oldValues[i]; var same = false; var removeIndex; var newValue; var found; while (old) { found = false; for (j = 0; j < newLength; j++) { if (valuePrimitive) { same = newValues[j] == old; } else { newValue = newValues[j]; newValue = newValue.get ? newValue.get(field) : newValue; same = newValue == (old.get ? old.get(field) : old); } if (same) { newValues.splice(j, 1); newLength -= 1; found = true; break; } } if (!found) { remove.push(old); splice.call(oldValues, i, 1); removeIndex = i; } else { i += 1; } old = oldValues[i]; } splice.apply(oldValues, [oldValues.length, 0].concat(newValues)); if (remove.length) { oldValues.trigger("change", { action: "remove", items: remove, index: removeIndex }); } if (newValues.length) { oldValues.trigger("change", { action: "add", items: newValues, index: oldValues.length - 1 }); } } else { that.bindings[VALUE].set(newValues); } that._initChange = false; }, refresh: function() { if (!this._initChange) { var field = this.options.dataValueField || this.options.dataTextField, value = this.bindings.value.get(), idx = 0, length, values = [], selectedValue; if (field) { if (value instanceof ObservableArray) { for (length = value.length; idx < length; idx++) { selectedValue = value[idx]; values[idx] = selectedValue.get ? selectedValue.get(field) : selectedValue; } value = values; } else if (value instanceof ObservableObject) { value = value.get(field); } } this.widget.value(value); } }, destroy: function() { this.widget.unbind(CHANGE, this._change); } }) } }; var BindingTarget = Class.extend( { init: function(target, options) { this.target = target; this.options = options; this.toDestroy = []; }, bind: function(bindings) { var nodeName = this.target.nodeName.toLowerCase(), key, hasValue, hasSource, hasEvents, specificBinders = binders[nodeName] || {}; for (key in bindings) { if (key == VALUE) { hasValue = true; } else if (key == SOURCE) { hasSource = true; } else if (key == EVENTS) { hasEvents = true; } else { this.applyBinding(key, bindings, specificBinders); } } if (hasSource) { this.applyBinding(SOURCE, bindings, specificBinders); } if (hasValue) { this.applyBinding(VALUE, bindings, specificBinders); } if (hasEvents) { this.applyBinding(EVENTS, bindings, specificBinders); } }, applyBinding: function(name, bindings, specificBinders) { var binder = specificBinders[name] || binders[name], toDestroy = this.toDestroy, attribute, binding = bindings[name]; if (binder) { binder = new binder(this.target, bindings, this.options); toDestroy.push(binder); if (binding instanceof Binding) { binder.bind(binding); toDestroy.push(binding); } else { for (attribute in binding) { binder.bind(binding, attribute); toDestroy.push(binding[attribute]); } } } else if (name !== "template") { throw new Error("The " + name + " binding is not supported by the " + this.target.nodeName.toLowerCase() + " element"); } }, destroy: function() { var idx, length, toDestroy = this.toDestroy; for (idx = 0, length = toDestroy.length; idx < length; idx++) { toDestroy[idx].destroy(); } } }); var WidgetBindingTarget = BindingTarget.extend( { bind: function(bindings) { var that = this, binding, hasValue = false, hasSource = false, specificBinders = binders.widget[that.target.options.name.toLowerCase()] || {}; for (binding in bindings) { if (binding == VALUE) { hasValue = true; } else if (binding == SOURCE) { hasSource = true; } else { that.applyBinding(binding, bindings); } } if (hasSource) { that.applyBinding(SOURCE, bindings); } if (hasValue) { that.applyBinding(VALUE, bindings, specificBinders[VALUE]); } }, applyBinding: function(name, bindings, specificBinder) { var binder = specificBinder || binders.widget[name], toDestroy = this.toDestroy, attribute, binding = bindings[name]; if (binder) { binder = new binder(this.target, bindings, this.target.options); toDestroy.push(binder); if (binding instanceof Binding) { binder.bind(binding); toDestroy.push(binding); } else { for (attribute in binding) { binder.bind(binding, attribute); toDestroy.push(binding[attribute]); } } } else { throw new Error("The " + name + " binding is not supported by the " + this.target.options.name + " widget"); } } }); function flattenGroups(data) { var idx, length, result = []; for (idx = 0, length = data.length; idx < length; idx++) { if (data[idx].hasSubgroups) { result = result.concat(flattenGroups(data[idx].items)); } else { result = result.concat.apply(result, data[idx].items); } } return result; } function bindingTargetForRole(element, roles) { var widget = kendo.initWidget(element, {}, roles); if (widget) { return new WidgetBindingTarget(widget); } } var keyValueRegExp = /[A-Za-z0-9_\-]+:(\{([^}]*)\}|[^,}]+)/g, whiteSpaceRegExp = /\s/g; function parseBindings(bind) { var result = {}, idx, length, token, colonIndex, key, value, tokens; tokens = bind.match(keyValueRegExp); for (idx = 0, length = tokens.length; idx < length; idx++) { token = tokens[idx]; colonIndex = token.indexOf(":"); key = token.substring(0, colonIndex); value = token.substring(colonIndex + 1); if (value.charAt(0) == "{") { value = parseBindings(value); } result[key] = value; } return result; } function createBindings(bindings, source, type) { var binding, result = {}; for (binding in bindings) { result[binding] = new type(source, bindings[binding]); } return result; } function bindElement(element, source, roles, parents) { var role = element.getAttribute("data-" + kendo.ns + "role"), idx, bind = element.getAttribute("data-" + kendo.ns + "bind"), children = element.children, childrenCopy = [], deep = true, bindings, options = {}, target; parents = parents || [source]; if (role || bind) { unbindElement(element); } if (role) { target = bindingTargetForRole(element, roles); } if (bind) { bind = parseBindings(bind.replace(whiteSpaceRegExp, "")); if (!target) { options = kendo.parseOptions(element, {textField: "", valueField: "", template: "", valueUpdate: CHANGE, valuePrimitive: false, autoBind: true}); options.roles = roles; target = new BindingTarget(element, options); } target.source = source; bindings = createBindings(bind, parents, Binding); if (options.template) { bindings.template = new TemplateBinding(parents, "", options.template); } if (bindings.click) { bind.events = bind.events || {}; bind.events.click = bind.click; delete bindings.click; } if (bindings.source) { deep = false; } if (bind.attr) { bindings.attr = createBindings(bind.attr, parents, Binding); } if (bind.style) { bindings.style = createBindings(bind.style, parents, Binding); } if (bind.events) { bindings.events = createBindings(bind.events, parents, EventBinding); } target.bind(bindings); } if (target) { element.kendoBindingTarget = target; } if (deep && children) { // https://github.com/telerik/kendo/issues/1240 for the weirdness. for (idx = 0; idx < children.length; idx++) { childrenCopy[idx] = children[idx]; } for (idx = 0; idx < childrenCopy.length; idx++) { bindElement(childrenCopy[idx], source, roles, parents); } } } function bind(dom, object) { var idx, length, node, roles = kendo.rolesFromNamespaces([].slice.call(arguments, 2)); object = kendo.observable(object); dom = $(dom); for (idx = 0, length = dom.length; idx < length; idx++) { node = dom[idx]; if (node.nodeType === 1) { bindElement(node, object, roles); } } } function unbindElement(element) { var bindingTarget = element.kendoBindingTarget; if (bindingTarget) { bindingTarget.destroy(); if ($.support.deleteExpando) { delete element.kendoBindingTarget; } else if (element.removeAttribute) { element.removeAttribute("kendoBindingTarget"); } else { element.kendoBindingTarget = null; } } } function unbindElementTree(element) { unbindElement(element); unbindElementChildren(element); } function unbindElementChildren(element) { var children = element.children; if (children) { for (var idx = 0, length = children.length; idx < length; idx++) { unbindElementTree(children[idx]); } } } function unbind(dom) { var idx, length; dom = $(dom); for (idx = 0, length = dom.length; idx < length; idx++ ) { unbindElementTree(dom[idx]); } } function notify(widget, namespace) { var element = widget.element, bindingTarget = element[0].kendoBindingTarget; if (bindingTarget) { bind(element, bindingTarget.source, namespace); } } kendo.unbind = unbind; kendo.bind = bind; kendo.data.binders = binders; kendo.data.Binder = Binder; kendo.notify = notify; kendo.observable = function(object) { if (!(object instanceof ObservableObject)) { object = new ObservableObject(object); } return object; }; kendo.observableHierarchy = function(array) { var dataSource = kendo.data.HierarchicalDataSource.create(array); function recursiveRead(data) { var i, children; for (i = 0; i < data.length; i++) { data[i]._initChildren(); children = data[i].children; children.fetch(); data[i].items = children.data(); recursiveRead(data[i].items); } } dataSource.fetch(); recursiveRead(dataSource.data()); dataSource._data._dataSource = dataSource; return dataSource._data; }; })(window.kendo.jQuery); kendo_module({ id: "validator", name: "Validator", category: "web", description: "The Validator offers an easy way to do a client-side form validation.", depends: [ "core" ] }); (function($, undefined) { var kendo = window.kendo, Widget = kendo.ui.Widget, NS = ".kendoValidator", INVALIDMSG = "k-invalid-msg", invalidMsgRegExp = new RegExp(INVALIDMSG,'i'), INVALIDINPUT = "k-invalid", emailRegExp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i, urlRegExp = /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i, INPUTSELECTOR = ":input:not(:button,[type=submit],[type=reset],[disabled],[readonly])", CHECKBOXSELECTOR = ":checkbox:not([disabled],[readonly])", NUMBERINPUTSELECTOR = "[type=number],[type=range]", BLUR = "blur", NAME = "name", FORM = "form", NOVALIDATE = "novalidate", proxy = $.proxy, patternMatcher = function(value, pattern) { if (typeof pattern === "string") { pattern = new RegExp('^(?:' + pattern + ')$'); } return pattern.test(value); }, matcher = function(input, selector, pattern) { var value = input.val(); if (input.filter(selector).length && value !== "") { return patternMatcher(value, pattern); } return true; }, hasAttribute = function(input, name) { if (input.length) { return input[0].attributes[name] != null; } return false; }; if (!kendo.ui.validator) { kendo.ui.validator = { rules: {}, messages: {} }; } function resolveRules(element) { var resolvers = kendo.ui.validator.ruleResolvers || {}, rules = {}, name; for (name in resolvers) { $.extend(true, rules, resolvers[name].resolve(element)); } return rules; } function decode(value) { return value.replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, "'") .replace(/</g, '<') .replace(/>/g, '>'); } function numberOfDecimalDigits(value) { value = (value + "").split('.'); if (value.length > 1) { return value[1].length; } return 0; } function parseHtml(text) { if ($.parseHTML) { return $($.parseHTML(text)); } return $(text); } var Validator = Widget.extend({ init: function(element, options) { var that = this, resolved = resolveRules(element); options = options || {}; options.rules = $.extend({}, kendo.ui.validator.rules, resolved.rules, options.rules); options.messages = $.extend({}, kendo.ui.validator.messages, resolved.messages, options.messages); Widget.fn.init.call(that, element, options); that._errorTemplate = kendo.template(that.options.errorTemplate); if (that.element.is(FORM)) { that.element.attr(NOVALIDATE, NOVALIDATE); } that._errors = {}; that._attachEvents(); }, events: [ "validate" ], options: { name: "Validator", errorTemplate: '' + ' #=message#', messages: { required: "{0} is required", pattern: "{0} is not valid", min: "{0} should be greater than or equal to {1}", max: "{0} should be smaller than or equal to {1}", step: "{0} is not valid", email: "{0} is not valid email", url: "{0} is not valid URL", date: "{0} is not valid date" }, rules: { required: function(input) { var checkbox = input.filter("[type=checkbox]").length && !input.is(":checked"), value = input.val(); return !(hasAttribute(input, "required") && (value === "" || !value || checkbox)); }, pattern: function(input) { if (input.filter("[type=text],[type=email],[type=url],[type=tel],[type=search],[type=password]").filter("[pattern]").length && input.val() !== "") { return patternMatcher(input.val(), input.attr("pattern")); } return true; }, min: function(input) { if (input.filter(NUMBERINPUTSELECTOR + ",[" + kendo.attr("type") + "=number]").filter("[min]").length && input.val() !== "") { var min = parseFloat(input.attr("min")) || 0, val = kendo.parseFloat(input.val()); return min <= val; } return true; }, max: function(input) { if (input.filter(NUMBERINPUTSELECTOR + ",[" + kendo.attr("type") + "=number]").filter("[max]").length && input.val() !== "") { var max = parseFloat(input.attr("max")) || 0, val = kendo.parseFloat(input.val()); return max >= val; } return true; }, step: function(input) { if (input.filter(NUMBERINPUTSELECTOR + ",[" + kendo.attr("type") + "=number]").filter("[step]").length && input.val() !== "") { var min = parseFloat(input.attr("min")) || 0, step = parseFloat(input.attr("step")) || 1, val = parseFloat(input.val()), decimals = numberOfDecimalDigits(step), raise; if (decimals) { raise = Math.pow(10, decimals); return (((val-min)*raise)%(step*raise)) / Math.pow(100, decimals) === 0; } return ((val-min)%step) === 0; } return true; }, email: function(input) { return matcher(input, "[type=email],[" + kendo.attr("type") + "=email]", emailRegExp); }, url: function(input) { return matcher(input, "[type=url],[" + kendo.attr("type") + "=url]", urlRegExp); }, date: function(input) { if (input.filter("[type^=date],[" + kendo.attr("type") + "=date]").length && input.val() !== "") { return kendo.parseDate(input.val(), input.attr(kendo.attr("format"))) !== null; } return true; } }, validateOnBlur: true }, destroy: function() { Widget.fn.destroy.call(this); this.element.off(NS); }, _submit: function(e) { if (!this.validate()) { e.stopPropagation(); e.stopImmediatePropagation(); e.preventDefault(); return false; } return true; }, _attachEvents: function() { var that = this; if (that.element.is(FORM)) { that.element.on("submit" + NS, proxy(that._submit, that)); } if (that.options.validateOnBlur) { if (!that.element.is(INPUTSELECTOR)) { that.element.on(BLUR + NS, INPUTSELECTOR, function() { that.validateInput($(this)); }); that.element.on("click" + NS, CHECKBOXSELECTOR, function() { that.validateInput($(this)); }); } else { that.element.on(BLUR + NS, function() { that.validateInput(that.element); }); if (that.element.is(CHECKBOXSELECTOR)) { that.element.on("click" + NS, function() { that.validateInput(that.element); }); } } } }, validate: function() { var inputs; var idx; var result = false; var length; this._errors = {}; if (!this.element.is(INPUTSELECTOR)) { var invalid = false; inputs = this.element.find(INPUTSELECTOR); for (idx = 0, length = inputs.length; idx < length; idx++) { if (!this.validateInput(inputs.eq(idx))) { invalid = true; } } result = !invalid; } else { result = this.validateInput(this.element); } this.trigger("validate", { valid: result }); return result; }, validateInput: function(input) { input = $(input); var that = this, template = that._errorTemplate, result = that._checkValidity(input), valid = result.valid, className = "." + INVALIDMSG, fieldName = (input.attr(NAME) || ""), lbl = that._findMessageContainer(fieldName).add(input.next(className)).hide(), messageText; input.removeAttr("aria-invalid"); if (!valid) { messageText = that._extractMessage(input, result.key); that._errors[fieldName] = messageText; var messageLabel = parseHtml(template({ message: decode(messageText) })); that._decorateMessageContainer(messageLabel, fieldName); if (!lbl.replaceWith(messageLabel).length) { messageLabel.insertAfter(input); } messageLabel.show(); input.attr("aria-invalid", true); } input.toggleClass(INVALIDINPUT, !valid); return valid; }, hideMessages: function() { var that = this, className = "." + INVALIDMSG, element = that.element; if (!element.is(INPUTSELECTOR)) { element.find(className).hide(); } else { element.next(className).hide(); } }, _findMessageContainer: function(fieldName) { var locators = kendo.ui.validator.messageLocators, name, containers = $(), children = this.element[0].getElementsByTagName("*"); for (var idx = 0, length = children.length; idx < length; idx++) { var element = children[idx]; if (invalidMsgRegExp.test(element.className)) { var attr = element.getAttribute(kendo.attr("for")); if (attr === fieldName) { containers = containers.add(element); } } } for (name in locators) { containers = containers.add(locators[name].locate(this.element, fieldName)); } return containers; }, _decorateMessageContainer: function(container, fieldName) { var locators = kendo.ui.validator.messageLocators, name; container.addClass(INVALIDMSG) .attr(kendo.attr("for"), fieldName || ""); for (name in locators) { locators[name].decorate(container, fieldName); } container.attr("role", "alert"); }, _extractMessage: function(input, ruleKey) { var that = this, customMessage = that.options.messages[ruleKey], fieldName = input.attr(NAME); customMessage = kendo.isFunction(customMessage) ? customMessage(input) : customMessage; return kendo.format(input.attr(kendo.attr(ruleKey + "-msg")) || input.attr("validationMessage") || input.attr("title") || customMessage || "", fieldName, input.attr(ruleKey)); }, _checkValidity: function(input) { var rules = this.options.rules, rule; for (rule in rules) { if (!rules[rule](input)) { return { valid: false, key: rule }; } } return { valid: true }; }, errors: function() { var results = [], errors = this._errors, error; for (error in errors) { results.push(errors[error]); } return results; } }); kendo.ui.plugin(Validator); })(window.kendo.jQuery); kendo_module({ id: "userevents", name: "User Events", category: "framework", depends: [ "core" ], hidden: true }); (function ($, undefined) { var kendo = window.kendo, support = kendo.support, document = window.document, Class = kendo.Class, Observable = kendo.Observable, now = $.now, extend = $.extend, OS = support.mobileOS, invalidZeroEvents = OS && OS.android, DEFAULT_MIN_HOLD = 800, DEFAULT_THRESHOLD = support.browser.ie ? 5 : 0, // WP8 and W8 are very sensitive and always report move. // UserEvents events PRESS = "press", HOLD = "hold", SELECT = "select", START = "start", MOVE = "move", END = "end", CANCEL = "cancel", TAP = "tap", RELEASE = "release", GESTURESTART = "gesturestart", GESTURECHANGE = "gesturechange", GESTUREEND = "gestureend", GESTURETAP = "gesturetap"; function touchDelta(touch1, touch2) { var x1 = touch1.x.location, y1 = touch1.y.location, x2 = touch2.x.location, y2 = touch2.y.location, dx = x1 - x2, dy = y1 - y2; return { center: { x: (x1 + x2) / 2, y: (y1 + y2) / 2 }, distance: Math.sqrt(dx*dx + dy*dy) }; } function getTouches(e) { var touches = [], originalEvent = e.originalEvent, currentTarget = e.currentTarget, idx = 0, length, changedTouches, touch; if (e.api) { touches.push({ id: 2, // hardcoded ID for API call; event: e, target: e.target, currentTarget: e.target, location: e }); } else if (e.type.match(/touch/)) { changedTouches = originalEvent ? originalEvent.changedTouches : []; for (length = changedTouches.length; idx < length; idx ++) { touch = changedTouches[idx]; touches.push({ location: touch, event: e, target: touch.target, currentTarget: currentTarget, id: touch.identifier }); } } else if (support.pointers || support.msPointers) { touches.push({ location: originalEvent, event: e, target: e.target, currentTarget: currentTarget, id: originalEvent.pointerId }); } else { touches.push({ id: 1, // hardcoded ID for mouse event; event: e, target: e.target, currentTarget: currentTarget, location: e }); } return touches; } var TouchAxis = Class.extend({ init: function(axis, location) { var that = this; that.axis = axis; that._updateLocationData(location); that.startLocation = that.location; that.velocity = that.delta = 0; that.timeStamp = now(); }, move: function(location) { var that = this, offset = location["page" + that.axis], timeStamp = now(), timeDelta = (timeStamp - that.timeStamp) || 1; // Firing manually events in tests can make this 0; if (!offset && invalidZeroEvents) { return; } that.delta = offset - that.location; that._updateLocationData(location); that.initialDelta = offset - that.startLocation; that.velocity = that.delta / timeDelta; that.timeStamp = timeStamp; }, _updateLocationData: function(location) { var that = this, axis = that.axis; that.location = location["page" + axis]; that.client = location["client" + axis]; that.screen = location["screen" + axis]; } }); var Touch = Class.extend({ init: function(userEvents, target, touchInfo) { var that = this; extend(that, { x: new TouchAxis("X", touchInfo.location), y: new TouchAxis("Y", touchInfo.location), userEvents: userEvents, target: target, currentTarget: touchInfo.currentTarget, initialTouch: touchInfo.target, id: touchInfo.id, _moved: false, _finished: false }); that.press = function() { that._trigger(PRESS, touchInfo); that._holdTimeout = setTimeout(function() { that._trigger(HOLD, touchInfo); }, userEvents.minHold); }; }, move: function(touchInfo) { var that = this; if (that._finished) { return; } that.x.move(touchInfo.location); that.y.move(touchInfo.location); if (!that._moved) { if (that._withinIgnoreThreshold()) { return; } if (!UserEvents.current || UserEvents.current === that.userEvents) { that._start(touchInfo); } else { return that.dispose(); } } // Event handlers may cancel the drag in the START event handler, hence the double check for pressed. if (!that._finished) { that._trigger(MOVE, touchInfo); } }, end: function(touchInfo) { var that = this; that.endTime = now(); if (that._finished) { return; } if (that._moved) { that._trigger(END, touchInfo); } else { that._trigger(TAP, touchInfo); } clearTimeout(that._holdTimeout); that._trigger(RELEASE, touchInfo); that.dispose(); }, dispose: function() { var that = this, userEvents = that.userEvents, activeTouches = userEvents.touches; that._finished = true; activeTouches.splice($.inArray(that, activeTouches), 1); }, skip: function() { this.dispose(); }, cancel: function() { this.dispose(); }, isMoved: function() { return this._moved; }, _start: function(touchInfo) { clearTimeout(this._holdTimeout); this.startTime = now(); this._moved = true; this._trigger(START, touchInfo); }, _trigger: function(name, touchInfo) { var that = this, jQueryEvent = touchInfo.event, data = { touch: that, x: that.x, y: that.y, target: that.target, event: jQueryEvent }; if(that.userEvents.notify(name, data)) { jQueryEvent.preventDefault(); } }, _withinIgnoreThreshold: function() { var xDelta = this.x.initialDelta, yDelta = this.y.initialDelta; return Math.sqrt(xDelta * xDelta + yDelta * yDelta) <= this.userEvents.threshold; } }); function preventTrigger(e) { e.preventDefault(); var target = $(e.data.root), // Determine the correct parent to receive the event and bubble. parent = target.closest(".k-widget").parent(); if (!parent[0]) { parent = target.parent(); } var fakeEventData = $.extend(true, {}, e, { target: target[0] }); parent.trigger($.Event(e.type, fakeEventData)); } function withEachUpEvent(callback) { var downEvents = kendo.eventMap.up.split(" "), idx = 0, length = downEvents.length; for(; idx < length; idx ++) { callback(downEvents[idx]); } } var UserEvents = Observable.extend({ init: function(element, options) { var that = this, filter, ns = kendo.guid(); options = options || {}; filter = that.filter = options.filter; that.threshold = options.threshold || DEFAULT_THRESHOLD; that.minHold = options.minHold || DEFAULT_MIN_HOLD; that.touches = []; that._maxTouches = options.multiTouch ? 2 : 1; that.allowSelection = options.allowSelection; that.captureUpIfMoved = options.captureUpIfMoved; that.eventNS = ns; element = $(element).handler(that); Observable.fn.init.call(that); extend(that, { element: element, surface: options.global ? $(document.documentElement) : $(options.surface || element), stopPropagation: options.stopPropagation, pressed: false }); that.surface.handler(that) .on(kendo.applyEventMap("move", ns), "_move") .on(kendo.applyEventMap("up cancel", ns), "_end"); element.on(kendo.applyEventMap("down", ns), filter, "_start"); if (support.pointers || support.msPointers) { element.css("-ms-touch-action", "pinch-zoom double-tap-zoom"); } if (options.preventDragEvent) { element.on(kendo.applyEventMap("dragstart", ns), kendo.preventDefault); } element.on(kendo.applyEventMap("mousedown selectstart", ns), filter, { root: element }, "_select"); if (that.captureUpIfMoved && support.eventCapture) { var surfaceElement = that.surface[0], preventIfMovingProxy = $.proxy(that.preventIfMoving, that); withEachUpEvent(function(eventName) { surfaceElement.addEventListener(eventName, preventIfMovingProxy, true); }); } that.bind([ PRESS, HOLD, TAP, START, MOVE, END, RELEASE, CANCEL, GESTURESTART, GESTURECHANGE, GESTUREEND, GESTURETAP, SELECT ], options); }, preventIfMoving: function(e) { if (this._isMoved()) { e.preventDefault(); } }, destroy: function() { var that = this; if (that._destroyed) { return; } that._destroyed = true; if (that.captureUpIfMoved && support.eventCapture) { var surfaceElement = that.surface[0]; withEachUpEvent(function(eventName) { surfaceElement.removeEventListener(eventName, that.preventIfMoving); }); } that.element.kendoDestroy(that.eventNS); that.surface.kendoDestroy(that.eventNS); that.element.removeData("handler"); that.surface.removeData("handler"); that._disposeAll(); that.unbind(); delete that.surface; delete that.element; }, capture: function() { UserEvents.current = this; }, cancel: function() { this._disposeAll(); this.trigger(CANCEL); }, notify: function(eventName, data) { var that = this, touches = that.touches; if (this._isMultiTouch()) { switch(eventName) { case MOVE: eventName = GESTURECHANGE; break; case END: eventName = GESTUREEND; break; case TAP: eventName = GESTURETAP; break; } extend(data, {touches: touches}, touchDelta(touches[0], touches[1])); } return this.trigger(eventName, data); }, // API press: function(x, y, target) { this._apiCall("_start", x, y, target); }, move: function(x, y) { this._apiCall("_move", x, y); }, end: function(x, y) { this._apiCall("_end", x, y); }, _isMultiTouch: function() { return this.touches.length > 1; }, _maxTouchesReached: function() { return this.touches.length >= this._maxTouches; }, _disposeAll: function() { var touches = this.touches; while (touches.length > 0) { touches.pop().dispose(); } }, _isMoved: function() { return $.grep(this.touches, function(touch) { return touch.isMoved(); }).length; }, _select: function(e) { if (!this.allowSelection || this.trigger(SELECT, { event: e })) { preventTrigger(e); } }, _start: function(e) { var that = this, idx = 0, filter = that.filter, target, touches = getTouches(e), length = touches.length, touch; if (that._maxTouchesReached()) { return; } UserEvents.current = null; that.currentTarget = e.currentTarget; if (that.stopPropagation) { e.stopPropagation(); } for (; idx < length; idx ++) { if (that._maxTouchesReached()) { break; } touch = touches[idx]; if (filter) { target = $(touch.currentTarget); // target.is(filter) ? target : target.closest(filter, that.element); } else { target = that.element; } if (!target.length) { continue; } touch = new Touch(that, target, touch); that.touches.push(touch); touch.press(); if (that._isMultiTouch()) { that.notify("gesturestart", {}); } } }, _move: function(e) { this._eachTouch("move", e); }, _end: function(e) { this._eachTouch("end", e); }, _eachTouch: function(methodName, e) { var that = this, dict = {}, touches = getTouches(e), activeTouches = that.touches, idx, touch, touchInfo, matchingTouch; for (idx = 0; idx < activeTouches.length; idx ++) { touch = activeTouches[idx]; dict[touch.id] = touch; } for (idx = 0; idx < touches.length; idx ++) { touchInfo = touches[idx]; matchingTouch = dict[touchInfo.id]; if (matchingTouch) { matchingTouch[methodName](touchInfo); } } }, _apiCall: function(type, x, y, target) { this[type]({ api: true, pageX: x, pageY: y, clientX: x, clientY: y, target: target || this.element, stopPropagation: $.noop, preventDefault: $.noop }); } }); kendo.getTouches = getTouches; kendo.touchDelta = touchDelta; kendo.UserEvents = UserEvents; })(window.kendo.jQuery); kendo_module({ id: "draganddrop", name: "Drag & drop", category: "framework", description: "Drag & drop functionality for any DOM element.", depends: [ "core", "userevents" ] }); (function ($, undefined) { var kendo = window.kendo, support = kendo.support, document = window.document, Class = kendo.Class, Widget = kendo.ui.Widget, Observable = kendo.Observable, UserEvents = kendo.UserEvents, proxy = $.proxy, extend = $.extend, getOffset = kendo.getOffset, draggables = {}, dropTargets = {}, dropAreas = {}, lastDropTarget, OS = support.mobileOS, invalidZeroEvents = OS && OS.android, mobileChrome = (invalidZeroEvents && OS.browser == "chrome"), KEYUP = "keyup", CHANGE = "change", // Draggable events DRAGSTART = "dragstart", HOLD = "hold", DRAG = "drag", DRAGEND = "dragend", DRAGCANCEL = "dragcancel", // DropTarget events DRAGENTER = "dragenter", DRAGLEAVE = "dragleave", DROP = "drop"; function contains(parent, child) { try { return $.contains(parent, child) || parent == child; } catch (e) { return false; } } function elementUnderCursor(e) { if (mobileChrome) { return document.elementFromPoint(e.x.screen, e.y.screen); } else { return document.elementFromPoint(e.x.client, e.y.client); } } function numericCssPropery(element, property) { return parseInt(element.css(property), 10) || 0; } function within(value, range) { return Math.min(Math.max(value, range.min), range.max); } function containerBoundaries(container, element) { var offset = getOffset(container), minX = offset.left + numericCssPropery(container, "borderLeftWidth") + numericCssPropery(container, "paddingLeft"), minY = offset.top + numericCssPropery(container, "borderTopWidth") + numericCssPropery(container, "paddingTop"), maxX = minX + container.width() - element.outerWidth(true), maxY = minY + container.height() - element.outerHeight(true); return { x: { min: minX, max: maxX }, y: { min: minY, max: maxY } }; } function checkTarget(target, targets, areas) { var theTarget, theFilter, i = 0, targetLen = targets && targets.length, areaLen = areas && areas.length; while (target && target.parentNode) { for (i = 0; i < targetLen; i ++) { theTarget = targets[i]; if (theTarget.element[0] === target) { return { target: theTarget, targetElement: target }; } } for (i = 0; i < areaLen; i ++) { theFilter = areas[i]; if (support.matchesSelector.call(target, theFilter.options.filter)) { return { target: theFilter, targetElement: target }; } } target = target.parentNode; } return undefined; } var TapCapture = Observable.extend({ init: function(element, options) { var that = this, domElement = element[0]; that.capture = false; if (domElement.addEventListener) { $.each(kendo.eventMap.down.split(" "), function() { domElement.addEventListener(this, proxy(that._press, that), true); }); $.each(kendo.eventMap.up.split(" "), function() { domElement.addEventListener(this, proxy(that._release, that), true); }); } else { $.each(kendo.eventMap.down.split(" "), function() { domElement.attachEvent(this, proxy(that._press, that)); }); $.each(kendo.eventMap.up.split(" "), function() { domElement.attachEvent(this, proxy(that._release, that)); }); } Observable.fn.init.call(that); that.bind(["press", "release"], options || {}); }, captureNext: function() { this.capture = true; }, cancelCapture: function() { this.capture = false; }, _press: function(e) { var that = this; that.trigger("press"); if (that.capture) { e.preventDefault(); } }, _release: function(e) { var that = this; that.trigger("release"); if (that.capture) { e.preventDefault(); that.cancelCapture(); } } }); var PaneDimension = Observable.extend({ init: function(options) { var that = this; Observable.fn.init.call(that); that.forcedEnabled = false; $.extend(that, options); that.scale = 1; if (that.horizontal) { that.measure = "offsetWidth"; that.scrollSize = "scrollWidth"; that.axis = "x"; } else { that.measure = "offsetHeight"; that.scrollSize = "scrollHeight"; that.axis = "y"; } }, makeVirtual: function() { $.extend(this, { virtual: true, forcedEnabled: true, _virtualMin: 1000, _virtualMax: -1000 }); }, virtualSize: function(min, max) { if (this._virtualMin !== min || this._virtualMax !== max) { this._virtualMin = min; this._virtualMax = max; this.update(); } }, outOfBounds: function(offset) { return offset > this.max || offset < this.min; }, forceEnabled: function() { this.forcedEnabled = true; }, getSize: function() { return this.container[0][this.measure]; }, getTotal: function() { return this.element[0][this.scrollSize]; }, rescale: function(scale) { this.scale = scale; }, update: function(silent) { var that = this, total = that.virtual ? that._virtualMax : that.getTotal(), scaledTotal = total * that.scale, size = that.getSize(); that.max = that.virtual ? -that._virtualMin : 0; that.size = size; that.total = scaledTotal; that.min = Math.min(that.max, size - scaledTotal); that.minScale = size / total; that.centerOffset = (scaledTotal - size) / 2; that.enabled = that.forcedEnabled || (scaledTotal > size); if (!silent) { that.trigger(CHANGE, that); } } }); var PaneDimensions = Observable.extend({ init: function(options) { var that = this; Observable.fn.init.call(that); that.x = new PaneDimension(extend({horizontal: true}, options)); that.y = new PaneDimension(extend({horizontal: false}, options)); that.container = options.container; that.forcedMinScale = options.minScale; that.maxScale = options.maxScale || 100; that.bind(CHANGE, options); }, rescale: function(newScale) { this.x.rescale(newScale); this.y.rescale(newScale); this.refresh(); }, centerCoordinates: function() { return { x: Math.min(0, -this.x.centerOffset), y: Math.min(0, -this.y.centerOffset) }; }, refresh: function() { var that = this; that.x.update(); that.y.update(); that.enabled = that.x.enabled || that.y.enabled; that.minScale = that.forcedMinScale || Math.min(that.x.minScale, that.y.minScale); that.fitScale = Math.max(that.x.minScale, that.y.minScale); that.trigger(CHANGE); } }); var PaneAxis = Observable.extend({ init: function(options) { var that = this; extend(that, options); Observable.fn.init.call(that); }, dragMove: function(delta) { var that = this, dimension = that.dimension, axis = that.axis, movable = that.movable, position = movable[axis] + delta; if (!dimension.enabled) { return; } if ((position < dimension.min && delta < 0) || (position > dimension.max && delta > 0)) { delta *= that.resistance; } movable.translateAxis(axis, delta); that.trigger(CHANGE, that); } }); var Pane = Class.extend({ init: function(options) { var that = this, x, y, resistance, movable; extend(that, {elastic: true}, options); resistance = that.elastic ? 0.5 : 0; movable = that.movable; that.x = x = new PaneAxis({ axis: "x", dimension: that.dimensions.x, resistance: resistance, movable: movable }); that.y = y = new PaneAxis({ axis: "y", dimension: that.dimensions.y, resistance: resistance, movable: movable }); that.userEvents.bind(["move", "end", "gesturestart", "gesturechange"], { gesturestart: function(e) { that.gesture = e; that.offset = that.dimensions.container.offset(); }, gesturechange: function(e) { var previousGesture = that.gesture, previousCenter = previousGesture.center, center = e.center, scaleDelta = e.distance / previousGesture.distance, minScale = that.dimensions.minScale, maxScale = that.dimensions.maxScale, coordinates; if (movable.scale <= minScale && scaleDelta < 1) { // Resist shrinking. Instead of shrinking from 1 to 0.5, it will shrink to 0.5 + (1 /* minScale */ - 0.5) * 0.8 = 0.9; scaleDelta += (1 - scaleDelta) * 0.8; } if (movable.scale * scaleDelta >= maxScale) { scaleDelta = maxScale / movable.scale; } var offsetX = movable.x + that.offset.left, offsetY = movable.y + that.offset.top; coordinates = { x: (offsetX - previousCenter.x) * scaleDelta + center.x - offsetX, y: (offsetY - previousCenter.y) * scaleDelta + center.y - offsetY }; movable.scaleWith(scaleDelta); x.dragMove(coordinates.x); y.dragMove(coordinates.y); that.dimensions.rescale(movable.scale); that.gesture = e; e.preventDefault(); }, move: function(e) { if (e.event.target.tagName.match(/textarea|input/i)) { return; } if (x.dimension.enabled || y.dimension.enabled) { x.dragMove(e.x.delta); y.dragMove(e.y.delta); e.preventDefault(); } else { e.touch.skip(); } }, end: function(e) { e.preventDefault(); } }); } }); var TRANSFORM_STYLE = support.transitions.prefix + "Transform", translate; if (support.hasHW3D) { translate = function(x, y, scale) { return "translate3d(" + x + "px," + y +"px,0) scale(" + scale + ")"; }; } else { translate = function(x, y, scale) { return "translate(" + x + "px," + y +"px) scale(" + scale + ")"; }; } var Movable = Observable.extend({ init: function(element) { var that = this; Observable.fn.init.call(that); that.element = $(element); that.element[0].style.webkitTransformOrigin = "left top"; that.x = 0; that.y = 0; that.scale = 1; that._saveCoordinates(translate(that.x, that.y, that.scale)); }, translateAxis: function(axis, by) { this[axis] += by; this.refresh(); }, scaleTo: function(scale) { this.scale = scale; this.refresh(); }, scaleWith: function(scaleDelta) { this.scale *= scaleDelta; this.refresh(); }, translate: function(coordinates) { this.x += coordinates.x; this.y += coordinates.y; this.refresh(); }, moveAxis: function(axis, value) { this[axis] = value; this.refresh(); }, moveTo: function(coordinates) { extend(this, coordinates); this.refresh(); }, refresh: function() { var that = this, x = that.x, y = that.y, newCoordinates; if (that.round) { x = Math.round(x); y = Math.round(y); } newCoordinates = translate(x, y, that.scale); if (newCoordinates != that.coordinates) { if (kendo.support.browser.msie && kendo.support.browser.version < 10) { that.element[0].style.position = "absolute"; that.element[0].style.left = that.x + "px"; that.element[0].style.top = that.y + "px"; } else { that.element[0].style[TRANSFORM_STYLE] = newCoordinates; } that._saveCoordinates(newCoordinates); that.trigger(CHANGE); } }, _saveCoordinates: function(coordinates) { this.coordinates = coordinates; } }); var DropTarget = Widget.extend({ init: function(element, options) { var that = this; Widget.fn.init.call(that, element, options); var group = that.options.group; if (!(group in dropTargets)) { dropTargets[group] = [ that ]; } else { dropTargets[group].push( that ); } }, events: [ DRAGENTER, DRAGLEAVE, DROP ], options: { name: "DropTarget", group: "default" }, destroy: function() { var groupName = this.options.group, group = dropTargets[groupName] || dropAreas[groupName], i; if (group.length > 1) { Widget.fn.destroy.call(this); for (i = 0; i < group.length; i++) { if (group[i] == this) { group.splice(i, 1); break; } } } else { DropTarget.destroyGroup(groupName); } }, _trigger: function(eventName, e) { var that = this, draggable = draggables[that.options.group]; if (draggable) { return that.trigger(eventName, extend({}, e.event, { draggable: draggable, dropTarget: e.dropTarget })); } }, _over: function(e) { this._trigger(DRAGENTER, e); }, _out: function(e) { this._trigger(DRAGLEAVE, e); }, _drop: function(e) { var that = this, draggable = draggables[that.options.group]; if (draggable) { draggable.dropped = !that._trigger(DROP, e); } } }); DropTarget.destroyGroup = function(groupName) { var group = dropTargets[groupName] || dropAreas[groupName], i; if (group) { for (i = 0; i < group.length; i++) { Widget.fn.destroy.call(group[i]); } group.length = 0; delete dropTargets[groupName]; delete dropAreas[groupName]; } }; DropTarget._cache = dropTargets; var DropTargetArea = DropTarget.extend({ init: function(element, options) { var that = this; Widget.fn.init.call(that, element, options); var group = that.options.group; if (!(group in dropAreas)) { dropAreas[group] = [ that ]; } else { dropAreas[group].push( that ); } }, options: { name: "DropTargetArea", group: "default", filter: null } }); var Draggable = Widget.extend({ init: function (element, options) { var that = this; Widget.fn.init.call(that, element, options); that._activated = false; that.userEvents = new UserEvents(that.element, { global: true, stopPropagation: true, filter: that.options.filter, threshold: that.options.distance, start: proxy(that._start, that), hold: proxy(that._hold, that), move: proxy(that._drag, that), end: proxy(that._end, that), cancel: proxy(that._cancel, that) }); that._afterEndHandler = proxy(that._afterEnd, that); that.captureEscape = function(e) { if (e.keyCode === kendo.keys.ESC) { that._trigger(DRAGCANCEL, {event: e}); that.userEvents.cancel(); } }; }, events: [ HOLD, DRAGSTART, DRAG, DRAGEND, DRAGCANCEL ], options: { name: "Draggable", distance: 5, group: "default", cursorOffset: null, axis: null, container: null, holdToDrag: false, dropped: false }, cancelHold: function() { this._activated = false; }, _updateHint: function(e) { var that = this, coordinates, options = that.options, boundaries = that.boundaries, axis = options.axis, cursorOffset = that.options.cursorOffset; if (cursorOffset) { coordinates = { left: e.x.location + cursorOffset.left, top: e.y.location + cursorOffset.top }; } else { that.hintOffset.left += e.x.delta; that.hintOffset.top += e.y.delta; coordinates = $.extend({}, that.hintOffset); } if (boundaries) { coordinates.top = within(coordinates.top, boundaries.y); coordinates.left = within(coordinates.left, boundaries.x); } if (axis === "x") { delete coordinates.top; } else if (axis === "y") { delete coordinates.left; } that.hint.css(coordinates); }, _start: function(e) { var that = this, options = that.options, container = options.container, hint = options.hint; if (options.holdToDrag && !that._activated) { that.userEvents.cancel(); return; } that.currentTarget = e.target; that.currentTargetOffset = getOffset(that.currentTarget); if (hint) { if (that.hint) { that.hint.stop(true, true).remove(); } that.hint = kendo.isFunction(hint) ? $(hint.call(that, that.currentTarget)) : hint; var offset = getOffset(that.currentTarget); that.hintOffset = offset; that.hint.css( { position: "absolute", zIndex: 20000, // the Window's z-index is 10000 and can be raised because of z-stacking left: offset.left, top: offset.top }) .appendTo(document.body); } draggables[options.group] = that; that.dropped = false; if (container) { that.boundaries = containerBoundaries(container, that.hint); } if (that._trigger(DRAGSTART, e)) { that.userEvents.cancel(); that._afterEnd(); } $(document).on(KEYUP, that.captureEscape); }, _hold: function(e) { this.currentTarget = e.target; if (this._trigger(HOLD, e)) { this.userEvents.cancel(); } else { this._activated = true; } }, _drag: function(e) { var that = this; e.preventDefault(); that._withDropTarget(e, function(target, targetElement) { if (!target) { if (lastDropTarget) { lastDropTarget._trigger(DRAGLEAVE, extend(e, { dropTarget: $(lastDropTarget.targetElement) })); lastDropTarget = null; } return; } if (lastDropTarget) { if (targetElement === lastDropTarget.targetElement) { return; } lastDropTarget._trigger(DRAGLEAVE, extend(e, { dropTarget: $(lastDropTarget.targetElement) })); } target._trigger(DRAGENTER, extend(e, { dropTarget: $(targetElement) })); lastDropTarget = extend(target, { targetElement: targetElement }); }); that._trigger(DRAG, e); if (that.hint) { that._updateHint(e); } }, _end: function(e) { var that = this; that._withDropTarget(e, function(target, targetElement) { if (target) { target._drop(extend({}, e, { dropTarget: $(targetElement) })); lastDropTarget = null; } }); that._trigger(DRAGEND, e); that._cancel(e.event); }, _cancel: function() { var that = this; that._activated = false; if (that.hint && !that.dropped) { setTimeout(function() { that.hint.stop(true, true).animate(that.currentTargetOffset, "fast", that._afterEndHandler); }, 0); } else { that._afterEnd(); } }, _trigger: function(eventName, e) { var that = this; return that.trigger( eventName, extend( {}, e.event, { x: e.x, y: e.y, currentTarget: that.currentTarget, dropTarget: e.dropTarget } )); }, _withDropTarget: function(e, callback) { var that = this, target, result, options = that.options, targets = dropTargets[options.group], areas = dropAreas[options.group]; if (targets && targets.length || areas && areas.length) { target = elementUnderCursor(e); if (that.hint && contains(that.hint[0], target)) { that.hint.hide(); target = elementUnderCursor(e); // IE8 does not return the element in iframe from first attempt if (!target) { target = elementUnderCursor(e); } that.hint.show(); } result = checkTarget(target, targets, areas); if (result) { callback(result.target, result.targetElement); } else { callback(); } } }, destroy: function() { var that = this; Widget.fn.destroy.call(that); that._afterEnd(); that.userEvents.destroy(); }, _afterEnd: function() { var that = this; if (that.hint) { that.hint.remove(); } delete draggables[that.options.group]; that.trigger("destroy"); $(document).off(KEYUP, that.captureEscape); } }); kendo.ui.plugin(DropTarget); kendo.ui.plugin(DropTargetArea); kendo.ui.plugin(Draggable); kendo.TapCapture = TapCapture; kendo.containerBoundaries = containerBoundaries; extend(kendo.ui, { Pane: Pane, PaneDimensions: PaneDimensions, Movable: Movable }); })(window.kendo.jQuery); kendo_module({ id: "mobile.scroller", name: "Scroller", category: "mobile", description: "The Kendo Mobile Scroller widget enables touch friendly kinetic scrolling for the contents of a given DOM element.", depends: [ "core", "fx", "draganddrop" ] }); (function($, undefined) { var kendo = window.kendo, mobile = kendo.mobile, fx = kendo.effects, ui = mobile.ui, proxy = $.proxy, extend = $.extend, Widget = ui.Widget, Class = kendo.Class, Movable = kendo.ui.Movable, Pane = kendo.ui.Pane, PaneDimensions = kendo.ui.PaneDimensions, Transition = fx.Transition, Animation = fx.Animation, abs = Math.abs, SNAPBACK_DURATION = 500, SCROLLBAR_OPACITY = 0.7, FRICTION = 0.96, VELOCITY_MULTIPLIER = 10, MAX_VELOCITY = 55, OUT_OF_BOUNDS_FRICTION = 0.5, ANIMATED_SCROLLER_PRECISION = 5, RELEASECLASS = "km-scroller-release", REFRESHCLASS = "km-scroller-refresh", PULL = "pull", CHANGE = "change", RESIZE = "resize", SCROLL = "scroll"; var ZoomSnapBack = Animation.extend({ init: function(options) { var that = this; Animation.fn.init.call(that); extend(that, options); that.userEvents.bind("gestureend", proxy(that.start, that)); that.tapCapture.bind("press", proxy(that.cancel, that)); }, done: function() { return this.dimensions.minScale - this.movable.scale < 0.01; }, tick: function() { var movable = this.movable; movable.scaleWith(1.1); this.dimensions.rescale(movable.scale); }, onEnd: function() { var movable = this.movable; movable.scaleTo(this.dimensions.minScale); this.dimensions.rescale(movable.scale); } }); var DragInertia = Animation.extend({ init: function(options) { var that = this; Animation.fn.init.call(that); extend(that, options, { transition: new Transition({ axis: options.axis, movable: options.movable, onEnd: function() { that._end(); } }) }); that.tapCapture.bind("press", function() { that.cancel(); }); that.userEvents.bind("end", proxy(that.start, that)); that.userEvents.bind("gestureend", proxy(that.start, that)); that.userEvents.bind("tap", proxy(that.onEnd, that)); }, onCancel: function() { this.transition.cancel(); }, freeze: function(location) { var that = this; that.cancel(); that._moveTo(location); }, onEnd: function() { var that = this; if (that._outOfBounds()) { that._snapBack(); } else { that._end(); } }, done: function() { return abs(this.velocity) < 1; }, start: function(e) { var that = this; if (!that.dimension.enabled) { return; } if (that._outOfBounds()) { that._snapBack(); } else { that.velocity = Math.max(Math.min( e.touch[that.axis].velocity * that.velocityMultiplier, MAX_VELOCITY), -MAX_VELOCITY); if (that.velocity) { that.tapCapture.captureNext(); Animation.fn.start.call(that); } else { that._end(); } } }, tick: function() { var that = this, dimension = that.dimension, friction = that._outOfBounds() ? OUT_OF_BOUNDS_FRICTION : that.friction, delta = (that.velocity *= friction), location = that.movable[that.axis] + delta; if (!that.elastic && dimension.outOfBounds(location)) { location = Math.max(Math.min(location, dimension.max), dimension.min); that.velocity = 0; } that.movable.moveAxis(that.axis, location); }, _end: function() { this.tapCapture.cancelCapture(); this.end(); }, _outOfBounds: function() { return this.dimension.outOfBounds(this.movable[this.axis]); }, _snapBack: function() { var that = this, dimension = that.dimension, snapBack = that.movable[that.axis] > dimension.max ? dimension.max : dimension.min; that._moveTo(snapBack); }, _moveTo: function(location) { this.transition.moveTo({ location: location, duration: SNAPBACK_DURATION, ease: Transition.easeOutExpo }); } }); var AnimatedScroller = Animation.extend({ init: function(options) { var that = this; kendo.effects.Animation.fn.init.call(this); extend(that, options, { origin: {}, destination: {}, offset: {} }); }, tick: function() { this._updateCoordinates(); this.moveTo(this.origin); }, done: function() { return abs(this.offset.y) < ANIMATED_SCROLLER_PRECISION && abs(this.offset.x) < ANIMATED_SCROLLER_PRECISION; }, onEnd: function() { this.moveTo(this.destination); }, setCoordinates: function(from, to) { this.offset = {}; this.origin = from; this.destination = to; }, _updateCoordinates: function() { this.offset = { x: (this.destination.x - this.origin.x) / 4, y: (this.destination.y - this.origin.y) / 4 }; this.origin = { y: this.origin.y + this.offset.y, x: this.origin.x + this.offset.x }; } }); var ScrollBar = Class.extend({ init: function(options) { var that = this, horizontal = options.axis === "x", element = $('
    '); extend(that, options, { element: element, elementSize: 0, movable: new Movable(element), scrollMovable: options.movable, size: horizontal ? "width" : "height" }); that.scrollMovable.bind(CHANGE, proxy(that._move, that)); that.container.append(element); }, _move: function() { var that = this, axis = that.axis, dimension = that.dimension, paneSize = dimension.size, scrollMovable = that.scrollMovable, sizeRatio = paneSize / dimension.total, position = Math.round(-scrollMovable[axis] * sizeRatio), size = Math.round(paneSize * sizeRatio); if (position + size > paneSize) { size = paneSize - position; } else if (position < 0) { size += position; position = 0; } if (that.elementSize != size) { that.element.css(that.size, size + "px"); that.elementSize = size; } that.movable.moveAxis(axis, position); }, show: function() { this.element.css({opacity: SCROLLBAR_OPACITY, visibility: "visible"}); }, hide: function() { this.element.css({opacity: 0}); } }); var Scroller = Widget.extend({ init: function(element, options) { var that = this; Widget.fn.init.call(that, element, options); element = that.element; that._native = that.options.useNative && kendo.support.hasNativeScrolling; if (that._native) { element.addClass("km-native-scroller") .prepend('
    '); extend(that, { scrollElement: element, fixedContainer: element.children().first() }); return; } element .css("overflow", "hidden") .addClass("km-scroll-wrapper") .wrapInner('
    ') .prepend('
    '); var inner = element.children().eq(1), tapCapture = new kendo.TapCapture(element), movable = new Movable(inner), dimensions = new PaneDimensions({ element: inner, container: element, forcedEnabled: that.options.zoom }), avoidScrolling = this.options.avoidScrolling, userEvents = new kendo.UserEvents(element, { allowSelection: true, preventDragEvent: true, captureUpIfMoved: true, multiTouch: that.options.zoom, start: function(e) { dimensions.refresh(); var velocityX = abs(e.x.velocity), velocityY = abs(e.y.velocity), horizontalSwipe = velocityX * 2 >= velocityY, verticalSwipe = velocityY * 2 >= velocityX; if (!avoidScrolling(e) && that.enabled && (dimensions.x.enabled && horizontalSwipe || dimensions.y.enabled && verticalSwipe)) { userEvents.capture(); } else { userEvents.cancel(); } } }), pane = new Pane({ movable: movable, dimensions: dimensions, userEvents: userEvents, elastic: that.options.elastic }), zoomSnapBack = new ZoomSnapBack({ movable: movable, dimensions: dimensions, userEvents: userEvents, tapCapture: tapCapture }), animatedScroller = new AnimatedScroller({ moveTo: function(coordinates) { that.scrollTo(coordinates.x, coordinates.y); } }); movable.bind(CHANGE, function() { that.scrollTop = - movable.y; that.scrollLeft = - movable.x; that.trigger(SCROLL, { scrollTop: that.scrollTop, scrollLeft: that.scrollLeft }); }); extend(that, { movable: movable, dimensions: dimensions, zoomSnapBack: zoomSnapBack, animatedScroller: animatedScroller, userEvents: userEvents, pane: pane, tapCapture: tapCapture, pulled: false, enabled: true, scrollElement: inner, scrollTop: 0, scrollLeft: 0, fixedContainer: element.children().first() }); that._initAxis("x"); that._initAxis("y"); dimensions.refresh(); if (that.options.pullToRefresh) { that._initPullToRefresh(); } }, makeVirtual: function() { this.dimensions.y.makeVirtual(); }, virtualSize: function(min, max) { this.dimensions.y.virtualSize(min, max); }, height: function() { return this.dimensions.y.size; }, scrollHeight: function() { return this.scrollElement[0].scrollHeight; }, scrollWidth: function() { return this.scrollElement[0].scrollWidth; }, options: { name: "Scroller", zoom: false, pullOffset: 140, elastic: true, useNative: false, avoidScrolling: function() { return false; }, pullToRefresh: false, pullTemplate: "Pull to refresh", releaseTemplate: "Release to refresh", refreshTemplate: "Refreshing" }, events: [ PULL, SCROLL, RESIZE ], _resize: function() { if (!this._native) { this.dimensions.refresh(); } this.reset(); }, setOptions: function(options) { var that = this; Widget.fn.setOptions.call(that, options); if (options.pullToRefresh) { that._initPullToRefresh(); } }, reset: function() { if (this._native) { this.scrollElement.scrollTop(0); } else { this.movable.moveTo({x: 0, y: 0}); this._scale(1); } }, zoomOut: function() { var dimensions = this.dimensions; dimensions.refresh(); this._scale(dimensions.fitScale); this.movable.moveTo(dimensions.centerCoordinates()); }, enable: function() { this.enabled = true; }, disable: function() { this.enabled = false; }, scrollTo: function(x, y) { if (this._native) { this.scrollElement.scrollLeft(x); this.scrollElement.scrollTop(y); } else { this.dimensions.refresh(); this.movable.moveTo({x: x, y: y}); } }, animatedScrollTo: function(x, y) { var from = { x: this.movable.x, y: this.movable.y }, to = { x: x, y: y }; this.animatedScroller.setCoordinates(from, to); this.animatedScroller.start(); }, pullHandled: function() { var that = this; that.refreshHint.removeClass(REFRESHCLASS); that.hintContainer.html(that.pullTemplate({})); that.yinertia.onEnd(); that.xinertia.onEnd(); }, destroy: function() { Widget.fn.destroy.call(this); if (this.userEvents) { this.userEvents.destroy(); } }, _scale: function(scale) { this.dimensions.rescale(scale); this.movable.scaleTo(scale); }, _initPullToRefresh: function() { var that = this; that.dimensions.y.forceEnabled(); that.pullTemplate = kendo.template(that.options.pullTemplate); that.releaseTemplate = kendo.template(that.options.releaseTemplate); that.refreshTemplate = kendo.template(that.options.refreshTemplate); that.scrollElement.prepend('' + that.pullTemplate({}) + ''); that.refreshHint = that.scrollElement.children().first(); that.hintContainer = that.refreshHint.children(".km-template"); that.pane.y.bind("change", proxy(that._paneChange, that)); that.userEvents.bind("end", proxy(that._dragEnd, that)); }, _dragEnd: function() { var that = this; if(!that.pulled) { return; } that.pulled = false; that.refreshHint.removeClass(RELEASECLASS).addClass(REFRESHCLASS); that.hintContainer.html(that.refreshTemplate({})); that.yinertia.freeze(that.options.pullOffset / 2); that.trigger("pull"); }, _paneChange: function() { var that = this; if (that.movable.y / OUT_OF_BOUNDS_FRICTION > that.options.pullOffset) { if (!that.pulled) { that.pulled = true; that.refreshHint.removeClass(REFRESHCLASS).addClass(RELEASECLASS); that.hintContainer.html(that.releaseTemplate({})); } } else if (that.pulled) { that.pulled = false; that.refreshHint.removeClass(RELEASECLASS); that.hintContainer.html(that.pullTemplate({})); } }, _initAxis: function(axis) { var that = this, movable = that.movable, dimension = that.dimensions[axis], tapCapture = that.tapCapture, scrollBar = new ScrollBar({ axis: axis, movable: movable, dimension: dimension, container: that.element }); that.pane[axis].bind(CHANGE, function() { scrollBar.show(); }); that[axis + "inertia"] = new DragInertia({ axis: axis, movable: movable, tapCapture: tapCapture, userEvents: that.userEvents, dimension: dimension, elastic: that.options.elastic, friction: that.options.friction || FRICTION, velocityMultiplier: that.options.velocityMultiplier || VELOCITY_MULTIPLIER, end: function() { scrollBar.hide(); that.trigger("scrollEnd", { axis: axis, scrollTop: that.scrollTop, scrollLeft: that.scrollLeft }); } }); } }); ui.plugin(Scroller); })(window.kendo.jQuery); kendo_module({ id: "groupable", name: "Groupable", category: "framework", depends: [ "core", "draganddrop" ], advanced: true }); (function ($, undefined) { var kendo = window.kendo, Widget = kendo.ui.Widget, proxy = $.proxy, isRtl = false, NS = ".kendoGroupable", indicatorTmpl = kendo.template('', { useWithBlock:false }), hint = function(target) { return $('
    ') .css({ width: target.width(), paddingLeft: target.css("paddingLeft"), paddingRight: target.css("paddingRight"), lineHeight: target.height() + "px", paddingTop: target.css("paddingTop"), paddingBottom: target.css("paddingBottom") }) .html(target.attr(kendo.attr("title")) || target.attr(kendo.attr("field"))) .prepend(''); }, dropCue = $('
    '), nameSpecialCharRegExp = /("|\%|'|\[|\]|\$|\.|\,|\:|\;|\+|\*|\&|\!|\#|\(|\)|<|>|\=|\?|\@|\^|\{|\}|\~|\/|\||`)/g; function dropCueOffsetTop(element) { return element.position().top + 3; } var Groupable = Widget.extend({ init: function(element, options) { var that = this, groupContainer, group = kendo.guid(), intializePositions = proxy(that._intializePositions, that), draggable, horizontalCuePosition, dropCuePositions = that._dropCuePositions = []; Widget.fn.init.call(that, element, options); isRtl = kendo.support.isRtl(element); horizontalCuePosition = isRtl ? "right" : "left"; that.draggable = draggable = that.options.draggable || new kendo.ui.Draggable(that.element, { filter: that.options.draggableElements, hint: hint, group: group }); groupContainer = that.groupContainer = $(that.options.groupContainer, that.element) .kendoDropTarget({ group: draggable.options.group, dragenter: function(e) { if (that._canDrag(e.draggable.currentTarget)) { e.draggable.hint.find(".k-drag-status").removeClass("k-denied").addClass("k-add"); dropCue.css("top", dropCueOffsetTop(groupContainer)).css(horizontalCuePosition, 0).appendTo(groupContainer); } }, dragleave: function(e) { e.draggable.hint.find(".k-drag-status").removeClass("k-add").addClass("k-denied"); dropCue.remove(); }, drop: function(e) { var targetElement = e.draggable.currentTarget, field = targetElement.attr(kendo.attr("field")), title = targetElement.attr(kendo.attr("title")), sourceIndicator = that.indicator(field), dropCuePositions = that._dropCuePositions, lastCuePosition = dropCuePositions[dropCuePositions.length - 1], position; if (!targetElement.hasClass("k-group-indicator") && !that._canDrag(targetElement)) { return; } if(lastCuePosition) { position = that._dropCuePosition(kendo.getOffset(dropCue).left + parseInt(lastCuePosition.element.css("marginLeft"), 10) * (isRtl ? -1 : 1) + parseInt(lastCuePosition.element.css("marginRight"), 10)); if(position && that._canDrop($(sourceIndicator), position.element, position.left)) { if(position.before) { position.element.before(sourceIndicator || that.buildIndicator(field, title)); } else { position.element.after(sourceIndicator || that.buildIndicator(field, title)); } that._change(); } } else { that.groupContainer.append(that.buildIndicator(field, title)); that._change(); } } }) .kendoDraggable({ filter: "div.k-group-indicator", hint: hint, group: draggable.options.group, dragcancel: proxy(that._dragCancel, that), dragstart: function(e) { var element = e.currentTarget, marginLeft = parseInt(element.css("marginLeft"), 10), elementPosition = element.position(), left = isRtl ? elementPosition.left - marginLeft : elementPosition.left + element.outerWidth(); intializePositions(); dropCue.css({top: dropCueOffsetTop(groupContainer), left: left}).appendTo(groupContainer); this.hint.find(".k-drag-status").removeClass("k-denied").addClass("k-add"); }, dragend: function() { that._dragEnd(this); }, drag: proxy(that._drag, that) }) .on("click" + NS, ".k-button", function(e) { e.preventDefault(); that._removeIndicator($(this).parent()); }) .on("click" + NS,".k-link", function(e) { var current = $(this).parent(), newIndicator = that.buildIndicator(current.attr(kendo.attr("field")), current.attr(kendo.attr("title")), current.attr(kendo.attr("dir")) == "asc" ? "desc" : "asc"); current.before(newIndicator).remove(); that._change(); e.preventDefault(); }); draggable.bind([ "dragend", "dragcancel", "dragstart", "drag" ], { dragend: function() { that._dragEnd(this); }, dragcancel: proxy(that._dragCancel, that), dragstart: function(e) { var element, marginRight, left; if (!that.options.allowDrag && !that._canDrag(e.currentTarget)) { e.preventDefault(); return; } intializePositions(); if(dropCuePositions.length) { element = dropCuePositions[dropCuePositions.length - 1].element; marginRight = parseInt(element.css("marginRight"), 10); left = element.position().left + element.outerWidth() + marginRight; } else { left = 0; } }, drag: proxy(that._drag, that) }); that.dataSource = that.options.dataSource; if(that.dataSource) { that._refreshHandler = proxy(that.refresh, that); that.dataSource.bind("change", that._refreshHandler); } }, refresh: function() { var that = this, dataSource = that.dataSource; that.groupContainer.empty().append( $.map(dataSource.group() || [], function(item) { var fieldName = item.field.replace(nameSpecialCharRegExp, "\\$1"); var element = that.element.find(that.options.filter).filter("[" + kendo.attr("field") + "=" + fieldName + "]"); return that.buildIndicator(item.field, element.attr(kendo.attr("title")), item.dir); }).join("") ); that._invalidateGroupContainer(); }, destroy: function() { var that = this; Widget.fn.destroy.call(that); that.groupContainer .off(NS) .kendoDropTarget("destroy") .kendoDraggable("destroy"); if (!that.options.draggable) { that.draggable.destroy(); } if (that.dataSource && that._refreshHandler) { that.dataSource.unbind("change", that._refreshHandler); } }, options: { name: "Groupable", filter: "th", draggableElements: "th", messages: { empty: "Drag a column header and drop it here to group by that column" } }, indicator: function(field) { var indicators = $(".k-group-indicator", this.groupContainer); return $.grep(indicators, function (item) { return $(item).attr(kendo.attr("field")) === field; })[0]; }, buildIndicator: function(field, title, dir) { return indicatorTmpl({ field: field.replace(/"/g, "'"), dir: dir, title: title, ns: kendo.ns }); }, descriptors: function() { var that = this, indicators = $(".k-group-indicator", that.groupContainer), aggregates, names, field, idx, length; aggregates = that.element.find(that.options.filter).map(function() { var cell = $(this), aggregate = cell.attr(kendo.attr("aggregates")), member = cell.attr(kendo.attr("field")); if (aggregate && aggregate !== "") { names = aggregate.split(","); aggregate = []; for (idx = 0, length = names.length; idx < length; idx++) { aggregate.push({ field: member, aggregate: names[idx] }); } } return aggregate; }).toArray(); return $.map(indicators, function(item) { item = $(item); field = item.attr(kendo.attr("field")); return { field: field, dir: item.attr(kendo.attr("dir")), aggregates: aggregates || [] }; }); }, _removeIndicator: function(indicator) { var that = this; indicator.remove(); that._invalidateGroupContainer(); that._change(); }, _change: function() { var that = this; if(that.dataSource) { that.dataSource.group(that.descriptors()); } }, _dropCuePosition: function(position) { var dropCuePositions = this._dropCuePositions; if(!dropCue.is(":visible") || dropCuePositions.length === 0) { return; } position = Math.ceil(position); var lastCuePosition = dropCuePositions[dropCuePositions.length - 1], left = lastCuePosition.left, right = lastCuePosition.right, marginLeft = parseInt(lastCuePosition.element.css("marginLeft"), 10), marginRight = parseInt(lastCuePosition.element.css("marginRight"), 10); if(position >= right && !isRtl || position < left && isRtl) { position = { left: lastCuePosition.element.position().left + (!isRtl ? lastCuePosition.element.outerWidth() + marginRight : - marginLeft), element: lastCuePosition.element, before: false }; } else { position = $.grep(dropCuePositions, function(item) { return (item.left <= position && position <= item.right) || (isRtl && position > item.right); })[0]; if(position) { position = { left: isRtl ? position.element.position().left + position.element.outerWidth() + marginRight : position.element.position().left - marginLeft, element: position.element, before: true }; } } return position; }, _drag: function(event) { var position = this._dropCuePosition(event.x.location); if (position) { dropCue.css({ left: position.left, right: "auto" }); } }, _canDrag: function(element) { var field = element.attr(kendo.attr("field")); return element.attr(kendo.attr("groupable")) != "false" && field && (element.hasClass("k-group-indicator") || !this.indicator(field)); }, _canDrop: function(source, target, position) { var next = source.next(), result = source[0] !== target[0] && (!next[0] || target[0] !== next[0] || (!isRtl && position > next.position().left || isRtl && position < next.position().left)); return result; }, _dragEnd: function(draggable) { var that = this, field = draggable.currentTarget.attr(kendo.attr("field")), sourceIndicator = that.indicator(field); if (draggable !== that.options.draggable && !draggable.dropped && sourceIndicator) { that._removeIndicator($(sourceIndicator)); } that._dragCancel(); }, _dragCancel: function() { dropCue.remove(); this._dropCuePositions = []; }, _intializePositions: function() { var that = this, indicators = $(".k-group-indicator", that.groupContainer), left; that._dropCuePositions = $.map(indicators, function(item) { item = $(item); left = kendo.getOffset(item).left; return { left: parseInt(left, 10), right: parseInt(left + item.outerWidth(), 10), element: item }; }); }, _invalidateGroupContainer: function() { var groupContainer = this.groupContainer; if(groupContainer.is(":empty")) { groupContainer.html(this.options.messages.empty); } } }); kendo.ui.plugin(Groupable); })(window.kendo.jQuery); kendo_module({ id: "reorderable", name: "Reorderable", category: "framework", depends: [ "core", "draganddrop" ], advanced: true }); (function ($, undefined) { var kendo = window.kendo, Widget = kendo.ui.Widget, CHANGE = "change", KREORDERABLE = "k-reorderable"; function toggleHintClass(hint, denied) { hint = $(hint); if (denied) { hint.find(".k-drag-status").removeClass("k-add").addClass("k-denied"); } else { hint.find(".k-drag-status").removeClass("k-denied").addClass("k-add"); } } var Reorderable = Widget.extend({ init: function(element, options) { var that = this, draggable, group = kendo.guid() + "-reorderable"; Widget.fn.init.call(that, element, options); element = that.element.addClass(KREORDERABLE); options = that.options; that.draggable = draggable = options.draggable || new kendo.ui.Draggable(element, { group: group, filter: options.filter, hint: options.hint }); that.reorderDropCue = $('
    '); element.find(draggable.options.filter).kendoDropTarget({ group: draggable.options.group, dragenter: function(e) { if (!that._draggable) { return; } var dropTarget = this.element, offset, same = dropTarget[0] === that._draggable[0]; toggleHintClass(e.draggable.hint, same); if (!same) { offset = kendo.getOffset(dropTarget); that.reorderDropCue.css({ height: dropTarget.outerHeight(), top: offset.top, left: offset.left + (dropTarget.index() > that._draggable.index() ? dropTarget.outerWidth() : 0) }) .appendTo(document.body); } }, dragleave: function(e) { toggleHintClass(e.draggable.hint, true); that.reorderDropCue.remove(); }, drop: function() { if (!that._draggable) { return; } var draggableElement = that._draggable[0], dropTarget = this.element[0], container; if (draggableElement !== dropTarget) { container = element.find(draggable.options.filter); that.trigger(CHANGE, { element: that._draggable, oldIndex: container.index(draggableElement), newIndex: container.index(dropTarget) }); } } }); draggable.bind([ "dragcancel", "dragend", "dragstart" ], { dragcancel: function() { that.reorderDropCue.remove(); that._draggable = null; }, dragend: function() { that.reorderDropCue.remove(); that._draggable = null; }, dragstart: function(e) { that._draggable = e.currentTarget; } } ); }, options: { name: "Reorderable", filter: "*" }, events: [ CHANGE ], destroy: function() { var that = this; Widget.fn.destroy.call(that); if (that.draggable) { that.draggable.destroy(); } kendo.destroy(that.element); } }); kendo.ui.plugin(Reorderable); })(window.kendo.jQuery); kendo_module({ id: "resizable", name: "Resizable", category: "framework", depends: [ "core", "draganddrop" ], advanced: true }); (function($, undefined) { var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, proxy = $.proxy, isFunction = kendo.isFunction, extend = $.extend, HORIZONTAL = "horizontal", VERTICAL = "vertical", START = "start", RESIZE = "resize", RESIZEEND = "resizeend"; var Resizable = Widget.extend({ init: function(element, options) { var that = this; Widget.fn.init.call(that, element, options); that.orientation = that.options.orientation.toLowerCase() != VERTICAL ? HORIZONTAL : VERTICAL; that._positionMouse = that.orientation == HORIZONTAL ? "x" : "y"; that._position = that.orientation == HORIZONTAL ? "left" : "top"; that._sizingDom = that.orientation == HORIZONTAL ? "outerWidth" : "outerHeight"; that.draggable = new ui.Draggable(element, { distance: 0, filter: options.handle, drag: proxy(that._resize, that), dragcancel: proxy(that._cancel, that), dragstart: proxy(that._start, that), dragend: proxy(that._stop, that) }); that.userEvents = that.draggable.userEvents; }, events: [ RESIZE, RESIZEEND, START ], options: { name: "Resizable", orientation: HORIZONTAL }, resize: function() { // Overrides base widget resize }, _max: function(e) { var that = this, hintSize = that.hint ? that.hint[that._sizingDom]() : 0, size = that.options.max; return isFunction(size) ? size(e) : size !== undefined ? (that._initialElementPosition + size) - hintSize : size; }, _min: function(e) { var that = this, size = that.options.min; return isFunction(size) ? size(e) : size !== undefined ? that._initialElementPosition + size : size; }, _start: function(e) { var that = this, hint = that.options.hint, el = $(e.currentTarget); that._initialElementPosition = el.position()[that._position]; that._initialMousePosition = e[that._positionMouse].startLocation; if (hint) { that.hint = isFunction(hint) ? $(hint(el)) : hint; that.hint.css({ position: "absolute" }) .css(that._position, that._initialElementPosition) .appendTo(that.element); } that.trigger(START, e); that._maxPosition = that._max(e); that._minPosition = that._min(e); $(document.body).css("cursor", el.css("cursor")); }, _resize: function(e) { var that = this, handle = $(e.currentTarget), maxPosition = that._maxPosition, minPosition = that._minPosition, currentPosition = that._initialElementPosition + (e[that._positionMouse].location - that._initialMousePosition), position; position = minPosition !== undefined ? Math.max(minPosition, currentPosition) : currentPosition; that.position = position = maxPosition !== undefined ? Math.min(maxPosition, position) : position; if(that.hint) { that.hint.toggleClass(that.options.invalidClass || "", position == maxPosition || position == minPosition) .css(that._position, position); } that.resizing = true; that.trigger(RESIZE, extend(e, { position: position })); }, _stop: function(e) { var that = this; if(that.hint) { that.hint.remove(); } that.resizing = false; that.trigger(RESIZEEND, extend(e, { position: that.position })); $(document.body).css("cursor", ""); }, _cancel: function(e) { var that = this; if (that.hint) { that.position = undefined; that.hint.css(that._position, that._initialElementPosition); that._stop(e); } }, destroy: function() { var that = this; Widget.fn.destroy.call(that); if (that.draggable) { that.draggable.destroy(); } }, press: function(target) { if (!target) { return; } var position = target.position(), that = this; that.userEvents.press(position.left, position.top, target[0]); that.targetPosition = position; that.target = target; }, move: function(delta) { var that = this, orientation = that._position, position = that.targetPosition, current = that.position; if (current === undefined) { current = position[orientation]; } position[orientation] = current + delta; that.userEvents.move(position.left, position.top); }, end: function() { this.userEvents.end(); this.target = this.position = undefined; } }); kendo.ui.plugin(Resizable); })(window.kendo.jQuery); kendo_module({ id: "sortable", name: "Sortable", category: "framework", depends: [ "data" ], advanced: true }); (function($, undefined) { var kendo = window.kendo, proxy = $.proxy, DIR = "dir", ASC = "asc", SINGLE = "single", FIELD = "field", DESC = "desc", NS = ".kendoSortable", TLINK = ".k-link", ARIASORT = "aria-sort", Widget = kendo.ui.Widget; var Sortable = Widget.extend({ init: function(element, options) { var that = this, link; Widget.fn.init.call(that, element, options); that._refreshHandler = proxy(that.refresh, that); that.dataSource = that.options.dataSource.bind("change", that._refreshHandler); link = that.element.find(TLINK); if (!link[0]) { link = that.element.wrapInner('').find(TLINK); } that.link = link; that.element.on("click" + NS, proxy(that._click, that)); }, options: { name: "Sortable", mode: SINGLE, allowUnsort: true, compare: null, filter: "" }, destroy: function() { var that = this; Widget.fn.destroy.call(that); that.element.off(NS); that.dataSource.unbind("change", that._refreshHandler); }, refresh: function() { var that = this, sort = that.dataSource.sort() || [], idx, length, descriptor, dir, element = that.element, field = element.attr(kendo.attr(FIELD)); element.removeAttr(kendo.attr(DIR)); element.removeAttr(ARIASORT); for (idx = 0, length = sort.length; idx < length; idx++) { descriptor = sort[idx]; if (field == descriptor.field) { element.attr(kendo.attr(DIR), descriptor.dir); } } dir = element.attr(kendo.attr(DIR)); element.find(".k-i-arrow-n,.k-i-arrow-s").remove(); if (dir === ASC) { $('').appendTo(that.link); element.attr(ARIASORT, "ascending"); } else if (dir === DESC) { $('').appendTo(that.link); element.attr(ARIASORT, "descending"); } }, _click: function(e) { var that = this, element = that.element, field = element.attr(kendo.attr(FIELD)), dir = element.attr(kendo.attr(DIR)), options = that.options, compare = that.options.compare, sort = that.dataSource.sort() || [], idx, length; e.preventDefault(); if (options.filter && !element.is(options.filter)) { return; } if (dir === ASC) { dir = DESC; } else if (dir === DESC && options.allowUnsort) { dir = undefined; } else { dir = ASC; } if (options.mode === SINGLE) { sort = [ { field: field, dir: dir, compare: compare } ]; } else if (options.mode === "multiple") { for (idx = 0, length = sort.length; idx < length; idx++) { if (sort[idx].field === field) { sort.splice(idx, 1); break; } } sort.push({ field: field, dir: dir, compare: compare }); } that.dataSource.sort(sort); } }); kendo.ui.plugin(Sortable); })(window.kendo.jQuery); kendo_module({ id: "selectable", name: "Selectable", category: "framework", depends: [ "core", "userevents" ], advanced: true }); (function ($, undefined) { var kendo = window.kendo, Widget = kendo.ui.Widget, proxy = $.proxy, abs = Math.abs, ARIASELECTED = "aria-selected", SELECTED = "k-state-selected", ACTIVE = "k-state-selecting", SELECTABLE = "k-selectable", CHANGE = "change", NS = ".kendoSelectable", UNSELECTING = "k-state-unselecting", supportEventDelegation = false; (function($) { (function() { $('
    ') .on("click", ">*", function() { supportEventDelegation = true; }) .find("span") .click() .end() .off(); })(); })($); var Selectable = Widget.extend({ init: function(element, options) { var that = this, multiple; Widget.fn.init.call(that, element, options); that._marquee = $("
    "); that._lastActive = null; that.element.addClass(SELECTABLE); multiple = that.options.multiple; that.userEvents = new kendo.UserEvents(that.element, { global: true, allowSelection: true, filter: (!supportEventDelegation ? "." + SELECTABLE + " " : "") + that.options.filter, tap: proxy(that._tap, that) }); if (multiple) { that.userEvents .bind("start", proxy(that._start, that)) .bind("move", proxy(that._move, that)) .bind("end", proxy(that._end, that)) .bind("select", proxy(that._select, that)); } }, events: [CHANGE], options: { name: "Selectable", filter: ">*", multiple: false }, _tap: function(e) { var target = $(e.target), that = this, ctrlKey = e.event.ctrlKey || e.event.metaKey, multiple = that.options.multiple, shiftKey = multiple && e.event.shiftKey, selected, whichCode = e.event.which, buttonCode = e.event.button; //in case of hierarchy or right-click if (target.closest("." + SELECTABLE)[0] !== that.element[0] || whichCode && whichCode == 3 || buttonCode && buttonCode == 2) { return; } selected = target.hasClass(SELECTED); if (!multiple || !ctrlKey) { that.clear(); } if (shiftKey) { that.selectRange(that._firstSelectee(), target); } else { if (selected && ctrlKey) { that._unselect(target); that._notify(CHANGE); } else { that.value(target); } that._lastActive = that._downTarget = target; } }, _start: function(e) { var that = this, target = $(e.target), selected = target.hasClass(SELECTED), ctrlKey = e.event.ctrlKey || e.event.metaKey; that._downTarget = target; //in case of hierarchy if (target.closest("." + SELECTABLE)[0] !== that.element[0]) { that.userEvents.cancel(); that._downTarget = null; return; } that._marquee .appendTo(document.body) .css({ left: e.x.client + 1, top: e.y.client + 1, width: 0, height: 0 }); if (!ctrlKey) { that.clear(); } if (selected) { that._selectElement(target, true); if (ctrlKey) { target.addClass(UNSELECTING); } } }, _move: function(e) { var that = this, position = { left: e.x.startLocation > e.x.location ? e.x.location : e.x.startLocation, top: e.y.startLocation > e.y.location ? e.y.location : e.y.startLocation, width: abs(e.x.initialDelta), height: abs(e.y.initialDelta) }, items = that.element.find(that.options.filter); that._marquee.css(position); invalidateSelectables(items, that._downTarget[0], position, (e.event.ctrlKey || e.event.metaKey)); e.preventDefault(); }, _end: function() { var that = this; that._marquee.remove(); that._unselect(that.element .find(that.options.filter + "." + UNSELECTING)) .removeClass(UNSELECTING); that.value(that.element.find(that.options.filter + "." + ACTIVE)); that._lastActive = that._downTarget; }, value: function(val) { var that = this, selectElement = proxy(that._selectElement, that); if(val) { val.each(function() { selectElement(this); }); that._notify(CHANGE); return; } return that.element.find(that.options.filter + "." + SELECTED); }, _firstSelectee: function() { var that = this, selected; if(that._lastActive !== null) { return that._lastActive; } selected = that.value(); return selected.length > 0 ? selected[0] : that.element.find(that.options.filter); }, _selectElement: function(element, preventNotify) { var toSelect = $(element), isPrevented = !preventNotify && this._notify("select", { element: element }); toSelect.removeClass(ACTIVE); if(!isPrevented) { toSelect.addClass(SELECTED); if (this.options.aria) { toSelect.attr(ARIASELECTED, true); } } }, _notify: function(name, args) { args = args || { }; return this.trigger(name, args); }, _unselect: function(element) { element.removeClass(SELECTED); if (this.options.aria) { element.attr(ARIASELECTED, false); } return element; }, _select: function(e) { var selector = "input,a,textarea,.k-multiselect-wrap,select", msie = kendo.support.browser.msie; if ($(e.event.target).is(selector)) { this.userEvents.cancel(); this._downTarget = null; } else if (!msie || (msie && !$(kendo._activeElement()).is(selector))) { e.preventDefault(); } }, clear: function() { var items = this.element.find(this.options.filter + "." + SELECTED); this._unselect(items); }, selectRange: function(start, end) { var that = this, found = false, idx, length, tmp, toSelect, items = that.element.find(that.options.filter), selectElement = proxy(that._selectElement, that); start = $(start)[0]; end = $(end)[0]; for (idx = 0, length = items.length; idx < length; idx ++) { toSelect = items[idx]; if(found) { selectElement(toSelect); found = toSelect !== end; } else if(toSelect === start) { found = start !== end; selectElement(toSelect); } else if(toSelect === end) { tmp = start; start = end; end = tmp; found = true; selectElement(toSelect); } else { $(toSelect).removeClass(SELECTED); } } that._notify(CHANGE); }, destroy: function() { var that = this; Widget.fn.destroy.call(that); that.element.off(NS); that.userEvents.destroy(); } }); function collision(element, position) { var elementPosition = kendo.getOffset(element), right = position.left + position.width, bottom = position.top + position.height; elementPosition.right = elementPosition.left + element.outerWidth(); elementPosition.bottom = elementPosition.top + element.outerHeight(); return !(elementPosition.left > right|| elementPosition.right < position.left || elementPosition.top > bottom || elementPosition.bottom < position.top); } function invalidateSelectables(items, target, position, ctrlKey) { var idx, length, toSelect; for (idx = 0, length = items.length; idx < length; idx ++) { toSelect = items.eq(idx); if (collision(toSelect, position)) { if(toSelect.hasClass(SELECTED)) { if(ctrlKey && target !== toSelect[0]) { toSelect.removeClass(SELECTED).addClass(UNSELECTING); } } else if (!toSelect.hasClass(ACTIVE) && !toSelect.hasClass(UNSELECTING)) { toSelect.addClass(ACTIVE); } } else { if (toSelect.hasClass(ACTIVE)) { toSelect.removeClass(ACTIVE); } else if(ctrlKey && toSelect.hasClass(UNSELECTING)) { toSelect.removeClass(UNSELECTING).addClass(SELECTED); } } } } kendo.ui.plugin(Selectable); })(window.kendo.jQuery); kendo_module({ id: "button", name: "Button", category: "web", description: "The Button widget displays styled buttons.", depends: [ "core" ] }); (function ($, undefined) { var kendo = window.kendo, Widget = kendo.ui.Widget, proxy = $.proxy, keys = kendo.keys, CLICK = "click", KBUTTON = "k-button", KBUTTONICON = "k-button-icon", KBUTTONICONTEXT = "k-button-icontext", NS = ".kendoButton", DISABLED = "disabled", DISABLEDSTATE = "k-state-disabled", FOCUSEDSTATE = "k-state-focused", SELECTEDSTATE = "k-state-selected"; var Button = Widget.extend({ init: function(element, options) { var that = this; Widget.fn.init.call(that, element, options); element = that.wrapper = that.element; options = that.options; element.addClass(KBUTTON).attr("role", "button"); options.enable = options.enable && !element.attr(DISABLED); that.enable(options.enable); that._tabindex(); that._graphics(); element .on(CLICK + NS, proxy(that._click, that)) .on("focus" + NS, proxy(that._focus, that)) .on("blur" + NS, proxy(that._blur, that)) .on("keydown" + NS, proxy(that._keydown, that)) .on("keyup" + NS, proxy(that._keyup, that)); kendo.notify(that); }, events: [ CLICK ], options: { name: "Button", icon: "", spriteCssClass: "", imageUrl: "", enable: true }, _isNativeButton: function() { return this.element.prop("tagName").toLowerCase() == "button"; }, _click: function(e) { if (this.options.enable) { this.trigger(CLICK, {event: e}); } }, _focus: function() { if (this.options.enable) { this.element.addClass(FOCUSEDSTATE); } }, _blur: function() { this.element.removeClass(FOCUSEDSTATE); }, _keydown: function(e) { var that = this; if (!that._isNativeButton()) { if (e.keyCode == keys.ENTER || e.keyCode == keys.SPACEBAR) { if (e.keyCode == keys.SPACEBAR) { e.preventDefault(); if (that.options.enable) { that.element.addClass(SELECTEDSTATE); } } that._click(e); } } }, _keyup: function() { this.element.removeClass(SELECTEDSTATE); }, _graphics: function() { var that = this, element = that.element, options = that.options, icon = options.icon, spriteCssClass = options.spriteCssClass, imageUrl = options.imageUrl, span, img, isEmpty; if (spriteCssClass || imageUrl || icon) { isEmpty = true; element.contents().not("span.k-sprite").not("span.k-icon").not("img.k-image").each(function(idx, el){ if (el.nodeType == 1 || el.nodeType == 3 && el.nodeValue.trim().length > 0) { isEmpty = false; } }); if (isEmpty) { element.addClass(KBUTTONICON); } else { element.addClass(KBUTTONICONTEXT); } } if (icon) { span = element.children("span.k-icon").first(); if (!span[0]) { span = $('').prependTo(element); } span.addClass("k-i-" + icon); } else if (spriteCssClass) { span = element.children("span.k-sprite").first(); if (!span[0]) { span = $('').prependTo(element); } span.addClass(spriteCssClass); } else if (imageUrl) { img = element.children("img.k-image").first(); if (!img[0]) { img = $('icon').prependTo(element); } img.attr("src", imageUrl); } }, enable: function(enable) { var that = this, element = that.element; if (enable === undefined) { enable = true; } enable = !!enable; that.options.enable = enable; element.toggleClass(DISABLEDSTATE, !enable) .attr("aria-disabled", !enable) .attr(DISABLED, !enable); } }); kendo.ui.plugin(Button); })(window.kendo.jQuery); kendo_module({ id: "pager", name: "Pager", category: "framework", depends: [ "data" ], advanced: true }); (function($, undefined) { var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, proxy = $.proxy, FIRST = ".k-i-seek-w", LAST = ".k-i-seek-e", PREV = ".k-i-arrow-w", NEXT = ".k-i-arrow-e", CHANGE = "change", NS = ".kendoPager", CLICK = "click", KEYDOWN = "keydown", DISABLED = "disabled", iconTemplate = kendo.template('
    #=text#'); function button(template, idx, text, numeric) { return template( { idx: idx, text: text, ns: kendo.ns, numeric: numeric }); } function icon(className, text, wrapClassName) { return iconTemplate({ className: className.substring(1), text: text, wrapClassName: wrapClassName || "" }); } function update(element, selector, page, disabled) { element.find(selector) .parent() .attr(kendo.attr("page"), page) .attr("tabindex", -1) .toggleClass("k-state-disabled", disabled); } function first(element, page) { update(element, FIRST, 1, page <= 1); } function prev(element, page) { update(element, PREV, Math.max(1, page - 1), page <= 1); } function next(element, page, totalPages) { update(element, NEXT, Math.min(totalPages, page + 1), page >= totalPages); } function last(element, page, totalPages) { update(element, LAST, totalPages, page >= totalPages); } var Pager = Widget.extend( { init: function(element, options) { var that = this, page, totalPages; Widget.fn.init.call(that, element, options); options = that.options; that.dataSource = kendo.data.DataSource.create(options.dataSource); that.linkTemplate = kendo.template(that.options.linkTemplate); that.selectTemplate = kendo.template(that.options.selectTemplate); page = that.page(); totalPages = that.totalPages(); that._refreshHandler = proxy(that.refresh, that); that.dataSource.bind(CHANGE, that._refreshHandler); if (options.previousNext) { if (!that.element.find(FIRST).length) { that.element.append(icon(FIRST, options.messages.first, "k-pager-first")); first(that.element, page, totalPages); } if (!that.element.find(PREV).length) { that.element.append(icon(PREV, options.messages.previous)); prev(that.element, page, totalPages); } } if (options.numeric) { that.list = that.element.find(".k-pager-numbers"); if (!that.list.length) { that.list = $('
      ').appendTo(that.element); } } if (options.input) { if (!that.element.find(".k-pager-input").length) { that.element.append(''+ options.messages.page + '' + kendo.format(options.messages.of, totalPages) + ''); } that.element.on(KEYDOWN + NS, ".k-pager-input input", proxy(that._keydown, that)); } if (options.previousNext) { if (!that.element.find(NEXT).length) { that.element.append(icon(NEXT, options.messages.next)); next(that.element, page, totalPages); } if (!that.element.find(LAST).length) { that.element.append(icon(LAST, options.messages.last, "k-pager-last")); last(that.element, page, totalPages); } } if (options.pageSizes){ if (!that.element.find(".k-pager-sizes").length){ $('' + kendo.format(options.messages.of, totalPages)) .find("input") .val(page) .attr(DISABLED, total < 1) .toggleClass("k-state-disabled", total < 1); } if (options.previousNext) { first(that.element, page, totalPages); prev(that.element, page, totalPages); next(that.element, page, totalPages); last(that.element, page, totalPages); } if (options.pageSizes) { that.element .find(".k-pager-sizes select") .val(pageSize) .filter("[" + kendo.attr("role") + "=dropdownlist]") .kendoDropDownList("value", pageSize) .kendoDropDownList("text", pageSize); // handles custom values } }, _keydown: function(e) { if (e.keyCode === kendo.keys.ENTER) { var input = this.element.find(".k-pager-input").find("input"), page = parseInt(input.val(), 10); if (isNaN(page) || page < 1 || page > this.totalPages()) { page = this.page(); } input.val(page); this.page(page); } }, _refreshClick: function(e) { e.preventDefault(); this.dataSource.read(); }, _change: function(e) { var pageSize = parseInt(e.currentTarget.value, 10); if (!isNaN(pageSize)){ this.dataSource.pageSize(pageSize); } }, _click: function(e) { var target = $(e.currentTarget); e.preventDefault(); if (!target.is(".k-state-disabled")) { this.page(target.attr(kendo.attr("page"))); } }, totalPages: function() { return Math.ceil((this.dataSource.total() || 0) / this.pageSize()); }, pageSize: function() { return this.dataSource.pageSize() || this.dataSource.total(); }, page: function(page) { if (page !== undefined) { this.dataSource.page(page); this.trigger(CHANGE, { index: page }); } else { if (this.dataSource.total() > 0) { return this.dataSource.page(); } else { return 0; } } } }); ui.plugin(Pager); })(window.kendo.jQuery); kendo_module({ id: "popup", name: "Pop-up", category: "framework", depends: [ "core" ], advanced: true }); (function($, undefined) { var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, support = kendo.support, getOffset = kendo.getOffset, activeElement = kendo._activeElement, OPEN = "open", CLOSE = "close", DEACTIVATE = "deactivate", ACTIVATE = "activate", CENTER = "center", LEFT = "left", RIGHT = "right", TOP = "top", BOTTOM = "bottom", ABSOLUTE = "absolute", HIDDEN = "hidden", BODY = "body", LOCATION = "location", POSITION = "position", VISIBLE = "visible", EFFECTS = "effects", ACTIVE = "k-state-active", ACTIVEBORDER = "k-state-border", ACTIVEBORDERREGEXP = /k-state-border-(\w+)/, ACTIVECHILDREN = ".k-picker-wrap, .k-dropdown-wrap, .k-link", MOUSEDOWN = "down", WINDOW = $(window), DOCUMENT_ELEMENT = $(document.documentElement), RESIZE_SCROLL = "resize scroll", cssPrefix = support.transitions.css, TRANSFORM = cssPrefix + "transform", extend = $.extend, NS = ".kendoPopup", styles = ["font-family", "font-size", "font-stretch", "font-style", "font-weight", "line-height"]; function contains(container, target) { return container === target || $.contains(container, target); } var Popup = Widget.extend({ init: function(element, options) { var that = this, parentPopup; options = options || {}; if (options.isRtl) { options.origin = options.origin || BOTTOM + " " + RIGHT; options.position = options.position || TOP + " " + RIGHT; } Widget.fn.init.call(that, element, options); element = that.element; options = that.options; that.collisions = options.collision ? options.collision.split(" ") : []; if (that.collisions.length === 1) { that.collisions.push(that.collisions[0]); } parentPopup = $(that.options.anchor).closest(".k-popup,.k-group").filter(":not([class^=km-])"); // When popup is in another popup, make it relative. options.appendTo = $($(options.appendTo)[0] || parentPopup[0] || BODY); that.element.hide() .addClass("k-popup k-group k-reset") .toggleClass("k-rtl", !!options.isRtl) .css({ position : ABSOLUTE }) .appendTo(options.appendTo) .on("mouseenter" + NS, function() { that._hovered = true; }) .on("mouseleave" + NS, function() { that._hovered = false; }); that.wrapper = $(); if (options.animation === false) { options.animation = { open: { effects: {} }, close: { hide: true, effects: {} } }; } extend(options.animation.open, { complete: function() { that.wrapper.css({ overflow: VISIBLE }); // Forcing refresh causes flickering in mobile. that.trigger(ACTIVATE); } }); extend(options.animation.close, { complete: function() { that.wrapper.hide(); var location = that.wrapper.data(LOCATION), anchor = $(options.anchor), direction, dirClass; if (location) { that.wrapper.css(location); } if (options.anchor != BODY) { direction = (anchor[0].className.match(ACTIVEBORDERREGEXP) || ["", "down"])[1]; dirClass = ACTIVEBORDER + "-" + direction; anchor .removeClass(dirClass) .children(ACTIVECHILDREN) .removeClass(ACTIVE) .removeClass(dirClass); element.removeClass(ACTIVEBORDER + "-" + kendo.directions[direction].reverse); } that._closing = false; that.trigger(DEACTIVATE); } }); that._mousedownProxy = function(e) { that._mousedown(e); }; that._resizeProxy = function(e) { that._resize(e); }; if (options.toggleTarget) { $(options.toggleTarget).on(options.toggleEvent + NS, $.proxy(that.toggle, that)); } }, events: [ OPEN, ACTIVATE, CLOSE, DEACTIVATE ], options: { name: "Popup", toggleEvent: "click", origin: BOTTOM + " " + LEFT, position: TOP + " " + LEFT, anchor: BODY, collision: "flip fit", viewport: window, copyAnchorStyles: true, autosize: false, modal: false, animation: { open: { effects: "slideIn:down", transition: true, duration: 200 }, close: { // if close animation effects are defined, they will be used instead of open.reverse duration: 100, hide: true } } }, destroy: function() { var that = this, options = that.options, element = that.element.off(NS), parent; Widget.fn.destroy.call(that); if (options.toggleTarget) { $(options.toggleTarget).off(NS); } if (!options.modal) { DOCUMENT_ELEMENT.unbind(MOUSEDOWN, that._mousedownProxy); WINDOW.unbind(RESIZE_SCROLL, that._resizeProxy); } if (options.appendTo[0] === document.body) { parent = element.parent(".k-animation-container"); if (parent[0]) { parent.remove(); } else { element.remove(); } } kendo.destroy(that.element.children()); }, open: function(x, y) { var that = this, fixed = { isFixed: !isNaN(parseInt(y,10)), x: x, y: y }, element = that.element, options = that.options, direction = "down", animation, wrapper, anchor = $(options.anchor); if (!that.visible()) { if (options.copyAnchorStyles) { element.css(kendo.getComputedStyles(anchor[0], styles)); } if (element.data("animating") || that.trigger(OPEN)) { return; } if (!options.modal) { DOCUMENT_ELEMENT.unbind(MOUSEDOWN, that._mousedownProxy) .bind(MOUSEDOWN, that._mousedownProxy); // this binding hangs iOS in editor if (!(support.mobileOS.ios || support.mobileOS.android)) { WINDOW.unbind(RESIZE_SCROLL, that._resizeProxy) .bind(RESIZE_SCROLL, that._resizeProxy); } } that.wrapper = wrapper = kendo.wrap(element, options.autosize) .css({ overflow: HIDDEN, display: "block", position: ABSOLUTE }); if (support.mobileOS.android) { wrapper.add(anchor).css(TRANSFORM, "translatez(0)"); // Android is VERY slow otherwise. Should be tested in other droids as well since it may cause blur. } wrapper.css(POSITION); if ($(options.appendTo)[0] == document.body) { wrapper.css(TOP, "-10000px"); } animation = extend(true, {}, options.animation.open); that.flipped = that._position(fixed); animation.effects = kendo.parseEffects(animation.effects, that.flipped); direction = animation.effects.slideIn ? animation.effects.slideIn.direction : direction; if (options.anchor != BODY) { var dirClass = ACTIVEBORDER + "-" + direction; element.addClass(ACTIVEBORDER + "-" + kendo.directions[direction].reverse); anchor .addClass(dirClass) .children(ACTIVECHILDREN) .addClass(ACTIVE) .addClass(dirClass); } element.data(EFFECTS, animation.effects) .kendoStop(true) .kendoAnimate(animation); } }, toggle: function() { var that = this; that[that.visible() ? CLOSE : OPEN](); }, visible: function() { return this.element.is(":" + VISIBLE); }, close: function() { var that = this, options = that.options, wrap, animation, openEffects, closeEffects; if (that.visible()) { wrap = (that.wrapper[0] ? that.wrapper : kendo.wrap(that.element).hide()); if (that._closing || that.trigger(CLOSE)) { return; } // Close all inclusive popups. that.element.find(".k-popup").each(function () { var that = $(this), popup = that.data("kendoPopup"); if (popup) { popup.close(); } }); DOCUMENT_ELEMENT.unbind(MOUSEDOWN, that._mousedownProxy); WINDOW.unbind(RESIZE_SCROLL, that._resizeProxy); animation = extend(true, {}, options.animation.close); openEffects = that.element.data(EFFECTS); closeEffects = animation.effects; if (!closeEffects && !kendo.size(closeEffects) && openEffects && kendo.size(openEffects)) { animation.effects = openEffects; animation.reverse = true; } that._closing = true; that.element.kendoStop(true); wrap.css({ overflow: HIDDEN }); // stop callback will remove hidden overflow that.element.kendoAnimate(animation); } }, _resize: function(e) { var that = this; if (e.type === "resize") { clearTimeout(that._resizeTimeout); that._resizeTimeout = setTimeout(function() { that._position(); that._resizeTimeout = null; }, 50); } else { if (!that._hovered && !contains(that.element[0], activeElement())) { that.close(); } } }, _mousedown: function(e) { var that = this, container = that.element[0], options = that.options, anchor = $(options.anchor)[0], toggleTarget = options.toggleTarget, target = kendo.eventTarget(e), popup = $(target).closest(".k-popup"), mobile = popup.parent().parent(".km-shim").length; popup = popup[0]; if (!mobile && popup && popup !== that.element[0]){ return; } // This MAY result in popup not closing in certain cases. if ($(e.target).closest("a").data("rel") === "popover") { return; } if (!contains(container, target) && !contains(anchor, target) && !(toggleTarget && contains($(toggleTarget)[0], target))) { that.close(); } }, _fit: function(position, size, viewPortSize) { var output = 0; if (position + size > viewPortSize) { output = viewPortSize - (position + size); } if (position < 0) { output = -position; } return output; }, _flip: function(offset, size, anchorSize, viewPortSize, origin, position, boxSize) { var output = 0; boxSize = boxSize || size; if (position !== origin && position !== CENTER && origin !== CENTER) { if (offset + boxSize > viewPortSize) { output += -(anchorSize + size); } if (offset + output < 0) { output += anchorSize + size; } } return output; }, _position: function(fixed) { var that = this, element = that.element.css(POSITION, ""), wrapper = that.wrapper, options = that.options, viewport = $(options.viewport), viewportOffset = $(viewport).offset(), anchor = $(options.anchor), origins = options.origin.toLowerCase().split(" "), positions = options.position.toLowerCase().split(" "), collisions = that.collisions, zoomLevel = support.zoomLevel(), siblingContainer, parents, parentZIndex, zIndex = 10002, idx = 0, length; siblingContainer = anchor.parents().filter(wrapper.siblings()); if (siblingContainer[0]) { parentZIndex = Number($(siblingContainer).css("zIndex")); if (parentZIndex) { zIndex = parentZIndex + 1; } else { parents = anchor.parentsUntil(siblingContainer); for (length = parents.length; idx < length; idx++) { parentZIndex = Number($(parents[idx]).css("zIndex")); if (parentZIndex && zIndex < parentZIndex) { zIndex = parentZIndex + 1; } } } } wrapper.css("zIndex", zIndex); if (fixed && fixed.isFixed) { wrapper.css({ left: fixed.x, top: fixed.y }); } else { wrapper.css(that._align(origins, positions)); } var pos = getOffset(wrapper, POSITION, anchor[0] === wrapper.offsetParent()[0]), offset = getOffset(wrapper), anchorParent = anchor.offsetParent().parent(".k-animation-container,.k-popup,.k-group"); // If the parent is positioned, get the current positions if (anchorParent.length) { pos = getOffset(wrapper, POSITION, true); offset = getOffset(wrapper); } if (viewport[0] === window) { offset.top -= (window.pageYOffset || document.documentElement.scrollTop || 0); offset.left -= (window.pageXOffset || document.documentElement.scrollLeft || 0); } else { offset.top -= viewportOffset.top; offset.left -= viewportOffset.left; } if (!that.wrapper.data(LOCATION)) { // Needed to reset the popup location after every closure - fixes the resize bugs. wrapper.data(LOCATION, extend({}, pos)); } var offsets = extend({}, offset), location = extend({}, pos); if (collisions[0] === "fit") { location.top += that._fit(offsets.top, wrapper.outerHeight(), viewport.height() / zoomLevel); } if (collisions[1] === "fit") { location.left += that._fit(offsets.left, wrapper.outerWidth(), viewport.width() / zoomLevel); } var flipPos = extend({}, location); if (collisions[0] === "flip") { location.top += that._flip(offsets.top, element.outerHeight(), anchor.outerHeight(), viewport.height() / zoomLevel, origins[0], positions[0], wrapper.outerHeight()); } if (collisions[1] === "flip") { location.left += that._flip(offsets.left, element.outerWidth(), anchor.outerWidth(), viewport.width() / zoomLevel, origins[1], positions[1], wrapper.outerWidth()); } element.css(POSITION, ABSOLUTE); wrapper.css(location); return (location.left != flipPos.left || location.top != flipPos.top); }, _align: function(origin, position) { var that = this, element = that.wrapper, anchor = $(that.options.anchor), verticalOrigin = origin[0], horizontalOrigin = origin[1], verticalPosition = position[0], horizontalPosition = position[1], anchorOffset = getOffset(anchor), appendTo = $(that.options.appendTo), appendToOffset, width = element.outerWidth(), height = element.outerHeight(), anchorWidth = anchor.outerWidth(), anchorHeight = anchor.outerHeight(), top = anchorOffset.top, left = anchorOffset.left, round = Math.round; if (appendTo[0] != document.body) { appendToOffset = getOffset(appendTo); top -= appendToOffset.top; left -= appendToOffset.left; } if (verticalOrigin === BOTTOM) { top += anchorHeight; } if (verticalOrigin === CENTER) { top += round(anchorHeight / 2); } if (verticalPosition === BOTTOM) { top -= height; } if (verticalPosition === CENTER) { top -= round(height / 2); } if (horizontalOrigin === RIGHT) { left += anchorWidth; } if (horizontalOrigin === CENTER) { left += round(anchorWidth / 2); } if (horizontalPosition === RIGHT) { left -= width; } if (horizontalPosition === CENTER) { left -= round(width / 2); } return { top: top, left: left }; } }); ui.plugin(Popup); })(window.kendo.jQuery); kendo_module({ id: "tooltip", name: "Tooltip", category: "web", description: "The Tooltip widget displays a popup hint for a given html element.", depends: [ "core", "popup" ] }); (function($, undefined) { var kendo = window.kendo, Widget = kendo.ui.Widget, Popup = kendo.ui.Popup, isFunction = kendo.isFunction, isPlainObject = $.isPlainObject, extend = $.extend, proxy = $.proxy, DOCUMENT = $(document), isLocalUrl = kendo.isLocalUrl, ARIAIDSUFFIX = "_tt_active", DESCRIBEDBY = "aria-describedby", SHOW = "show", HIDE = "hide", ERROR = "error", CONTENTLOAD = "contentLoad", REQUESTSTART = "requestStart", KCONTENTFRAME = "k-content-frame", TEMPLATE = '', IFRAMETEMPLATE = kendo.template( ""), NS = ".kendoTooltip", POSITIONS = { bottom: { origin: "bottom center", position: "top center" }, top: { origin: "top center", position: "bottom center" }, left: { origin: "center left", position: "center right", collision: "fit flip" }, right: { origin: "center right", position: "center left", collision: "fit flip" }, center: { position: "center center", origin: "center center" } }, REVERSE = { "top": "bottom", "bottom": "top", "left": "right", "right": "left", "center": "center" }, DIRCLASSES = { bottom: "n", top: "s", left: "e", right: "w", center: "n" }, DIMENSIONS = { "horizontal": { offset: "top", size: "outerHeight" }, "vertical": { offset: "left", size: "outerWidth" } }, DEFAULTCONTENT = function(e) { return e.target.data(kendo.ns + "title"); }; function restoreTitle(element) { while(element.length) { restoreTitleAttributeForElement(element); element = element.parent(); } } function restoreTitleAttributeForElement(element) { var title = element.data(kendo.ns + "title"); if (title) { element.attr("title", title); element.removeData(kendo.ns + "title"); } } function saveTitleAttributeForElement(element) { var title = element.attr("title"); if (title) { element.data(kendo.ns + "title", title); element.attr("title", ""); } } function saveTitleAttributes(element) { while(element.length && !element.is("body")) { saveTitleAttributeForElement(element); element = element.parent(); } } var Tooltip = Widget.extend({ init: function(element, options) { var that = this, axis; Widget.fn.init.call(that, element, options); axis = that.options.position.match(/left|right/) ? "horizontal" : "vertical"; that.dimensions = DIMENSIONS[axis]; that._documentKeyDownHandler = proxy(that._documentKeyDown, that); that.element .on(that.options.showOn + NS, that.options.filter, proxy(that._showOn, that)) .on("mouseenter" + NS, that.options.filter, proxy(that._mouseenter, that)); if (this.options.autoHide) { that.element.on("mouseleave" + NS, that.options.filter, proxy(that._mouseleave, that)); } }, options: { name: "Tooltip", filter: "", content: DEFAULTCONTENT, showAfter: 100, callout: true, position: "bottom", showOn: "mouseenter", autoHide: true, width: null, height: null, animation: { open: { effects: "fade:in", duration: 0 }, close: { effects: "fade:out", duration: 40, hide: true } } }, events: [ SHOW, HIDE, CONTENTLOAD, ERROR, REQUESTSTART ], _mouseenter: function(e) { saveTitleAttributes($(e.currentTarget)); }, _showOn: function(e) { var that = this; var currentTarget = $(e.currentTarget); if (that.options.showOn && that.options.showOn.match(/click|focus/)) { that._show(currentTarget); } else { clearTimeout(that.timeout); that.timeout = setTimeout(function() { that._show(currentTarget); }, that.options.showAfter); } }, _appendContent: function(target) { var that = this, contentOptions = that.options.content, element = that.content, showIframe = that.options.iframe, iframe; if (isPlainObject(contentOptions) && contentOptions.url) { if (!("iframe" in that.options)) { showIframe = !isLocalUrl(contentOptions.url); } that.trigger(REQUESTSTART, { options: contentOptions, target: target }); if (!showIframe) { element.empty(); kendo.ui.progress(element, true); // perform AJAX request that._ajaxRequest(contentOptions); } else { element.hide(); iframe = element.find("." + KCONTENTFRAME)[0]; if (iframe) { // refresh existing iframe iframe.src = contentOptions.url || iframe.src; } else { element.html(IFRAMETEMPLATE({ content: contentOptions })); } element.find("." + KCONTENTFRAME) .off("load" + NS) .on("load" + NS, function(){ that.trigger(CONTENTLOAD); element.show(); }); } } else if (contentOptions && isFunction(contentOptions)) { contentOptions = contentOptions({ sender: this, target: target }); that.content.html(contentOptions || ""); } else { that.content.html(contentOptions); } }, _ajaxRequest: function(options) { var that = this; jQuery.ajax(extend({ type: "GET", dataType: "html", cache: false, error: function (xhr, status) { kendo.ui.progress(that.content, false); that.trigger(ERROR, { status: status, xhr: xhr }); }, success: proxy(function (data) { kendo.ui.progress(that.content, false); that.content.html(data); that.trigger(CONTENTLOAD); }, that) }, options)); }, _documentKeyDown: function(e) { if (e.keyCode === kendo.keys.ESC) { this.hide(); } }, refresh: function() { var that = this, popup = that.popup; if (popup && popup.options.anchor) { that._appendContent(popup.options.anchor); } }, hide: function() { if (this.popup) { this.popup.close(); } }, show: function(target) { target = target || this.element; saveTitleAttributes(target); this._show(target); }, _show: function(target) { var that = this, current = that.target(); if (!that.popup) { that._initPopup(); } if (current && current[0] != target[0]) { that.popup.close(); that.popup.element.kendoStop(true, true);// animation can be too long to hide the element before it is shown again } if (!current || current[0] != target[0]) { that._appendContent(target); that.popup.options.anchor = target; } that.popup.one("deactivate", function() { restoreTitle(target); target.removeAttr(DESCRIBEDBY); this.element .removeAttr("id") .attr("aria-hidden", true); DOCUMENT.off("keydown" + NS, that._documentKeyDownHandler); }); that.popup.open(); }, _initPopup: function() { var that = this, options = that.options, wrapper = $(kendo.template(TEMPLATE)({ callout: options.callout && options.position !== "center", dir: DIRCLASSES[options.position], autoHide: options.autoHide })); that.popup = new Popup(wrapper, extend({ activate: function() { var anchor = this.options.anchor, ariaId = anchor[0].id || that.element[0].id; if (ariaId) { anchor.attr(DESCRIBEDBY, ariaId + ARIAIDSUFFIX); this.element.attr("id", ariaId + ARIAIDSUFFIX); } if (options.callout) { that._positionCallout(); } this.element.removeAttr("aria-hidden"); DOCUMENT.on("keydown" + NS, that._documentKeyDownHandler); that.trigger(SHOW); }, close: function() { that.trigger(HIDE); }, copyAnchorStyles: false, animation: options.animation }, POSITIONS[options.position])); wrapper.css({ width: options.width, height: options.height }); that.content = wrapper.find(".k-tooltip-content"); that.arrow = wrapper.find(".k-callout"); if (options.autoHide) { wrapper.on("mouseleave" + NS, proxy(that._mouseleave, that)); } else { wrapper.on("click" + NS, ".k-tooltip-button", proxy(that._closeButtonClick, that)); } }, _closeButtonClick: function(e) { e.preventDefault(); this.hide(); }, _mouseleave: function(e) { if (this.popup) { var element = $(e.currentTarget), offset = element.offset(), pageX = e.pageX, pageY = e.pageY; offset.right = offset.left + element.outerWidth(); offset.bottom = offset.top + element.outerHeight(); if (pageX > offset.left && pageX < offset.right && pageY > offset.top && pageY < offset.bottom) { return; } this.popup.close(); } else { restoreTitle($(e.currentTarget)); } clearTimeout(this.timeout); }, _positionCallout: function() { var that = this, position = that.options.position, dimensions = that.dimensions, offset = dimensions.offset, popup = that.popup, anchor = popup.options.anchor, anchorOffset = $(anchor).offset(), arrowBorder = parseInt(that.arrow.css("border-top-width"), 10), elementOffset = $(popup.element).offset(), cssClass = DIRCLASSES[popup.flipped ? REVERSE[position] : position], offsetAmount = anchorOffset[offset] - elementOffset[offset] + ($(anchor)[dimensions.size]() / 2) - arrowBorder; that.arrow .removeClass("k-callout-n k-callout-s k-callout-w k-callout-e") .addClass("k-callout-" + cssClass) .css(offset, offsetAmount); }, target: function() { if (this.popup) { return this.popup.options.anchor; } return null; }, destroy: function() { var popup = this.popup; if (popup) { popup.element.off(NS); popup.destroy(); } this.element.off(NS); DOCUMENT.off("keydown" + NS, this._documentKeyDownHandler); Widget.fn.destroy.call(this); } }); kendo.ui.plugin(Tooltip); })(window.kendo.jQuery); kendo_module({ id: "list", name: "List", category: "framework", depends: [ "data", "popup" ], hidden: true }); (function($, undefined) { var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, keys = kendo.keys, support = kendo.support, htmlEncode = kendo.htmlEncode, activeElement = kendo._activeElement, ID = "id", LI = "li", CHANGE = "change", CHARACTER = "character", FOCUSED = "k-state-focused", HOVER = "k-state-hover", LOADING = "k-loading", OPEN = "open", CLOSE = "close", SELECT = "select", SELECTED = "selected", PROGRESS = "progress", REQUESTEND = "requestEnd", WIDTH = "width", extend = $.extend, proxy = $.proxy, browser = support.browser, isIE8 = browser.msie && browser.version < 9, quotRegExp = /"/g, alternativeNames = { "ComboBox": "DropDownList", "DropDownList": "ComboBox" }; var List = Widget.extend({ init: function(element, options) { var that = this, ns = that.ns, id; Widget.fn.init.call(that, element, options); element = that.element; that._isSelect = element.is(SELECT); that._template(); that.ul = $('
        ') .css({ overflow: support.kineticScrollNeeded ? "": "auto" }) .on("mouseenter" + ns, LI, function() { $(this).addClass(HOVER); }) .on("mouseleave" + ns, LI, function() { $(this).removeClass(HOVER); }) .on("click" + ns, LI, proxy(that._click, that)) .attr({ tabIndex: -1, role: "listbox", "aria-hidden": true }); that.list = $("
        ") .append(that.ul) .on("mousedown" + ns, function(e) { e.preventDefault(); }); id = element.attr(ID); if (id) { that.list.attr(ID, id + "-list"); that.ul.attr(ID, id + "_listbox"); that._optionID = id + "_option_selected"; } that._header(); that._accessors(); that._initValue(); }, options: { valuePrimitive: false, headerTemplate: "" }, setOptions: function(options) { Widget.fn.setOptions.call(this, options); if (options && options.enable !== undefined) { options.enabled = options.enable; } }, focus: function() { this._focused.focus(); }, readonly: function(readonly) { this._editable({ readonly: readonly === undefined ? true : readonly, disable: false }); }, enable: function(enable) { this._editable({ readonly: false, disable: !(enable = enable === undefined ? true : enable) }); }, _filterSource: function(filter) { var that = this, options = that.options, dataSource = that.dataSource, expression = dataSource.filter() || {}; removeFiltersForField(expression, options.dataTextField); if (filter) { expression = expression.filters || []; expression.push(filter); } dataSource.filter(expression); }, _header: function() { var template = this.options.headerTemplate; var header; if ($.isFunction(template)) { template = template(); } if (template) { this.list.prepend(template); header = this.ul.prev(); this.header = header[0] ? header : null; } }, _initValue: function() { var that = this, value = that.options.value; if (value !== null) { that.element.val(value); } else { value = that._accessor(); that.options.value = value; } that._old = value; }, _ignoreCase: function() { var that = this, model = that.dataSource.reader.model, field; if (model && model.fields) { field = model.fields[that.options.dataTextField]; if (field && field.type && field.type !== "string") { that.options.ignoreCase = false; } } }, items: function() { return this.ul[0].children; }, current: function(candidate) { var that = this, id = that._optionID; if (candidate !== undefined) { if (that._current) { that._current .removeClass(FOCUSED) .removeAttr("aria-selected") .removeAttr(ID); that._focused .removeAttr("aria-activedescendant"); } if (candidate) { candidate.addClass(FOCUSED); that._scroll(candidate); if (id) { candidate.attr("id", id); that._focused.attr("aria-activedescendant", id); } } that._current = candidate; } else { return that._current; } }, destroy: function() { var that = this, ns = that.ns; Widget.fn.destroy.call(that); that._unbindDataSource(); that.ul.off(ns); that.list.off(ns); that.popup.destroy(); if (that._form) { that._form.off("reset", that._resetHandler); } }, dataItem: function(index) { var that = this; if (index === undefined) { index = that.selectedIndex; } return that._data()[index]; }, _accessors: function() { var that = this, element = that.element, options = that.options, getter = kendo.getter, textField = element.attr(kendo.attr("text-field")), valueField = element.attr(kendo.attr("value-field")); if (textField) { options.dataTextField = textField; } if (valueField) { options.dataValueField = valueField; } that._text = getter(options.dataTextField); that._value = getter(options.dataValueField); }, _aria: function(id) { var that = this, options = that.options, element = that._focused; if (options.suggest !== undefined) { element.attr("aria-autocomplete", options.suggest ? "both" : "list"); } id = id ? id + " " + that.ul[0].id : that.ul[0].id; element.attr("aria-owns", id); that.ul.attr("aria-live", !options.filter || options.filter === "none" ? "off" : "polite"); }, _blur: function() { var that = this; that._change(); that.close(); }, _change: function() { var that = this, index = that.selectedIndex, optionValue = that.options.value, value = that.value(), trigger; if (that._isSelect && !that._bound && optionValue) { value = optionValue; } if (value !== that._old) { trigger = true; } else if (index !== undefined && index !== that._oldIndex) { trigger = true; } if (trigger) { that._old = value; that._oldIndex = index; that.trigger(CHANGE); // trigger the DOM change event so any subscriber gets notified that.element.trigger(CHANGE); } }, _click: function(e) { if (!e.isDefaultPrevented()) { this._accept($(e.currentTarget)); } }, _data: function() { return this.dataSource.view(); }, _enable: function() { var that = this, options = that.options, disabled = that.element.is("[disabled]"); if (options.enable !== undefined) { options.enabled = options.enable; } if (!options.enabled || disabled) { that.enable(false); } else { that.readonly(that.element.is("[readonly]")); } }, _focus: function(li) { var that = this; if (that.popup.visible() && li && that.trigger(SELECT, {item: li})) { that.close(); return; } that._select(li); that._triggerCascade(); that._blur(); }, _index: function(value) { var that = this, idx, length, data = that._data(); for (idx = 0, length = data.length; idx < length; idx++) { if (that._dataValue(data[idx]) == value) { return idx; } } return -1; }, _dataValue: function(dataItem) { var value = this._value(dataItem); if (value === undefined) { value = this._text(dataItem); } return value; }, _height: function(length) { if (length) { var that = this, list = that.list, visible = that.popup.visible(), height = that.options.height; list = list.add(list.parent(".k-animation-container")).show() .height(that.ul[0].scrollHeight > height ? height : "auto"); if (!visible) { list.hide(); } } }, _adjustListWidth: function() { var list = this.list, width = list[0].style.width, wrapper = this.wrapper, computedStyle, computedWidth; if (!list.data(WIDTH) && width) { return; } computedStyle = window.getComputedStyle ? window.getComputedStyle(wrapper[0], null) : 0; computedWidth = computedStyle ? parseFloat(computedStyle.width) : wrapper.outerWidth(); if (computedStyle && (browser.mozilla || browser.msie)) { // getComputedStyle returns different box in FF and IE. computedWidth += parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight) + parseFloat(computedStyle.borderLeftWidth) + parseFloat(computedStyle.borderRightWidth); } if (list.css("box-sizing") !== "border-box") { width = computedWidth - (list.outerWidth() - list.width()); } else { width = computedWidth; } list.css({ fontFamily: wrapper.css("font-family"), width: width }) .data(WIDTH, width); return true; }, _popup: function() { var that = this, list = that.list, focused = that._focused, options = that.options, wrapper = that.wrapper; that.popup = new ui.Popup(list, extend({}, options.popup, { anchor: wrapper, open: function(e) { that._adjustListWidth(); if (that.trigger(OPEN)) { e.preventDefault(); } else { focused.attr("aria-expanded", true); that.ul.attr("aria-hidden", false); } }, close: function(e) { if (that.trigger(CLOSE)) { e.preventDefault(); } else { focused.attr("aria-expanded", false); that.ul.attr("aria-hidden", true); } }, animation: options.animation, isRtl: support.isRtl(wrapper) })); that.popup.one(OPEN, function() { that._height(that._data().length); }); that._touchScroller = kendo.touchScroller(that.popup.element); }, _makeUnselectable: function() { if (isIE8) { this.list.find("*").attr("unselectable", "on"); } }, _toggleHover: function(e) { $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter"); }, _toggle: function(open) { var that = this; open = open !== undefined? open : !that.popup.visible(); if (!support.touch && that._focused[0] !== activeElement()) { that._focused.focus(); } that[open ? OPEN : CLOSE](); }, _scroll: function (item) { if (!item) { return; } if (item[0]) { item = item[0]; } var ul = this.ul[0], itemOffsetTop = item.offsetTop, itemOffsetHeight = item.offsetHeight, ulScrollTop = ul.scrollTop, ulOffsetHeight = ul.clientHeight, bottomDistance = itemOffsetTop + itemOffsetHeight, touchScroller = this._touchScroller, yDimension, headerHeight; if (touchScroller) { yDimension = touchScroller.dimensions.y; if (yDimension.enabled && itemOffsetTop > yDimension.size) { itemOffsetTop = itemOffsetTop - yDimension.size + itemOffsetHeight + 4; touchScroller.scrollTo(0, -itemOffsetTop); } } else { headerHeight = this.header ? this.header.outerHeight() : 0; ul.scrollTop = ulScrollTop > itemOffsetTop ? (itemOffsetTop - headerHeight) : bottomDistance > (ulScrollTop + ulOffsetHeight) ? (bottomDistance - ulOffsetHeight - headerHeight) : ulScrollTop; } }, _template: function() { var that = this, options = that.options, template = options.template, hasDataSource = options.dataSource; if (that._isSelect && that.element[0].length) { if (!hasDataSource) { options.dataTextField = options.dataTextField || "text"; options.dataValueField = options.dataValueField || "value"; } } if (!template) { that.template = kendo.template('
      • ${' + kendo.expr(options.dataTextField, "data") + "}
      • ", { useWithBlock: false }); } else { template = kendo.template(template); that.template = function(data) { return '
      • ' + template(data) + "
      • "; }; } }, _triggerCascade: function() { var that = this, value = that.value(); if ((!that._bound && value) || that._old !== value) { that.trigger("cascade"); } }, _unbindDataSource: function() { var that = this; that.dataSource.unbind(CHANGE, that._refreshHandler) .unbind(PROGRESS, that._progressHandler) .unbind(REQUESTEND, that._requestEndHandler) .unbind("error", that._errorHandler); } }); extend(List, { caret: function(element) { var caret, selection = element.ownerDocument.selection; if (selection) { caret = Math.abs(selection.createRange().moveStart(CHARACTER, -element.value.length)); } else { caret = element.selectionStart; } return caret; }, selectText: function (element, selectionStart, selectionEnd) { try { if (element.createTextRange) { element.focus(); var textRange = element.createTextRange(); textRange.collapse(true); textRange.moveStart(CHARACTER, selectionStart); textRange.moveEnd(CHARACTER, selectionEnd - selectionStart); textRange.select(); } else { element.setSelectionRange(selectionStart, selectionEnd); } } catch(e) { /* element is not focused or it is not in the DOM */ } }, inArray: function(node, parentNode) { var idx, length, siblings = parentNode.children; if (!node || node.parentNode !== parentNode) { return -1; } for (idx = 0, length = siblings.length; idx < length; idx++) { if (node === siblings[idx]) { return idx; } } return -1; } }); kendo.ui.List = List; ui.Select = List.extend({ init: function(element, options) { List.fn.init.call(this, element, options); this._initial = this.element.val(); }, setDataSource: function(dataSource) { this.options.dataSource = dataSource; this._dataSource(); this._bound = false; if (this.options.autoBind) { this.dataSource.fetch(); } }, close: function() { this.popup.close(); }, select: function(li) { var that = this; if (li === undefined) { return that.selectedIndex; } else { that._select(li); that._triggerCascade(); that._old = that._accessor(); that._oldIndex = that.selectedIndex; } }, _accessor: function(value, idx) { var element = this.element[0], isSelect = this._isSelect, selectedIndex = element.selectedIndex, option; if (value === undefined) { if (isSelect) { if (selectedIndex > -1) { option = element.options[selectedIndex]; if (option) { value = option.value; } } } else { value = element.value; } return value; } else { if (isSelect) { if (selectedIndex > -1) { element.options[selectedIndex].removeAttribute(SELECTED); } element.selectedIndex = idx; option = element.options[idx]; if (option) { option.setAttribute(SELECTED, SELECTED); } } else { element.value = value; } } }, _hideBusy: function () { var that = this; clearTimeout(that._busy); that._arrow.removeClass(LOADING); that._focused.attr("aria-busy", false); that._busy = null; }, _showBusy: function () { var that = this; that._request = true; if (that._busy) { return; } that._busy = setTimeout(function () { that._focused.attr("aria-busy", true); that._arrow.addClass(LOADING); }, 100); }, _requestEnd: function() { this._request = false; }, _dataSource: function() { var that = this, element = that.element, options = that.options, dataSource = options.dataSource || {}, idx; dataSource = $.isArray(dataSource) ? {data: dataSource} : dataSource; if (that._isSelect) { idx = element[0].selectedIndex; if (idx > -1) { options.index = idx; } dataSource.select = element; dataSource.fields = [{ field: options.dataTextField }, { field: options.dataValueField }]; } if (that.dataSource && that._refreshHandler) { that._unbindDataSource(); } else { that._refreshHandler = proxy(that.refresh, that); that._progressHandler = proxy(that._showBusy, that); that._requestEndHandler = proxy(that._requestEnd, that); that._errorHandler = proxy(that._hideBusy, that); } that.dataSource = kendo.data.DataSource.create(dataSource) .bind(CHANGE, that._refreshHandler) .bind(PROGRESS, that._progressHandler) .bind(REQUESTEND, that._requestEndHandler) .bind("error", that._errorHandler); }, _get: function(li) { var that = this, data = that._data(), idx, length; if (typeof li === "function") { for (idx = 0, length = data.length; idx < length; idx++) { if (li(data[idx])) { li = idx; break; } } } if (typeof li === "number") { if (li < 0) { return $(); } li = $(that.ul[0].children[li]); } if (li && li.nodeType) { li = $(li); } return li; }, _move: function(e) { var that = this, key = e.keyCode, ul = that.ul[0], methodName = that.popup.visible() ? "_select" : "_accept", current = that._current, down = key === keys.DOWN, firstChild, pressed; if (key === keys.UP || down) { if (e.altKey) { that.toggle(down); } else { firstChild = ul.firstChild; if (!firstChild && !that._accessor() && that._state !== "filter") { if (!that._fetch) { that.dataSource.one(CHANGE, function() { that._move(e); that._fetch = false; }); that._fetch = true; that._filterSource(); } e.preventDefault(); return true; //pressed } if (down) { if (!current || (that.selectedIndex === -1 && !that.value() && current[0] === firstChild)) { current = firstChild; } else { current = current[0].nextSibling; if (!current && firstChild === ul.lastChild) { current = firstChild; } } that[methodName](current); } else { current = current ? current[0].previousSibling : ul.lastChild; if (!current && firstChild === ul.lastChild) { current = firstChild; } that[methodName](current); } } e.preventDefault(); pressed = true; } else if (key === keys.ENTER || key === keys.TAB) { if (that.popup.visible()) { e.preventDefault(); } that._accept(current); pressed = true; } else if (key === keys.ESC) { if (that.popup.visible()) { e.preventDefault(); } that.close(); pressed = true; } return pressed; }, _selectItem: function() { var that = this, options = that.options, index = that.selectedIndex, useOptionIndex, value; useOptionIndex = that._isSelect && !that._initial && !options.value && options.index && !that._bound; if (!useOptionIndex) { value = that._selectedValue || options.value || that._accessor(); } if (value) { that.value(value); } else if (!that._bound || index > -1) { if (!that._bound) { index = options.index; } that.select(index); } }, _fetchItems: function(value) { var that = this, hasItems = that.ul[0].firstChild; //if request is started avoid datasource.fetch if (that._request) { return true; } if (!that._fetch && !hasItems) { if (that.options.cascadeFrom) { return !hasItems; } that.dataSource.one(CHANGE, function() { that.value(value); that._fetch = false; }); that._fetch = true; that.dataSource.fetch(); return true; } }, _options: function(data, optionLabel) { var that = this, element = that.element, selectedIndex = element[0].selectedIndex, length = data.length, options = "", option, dataItem, dataText, dataValue, idx = 0; if (optionLabel) { idx = 1; options = optionLabel; if (optionLabel.indexOf($(element[0].firstChild).text()) === -1) { selectedIndex += 1; } } for (; idx < length; idx++) { option = "#=data.value#', { useWithBlock: false }), emptyCellTemplate = template(' ', { useWithBlock: false }), browser = kendo.support.browser, isIE8 = browser.msie && browser.version < 9, ns = ".kendoCalendar", CLICK = "click" + ns, KEYDOWN_NS = "keydown" + ns, ID = "id", MIN = "min", LEFT = "left", SLIDE = "slideIn", MONTH = "month", CENTURY = "century", CHANGE = "change", NAVIGATE = "navigate", VALUE = "value", HOVER = "k-state-hover", DISABLED = "k-state-disabled", FOCUSED = "k-state-focused", OTHERMONTH = "k-other-month", OTHERMONTHCLASS = ' class="' + OTHERMONTH + '"', TODAY = "k-nav-today", CELLSELECTOR = "td:has(.k-link)", BLUR = "blur" + ns, FOCUS = "focus", FOCUS_WITH_NS = FOCUS + ns, MOUSEENTER = support.touch ? "touchstart" : "mouseenter", MOUSEENTER_WITH_NS = support.touch ? "touchstart" + ns : "mouseenter" + ns, MOUSELEAVE = support.touch ? "touchend" + ns + " touchmove" + ns : "mouseleave" + ns, MS_PER_MINUTE = 60000, MS_PER_DAY = 86400000, PREVARROW = "_prevArrow", NEXTARROW = "_nextArrow", ARIA_DISABLED = "aria-disabled", ARIA_SELECTED = "aria-selected", proxy = $.proxy, extend = $.extend, DATE = Date, views = { month: 0, year: 1, decade: 2, century: 3 }; var Calendar = Widget.extend({ init: function(element, options) { var that = this, value, id; Widget.fn.init.call(that, element, options); element = that.wrapper = that.element; options = that.options; options.url = window.unescape(options.url); that._templates(); that._header(); that._footer(that.footer); id = element .addClass("k-widget k-calendar") .on(MOUSEENTER_WITH_NS + " " + MOUSELEAVE, CELLSELECTOR, mousetoggle) .on(KEYDOWN_NS, "table.k-content", proxy(that._move, that)) .on(CLICK, CELLSELECTOR, function(e) { var link = e.currentTarget.firstChild; if (link.href.indexOf("#") != -1) { e.preventDefault(); } that._click($(link)); }) .on("mouseup" + ns, function() { that._focusView(that.options.focusOnNav !== false); }) .attr(ID); if (id) { that._cellID = id + "_cell_selected"; } normalize(options); value = parse(options.value, options.format, options.culture); that._index = views[options.start]; that._current = new DATE(+restrictValue(value, options.min, options.max)); that._addClassProxy = function() { that._active = true; that._cell.addClass(FOCUSED); }; that._removeClassProxy = function() { that._active = false; that._cell.removeClass(FOCUSED); }; that.value(value); kendo.notify(that); }, options: { name: "Calendar", value: null, min: new DATE(1900, 0, 1), max: new DATE(2099, 11, 31), dates: [], url: "", culture: "", footer : "", format : "", month : {}, start: MONTH, depth: MONTH, animation: { horizontal: { effects: SLIDE, reverse: true, duration: 500, divisor: 2 }, vertical: { effects: "zoomIn", duration: 400 } } }, events: [ CHANGE, NAVIGATE ], setOptions: function(options) { normalize(options); Widget.fn.setOptions.call(this, options); }, destroy: function() { var that = this, today = that._today; that.element.off(ns); that._title.off(ns); that[PREVARROW].off(ns); that[NEXTARROW].off(ns); kendo.destroy(that._table); if (today) { kendo.destroy(today.off(ns)); } Widget.fn.destroy.call(that); }, current: function() { return this._current; }, view: function() { return this._view; }, focus: function(table) { table = table || this._table; this._bindTable(table); table.focus(); }, min: function(value) { return this._option(MIN, value); }, max: function(value) { return this._option("max", value); }, navigateToPast: function() { this._navigate(PREVARROW, -1); }, navigateToFuture: function() { this._navigate(NEXTARROW, 1); }, navigateUp: function() { var that = this, index = that._index; if (that._title.hasClass(DISABLED)) { return; } that.navigate(that._current, ++index); }, navigateDown: function(value) { var that = this, index = that._index, depth = that.options.depth; if (!value) { return; } if (index === views[depth]) { if (+that._value != +value) { that.value(value); that.trigger(CHANGE); } return; } that.navigate(value, --index); }, navigate: function(value, view) { view = isNaN(view) ? views[view] : view; var that = this, options = that.options, culture = options.culture, min = options.min, max = options.max, title = that._title, from = that._table, old = that._oldTable, selectedValue = that._value, currentValue = that._current, future = value && +value > +currentValue, vertical = view !== undefined && view !== that._index, to, currentView, compare, disabled; if (!value) { value = currentValue; } that._current = value = new DATE(+restrictValue(value, min, max)); if (view === undefined) { view = that._index; } else { that._index = view; } that._view = currentView = calendar.views[view]; compare = currentView.compare; disabled = view === views[CENTURY]; title.toggleClass(DISABLED, disabled).attr(ARIA_DISABLED, disabled); disabled = compare(value, min) < 1; that[PREVARROW].toggleClass(DISABLED, disabled).attr(ARIA_DISABLED, disabled); disabled = compare(value, max) > -1; that[NEXTARROW].toggleClass(DISABLED, disabled).attr(ARIA_DISABLED, disabled); if (from && old && old.data("animating")) { old.kendoStop(true, true); from.kendoStop(true, true); } that._oldTable = from; if (!from || that._changeView) { title.html(currentView.title(value, min, max, culture)); that._table = to = $(currentView.content(extend({ min: min, max: max, date: value, url: options.url, dates: options.dates, format: options.format, culture: culture }, that[currentView.name]))); makeUnselectable(to); that._animate({ from: from, to: to, vertical: vertical, future: future }); that._focus(value); that.trigger(NAVIGATE); } if (view === views[options.depth] && selectedValue) { that._class("k-state-selected", currentView.toDateString(selectedValue)); } that._class(FOCUSED, currentView.toDateString(value)); if (!from && that._cell) { that._cell.removeClass(FOCUSED); } that._changeView = true; }, value: function(value) { var that = this, view = that._view, options = that.options, old = that._view, min = options.min, max = options.max; if (value === undefined) { return that._value; } value = parse(value, options.format, options.culture); if (value !== null) { value = new DATE(+value); if (!isInRange(value, min, max)) { value = null; } } that._value = value; if (old && value === null && that._cell) { that._cell.removeClass("k-state-selected"); } else { that._changeView = !value || view && view.compare(value, that._current) !== 0; that.navigate(value); } }, _move: function(e) { var that = this, options = that.options, key = e.keyCode, view = that._view, index = that._index, currentValue = new DATE(+that._current), isRtl = kendo.support.isRtl(that.wrapper), value, prevent, method, temp; if (e.target === that._table[0]) { that._active = true; } if (e.ctrlKey) { if (key == keys.RIGHT && !isRtl || key == keys.LEFT && isRtl) { that.navigateToFuture(); prevent = true; } else if (key == keys.LEFT && !isRtl || key == keys.RIGHT && isRtl) { that.navigateToPast(); prevent = true; } else if (key == keys.UP) { that.navigateUp(); prevent = true; } else if (key == keys.DOWN) { that._click($(that._cell[0].firstChild)); prevent = true; } } else { if (key == keys.RIGHT && !isRtl || key == keys.LEFT && isRtl) { value = 1; prevent = true; } else if (key == keys.LEFT && !isRtl || key == keys.RIGHT && isRtl) { value = -1; prevent = true; } else if (key == keys.UP) { value = index === 0 ? -7 : -4; prevent = true; } else if (key == keys.DOWN) { value = index === 0 ? 7 : 4; prevent = true; } else if (key == keys.ENTER) { that._click($(that._cell[0].firstChild)); prevent = true; } else if (key == keys.HOME || key == keys.END) { method = key == keys.HOME ? "first" : "last"; temp = view[method](currentValue); currentValue = new DATE(temp.getFullYear(), temp.getMonth(), temp.getDate(), currentValue.getHours(), currentValue.getMinutes(), currentValue.getSeconds(), currentValue.getMilliseconds()); prevent = true; } else if (key == keys.PAGEUP) { prevent = true; that.navigateToPast(); } else if (key == keys.PAGEDOWN) { prevent = true; that.navigateToFuture(); } if (value || method) { if (!method) { view.setDate(currentValue, value); } that._focus(restrictValue(currentValue, options.min, options.max)); } } if (prevent) { e.preventDefault(); } return that._current; }, _animate: function(options) { var that = this, from = options.from, to = options.to, active = that._active; if (!from) { to.insertAfter(that.element[0].firstChild); that._bindTable(to); } else if (from.parent().data("animating")) { from.parent().kendoStop(true, true).remove(); from.remove(); to.insertAfter(that.element[0].firstChild); that._focusView(active); } else if (!from.is(":visible") || that.options.animation === false) { to.insertAfter(from); from.remove(); that._focusView(active); } else { that[options.vertical ? "_vertical" : "_horizontal"](from, to, options.future); } }, _horizontal: function(from, to, future) { var that = this, active = that._active, horizontal = that.options.animation.horizontal, effects = horizontal.effects, viewWidth = from.outerWidth(); if (effects && effects.indexOf(SLIDE) != -1) { from.add(to).css({ width: viewWidth }); from.wrap("
        "); that._focusView(active, from); from.parent() .css({ position: "relative", width: viewWidth * 2, "float": LEFT, "margin-left": future ? 0 : -viewWidth }); to[future ? "insertAfter" : "insertBefore"](from); extend(horizontal, { effects: SLIDE + ":" + (future ? "right" : LEFT), complete: function() { from.remove(); to.unwrap(); that._focusView(active); that._oldTable = undefined; } }); from.parent().kendoStop(true, true).kendoAnimate(horizontal); } }, _vertical: function(from, to) { var that = this, vertical = that.options.animation.vertical, effects = vertical.effects, active = that._active, //active state before from's blur cell, position; if (effects && effects.indexOf("zoom") != -1) { to.css({ position: "absolute", top: from.prev().outerHeight(), left: 0 }).insertBefore(from); if (transitionOrigin) { cell = that._cellByDate(that._view.toDateString(that._current)); position = cell.position(); position = (position.left + parseInt(cell.width() / 2, 10)) + "px" + " " + (position.top + parseInt(cell.height() / 2, 10) + "px"); to.css(transitionOrigin, position); } from.kendoStop(true, true).kendoAnimate({ effects: "fadeOut", duration: 600, complete: function() { from.remove(); to.css({ position: "static", top: 0, left: 0 }); that._focusView(active); that._oldTable = undefined; } }); to.kendoStop(true, true).kendoAnimate(vertical); } }, _cellByDate: function(value) { return this._table.find("td:not(." + OTHERMONTH + ")") .filter(function() { return $(this.firstChild).attr(kendo.attr(VALUE)) === value; }); }, _class: function(className, value) { var that = this, id = that._cellID, cell = that._cell; if (cell) { cell.removeAttr(ARIA_SELECTED) .removeAttr(ID); } cell = that._table .find("td:not(." + OTHERMONTH + ")") .removeClass(className) .filter(function() { return $(this.firstChild).attr(kendo.attr(VALUE)) === value; }) .attr(ARIA_SELECTED, true); if (className === FOCUSED && !that._active && that.options.focusOnNav !== false) { className = ""; } cell.addClass(className); if (cell[0]) { that._cell = cell; } if (id) { cell.attr(ID, id); that._table.removeAttr("aria-activedescendant").attr("aria-activedescendant", id); } }, _bindTable: function (table) { table .on(FOCUS_WITH_NS, this._addClassProxy) .on(BLUR, this._removeClassProxy); }, _click: function(link) { var that = this, options = that.options, currentValue = new Date(+that._current), value = link.attr(kendo.attr(VALUE)).split("/"); //Safari cannot create correctly date from "1/1/2090" value = new DATE(value[0], value[1], value[2]); adjustDST(value, 0); that._view.setDate(currentValue, value); that.navigateDown(restrictValue(currentValue, options.min, options.max)); }, _focus: function(value) { var that = this, view = that._view; if (view.compare(value, that._current) !== 0) { that.navigate(value); } else { that._current = value; that._class(FOCUSED, view.toDateString(value)); } }, _focusView: function(active, table) { if (active) { this.focus(table); } }, _footer: function(template) { var that = this, today = getToday(), element = that.element, footer = element.find(".k-footer"); if (!template) { that._toggle(false); footer.hide(); return; } if (!footer[0]) { footer = $('').appendTo(element); } that._today = footer.show() .find(".k-link") .html(template(today)) .attr("title", kendo.toString(today, "D", that.options.culture)); that._toggle(); }, _header: function() { var that = this, element = that.element, active = that.options.focusOnNav !== false, links; if (!element.find(".k-header")[0]) { element.html('
        ' + '' + '' + '' + '
        '); } links = element.find(".k-link") .on(MOUSEENTER_WITH_NS + " " + MOUSELEAVE + " " + FOCUS_WITH_NS + " " + BLUR, mousetoggle) .click(false); that._title = links.eq(1).on(CLICK, function() { that._focusView(active); that.navigateUp(); }); that[PREVARROW] = links.eq(0).on(CLICK, function() { that._focusView(active); that.navigateToPast(); }); that[NEXTARROW] = links.eq(2).on(CLICK, function() { that._focusView(active); that.navigateToFuture(); }); }, _navigate: function(arrow, modifier) { var that = this, index = that._index + 1, currentValue = new DATE(+that._current); arrow = that[arrow]; if (!arrow.hasClass(DISABLED)) { if (index > 3) { currentValue.setFullYear(currentValue.getFullYear() + 100 * modifier); } else { calendar.views[index].setDate(currentValue, modifier); } that.navigate(currentValue); } }, _option: function(option, value) { var that = this, options = that.options, currentValue = that._value || that._current, isBigger; if (value === undefined) { return options[option]; } value = parse(value, options.format, options.culture); if (!value) { return; } options[option] = new DATE(+value); if (option === MIN) { isBigger = value > currentValue; } else { isBigger = currentValue > value; } if (isBigger || isEqualMonth(currentValue, value)) { that._value = null; that._changeView = true; } if (!that._changeView) { that._changeView = !!(options.month.content || options.month.empty); } that.navigate(that._value); that._toggle(); }, _toggle: function(toggle) { var that = this, options = that.options, link = that._today; if (toggle === undefined) { toggle = isInRange(getToday(), options.min, options.max); } if (link) { link.off(CLICK); if (toggle) { link.addClass(TODAY) .removeClass(DISABLED) .on(CLICK, proxy(that._todayClick, that)); } else { link.removeClass(TODAY) .addClass(DISABLED) .on(CLICK, prevent); } } }, _todayClick: function(e) { var that = this, depth = views[that.options.depth], today = getToday(); e.preventDefault(); if (that._view.compare(that._current, today) === 0 && that._index == depth) { that._changeView = false; } that._value = today; that.navigate(today, depth); that.trigger(CHANGE); }, _templates: function() { var that = this, options = that.options, footer = options.footer, month = options.month, content = month.content, empty = month.empty; that.month = { content: template('' + (content || "#=data.value#") + '', { useWithBlock: !!content }), empty: template('' + (empty || " ") + "", { useWithBlock: !!empty }) }; if (footer !== false) { that.footer = template(footer || '#= kendo.toString(data,"D","' + options.culture +'") #', { useWithBlock: false }); } } }); ui.plugin(Calendar); var calendar = { firstDayOfMonth: function (date) { return new DATE( date.getFullYear(), date.getMonth(), 1 ); }, firstVisibleDay: function (date, calendarInfo) { calendarInfo = calendarInfo || kendo.culture().calendar; var firstDay = calendarInfo.firstDay, firstVisibleDay = new DATE(date.getFullYear(), date.getMonth(), 0, date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()); while (firstVisibleDay.getDay() != firstDay) { calendar.setTime(firstVisibleDay, -1 * MS_PER_DAY); } return firstVisibleDay; }, setTime: function (date, time) { var tzOffsetBefore = date.getTimezoneOffset(), resultDATE = new DATE(date.getTime() + time), tzOffsetDiff = resultDATE.getTimezoneOffset() - tzOffsetBefore; date.setTime(resultDATE.getTime() + tzOffsetDiff * MS_PER_MINUTE); }, views: [{ name: MONTH, title: function(date, min, max, culture) { return getCalendarInfo(culture).months.names[date.getMonth()] + " " + date.getFullYear(); }, content: function(options) { var that = this, idx = 0, min = options.min, max = options.max, date = options.date, dates = options.dates, format = options.format, culture = options.culture, navigateUrl = options.url, hasUrl = navigateUrl && dates[0], currentCalendar = getCalendarInfo(culture), firstDayIdx = currentCalendar.firstDay, days = currentCalendar.days, names = shiftArray(days.names, firstDayIdx), shortNames = shiftArray(days.namesShort, firstDayIdx), start = calendar.firstVisibleDay(date, currentCalendar), firstDayOfMonth = that.first(date), lastDayOfMonth = that.last(date), toDateString = that.toDateString, today = new DATE(), html = ''; for (; idx < 7; idx++) { html += ''; } today = new DATE(today.getFullYear(), today.getMonth(), today.getDate()); adjustDST(today, 0); today = +today; return view({ cells: 42, perRow: 7, html: html += '', start: new DATE(start.getFullYear(), start.getMonth(), start.getDate()), min: new DATE(min.getFullYear(), min.getMonth(), min.getDate()), max: new DATE(max.getFullYear(), max.getMonth(), max.getDate()), content: options.content, empty: options.empty, setter: that.setDate, build: function(date) { var cssClass = [], day = date.getDay(), linkClass = "", url = "#"; if (date < firstDayOfMonth || date > lastDayOfMonth) { cssClass.push(OTHERMONTH); } if (+date === today) { cssClass.push("k-today"); } if (day === 0 || day === 6) { cssClass.push("k-weekend"); } if (hasUrl && inArray(+date, dates)) { url = navigateUrl.replace("{0}", kendo.toString(date, format, culture)); linkClass = " k-action-link"; } return { date: date, dates: dates, ns: kendo.ns, title: kendo.toString(date, "D", culture), value: date.getDate(), dateString: toDateString(date), cssClass: cssClass[0] ? ' class="' + cssClass.join(" ") + '"' : "", linkClass: linkClass, url: url }; } }); }, first: function(date) { return calendar.firstDayOfMonth(date); }, last: function(date) { var last = new DATE(date.getFullYear(), date.getMonth() + 1, 0), first = calendar.firstDayOfMonth(date), timeOffset = Math.abs(last.getTimezoneOffset() - first.getTimezoneOffset()); if (timeOffset) { last.setHours(first.getHours() + (timeOffset / 60)); } return last; }, compare: function(date1, date2) { var result, month1 = date1.getMonth(), year1 = date1.getFullYear(), month2 = date2.getMonth(), year2 = date2.getFullYear(); if (year1 > year2) { result = 1; } else if (year1 < year2) { result = -1; } else { result = month1 == month2 ? 0 : month1 > month2 ? 1 : -1; } return result; }, setDate: function(date, value) { var hours = date.getHours(); if (value instanceof DATE) { date.setFullYear(value.getFullYear(), value.getMonth(), value.getDate()); } else { calendar.setTime(date, value * MS_PER_DAY); } adjustDST(date, hours); }, toDateString: function(date) { return date.getFullYear() + "/" + date.getMonth() + "/" + date.getDate(); } }, { name: "year", title: function(date) { return date.getFullYear(); }, content: function(options) { var namesAbbr = getCalendarInfo(options.culture).months.namesAbbr, toDateString = this.toDateString, min = options.min, max = options.max; return view({ min: new DATE(min.getFullYear(), min.getMonth(), 1), max: new DATE(max.getFullYear(), max.getMonth(), 1), start: new DATE(options.date.getFullYear(), 0, 1), setter: this.setDate, build: function(date) { return { value: namesAbbr[date.getMonth()], ns: kendo.ns, dateString: toDateString(date), cssClass: "" }; } }); }, first: function(date) { return new DATE(date.getFullYear(), 0, date.getDate()); }, last: function(date) { return new DATE(date.getFullYear(), 11, date.getDate()); }, compare: function(date1, date2){ return compare(date1, date2); }, setDate: function(date, value) { var month, hours = date.getHours(); if (value instanceof DATE) { month = value.getMonth(); date.setFullYear(value.getFullYear(), month, date.getDate()); if (month !== date.getMonth()) { date.setDate(0); } } else { month = date.getMonth() + value; date.setMonth(month); if (month > 11) { month -= 12; } if (month > 0 && date.getMonth() != month) { date.setDate(0); } } adjustDST(date, hours); }, toDateString: function(date) { return date.getFullYear() + "/" + date.getMonth() + "/1"; } }, { name: "decade", title: function(date, min, max) { return title(date, min, max, 10); }, content: function(options) { var year = options.date.getFullYear(), toDateString = this.toDateString; return view({ start: new DATE(year - year % 10 - 1, 0, 1), min: new DATE(options.min.getFullYear(), 0, 1), max: new DATE(options.max.getFullYear(), 0, 1), setter: this.setDate, build: function(date, idx) { return { value: date.getFullYear(), ns: kendo.ns, dateString: toDateString(date), cssClass: idx === 0 || idx == 11 ? OTHERMONTHCLASS : "" }; } }); }, first: function(date) { var year = date.getFullYear(); return new DATE(year - year % 10, date.getMonth(), date.getDate()); }, last: function(date) { var year = date.getFullYear(); return new DATE(year - year % 10 + 9, date.getMonth(), date.getDate()); }, compare: function(date1, date2) { return compare(date1, date2, 10); }, setDate: function(date, value) { setDate(date, value, 1); }, toDateString: function(date) { return date.getFullYear() + "/0/1"; } }, { name: CENTURY, title: function(date, min, max) { return title(date, min, max, 100); }, content: function(options) { var year = options.date.getFullYear(), min = options.min.getFullYear(), max = options.max.getFullYear(), toDateString = this.toDateString, minYear = min, maxYear = max; minYear = minYear - minYear % 10; maxYear = maxYear - maxYear % 10; if (maxYear - minYear < 10) { maxYear = minYear + 9; } return view({ start: new DATE(year - year % 100 - 10, 0, 1), min: new DATE(minYear, 0, 1), max: new DATE(maxYear, 0, 1), setter: this.setDate, build: function(date, idx) { var start = date.getFullYear(), end = start + 9; if (start < min) { start = min; } if (end > max) { end = max; } return { ns: kendo.ns, value: start + " - " + end, dateString: toDateString(date), cssClass: idx === 0 || idx == 11 ? OTHERMONTHCLASS : "" }; } }); }, first: function(date) { var year = date.getFullYear(); return new DATE(year - year % 100, date.getMonth(), date.getDate()); }, last: function(date) { var year = date.getFullYear(); return new DATE(year - year % 100 + 99, date.getMonth(), date.getDate()); }, compare: function(date1, date2) { return compare(date1, date2, 100); }, setDate: function(date, value) { setDate(date, value, 10); }, toDateString: function(date) { var year = date.getFullYear(); return (year - year % 10) + "/0/1"; } }] }; function title(date, min, max, modular) { var start = date.getFullYear(), minYear = min.getFullYear(), maxYear = max.getFullYear(), end; start = start - start % modular; end = start + (modular - 1); if (start < minYear) { start = minYear; } if (end > maxYear) { end = maxYear; } return start + "-" + end; } function view(options) { var idx = 0, data, min = options.min, max = options.max, start = options.start, setter = options.setter, build = options.build, length = options.cells || 12, cellsPerRow = options.perRow || 4, content = options.content || cellTemplate, empty = options.empty || emptyCellTemplate, html = options.html || '
        ' + shortNames[idx] + '
        '; for(; idx < length; idx++) { if (idx > 0 && idx % cellsPerRow === 0) { html += ''; } data = build(start, idx); html += isInRange(start, min, max) ? content(data) : empty(data); setter(start, 1); } return html + "
        "; } function compare(date1, date2, modifier) { var year1 = date1.getFullYear(), start = date2.getFullYear(), end = start, result = 0; if (modifier) { start = start - start % modifier; end = start - start % modifier + modifier - 1; } if (year1 > end) { result = 1; } else if (year1 < start) { result = -1; } return result; } function getToday() { var today = new DATE(); return new DATE(today.getFullYear(), today.getMonth(), today.getDate()); } function restrictValue (value, min, max) { var today = getToday(); if (value) { today = new DATE(+value); } if (min > today) { today = new DATE(+min); } else if (max < today) { today = new DATE(+max); } return today; } function isInRange(date, min, max) { return +date >= +min && +date <= +max; } function shiftArray(array, idx) { return array.slice(idx).concat(array.slice(0, idx)); } function setDate(date, value, multiplier) { value = value instanceof DATE ? value.getFullYear() : date.getFullYear() + multiplier * value; date.setFullYear(value); } function mousetoggle(e) { $(this).toggleClass(HOVER, MOUSEENTER.indexOf(e.type) > -1 || e.type == FOCUS); } function prevent (e) { e.preventDefault(); } function getCalendarInfo(culture) { return getCulture(culture).calendars.standard; } function normalize(options) { var start = views[options.start], depth = views[options.depth], culture = getCulture(options.culture); options.format = extractFormat(options.format || culture.calendars.standard.patterns.d); if (isNaN(start)) { start = 0; options.start = MONTH; } if (depth === undefined || depth > start) { options.depth = MONTH; } if (!options.dates) { options.dates = []; } } function makeUnselectable(element) { if (isIE8) { element.find("*").attr("unselectable", "on"); } } function inArray(date, dates) { for(var i = 0, length = dates.length; i < length; i++) { if (date === +dates[i]) { return true; } } return false; } function isEqualDatePart(value1, value2) { if (value1) { return value1.getFullYear() === value2.getFullYear() && value1.getMonth() === value2.getMonth() && value1.getDate() === value2.getDate(); } return false; } function isEqualMonth(value1, value2) { if (value1) { return value1.getFullYear() === value2.getFullYear() && value1.getMonth() === value2.getMonth(); } return false; } calendar.isEqualDatePart = isEqualDatePart; calendar.makeUnselectable = makeUnselectable; calendar.restrictValue = restrictValue; calendar.isInRange = isInRange; calendar.normalize = normalize; calendar.viewsEnum = views; kendo.calendar = calendar; })(window.kendo.jQuery); kendo_module({ id: "datepicker", name: "DatePicker", category: "web", description: "The DatePicker widget allows the user to select a date from a calendar or by direct input.", depends: [ "calendar", "popup" ] }); (function($, undefined) { var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, parse = kendo.parseDate, keys = kendo.keys, template = kendo.template, activeElement = kendo._activeElement, DIV = "
        ", SPAN = "", ns = ".kendoDatePicker", CLICK = "click" + ns, OPEN = "open", CLOSE = "close", CHANGE = "change", DATEVIEW = "dateView", DISABLED = "disabled", READONLY = "readonly", DEFAULT = "k-state-default", FOCUSED = "k-state-focused", SELECTED = "k-state-selected", STATEDISABLED = "k-state-disabled", HOVER = "k-state-hover", KEYDOWN = "keydown" + ns, HOVEREVENTS = "mouseenter" + ns + " mouseleave" + ns, MOUSEDOWN = "mousedown" + ns, ID = "id", MIN = "min", MAX = "max", MONTH = "month", ARIA_DISABLED = "aria-disabled", ARIA_EXPANDED = "aria-expanded", ARIA_HIDDEN = "aria-hidden", ARIA_READONLY = "aria-readonly", calendar = kendo.calendar, isInRange = calendar.isInRange, restrictValue = calendar.restrictValue, isEqualDatePart = calendar.isEqualDatePart, extend = $.extend, proxy = $.proxy, DATE = Date; function normalize(options) { var parseFormats = options.parseFormats, format = options.format; calendar.normalize(options); parseFormats = $.isArray(parseFormats) ? parseFormats : [parseFormats]; if ($.inArray(format, parseFormats) === -1) { parseFormats.splice(0, 0, options.format); } options.parseFormats = parseFormats; } function preventDefault(e) { e.preventDefault(); } var DateView = function(options) { var that = this, id, body = document.body, sharedCalendar = DatePicker.sharedCalendar, div = $(DIV).attr(ARIA_HIDDEN, "true") .addClass("k-calendar-container") .appendTo(body); if (!sharedCalendar) { sharedCalendar = DatePicker.sharedCalendar = new ui.Calendar($(DIV).attr(ID, kendo.guid()).hide().appendTo(body), { focusOnNav: false }); calendar.makeUnselectable(sharedCalendar.element); } that.calendar = sharedCalendar; that.options = options = options || {}; id = options.id; if (id) { id += "_dateview"; div.attr(ID, id); that._dateViewID = id; } that.popup = new ui.Popup(div, extend(options.popup, options, { name: "Popup", isRtl: kendo.support.isRtl(options.anchor) })); that.div = div; that._templates(); that.value(options.value); }; DateView.prototype = { _calendar: function() { var that = this, popup = that.popup, options = that.options, calendar = that.calendar, element = calendar.element; if (element.data(DATEVIEW) !== that) { element.appendTo(popup.element) .data(DATEVIEW, that) .off(CLICK + " " + KEYDOWN) .on(CLICK, "td:has(.k-link)", proxy(that._click, that)) .on(MOUSEDOWN, preventDefault) .show(); calendar.unbind(CHANGE) .bind(CHANGE, options); calendar.month = that.month; calendar.options.dates = options.dates; calendar.options.depth = options.depth; calendar.options.culture = options.culture; calendar._footer(that.footer); calendar.min(options.min); calendar.max(options.max); calendar._value = null; calendar.navigate(that._value || that._current, options.start); that.value(that._value); } }, destroy: function() { var that = this, calendar = that.calendar, element = calendar.element, dv = element.data(DATEVIEW), popups; if (dv === undefined || dv === that) { popups = $(".k-calendar-container"); if (popups.length > 1) { element.hide().appendTo(document.body); } else { element.off(ns); calendar.destroy(); calendar.element.remove(); DatePicker.sharedCalendar = null; } } that.popup.destroy(); }, open: function() { var that = this; that._calendar(); that.popup.open(); }, close: function() { this.popup.close(); }, min: function(value) { this._option(MIN, value); }, max: function(value) { this._option(MAX, value); }, toggle: function() { var that = this; that[that.popup.visible() ? CLOSE : OPEN](); }, move: function(e) { var that = this, key = e.keyCode, calendar = that.calendar, selectIsClicked = e.ctrlKey && key == keys.DOWN || key == keys.ENTER; if (key == keys.ESC) { that.close(); return; } if (e.altKey) { if (key == keys.DOWN) { that.open(); e.preventDefault(); } else if (key == keys.UP) { that.close(); e.preventDefault(); } return; } if (!that.popup.visible()){ return; } if (selectIsClicked && calendar._cell.hasClass(SELECTED)) { that.close(); e.preventDefault(); return; } that._current = calendar._move(e); }, value: function(value) { var that = this, calendar = that.calendar, options = that.options; that._value = value; that._current = new DATE(+restrictValue(value, options.min, options.max)); if (calendar.element.data(DATEVIEW) === that) { calendar.value(value); } }, _click: function(e) { if (e.currentTarget.className.indexOf(SELECTED) !== -1) { this.close(); } }, _option: function(option, value) { var that = this, options = that.options, calendar = that.calendar; options[option] = value; if (calendar.element.data(DATEVIEW) === that) { calendar[option](value); } }, _templates: function() { var that = this, options = that.options, footer = options.footer, month = options.month || {}, content = month.content, empty = month.empty; that.month = { content: template('' + (content || "#=data.value#") + '', { useWithBlock: !!content }), empty: template("" + (empty || " ") + "", { useWithBlock: !!empty }) }; if (footer !== false) { that.footer = template(footer || '#= kendo.toString(data,"D","' + options.culture +'") #', { useWithBlock: false }); } } }; DateView.normalize = normalize; kendo.DateView = DateView; var DatePicker = Widget.extend({ init: function(element, options) { var that = this, disabled, div; Widget.fn.init.call(that, element, options); element = that.element; options = that.options; normalize(options); that._wrapper(); that.dateView = new DateView(extend({}, options, { id: element.attr(ID), anchor: that.wrapper, change: function() { // calendar is the current scope that._change(this.value()); that.close(); }, close: function(e) { if (that.trigger(CLOSE)) { e.preventDefault(); } else { element.attr(ARIA_EXPANDED, false); div.attr(ARIA_HIDDEN, true); } }, open: function(e) { var options = that.options, date; if (that.trigger(OPEN)) { e.preventDefault(); } else { if (that.element.val() !== that._oldText) { date = parse(element.val(), options.parseFormats, options.culture); if (!date) { that.dateView.value(date); } else { that.dateView._current = date; that.dateView.calendar._focus(date); } } element.attr(ARIA_EXPANDED, true); div.attr(ARIA_HIDDEN, false); } } })); div = that.dateView.div; that._icon(); try { element[0].setAttribute("type", "text"); } catch(e) { element[0].type = "text"; } element .addClass("k-input") .attr({ role: "textbox", "aria-haspopup": true, "aria-expanded": false, "aria-owns": that.dateView._dateViewID }); that._reset(); that._template(); disabled = element.is("[disabled]"); if (disabled) { that.enable(false); } else { that.readonly(element.is("[readonly]")); } that._old = that._update(options.value || that.element.val()); that._oldText = element.val(); kendo.notify(that); }, events: [ OPEN, CLOSE, CHANGE], options: { name: "DatePicker", value: null, footer: "", format: "", culture: "", parseFormats: [], min: new Date(1900, 0, 1), max: new Date(2099, 11, 31), start: MONTH, depth: MONTH, animation: {}, month : {}, dates: [], ARIATemplate: 'Current focused date is #=kendo.toString(data.current, "D")#' }, setOptions: function(options) { var that = this, dateView = that.dateView, dateViewOptions = dateView.options; Widget.fn.setOptions.call(that, options); normalize(that.options); dateView.options = extend(dateViewOptions, that.options, { change: dateViewOptions.change, close: dateViewOptions.close, open: dateViewOptions.open }); }, _editable: function(options) { var that = this, icon = that._dateIcon.off(ns), element = that.element.off(ns), wrapper = that._inputWrapper.off(ns), readonly = options.readonly, disable = options.disable; if (!readonly && !disable) { wrapper .addClass(DEFAULT) .removeClass(STATEDISABLED) .on(HOVEREVENTS, that._toggleHover); element.removeAttr(DISABLED) .removeAttr(READONLY) .attr(ARIA_DISABLED, false) .attr(ARIA_READONLY, false) .on("keydown" + ns, proxy(that._keydown, that)) .on("blur" + ns, proxy(that._blur, that)) .on("focus" + ns, function() { that._inputWrapper.addClass(FOCUSED); }); icon.on(CLICK, proxy(that._click, that)) .on(MOUSEDOWN, preventDefault); } else { wrapper .addClass(disable ? STATEDISABLED : DEFAULT) .removeClass(disable ? DEFAULT : STATEDISABLED); element.attr(DISABLED, disable) .attr(READONLY, readonly) .attr(ARIA_DISABLED, disable) .attr(ARIA_READONLY, readonly); } }, readonly: function(readonly) { this._editable({ readonly: readonly === undefined ? true : readonly, disable: false }); }, enable: function(enable) { this._editable({ readonly: false, disable: !(enable = enable === undefined ? true : enable) }); }, destroy: function() { var that = this; Widget.fn.destroy.call(that); that.dateView.destroy(); that.element.off(ns); that._dateIcon.off(ns); that._inputWrapper.off(ns); if (that._form) { that._form.off("reset", that._resetHandler); } }, open: function() { this.dateView.open(); }, close: function() { this.dateView.close(); }, min: function(value) { return this._option(MIN, value); }, max: function(value) { return this._option(MAX, value); }, value: function(value) { var that = this; if (value === undefined) { return that._value; } that._old = that._update(value); if (that._old === null) { that.element.val(""); } that._oldText = that.element.val(); }, _toggleHover: function(e) { $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter"); }, _blur: function() { var that = this, value = that.element.val(); that.close(); if (value !== that._oldText) { that._change(value); } that._inputWrapper.removeClass(FOCUSED); }, _click: function() { var that = this, element = that.element; that.dateView.toggle(); if (!kendo.support.touch && element[0] !== activeElement()) { element.focus(); } }, _change: function(value) { var that = this; value = that._update(value); if (+that._old != +value) { that._old = value; that._oldText = that.element.val(); that.trigger(CHANGE); // trigger the DOM change event so any subscriber gets notified that.element.trigger(CHANGE); } }, _keydown: function(e) { var that = this, dateView = that.dateView, value = that.element.val(); if (!dateView.popup.visible() && e.keyCode == keys.ENTER && value !== that._oldText) { that._change(value); } else { dateView.move(e); that._updateARIA(dateView._current); } }, _icon: function() { var that = this, element = that.element, icon; icon = element.next("span.k-select"); if (!icon[0]) { icon = $('select').insertAfter(element); } that._dateIcon = icon.attr({ "role": "button", "aria-controls": that.dateView._dateViewID }); }, _option: function(option, value) { var that = this, options = that.options; if (value === undefined) { return options[option]; } value = parse(value, options.parseFormats, options.culture); if (!value) { return; } options[option] = new DATE(+value); that.dateView[option](value); }, _update: function(value) { var that = this, options = that.options, min = options.min, max = options.max, date = parse(value, options.parseFormats, options.culture), formattedValue; if (+date === +that._value) { formattedValue = kendo.toString(date, options.format, options.culture); if (formattedValue !== value) { that.element.val(date === null ? value : formattedValue); } return date; } if (date !== null && isEqualDatePart(date, min)) { date = restrictValue(date, min, max); } else if (!isInRange(date, min, max)) { date = null; } that._value = date; that.dateView.value(date); that.element.val(date ? kendo.toString(date, options.format, options.culture) : value); that._updateARIA(date); return date; }, _wrapper: function() { var that = this, element = that.element, wrapper; wrapper = element.parents(".k-datepicker"); if (!wrapper[0]) { wrapper = element.wrap(SPAN).parent().addClass("k-picker-wrap k-state-default"); wrapper = wrapper.wrap(SPAN).parent(); } wrapper[0].style.cssText = element[0].style.cssText; element.css({ width: "100%", height: element[0].style.height }); that.wrapper = wrapper.addClass("k-widget k-datepicker k-header") .addClass(element[0].className); that._inputWrapper = $(wrapper[0].firstChild); }, _reset: function() { var that = this, element = that.element, formId = element.attr("form"), form = formId ? $("#" + formId) : element.closest("form"); if (form[0]) { that._resetHandler = function() { that.value(element[0].defaultValue); }; that._form = form.on("reset", that._resetHandler); } }, _template: function() { this._ariaTemplate = template(this.options.ARIATemplate); }, _updateARIA: function(date) { this.element.attr("aria-label", this._ariaTemplate({ current: date })); } }); ui.plugin(DatePicker); })(window.kendo.jQuery); kendo_module({ id: "autocomplete", name: "AutoComplete", category: "web", description: "The AutoComplete widget provides suggestions depending on the typed text.It also allows multiple value entries.", depends: [ "list" ], features: [ { id: "mobile-scroller", name: "Mobile scroller", description: "Support for kinetic scrolling in mobile device", depends: [ "mobile.scroller" ] } ] }); (function ($, undefined) { var kendo = window.kendo, support = kendo.support, activeElement = kendo._activeElement, placeholderSupported = support.placeholder, ui = kendo.ui, keys = kendo.keys, DataSource = kendo.data.DataSource, List = ui.List, ARIA_DISABLED = "aria-disabled", ARIA_READONLY = "aria-readonly", DEFAULT = "k-state-default", DISABLED = "disabled", READONLY = "readonly", FOCUSED = "k-state-focused", SELECTED = "k-state-selected", STATEDISABLED = "k-state-disabled", HOVER = "k-state-hover", ns = ".kendoAutoComplete", HOVEREVENTS = "mouseenter" + ns + " mouseleave" + ns, caretPosition = List.caret, selectText = List.selectText, proxy = $.proxy; function indexOfWordAtCaret(caret, text, separator) { return separator ? text.substring(0, caret).split(separator).length - 1 : 0; } function wordAtCaret(caret, text, separator) { return text.split(separator)[indexOfWordAtCaret(caret, text, separator)]; } function replaceWordAtCaret(caret, text, word, separator) { var words = text.split(separator); words.splice(indexOfWordAtCaret(caret, text, separator), 1, word); if (separator && words[words.length - 1] !== "") { words.push(""); } return words.join(separator); } function moveCaretAtEnd(element) { var length = element.value.length; selectText(element, length, length); } var AutoComplete = List.extend({ init: function (element, options) { var that = this, wrapper; that.ns = ns; options = $.isArray(options) ? { dataSource: options} : options; List.fn.init.call(that, element, options); element = that.element; options = that.options; options.placeholder = options.placeholder || element.attr("placeholder"); if (placeholderSupported) { element.attr("placeholder", options.placeholder); } that._wrapper(); that._loader(); that._dataSource(); that._ignoreCase(); element[0].type = "text"; wrapper = that.wrapper; that._popup(); element .addClass("k-input") .on("keydown" + ns, proxy(that._keydown, that)) .on("paste" + ns, proxy(that._search, that)) .on("focus" + ns, function () { that._prev = that._accessor(); that._placeholder(false); wrapper.addClass(FOCUSED); }) .on("blur" + ns, function () { that._change(); that._placeholder(); wrapper.removeClass(FOCUSED); }) .attr({ autocomplete: "off", role: "textbox", "aria-haspopup": true }); that._enable(); that._old = that._accessor(); if (element[0].id) { element.attr("aria-owns", that.ul[0].id); } that._aria(); that._placeholder(); kendo.notify(that); }, options: { name: "AutoComplete", enabled: true, suggest: false, template: "", dataTextField: "", minLength: 1, delay: 200, height: 200, filter: "startswith", ignoreCase: true, highlightFirst: false, separator: null, placeholder: "", animation: {}, value: null }, _dataSource: function() { var that = this; if (that.dataSource && that._refreshHandler) { that._unbindDataSource(); } else { that._refreshHandler = proxy(that.refresh, that); that._progressHandler = proxy(that._showBusy, that); } that.dataSource = DataSource.create(that.options.dataSource) .bind("change", that._refreshHandler) .bind("progress", that._progressHandler); }, setDataSource: function(dataSource) { this.options.dataSource = dataSource; this._dataSource(); }, events: [ "open", "close", "change", "select", "dataBinding", "dataBound" ], setOptions: function(options) { List.fn.setOptions.call(this, options); this._template(); this._accessors(); this._aria(); }, _editable: function(options) { var that = this, element = that.element, wrapper = that.wrapper.off(ns), readonly = options.readonly, disable = options.disable; if (!readonly && !disable) { wrapper .addClass(DEFAULT) .removeClass(STATEDISABLED) .on(HOVEREVENTS, that._toggleHover); element.removeAttr(DISABLED) .removeAttr(READONLY) .attr(ARIA_DISABLED, false) .attr(ARIA_READONLY, false); } else { wrapper .addClass(disable ? STATEDISABLED : DEFAULT) .removeClass(disable ? DEFAULT : STATEDISABLED); element.attr(DISABLED, disable) .attr(READONLY, readonly) .attr(ARIA_DISABLED, disable) .attr(ARIA_READONLY, readonly); } }, close: function () { var that = this, current = that._current; if (current) { current.removeClass(SELECTED); } that.current(null); that.popup.close(); }, destroy: function() { var that = this; that.element.off(ns); that.wrapper.off(ns); List.fn.destroy.call(that); }, refresh: function () { var that = this, ul = that.ul[0], popup = that.popup, options = that.options, data = that._data(), length = data.length, action; that.trigger("dataBinding"); ul.innerHTML = kendo.render(that.template, data); that._height(length); if (popup.visible()) { popup._position(); } if (length) { if (options.highlightFirst) { that.current($(ul.firstChild)); } if (options.suggest) { that.suggest($(ul.firstChild)); } } if (that._open) { that._open = false; action = length ? "open" : "close"; if (that._typing && that.element[0] !== activeElement()) { action = "close"; } popup[action](); that._typing = undefined; } if (that._touchScroller) { that._touchScroller.reset(); } that._makeUnselectable(); that._hideBusy(); that.trigger("dataBound"); }, select: function (li) { this._select(li); }, search: function (word) { var that = this, options = that.options, ignoreCase = options.ignoreCase, separator = options.separator, length; word = word || that._accessor(); that._current = null; clearTimeout(that._typing); if (separator) { word = wordAtCaret(caretPosition(that.element[0]), word, separator); } length = word.length; if (!length) { that.popup.close(); } else if (length >= that.options.minLength) { that._open = true; that._filterSource({ value: ignoreCase ? word.toLowerCase() : word, operator: options.filter, field: options.dataTextField, ignoreCase: ignoreCase }); } }, suggest: function (word) { var that = this, key = that._last, value = that._accessor(), element = that.element[0], caret = caretPosition(element), separator = that.options.separator, words = value.split(separator), wordIndex = indexOfWordAtCaret(caret, value, separator), selectionEnd = caret, idx; if (key == keys.BACKSPACE || key == keys.DELETE) { that._last = undefined; return; } word = word || ""; if (typeof word !== "string") { idx = List.inArray(word[0], that.ul[0]); if (idx > -1) { word = that._text(that._data()[idx]); } else { word = ""; } } if (caret <= 0) { caret = value.toLowerCase().indexOf(word.toLowerCase()) + 1; } idx = value.substring(0, caret).lastIndexOf(separator); idx = idx > -1 ? caret - (idx + separator.length) : caret; value = words[wordIndex].substring(0, idx); if (word) { idx = word.toLowerCase().indexOf(value.toLowerCase()); if (idx > -1) { word = word.substring(idx + value.length); selectionEnd = caret + word.length; value += word; } if (separator && words[words.length - 1] !== "") { words.push(""); } } words[wordIndex] = value; that._accessor(words.join(separator || "")); if (element === activeElement()) { selectText(element, caret, selectionEnd); } }, value: function (value) { if (value !== undefined) { this._accessor(value); this._old = value; } else { return this._accessor(); } }, _accessor: function (value) { var that = this, element = that.element[0]; if (value !== undefined) { element.value = value === null ? "" : value; that._placeholder(); } else { value = element.value; if (element.className.indexOf("k-readonly") > -1) { if (value === that.options.placeholder) { return ""; } else { return value; } } return value; } }, _accept: function (li) { var that = this; that._focus(li); moveCaretAtEnd(that.element[0]); }, _keydown: function (e) { var that = this, ul = that.ul[0], key = e.keyCode, current = that._current, visible = that.popup.visible(); that._last = key; if (key === keys.DOWN) { if (visible) { that._move(current ? current.next() : $(ul.firstChild)); } e.preventDefault(); } else if (key === keys.UP) { if (visible) { that._move(current ? current.prev() : $(ul.lastChild)); } e.preventDefault(); } else if (key === keys.ENTER || key === keys.TAB) { if (key === keys.ENTER && that.popup.visible()) { e.preventDefault(); } that._accept(current); } else if (key === keys.ESC) { if (that.popup.visible()) { e.preventDefault(); } that.close(); } else { that._search(); } }, _move: function (li) { var that = this; li = li[0] ? li : null; that.current(li); if (that.options.suggest) { that.suggest(li); } }, _hideBusy: function () { var that = this; clearTimeout(that._busy); that._loading.hide(); that.element.attr("aria-busy", false); that._busy = null; }, _showBusy: function () { var that = this; if (that._busy) { return; } that._busy = setTimeout(function () { that.element.attr("aria-busy", true); that._loading.show(); }, 100); }, _placeholder: function(show) { if (placeholderSupported) { return; } var that = this, element = that.element, placeholder = that.options.placeholder, value; if (placeholder) { value = element.val(); if (show === undefined) { show = !value; } if (!show) { if (value !== placeholder) { placeholder = value; } else { placeholder = ""; } } if (value === that._old && !show) { return; } element.toggleClass("k-readonly", show) .val(placeholder); if (!placeholder && element[0] === document.activeElement) { List.selectText(element[0], 0, 0); } } }, _search: function () { var that = this; clearTimeout(that._typing); that._typing = setTimeout(function () { if (that._prev !== that._accessor()) { that._prev = that._accessor(); that.search(); } }, that.options.delay); }, _select: function (li) { var that = this, separator = that.options.separator, data = that._data(), text, idx; li = $(li); if (li[0] && !li.hasClass(SELECTED)) { idx = List.inArray(li[0], that.ul[0]); if (idx > -1) { data = data[idx]; text = that._text(data); if (separator) { text = replaceWordAtCaret(caretPosition(that.element[0]), that._accessor(), text, separator); } that._accessor(text); that.current(li.addClass(SELECTED)); } } }, _loader: function() { this._loading = $('').insertAfter(this.element); }, _toggleHover: function(e) { $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter"); }, _wrapper: function () { var that = this, element = that.element, DOMelement = element[0], wrapper; wrapper = element.parent(); if (!wrapper.is("span.k-widget")) { wrapper = element.wrap("").parent(); } //aria wrapper.attr("tabindex", -1); wrapper.attr("role", "presentation"); //end wrapper[0].style.cssText = DOMelement.style.cssText; element.css({ width: "100%", height: DOMelement.style.height }); that._focused = that.element; that.wrapper = wrapper .addClass("k-widget k-autocomplete k-header") .addClass(DOMelement.className); } }); ui.plugin(AutoComplete); })(window.kendo.jQuery); kendo_module({ id: "dropdownlist", name: "DropDownList", category: "web", description: "The DropDownList widget displays a list of values and allows the selection of a single value from the list.", depends: [ "list" ], features: [ { id: "mobile-scroller", name: "Mobile scroller", description: "Support for kinetic scrolling in mobile device", depends: [ "mobile.scroller" ] } ] }); (function($, undefined) { var kendo = window.kendo, ui = kendo.ui, Select = ui.Select, os = kendo.support.mobileOS, ns = ".kendoDropDownList", DISABLED = "disabled", READONLY = "readonly", CHANGE = "change", FOCUSED = "k-state-focused", DEFAULT = "k-state-default", STATEDISABLED = "k-state-disabled", ARIA_DISABLED = "aria-disabled", ARIA_READONLY = "aria-readonly", SELECTED = "k-state-selected", HOVEREVENTS = "mouseenter" + ns + " mouseleave" + ns, TABINDEX = "tabindex", proxy = $.proxy; var DropDownList = Select.extend( { init: function(element, options) { var that = this, index = options && options.index, optionLabel, useOptionLabel, text; that.ns = ns; options = $.isArray(options) ? { dataSource: options } : options; Select.fn.init.call(that, element, options); that._focusHandler = function() { that.wrapper.focus(); }; options = that.options; element = that.element.on("focus" + ns, that._focusHandler); this._inputTemplate(); that._reset(); that._word = ""; that._wrapper(); that._tabindex(); that.wrapper.data(TABINDEX, that.wrapper.attr(TABINDEX)); that._aria(); that._span(); that._popup(); that._mobile(); that._dataSource(); that._ignoreCase(); that._enable(); that._oldIndex = that.selectedIndex = -1; that._cascade(); if (index !== undefined) { options.index = index; } if (options.autoBind) { that.dataSource.fetch(); } else if (that.selectedIndex === -1) { text = options.text || ""; if (!text) { optionLabel = options.optionLabel, useOptionLabel = optionLabel && options.index === 0; if (that._isSelect) { if (useOptionLabel) { text = optionLabel; } else { text = element.children(":selected").text(); } } else if (!element[0].value && useOptionLabel) { text = optionLabel; } } that._textAccessor(text); } kendo.notify(that); }, options: { name: "DropDownList", enabled: true, autoBind: true, index: 0, text: null, value: null, template: "", valueTemplate: "", delay: 500, height: 200, dataTextField: "", dataValueField: "", optionLabel: "", cascadeFrom: "", cascadeFromField: "", ignoreCase: true, animation: {} }, events: [ "open", "close", CHANGE, "select", "dataBinding", "dataBound", "cascade" ], setOptions: function(options) { Select.fn.setOptions.call(this, options); this._template(); this._inputTemplate(); this._accessors(); this._aria(); }, destroy: function() { var that = this; that.wrapper.off(ns); that.element.off(ns); that._inputWrapper.off(ns); Select.fn.destroy.call(that); }, open: function() { var that = this; if (!that.ul[0].firstChild) { that._open = true; if (!that._request) { that.dataSource.fetch(); } } else { that.popup.open(); that._scroll(that._current); } }, toggle: function(toggle) { this._toggle(toggle); }, refresh: function() { var that = this, data = that._data(), length = data.length, optionLabel = that.options.optionLabel; that.trigger("dataBinding"); if (that._current) { that.current(null); } that.ul[0].innerHTML = kendo.render(that.template, data); that._height(length); if (that.popup.visible()) { that.popup._position(); } if (that._isSelect) { if (optionLabel && length) { optionLabel = that._optionLabelText(optionLabel); optionLabel = '"; } that._options(data, optionLabel); } if (that._open) { that._open = false; that.toggle(!!length); } that._hideBusy(); that._makeUnselectable(); if (!that._fetch) { if (length) { that._selectItem(); } else if (that._textAccessor() !== optionLabel) { that.element.val(""); that._textAccessor(""); } } that._bound = !!length; that.trigger("dataBound"); }, search: function(word) { if (word) { var that = this, ignoreCase = that.options.ignoreCase; if (ignoreCase) { word = word.toLowerCase(); } that._select(function(dataItem) { var text = that._text(dataItem); if (text !== undefined) { text = (text + ""); if (ignoreCase) { text = text.toLowerCase(); } return text.indexOf(word) === 0; } }); } }, text: function (text) { var that = this; var dataItem, loweredText; var ignoreCase = that.options.ignoreCase; text = text === null ? "" : text; if (text !== undefined) { if (typeof text === "string") { loweredText = ignoreCase ? text.toLowerCase() : text; dataItem = that._select(function(data) { data = that._text(data); if (ignoreCase) { data = (data + "").toLowerCase(); } return data === loweredText; }); if (dataItem) { text = dataItem; } } that._textAccessor(text); } else { return that._textAccessor(); } }, value: function(value) { var that = this, idx, hasValue; if (value !== undefined) { if (value !== null) { value = value.toString(); } that._selectedValue = value; hasValue = value || (that.options.optionLabel && !that.element[0].disabled && value === ""); if (hasValue && that._fetchItems(value)) { return; } idx = that._index(value); that.select(idx > -1 ? idx : 0); } else { return that._accessor(); } }, _editable: function(options) { var that = this, element = that.element, disable = options.disable, readonly = options.readonly, wrapper = that.wrapper.off(ns), dropDownWrapper = that._inputWrapper.off(HOVEREVENTS), focusin = function() { dropDownWrapper.addClass(FOCUSED); that._blured = false; }, focusout = function() { if (!that._blured) { that._triggerCascade(); var isIFrame = window.self !== window.top; if (kendo.support.mobileOS.ios && isIFrame) { that._change(); } else { that._blur(); } dropDownWrapper.removeClass(FOCUSED); that._blured = true; element.blur(); } }; if (!readonly && !disable) { element.removeAttr(DISABLED).removeAttr(READONLY); dropDownWrapper .addClass(DEFAULT) .removeClass(STATEDISABLED) .on(HOVEREVENTS, that._toggleHover); wrapper .attr(TABINDEX, wrapper.data(TABINDEX)) .attr(ARIA_DISABLED, false) .attr(ARIA_READONLY, false) .on("click" + ns, function(e) { that._blured = false; e.preventDefault(); that.toggle(); }) .on("keydown" + ns, proxy(that._keydown, that)) .on("keypress" + ns, proxy(that._keypress, that)) .on("focusin" + ns, focusin) .on("focusout" + ns, focusout); } else { if (disable) { wrapper.removeAttr(TABINDEX); dropDownWrapper .addClass(STATEDISABLED) .removeClass(DEFAULT); } else { dropDownWrapper .addClass(DEFAULT) .removeClass(STATEDISABLED); wrapper .on("focusin" + ns, focusin) .on("focusout" + ns, focusout); } element.attr(DISABLED, disable) .attr(READONLY, readonly); wrapper.attr(ARIA_DISABLED, disable) .attr(ARIA_READONLY, readonly); } }, _accept: function(li) { this._focus(li); }, _optionLabelText: function() { var options = this.options, dataTextField = options.dataTextField, optionLabel = options.optionLabel; if (optionLabel && dataTextField && typeof optionLabel === "object") { return this._text(optionLabel); } return optionLabel; }, _data: function() { var that = this, options = that.options, optionLabel = options.optionLabel, textField = options.dataTextField, valueField = options.dataValueField, data = that.dataSource.view(), length = data.length, first = optionLabel, idx = 0; if (optionLabel && length) { if (typeof optionLabel === "object") { first = optionLabel; } else if (textField) { first = {}; textField = textField.split("."); valueField = valueField.split("."); assign(first, valueField, ""); assign(first, textField, optionLabel); } first = new kendo.data.ObservableArray([first]); for (; idx < length; idx++) { first.push(data[idx]); } data = first; } return data; }, _keydown: function(e) { var that = this, key = e.keyCode, keys = kendo.keys, ul = that.ul[0]; if (key === keys.LEFT) { key = keys.UP; } else if (key === keys.RIGHT) { key = keys.DOWN; } e.keyCode = key; that._move(e); if (key === keys.HOME) { e.preventDefault(); that._select(ul.firstChild); } else if (key === keys.END) { e.preventDefault(); that._select(ul.lastChild); } }, _selectNext: function(word, index) { var that = this, text, startIndex = index, data = that._data(), length = data.length, ignoreCase = that.options.ignoreCase, action = function(text, index) { text = text + ""; if (ignoreCase) { text = text.toLowerCase(); } if (text.indexOf(word) === 0) { that._select(index); that._triggerEvents(); return true; } }; for (; index < length; index++) { text = that._text(data[index]); if (text && action(text, index)) { return true; } } if (startIndex > 0) { index = 0; for (; index <= startIndex; index++) { text = that._text(data[index]); if (text && action(text, index)) { return true; } } } return false; }, _keypress: function(e) { if (e.charCode === 0) { return; } var that = this, character = String.fromCharCode(e.charCode || e.keyCode), index = that.selectedIndex, word = that._word; if (that.options.ignoreCase) { character = character.toLowerCase(); } if (character === " ") { e.preventDefault(); } if (that._last === character && word.length <= 1 && index > -1) { if (!word) { word = character; } if (that._selectNext(word, index + 1)) { return; } } that._word = word + character; that._last = character; that._search(); }, _popup: function() { Select.fn._popup.call(this); this.popup.one("open", function() { this.wrapper = kendo.wrap(this.element) .addClass("km-popup"); }); }, _search: function() { var that = this, dataSource = that.dataSource, index = that.selectedIndex, word = that._word; clearTimeout(that._typing); that._typing = setTimeout(function() { that._word = ""; }, that.options.delay); if (!that.ul[0].firstChild) { dataSource.one(CHANGE, function () { if (dataSource.data()[0]) { that._selectNext(word, index); } }).fetch(); return; } that._selectNext(word, index); that._triggerEvents(); }, _select: function(li) { var that = this, current = that._current, data = null, value, idx; li = that._get(li); if (li && li[0] && !li.hasClass(SELECTED)) { if (current) { current.removeClass(SELECTED); } idx = ui.List.inArray(li[0], that.ul[0]); if (idx > -1) { data = that._data()[idx]; value = that._value(data); that.selectedIndex = idx; that._textAccessor(data); that._accessor(value !== undefined ? value : that._text(data), idx); that._selectedValue = that._accessor(); that.current(li.addClass(SELECTED)); if (that._optionID) { that._current.attr("aria-selected", true); } } } return data; }, _triggerEvents: function() { if (!this.popup.visible()) { this._triggerCascade(); this._change(); } }, _mobile: function() { var that = this, popup = that.popup, root = popup.element.parents(".km-root").eq(0); if (root.length && os) { popup.options.animation.open.effects = (os.android || os.meego) ? "fadeIn" : (os.ios || os.wp) ? "slideIn:up" : popup.options.animation.open.effects; } }, _span: function() { var that = this, wrapper = that.wrapper, SELECTOR = "span.k-input", span; span = wrapper.find(SELECTOR); if (!span[0]) { wrapper.append(' select') .append(that.element); span = wrapper.find(SELECTOR); } that.span = span; that._inputWrapper = $(wrapper[0].firstChild); that._arrow = wrapper.find(".k-icon").mousedown(function(e) { e.preventDefault(); }); }, _wrapper: function() { var that = this, element = that.element, DOMelement = element[0], wrapper; wrapper = element.parent(); if (!wrapper.is("span.k-widget")) { wrapper = element.wrap("").parent(); wrapper[0].style.cssText = DOMelement.style.cssText; } element.hide(); that._focused = that.wrapper = wrapper .addClass("k-widget k-dropdown k-header") .addClass(DOMelement.className) .css("display", "") .attr({ unselectable: "on", role: "listbox", "aria-haspopup": true, "aria-expanded": false }); }, _clearSelection: function() { var that = this, optionLabel = that.options.optionLabel; if (that.dataSource.view()[0] && optionLabel) { that.select(0); return; } that.selectedIndex = -1; that.element.val(""); that._textAccessor(optionLabel); }, _inputTemplate: function() { var that = this, template = that.options.valueTemplate; if (!template) { template = $.proxy(kendo.template('#:this._text(data)#'), that); } else { template = kendo.template(template); } that.valueTemplate = template; }, _textAccessor: function(text) { var dataItem = this.dataItem(); var span = this.span; if (text !== undefined) { if ($.isPlainObject(text) || text instanceof kendo.data.ObservableObject) { dataItem = text; } else if (!dataItem || this._text(dataItem) !== text) { if (this.options.dataTextField) { dataItem = {}; dataItem[this.options.dataTextField] = text; dataItem[this.options.dataValueField] = this._accessor(); } else { dataItem = text; } } span.html(this.valueTemplate(dataItem)); } else { return span.text(); } } }); function assign(instance, fields, value) { var idx = 0, lastIndex = fields.length - 1, field; for (; idx < lastIndex; ++idx) { field = fields[idx]; if (!(field in instance)) { instance[field] = {}; } instance = instance[field]; } instance[fields[lastIndex]] = value; } ui.plugin(DropDownList); })(window.kendo.jQuery); kendo_module({ id: "combobox", name: "ComboBox", category: "web", description: "The ComboBox widget allows the selection from pre-defined values or entering a new value.", depends: [ "list" ], features: [ { id: "mobile-scroller", name: "Mobile scroller", description: "Support for kinetic scrolling in mobile device", depends: [ "mobile.scroller" ] } ] }); (function($, undefined) { var kendo = window.kendo, ui = kendo.ui, List = ui.List, Select = ui.Select, support = kendo.support, placeholderSupported = support.placeholder, activeElement = kendo._activeElement, keys = kendo.keys, ns = ".kendoComboBox", CLICK = "click" + ns, MOUSEDOWN = "mousedown" + ns, DISABLED = "disabled", READONLY = "readonly", CHANGE = "change", DEFAULT = "k-state-default", FOCUSED = "k-state-focused", STATEDISABLED = "k-state-disabled", ARIA_DISABLED = "aria-disabled", ARIA_READONLY = "aria-readonly", STATE_SELECTED = "k-state-selected", STATE_FILTER = "filter", STATE_ACCEPT = "accept", STATE_REBIND = "rebind", HOVEREVENTS = "mouseenter" + ns + " mouseleave" + ns, NULL = null, proxy = $.proxy; var ComboBox = Select.extend({ init: function(element, options) { var that = this, text; that.ns = ns; options = $.isArray(options) ? { dataSource: options } : options; Select.fn.init.call(that, element, options); that._focusHandler = function() { that.input.focus(); }; options = that.options; element = that.element.on("focus" + ns, that._focusHandler); options.placeholder = options.placeholder || element.attr("placeholder"); that._reset(); that._wrapper(); that._input(); that._tabindex(that.input); that._popup(); that._dataSource(); that._ignoreCase(); that._enable(); that._cascade(); that._aria(); that._oldIndex = that.selectedIndex = -1; if (options.autoBind) { that._filterSource(); } else { text = options.text; if (!text && that._isSelect) { text = element.children(":selected").text(); } if (text) { that.input.val(text); that._prev = text; } } if (!text) { that._placeholder(); } kendo.notify(that); }, options: { name: "ComboBox", enabled: true, index: -1, text: null, value: null, autoBind: true, delay: 200, dataTextField: "", dataValueField: "", minLength: 0, height: 200, highlightFirst: true, template: "", filter: "none", placeholder: "", suggest: false, cascadeFrom: "", cascadeFromField: "", ignoreCase: true, animation: {} }, events:[ "open", "close", CHANGE, "select", "dataBinding", "dataBound", "cascade" ], setOptions: function(options) { Select.fn.setOptions.call(this, options); this._template(); this._accessors(); this._aria(); }, current: function(li) { var that = this, current = that._current; if (li === undefined) { return current; } if (current) { current.removeClass(STATE_SELECTED); } Select.fn.current.call(that, li); }, destroy: function() { var that = this; that.input.off(ns); that.element.off(ns); that._inputWrapper.off(ns); Select.fn.destroy.call(that); }, _editable: function(options) { var that = this, disable = options.disable, readonly = options.readonly, wrapper = that._inputWrapper.off(ns), input = that.element.add(that.input.off(ns)), arrow = that._arrow.parent().off(CLICK + " " + MOUSEDOWN); if (!readonly && !disable) { wrapper .addClass(DEFAULT) .removeClass(STATEDISABLED) .on(HOVEREVENTS, that._toggleHover); input.removeAttr(DISABLED) .removeAttr(READONLY) .attr(ARIA_DISABLED, false) .attr(ARIA_READONLY, false); arrow.on(CLICK, function() { that.toggle(); }) .on(MOUSEDOWN, function(e) { e.preventDefault(); }); that.input .on("keydown" + ns, proxy(that._keydown, that)) .on("focus" + ns, function() { wrapper.addClass(FOCUSED); that._placeholder(false); }) .on("blur" + ns, function() { wrapper.removeClass(FOCUSED); clearTimeout(that._typing); if (that.options.text !== that.input.val()) { that.text(that.text()); } that._placeholder(); that._blur(); that.element.blur(); }); } else { wrapper .addClass(disable ? STATEDISABLED : DEFAULT) .removeClass(disable ? DEFAULT : STATEDISABLED); input.attr(DISABLED, disable) .attr(READONLY, readonly) .attr(ARIA_DISABLED, disable) .attr(ARIA_READONLY, readonly); } }, open: function() { var that = this, serverFiltering = that.dataSource.options.serverFiltering; if (that.popup.visible()) { return; } if (!that.ul[0].firstChild || (that._state === STATE_ACCEPT && !serverFiltering)) { that._open = true; that._state = STATE_REBIND; that._filterSource(); } else { that.popup.open(); that._scroll(that._current); } }, refresh: function() { var that = this, ul = that.ul[0], options = that.options, state = that._state, data = that._data(), length = data.length, hasChild, value, open, custom; that.trigger("dataBinding"); ul.innerHTML = kendo.render(that.template, data); that._height(length); if (that.popup.visible()) { that.popup._position(); } if (that._isSelect) { hasChild = that.element[0].firstChild; if (state === STATE_REBIND) { that._state = ""; value = that.value(); } custom = that._option; that._option = undefined; that._options(data); if (custom && custom[0].selected) { that._custom(custom.val()); } else if (!that._bound && !hasChild) { that._custom(""); } } if (length) { if (options.highlightFirst) { that.current($(ul.firstChild)); } if (options.suggest && that.input.val() && that._request !== undefined /*first refresh ever*/) { that.suggest($(ul.firstChild)); } } if (state !== STATE_FILTER && !that._fetch) { that._selectItem(); } if (that._open) { that._open = false; open = !!length; if (that._typing && that.input[0] !== activeElement()) { open = false; } that.toggle(open); that._typing = undefined; } if (that._touchScroller) { that._touchScroller.reset(); } that._makeUnselectable(); that._hideBusy(); that._bound = true; that.trigger("dataBound"); }, search: function(word) { word = typeof word === "string" ? word : this.text(); var that = this, length = word.length, options = that.options, ignoreCase = options.ignoreCase, filter = options.filter, field = options.dataTextField; clearTimeout(that._typing); if (length >= options.minLength) { that._state = STATE_FILTER; if (filter === "none") { that._filter(word); } else { that._open = true; that._filterSource({ value: ignoreCase ? word.toLowerCase() : word, field: field, operator: filter, ignoreCase: ignoreCase }); } } }, suggest: function(word) { var that = this, element = that.input[0], value = that.text(), caret = List.caret(element), key = that._last, idx; if (key == keys.BACKSPACE || key == keys.DELETE) { that._last = undefined; return; } word = word || ""; if (typeof word !== "string") { idx = List.inArray(word[0], that.ul[0]); if (idx > -1) { word = that._text(that.dataSource.view()[idx]); } else { word = ""; } } if (caret <= 0) { caret = value.toLowerCase().indexOf(word.toLowerCase()) + 1; } if (word) { idx = word.toLowerCase().indexOf(value.toLowerCase()); if (idx > -1) { value += word.substring(idx + value.length); } } else { value = value.substring(0, caret); } if (value.length !== caret || !word) { element.value = value; if (element === activeElement()) { List.selectText(element, caret, value.length); } } }, text: function (text) { text = text === null ? "" : text; var that = this, input = that.input[0], ignoreCase = that.options.ignoreCase, loweredText = text, dataItem; if (text !== undefined) { dataItem = that.dataItem(); if (dataItem && that._text(dataItem) === text && that._value(dataItem).toString() === that._old) { that._triggerCascade(); return; } if (ignoreCase) { loweredText = loweredText.toLowerCase(); } that._select(function(data) { data = that._text(data); if (ignoreCase) { data = (data + "").toLowerCase(); } return data === loweredText; }); if (that.selectedIndex < 0) { that._custom(text); input.value = text; } that._prev = input.value; that._triggerCascade(); } else { return input.value; } }, toggle: function(toggle) { var that = this; that._toggle(toggle); }, value: function(value) { var that = this, options = that.options, idx; if (value !== undefined) { if (value !== null) { value = value.toString(); } that._selectedValue = value; if (!that._open && value && that._fetchItems(value)) { return; } idx = that._index(value); if (idx > -1) { that.select(idx); } else { that.current(NULL); that._custom(value); if (options.value !== value || options.text !== that.input.val()) { that.text(value); that._placeholder(); } } that._old = that._accessor(); that._oldIndex = that.selectedIndex; } else { return that._accessor(); } }, _accept: function(li) { var that = this; if (li) { that._focus(li); } else { that.text(that.text()); that._change(); } }, _custom: function(value) { var that = this, element = that.element, custom = that._option; if (that._state === STATE_FILTER) { that._state = STATE_ACCEPT; } if (that._isSelect) { if (!custom) { custom = that._option = $("