<!DOCTYPE html>
<html>

  <head>
    <link href="http://cdn.kendostatic.com/2013.1.319/styles/kendo.common.min.css" rel="stylesheet">
    <link href="http://cdn.kendostatic.com/2013.1.319/styles/kendo.bootstrap.min.css" rel="stylesheet">  
    <link rel="stylesheet" href="style.css" />
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="kendo.all.js"></script>
    <script src="http://cdn.kendostatic.com/2013.1.319/js/cultures/kendo.culture.fr-BE.min.js"></script>
    <script src="http://code.angularjs.org/1.1.5/angular.js"></script>
    <script src="angular-kendo.js"></script>
    <script src="script.js"></script>
  </head>
  <body ng-app="app" ng-controller="myCtrl">
    <h1>AngularJS + kendo</h1>
    <form name="testForm">
        <h2>Date picker</h2>
      <div>
        <input name="birthDate" id="birthDate" data-ng-model="model.birthDate"           
        type="text" required kendo-date-picker  k-format="'dd/MM/yyyy'" k-rebind="model.minDate"
        k-parse-formats="'yyyy-MM-dd'" k-min="model.minDate" k-max="model.maxDate" style="width:100px" />
        <span data-ng-show="testForm.birthDate.$error.required" class="help-inline">*</span>
      </div>
          <h2>Numeric box</h2>
      <div>
      <p>
        <input type="number" step="1" ng-model="model.min" />
        <br/>
        <input type="number" step="1" ng-model="model.max" />
        <br/>
        <input type="number" step="1" max="{{model.max}}" min="{{model.min}}" ng-model="val" />
        </p>
        <input name="val" id="val" data-ng-model="model.val" kendo-numeric-text-box 
        k-min="model.min" k-max="model.max"   />
        <!-- no readonly by properties, only by method -->
        <span data-ng-show="testForm.val.$error.required" class="help-inline">*</span>
      </div>
      <div>selected date:{{model.birthDate}}</div>
      <div>selected val:{{model.val}}</div>
      <button data-ng-click="reset()">reset</button>
    </form>
  </body>
</html>
// Code goes here

var app = angular.module('app', ['kendo.directives']); 

app.controller("myCtrl",['$scope', function ($scope) {
  
  $scope.model={};
  
  $scope.model.minDate=new Date("2000-01-01T00:00:00.000Z");
  $scope.model.maxDate=new Date("2099-12-31T00:00:00.000Z");
  $scope.model.birthDate=new Date("2013-07-18T00:00:00.000Z");
  
  $scope.model.max=10;
  $scope.model.min=1;
  $scope.model.val=5;
  
  $scope.reset=function(){
    $scope.model.minDate=new Date("2010-01-01T00:00:00.000Z");
    $scope.model.birthDate=new Date("2013-07-18T00:00:00.000Z");
    $scope.model.max=10;
    $scope.model.min=1;
    $scope.model.val=5;
  }

}]);
/* Styles go here */

'use strict';

// declare all the module
angular.module('kendo.directives', []);
angular.module('kendo.directives', [], ['$provide', function ($provide) {

    // Iterate over the kendo.ui and kendo.dataviz.ui namespace objects to get the Kendo UI widgets adding
    // them to the 'widgets' array.
    var widgets = [];

    angular.forEach([kendo.ui, kendo.dataviz && kendo.dataviz.ui], function (namespace) {
        angular.forEach(namespace, function (value, key) {
            // add all widgets
            if (key.match(/^[A-Z]/) && key !== 'Widget') {
                widgets.push("kendo" + key);
            }
        });
    });

    $provide.value('kendoWidgets', widgets);

}]);

angular.module('kendo.directives').factory('widgetFactory', ['$parse', '$log', 'kendoDecorator', function ($parse, $log, kendoDecorator) {

    // k-* attributes that should not be $parsed or $evaluated by gatherOptions
    var ignoredAttributes = {
        kDataSource: true,
        kOptions: true,
        kRebind: true
    };

    var mixin = function (kendoWidget, scope, options, attrName, attrValue) {

        // regexp for matching regular options attributes and event handler attributes
        // The first matching group will be defined only when the attribute starts by k-on- for event handlers.
        // The second matching group will contain the option name.
        var matchExp = /k(On)?([A-Z].*)/;

        // ignore attributes that do not map to widget configuration options
        if (ignoredAttributes[attrName]) {
            return;
        }

        var match = attrName.match(matchExp), optionName, fn;

        if (match) {
            // Lowercase the first letter to match the option name kendo expects.
            optionName = match[2].charAt(0).toLowerCase() + match[2].slice(1);

            if (match[1]) {
                // This is an event handler attribute (k-on-*)
                // Parse the expression so that it get evaluated later.
                fn = $parse(attrValue);
                // Add a kendo event listener to the options.
                options[optionName] = function (e) {
                    // Make sure this gets invoked in the angularjs lifecycle.
                    if (scope.$root.$$phase === '$apply' || scope.$root.$$phase === '$digest') {
                        fn({ kendoEvent: e });
                    } else {
                        scope.$apply(function () {
                            // Invoke the parsed expression with a kendoEvent local that the expression can use.
                            fn(scope, { kendoEvent: e });
                        });
                    }
                };
            } else {
                // Evaluate the angular expression and put its result in the widget's options object.
                // Here we make a copy because the kendo widgets make changes to the objects passed in the options
                // and kendo-refresh would not be able to refresh with the initial values otherwise.
                options[optionName] = angular.copy(scope.$eval(attrValue));
                if (options[optionName] === undefined && attrValue.match(/^\w*$/)) {
                    // if the user put a single word as the attribute value and the expression evaluates to undefined,
                    // she may have wanted to use a string literal.
                    $log.warn(kendoWidget + '\'s ' + attrName + ' attribute resolved to undefined. Maybe you meant to use a string literal like: \'' + attrValue + '\'?');
                }
            }
        }
    };

    // Gather the options from defaults and from attributes
    var gatherOptions = function (scope, element, attrs, kendoWidget) {
        // TODO: add kendoDefaults value service and use it to get a base options object?
        // var options = kendoDefaults[kendoWidget];

        // make a deep clone of the options object provided by the k-options attribute, if any.
        var options = angular.element.extend(true, {}, scope.$eval(attrs.kOptions));

        // Mixin the data from the element's k-* attributes in the options
        angular.forEach(attrs, function (value, name) {
            mixin(kendoWidget, scope, options, name, value);
        });

        // The kDataSource directive sets the $kendoDataSource data on the element it is put on.
        // A datasource set in this way takes precedence over the one that could have been provided in options object passed
        // in the directive's attribute and that is used as the initial options object.
        options.dataSource = element.inheritedData('$kendoDataSource') || options.dataSource;

        // decorate options, if any decorators have been registered on this element or any global ones are registered for
        // the kendo widget
        kendoDecorator.decorateOptions(element, kendoWidget, options);

        return options;

    };

    // Create the kendo widget with gathered options
    var create = function (scope, element, attrs, kendoWidget) {

        // Create the options object
        var options = gatherOptions(scope, element, attrs, kendoWidget);

        // Bind the kendo widget to the element and return a reference to the widget.
        return element[kendoWidget](options).data(kendoWidget);
    };

    return {
        create: create
    };

}]);

angular.module('kendo.directives').factory('directiveFactory', ['widgetFactory', '$timeout', '$parse',
  function (widgetFactory, $timeout, $parse) {

      function exposeWidget(widget, scope, attrs, kendoWidget) {
          if (attrs[kendoWidget]) {
              // expose the widget object
              var set = $parse(attrs[kendoWidget]).assign;
              if (set) {
                  // set the value of the expression to the kendo widget object to expose its api
                  set(scope, widget);
              } else {
                  throw new Error(kendoWidget + ' attribute used but expression in it is not assignable: ' + attrs[kendoWidget]);
              }
          }
      }

      var create = function (kendoWidget) {

          return {
              // Parse the directive for attributes and classes
              restrict: 'ACE',
              transclude: true,
              require: '?ngModel',
              scope: false,
              controller: ['$scope', '$attrs', '$element', '$transclude', function ($scope, $attrs, $element, $transclude) {

                  // Make the element's contents available to the kendo widget to allow creating some widgets from existing elements.
                  $transclude(function (clone) {
                      $element.append(clone);
                  });

              }],

              link: function (scope, element, attrs, ngModel) {

                  var widget;

                  // Bind kendo widget to element only once interpolation on attributes is done.
                  // Using a $timeout with no delay simply makes sure the function will be executed next in the event queue
                  // after the current $digest cycle is finished. Other directives on the same element (select for example)
                  // will have been processed, and interpolation will have happened on the attributes.
                  $timeout(function () {

                      // create the kendo widget and bind it to the element.
                      widget = widgetFactory.create(scope, element, attrs, kendoWidget);

                      exposeWidget(widget, scope, attrs, kendoWidget);

                      // if k-rebind attribute is provided, rebind the kendo widget when
                      // the watched value changes
                      if (attrs.kRebind) {
                          // watch for changes on the expression passed in the k-rebind attribute
                          scope.$watch(attrs.kRebind, function (newValue, oldValue) {
                              if (newValue !== oldValue) {
                                  // create the kendo widget and bind it to the element.
                                  widget = widgetFactory.create(scope, element, attrs, kendoWidget);
                                  exposeWidget(widget, scope, attrs, kendoWidget);
                              }
                          }, true); // watch for object equality. Use native or simple values.
                      }

                      // Cleanup after ourselves
                      scope.$on('$destroy', function () {
                          widget.destroy();
                      });

                      // if ngModel is on the element, we setup bi-directional data binding
                      if (ngModel) {
                          if (!widget.value) {
                              throw new Error('ng-model used but ' + kendoWidget + ' does not define a value accessor');
                          }

                          // Angular will invoke $render when the view needs to be updated with the view value.
                          ngModel.$render = function () {
                              // Update the widget with the view value.
                              widget.value(ngModel.$viewValue || null);
                          };

                          // if the model value is undefined, then we set the widget value to match ( == null/undefined )
                          if (widget.value !== undefined) {
                              widget.value(ngModel.$viewValue || null);
                          }

                          // In order to be able to update the angular scope objects, we need to know when the change event is fired for a Kendo UI Widget.
                          widget.bind("change", function (e) {
                              if (scope.$root.$$phase === '$apply' || scope.$root.$$phase === '$digest') {
                                  ngModel.$setViewValue(widget.value());
                              } else {
                                  scope.$apply(function () {
                                      ngModel.$setViewValue(widget.value());
                                  });
                              }
                          });
                      }
                  });
              }
          };
      };

      return {
          create: create
      };
  }
]);

(function (angular) {

    var widgets = angular.injector(['kendo.directives']).get('kendoWidgets');

    // loop through all the widgets and create a directive
    angular.forEach(widgets, function (widget) {
        angular.module('kendo.directives').directive(widget, ['directiveFactory',
          function (directiveFactory) {
              return directiveFactory.create(widget);
          }
        ]);
    });

}(angular));


// ## The kendoSource directive allows setting the Kendo UI DataSource of a widget directly from the HTML.
angular.module('kendo.directives').directive('kDataSource', [function () {

    // Transforms the object into a Kendo UI DataSource.
    var toDataSource = function (ds) {
        // TODO: if ds is a $resource, wrap it in a kendo dataSource using an injected service
        return kendo.data.DataSource.create(ds);
    };

    var widgetInstance = function ($element) {
        return kendo.widgetInstance($element, kendo.ui) || kendo.widgetInstance($element, kendo.dataviz.ui);
    };

    return {
        // This is an attribute directive
        restrict: 'A',
        controller: ['$scope', '$attrs', '$element', function ($scope, $attrs, $element) {

            // can we intercept the data source
            var ds = $scope.$eval($attrs.kDataSource);

            // Set $kendoDataSource in the element's data. 3rd parties can define their own dataSource creation
            // directive and provide this data on the element.
            $element.data('$kendoDataSource', toDataSource(ds));

            // if this data source is set to an array, we need to add a watch to keep the widget in sync
            if ($.isArray(ds)) {
                // add a watch to the array (value comparison: true)
                $scope.$watch($attrs.kDataSource, function (oldVal, newVal) {
                    // if the array values differ
                    if (oldVal !== newVal) {
                        // get a reference to the widget
                        var widget = widgetInstance($element);
                        // if the widget exists and has a dataSource
                        if (widget && widget.dataSource) {
                            // read the data again which updates the widget
                            widget.dataSource.read();
                        }
                    }
                }, true);
            }
        }]
    };

}]);

angular.module('kendo.directives').directive('kendoGrid', ['$compile', 'kendoDecorator', '$parse', function ($compile, kendoDecorator, $parse) {

    function dataBoundHandler(scope, element, rowDataVar) {
        var grid = element.data('kendoGrid');
        var rows = grid.tbody.children('tr');

        // lnu:create scope toolbar if defined //
        var toolbar = element.find(".k-grid-toolbar");
        if (toolbar.children().length) {
            //hack waiting for proper toolbar created event
            toolbar.children().andSelf().unbind();
            var toolbarScope = scope.$new();
            $compile(toolbar)(toolbarScope);
        }
        // lnu //

        // Here we mimic ng-repeat in that we create a scope for each row that we can then destroy in dataBinding event.
        // Creating a scope for each row ensures you don't leak scopes when the
        // kendo widget regenerates the dom on pagination for example.
        rows.each(function (index, row) {
            var rowScope = scope.$new();
            // provide index of the row using the same $index var as ngRepeat
            rowScope.$index = index;
            // provide the data object for that row in the scope
            rowScope[rowDataVar] = grid.dataItem(row);

            // compile the row. You can now use angular templates in that row.
            $compile(row)(rowScope);
        });
    }

    function dataBindingHandler(element) {

        // lnu:create scope toolbar if defined //
        var toolbar = element.find(".k-grid-toolbar.ng-scope");
        if (toolbar.length === 1) {
            var rowScope = angular.element(toolbar).scope();
            // destroy the scope
            rowScope.$destroy();
        }
        // lnu //

        // find all the rows that we compiled in dataBound handler
        var rows = element.data('kendoGrid').tbody.children('tr.ng-scope');

        // here we need to destroy the scopes that we created in dataBound handler to make sure no scopes are leaked.
        rows.each(function (index, rowElement) {
            var rowScope = angular.element(rowElement).scope();
            // destroy the scope
            rowScope.$destroy();
        });
    }

    function createCompileRowsDecorator(scope, rowDataVar) {
        return function (element, options) {
            // keep a reference on the original event callbacks
            var origDataBinding = options.dataBinding;
            var origDataBound = options.dataBound;

            // The kendoGrid invokes this handler after it has created row elements for the data.
            options.dataBound = function () {
                dataBoundHandler(scope, element, rowDataVar);

                // invoke the original dataBound handler, if any
                if (angular.isFunction(origDataBound)) {
                    origDataBound();
                }
            };

            // The kendoGrid invokes this handler before it creates new rows in the dom
            options.dataBinding = function () {
                dataBindingHandler(element);
                // invoke the original dataBinding handler, if any
                if (angular.isFunction(origDataBinding)) {
                    origDataBinding();
                }
            };

        };
    }

    function createChangeDecorator(scope, changeExpFn) {
        return function (element, options) {
            options.change = function (e) {
                var cell, multiple, locals = { kendoEvent: e }, elems, items, columns, colIdx;
                if (angular.isString(options.selectable)) {
                    cell = options.selectable.indexOf('cell') !== -1;
                    multiple = options.selectable.indexOf('multiple') !== -1;
                }

                elems = locals.selected = this.select();
                items = locals.data = [];
                columns = locals.columns = [];

                for (var i = 0; i < elems.length; i++) {
                    var dataItem = this.dataItem(cell ? elems[i].parentNode : elems[i]);
                    if (cell) {
                        if (angular.element.inArray(dataItem, items) < 0) {
                            items.push(dataItem);
                        }
                        colIdx = angular.element(elems[i]).index();
                        if (angular.element.inArray(colIdx, columns) < 0) {
                            columns.push(colIdx);
                        }
                    } else {
                        items.push(dataItem);
                    }
                }

                if (!multiple) {
                    locals.data = items[0];
                    locals.selected = elems[0];
                }

                // Make sure this gets invoked in the angularjs lifecycle.
                scope.$apply(function () {
                    // Invoke the parsed expression with a kendoEvent local that the expression can use.
                    changeExpFn(scope, locals);
                });
            };
        };
    }

    return {
        restrict: 'ACE',
        link: function (scope, element, attrs) {
            kendoDecorator.addOptionsDecorator(element, createCompileRowsDecorator(scope, 'dataItem'));

            // if k-on-change was defined, expose the selected rows/cells and not just the kendo event
            if (attrs.kOnChange) {
                kendoDecorator.addOptionsDecorator(element, createChangeDecorator(scope, $parse(attrs.kOnChange)));
            }

        }
    };

}]);

angular.module('kendo.directives').provider('kendoDecorator', [function () {
    var provider = this, DECORATORS = '$kendoOptionsDecorators';

    var globalOptionsDecorators = {};

    // add an options decorator to be applied on all specified widget, not specific instances
    provider.addGlobalOptionsDecorator = function (widgetName, decorator) {
        if (angular.isString(widgetName) && angular.isFunction(decorator)) {
            globalOptionsDecorators[widgetName] = globalOptionsDecorators[widgetName] || [];
            globalOptionsDecorators[widgetName].push(decorator);
            return function () {
                globalOptionsDecorators[widgetName].splice(globalOptionsDecorators[widgetName].indexOf(decorator), 1);
            };
        }
        throw new Error('Illegal Arguments');
    };

    // get the global list of options decorators for a specified widget. Useful for ordering
    provider.getGlobalOptionsDecorator = function (widgetName) {
        return globalOptionsDecorators[widgetName] || [];
    };

    provider.$get = [function () {

        // returns the decorators associated to the specified element
        function getOptionsDecorators(element) {
            var decorators = element.data(DECORATORS);
            if (!angular.isArray(decorators)) {
                decorators = [];
                element.data(DECORATORS, decorators);
            }
            return decorators;
        }

        function invokeDecorators(element, decorators, opts) {
            for (var i = 0; i < decorators.length; i++) {
                decorators[i](element, opts);
            }
        }

        // invokes the provided element's decorators and global operators on the provided options object.
        function decorateOptions(element, widgetName, opts) {
            var i, decorators = provider.getGlobalOptionsDecorator(widgetName);
            invokeDecorators(element, decorators, opts);

            // get decorators for element
            decorators = element.data(DECORATORS) || [];
            invokeDecorators(element, decorators, opts);
        }

        function addOptionsDecorator(element, decorator) {
            if (angular.isFunction(decorator)) {
                var decorators = getOptionsDecorators(element);
                decorators.push(decorator);
                return function () {
                    decorators.splice(decorators.indexOf(decorator), 1);
                };
            }
        }

        return {
            getOptionsDecorator: getOptionsDecorators,
            addOptionsDecorator: addOptionsDecorator,
            decorateOptions: decorateOptions
        };
    }];

}]);
/*
* Kendo UI Complete v2013.1.319 (http://kendoui.com)
* Copyright 2013 Telerik AD. All rights reserved.
*
* Kendo UI Complete commercial licenses may be obtained at
* https://www.kendoui.com/purchase/license-agreement/kendo-ui-complete-commercial.aspx
* If you do not own a commercial license, this file shall be governed by the trial license terms.
*/
(function($, evil, undefined) {
    var kendo = window.kendo = window.kendo || {}, extend = $.extend, each = $.each, proxy = $.proxy, isArray = $.isArray, noop = $.noop, isFunction = $.isFunction, math = Math, Template, JSON = window.JSON || {}, support = {}, percentRegExp = /%/, formatRegExp = /\{(\d+)(:[^\}]+)?\}/g, boxShadowRegExp = /(\d+?)px\s*(\d+?)px\s*(\d+?)px\s*(\d+?)?/i, FUNCTION = "function", STRING = "string", NUMBER = "number", OBJECT = "object", NULL = "null", BOOLEAN = "boolean", UNDEFINED = "undefined", getterCache = {}, setterCache = {}, slice = [].slice, globalize = window.Globalize;
    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;
    };
    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, length;
            if (eventName === undefined) {
                that._events = {};
            } else if (events) {
                if (handler) {
                    for (idx = 0, length = events.length; idx < length; 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;", 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 {
                return new Function(argumentName, functionBody);
            } 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",
            "\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";
        //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();
                }
                // multiply if format is percent
                if (isPercent) {
                    number *= 100;
                }
                number = number.toFixed(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;
            }
            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];
            }
            if (format.indexOf("'") > -1 || format.indexOf('"') > -1) {
                format = format.replace(literalRegExp, function(match) {
                    literals.push(match);
                    return PLACEHOLDER;
                });
            }
            percentIndex = format.indexOf("%");
            currencyIndex = format.indexOf("$");
            isPercent = percentIndex != -1;
            isCurrency = currencyIndex != -1;
            //multiply number if the format has percent
            if (isPercent) {
                if (format[percentIndex - 1] !== "\\") {
                    number *= 100;
                } else {
                    format = format.split("\\").join("");
                }
            }
            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) {
                zeroIndex = format.lastIndexOf(ZERO) - decimalIndex;
                sharpIndex = format.lastIndexOf(SHARP) - decimalIndex;
                fraction = number.toString().split(POINT)[1] || EMPTY;
                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 = number.toFixed(idx);
                }
            } else {
                number = number.toFixed(0);
            }
            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;
                }
                if (literals[0]) {
                    length = literals.length;
                    for (idx = 0; idx < length; idx++) {
                        number = number.replace(PLACEHOLDER, literals[idx]);
                    }
                }
            }
            return number;
        }
        var toString = function(value, fmt, culture) {
            if (fmt) {
                if (value instanceof 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.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}/
        };
        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 adjustDate(date, hours) {
            if (!hours && date.getHours() === 23) {
                date.setHours(date.getHours() + 2);
            }
        }
        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) {
                var i = 0, length = names.length, name, nameLength;
                for (;i < length; i++) {
                    name = names[i];
                    nameLength = name.length;
                    if (value.substr(valueIdx, nameLength) == 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;
            if (!format) {
                format = "d";
            }
            //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");
                        day = count < 3 ? getNumber(2) : getIndexByName(calendar.days[count == 3 ? "namesAbbr" : "names"]);
                        if (day === null || outOfRange(day, 1, 31)) {
                            return null;
                        }
                    } else if (ch === "M") {
                        count = lookAhead("M");
                        month = count < 3 ? getNumber(2) : getIndexByName(calendar.months[count == 3 ? "namesAbbr" : "names"]);
                        if (month === null || outOfRange(month, 1, 12)) {
                            return null;
                        }
                        month -= 1;
                    } 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;
                    }
                }
            }
            if (year === null) {
                year = defaultYear;
            }
            if (pmHour && hours < 12) {
                hours += 12;
            }
            if (day === null) {
                day = 1;
            }
            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);
                adjustDate(value, hours);
            }
            if (year < 100) {
                value.setFullYear(year);
            }
            if (value.getDate() !== day && UTC === undefined) {
                return null;
            }
            return value;
        }
        kendo._adjustDate = adjustDate;
        kendo.parseDate = function(value, formats, culture) {
            if (value instanceof 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]];
                }
                formats[idx] = "ddd MMM dd yyyy HH:mm:ss";
                formats[++idx] = "yyyy-MM-ddTHH:mm:ss.fffffffzzz";
                formats[++idx] = "yyyy-MM-ddTHH:mm:ss.fffzzz";
                formats[++idx] = "yyyy-MM-ddTHH:mm:sszzz";
                formats[++idx] = "yyyy-MM-ddTHH:mmzzz";
                formats[++idx] = "yyyy-MM-ddTHH:mmzz";
                formats[++idx] = "yyyy-MM-ddTHH:mm:ss";
                formats[++idx] = "yyyy-MM-ddTHH:mm";
                formats[++idx] = "yyyy-MM-dd";
                idx = 0;
            }
            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("-") > -1, parts, isPercent;
            //handle exponential number
            if (exponentRegExp.test(value)) {
                value = parseFloat(value);
                if (isNaN(value)) {
                    value = null;
                }
                return value;
            }
            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 (value instanceof Date) {
                    return value;
                }
                return globalize.parseDate(value, format, culture);
            };
            kendo.parseFloat = function(value, culture) {
                if (typeof value === NUMBER) {
                    return value;
                }
                if (value === undefined) {
                    return null;
                }
                return globalize.parseFloat(value, culture);
            };
        }
    })();
    function wrap(element) {
        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) {
                width = element.outerWidth();
            }
            if (!percentHeight) {
                height = element.outerHeight();
            }
            element.wrap($("<div/>").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()
                });
            }
        }
        if (browser.msie && math.floor(browser.version) <= 7) {
            element.css({
                zoom: 1
            });
        }
        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, 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) {
                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 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[value.replace(/\-(\w)/g, function(strMatch, g1) {
                        return g1.toUpperCase();
                    })];
                });
            }
        }
        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 = "&nbsp;";
            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 = "<tr><td></td></tr>";
            support.tbodyInnerHtml = true;
        } catch (e) {
            support.tbodyInnerHtml = false;
        }
        support.touch = "ontouchstart" in window;
        support.pointers = navigator.msPointerEnabled;
        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;
        support.screenWidth = window.outerWidth || window.screen ? window.screen.availWidth : window.innerWidth;
        support.screenHeight = window.outerHeight || window.screen ? window.screen.availHeight : window.innerHeight;
        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/.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.kineticScrollNeeded = mobileOS && (support.touch || 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);
        function detectBrowser(ua) {
            var browser = false, match = [], browserRxs = {
                webkit: /(chrome)[ \/]([\w.]+)/i,
                safari: /(webkit)[ \/]([\w.]+)/i,
                opera: /(opera)(?:.*version|)[ \/]([\w.]+)/i,
                msie: /(msie) ([\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 = detectBrowser(navigator.userAgent);
        (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.zoomLevel = function() {
            return support.touch ? document.documentElement.clientWidth / window.innerWidth : support.pointers ? window.outerWidth / window.innerWidth : 1;
        };
        support.eventCapture = document.documentElement.addEventListener;
        support.placeholder = "placeholder" in document.createElement("input");
        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;
        };
    })();
    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 isNodeEmpty(element) {
        return $.trim($(element).contents().filter(function() {
            return this.nodeType != 8;
        }).html()) === "";
    }
    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
                };
            }
        }
        if (kendo.support.pointers && !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.fx.Element(element);
    }
    $.extend(fx, {
        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);
            }
            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);
            }
            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
        });
    }
    function animate(element, options, duration, reverse, complete) {
        var idx = 0, length = element.length, instance;
        for (;idx < length; idx++) {
            instance = $(element[idx]);
            instance.queue(function() {
                fx.promise(instance, prepareAnimationOptions(options, duration, reverse, complete));
            });
        }
        return element;
    }
    function animateTo(element, destination, options, duration, reverse, complete) {
        return fx.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, gtRegExp = />/g;
    function htmlEncode(value) {
        return ("" + value).replace(ampRegExp, "&amp;").replace(ltRegExp, "&lt;").replace(gtRegExp, "&gt;");
    }
    var eventTarget = function(e) {
        return e.target;
    };
    if (support.touch) {
        var mobileChrome = support.mobileOS.browser == "chrome" && !support.mobileOS.ios;
        eventTarget = function(e) {
            var touches = "originalEvent" in e ? e.originalEvent.changedTouches : "changedTouches" in e ? e.changedTouches : null, property = mobileChrome ? "screen" : "client";
            return touches ? document.elementFromPoint(touches[0][property + "X"], touches[0][property + "Y"]) : 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 = "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,
        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,
        isNodeEmpty: isNodeEmpty,
        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);
        },
        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: ""
        },
        _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);
        },
        destroy: function() {
            var that = this;
            that.element.removeData("kendo" + that.options.prefix + that.options.name);
            that.unbind();
        }
    });
    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 (!isNaN(parseFloat(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]").andSelf().each(function() {
            kendo.initWidget(this, {}, roles);
        });
    };
    kendo.destroy = function(element) {
        $(element).find("[data-" + kendo.ns + "role]").andSelf().each(function() {
            var element = $(this), widget = kendo.widgetInstance(element, kendo.ui) || kendo.widgetInstance(element, kendo.mobile.ui) || kendo.widgetInstance(element, kendo.dataviz.ui);
            if (widget) {
                widget.destroy();
            }
        });
    };
    kendo.parseOptions = parseOptions;
    extend(kendo.ui, {
        Widget: Widget,
        roles: {},
        progress: function(container, toggle) {
            var mask = container.find(".k-loading-mask");
            if (toggle) {
                if (!mask.length) {
                    mask = $("<div class='k-loading-mask'><span class='k-loading-text'>Loading...</span><div class='k-loading-image'/><div class='k-loading-color'/></div>").width("100%").height("100%").prependTo(container).css({
                        top: container.scrollTop(),
                        left: container.scrollLeft()
                    });
                }
            } 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: $.noop
    };
    var MobileWidget = Widget.extend({
        init: function(element, options) {
            Widget.fn.init.call(this, element, options);
            this.element.autoApplyNS();
            this.wrapper = this.element;
        },
        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"));
            return kendo.widgetInstance(viewElement, kendo.mobile.ui);
        },
        container: function() {
            var element = this.element.closest(kendo.roleSelector("view layout modalview"));
            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 widget = suite.roles[element.data(kendo.ns + "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, 200);
            };
        }
        $(window).on(support.resize, handler);
    };
    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().andSelf().filter(function() {
            return $.css(this, "visibility") === "hidden" || $.expr.filters.hidden(this);
        }).length;
    }
    $.extend($.expr[":"], {
        focusable: 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"
        };
    }
    if (support.pointers) {
        eventMap = {
            down: "MSPointerDown",
            move: "MSPointerMove",
            up: "MSPointerUp",
            cancel: "MSPointerCancel MSPointerLeave"
        };
        // Create MSPointerEnter/MSPointerLeave events using mouseover/out and event-time checks
        jQuery.each({
            MSPointerEnter: "MSPointerOver",
            MSPointerLeave: "MSPointerOut"
        }, function(orig, fix) {
            jQuery.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 && !jQuery.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;
})(jQuery, eval);

/*global kendo_module:true */
if (typeof kendo_module === "undefined") {
    kendo_module = function() {};
}

(function($, undefined) {
    var kendo = window.kendo, location = window.location, history = window.history, _checkUrlInterval = 50, hashStrip = /^#*/, documentMode = window.document.documentMode, oldIE = kendo.support.browser.msie && (!documentMode || documentMode <= 8), hashChangeSupported = "onhashchange" in window && !oldIE, document = window.document;
    var History = kendo.Observable.extend({
        start: function(options) {
            options = options || {};
            var that = this;
            that._pushStateRequested = !!options.pushState;
            that._pushState = that._pushStateRequested && that._pushStateSupported();
            that.root = options.root || "/";
            that._interval = 0;
            this.bind([ "change", "ready" ], options);
            if (that._normalizeUrl()) {
                return true;
            }
            that.current = that._currentLocation();
            that._listenToLocationChange();
            that.trigger("ready", {
                url: that.current
            });
        },
        stop: function() {
            $(window).unbind(".kendo");
            this.unbind("change");
            this.unbind("ready");
            clearInterval(this._interval);
        },
        _normalizeUrl: function() {
            var that = this, pushStateUrl, atRoot = that.root == location.pathname, pushStateUrlNeedsTransform = that._pushStateRequested && !that._pushStateSupported() && !atRoot, hashUrlNeedsTransform = that._pushState && atRoot && location.hash;
            if (pushStateUrlNeedsTransform) {
                location.replace(that.root + "#" + that._stripRoot(location.pathname));
                return true;
            } else if (hashUrlNeedsTransform) {
                pushStateUrl = that._makePushStateUrl(location.hash.replace(hashStrip, ""));
                history.replaceState({}, document.title, pushStateUrl);
                return false;
            }
            return false;
        },
        _listenToLocationChange: function() {
            var that = this, _checkUrlProxy = $.proxy(that._checkUrl, that);
            if (this._pushState) {
                $(window).bind("popstate.kendo", _checkUrlProxy);
            } else if (hashChangeSupported) {
                $(window).bind("hashchange.kendo", _checkUrlProxy);
            } else {
                that._interval = setInterval(_checkUrlProxy, _checkUrlInterval);
            }
        },
        _pushStateSupported: function() {
            return window.history && window.history.pushState;
        },
        _checkUrl: function() {
            var that = this, current = that._currentLocation();
            if (current != that.current) {
                that.navigate(current);
            }
        },
        _stripRoot: function(url) {
            var that = this;
            if (url.indexOf(that.root) === 0) {
                return ("/" + url.substr(that.root.length)).replace(/\/\//g, "/");
            } else {
                return url;
            }
        },
        _makePushStateUrl: function(address) {
            var that = this;
            if (address.indexOf(that.root) !== 0) {
                address = (that.root + address).replace(/\/\//g, "/");
            }
            return location.protocol + "//" + location.host + address;
        },
        _currentLocation: function() {
            var that = this, current;
            if (that._pushState) {
                current = location.pathname;
                if (location.search) {
                    current += location.search;
                }
                return that._stripRoot(current);
            } else {
                return location.hash.replace(hashStrip, "");
            }
        },
        change: function(callback) {
            this.bind("change", callback);
        },
        navigate: function(to, silent) {
            var that = this;
            if (to === "#:back") {
                history.back();
                return;
            }
            to = to.replace(hashStrip, "");
            if (that.current === to || that.current === decodeURIComponent(to)) {
                return;
            }
            if (!silent) {
                if (that.trigger("change", {
                    url: to
                })) {
                    to = that.current;
                }
            }
            if (that._pushState) {
                history.pushState({}, document.title, that._makePushStateUrl(to));
                that.current = to;
            } else {
                location.hash = that.current = to;
            }
        }
    });
    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", 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, "(.*?)") + "$");
    }
    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 = this.route.exec(url).slice(1), idx = 0, length = params.length;
            for (;idx < length; idx++) {
                if (typeof params[idx] !== "undefined") {
                    params[idx] = decodeURIComponent(params[idx]);
                }
            }
            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.bind([ INIT, ROUTE_MISSING, CHANGE ], options);
        },
        destroy: function() {
            history.unbind("ready", this._readyProxy);
            history.unbind("change", this._urlChangedProxy);
            this.unbind();
        },
        start: function() {
            var that = this, readyProxy = function(e) {
                if (!e.url) {
                    e.url = "/";
                }
                if (!that.trigger(INIT, e)) {
                    that._urlChanged(e);
                }
            }, urlChangedProxy = function(e) {
                that._urlChanged(e);
            };
            kendo.history.start({
                ready: readyProxy,
                change: urlChangedProxy
            });
            this._urlChangedProxy = urlChangedProxy;
            this._readyProxy = readyProxy;
        },
        route: function(route, callback) {
            this.routes.push(new Route(route, callback));
        },
        navigate: function(url, silent) {
            kendo.history.navigate(url, silent);
        },
        _urlChanged: function(e) {
            var url = e.url;
            if (!url) {
                url = "/";
            }
            if (this.trigger(CHANGE, {
                url: 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;
                }
            }
            this.trigger(ROUTE_MISSING, {
                url: url
            });
        }
    });
    kendo.Router = Router;
})();

(function($, undefined) {
    var kendo = window.kendo, Observable = kendo.Observable, SCRIPT = "SCRIPT", INIT = "init", SHOW = "show", HIDE = "hide";
    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.bind([ INIT, SHOW, HIDE ], options);
        },
        render: function(container) {
            var that = this, element, content;
            if (!that.element) {
                element = $("<" + that.tagName + " />");
                content = $(document.getElementById(that.content) || that.content);
                // support passing id without #
                element.append(content[0].tagName === SCRIPT ? content.html() : content);
                that.element = element;
                kendo.bind(that.element, that.model);
                this.trigger(INIT);
            }
            if (container) {
                this.trigger(SHOW);
                $(container).append(that.element);
            }
            return that.element;
        },
        hide: function() {
            this.element.detach();
            this.trigger(HIDE);
        },
        destroy: function() {
            if (this.element) {
                kendo.unbind(this.element);
                this.element.remove();
            }
        }
    });
    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);

(function($, undefined) {
    /**
     * @name kendo.fx
     * @namespace This object contains the fx library that is used by all widgets using animation.
     * If this file is not included, all animations will be disabled but the basic functionality preserved.
     */
    var kendo = window.kendo, fx = kendo.fx, 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, Effects = {}, 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", COMPLETE_CALLBACK = "completeCallback", TRANSITION = cssPrefix + "transition", TRANSFORM = cssPrefix + "transform", BACKFACE = cssPrefix + "backface-visibility", PERSPECTIVE = cssPrefix + "perspective", DEFAULT_PERSPECTIVE = "800px", TRANSFORM_PERSPECTIVE = "perspective(" + DEFAULT_PERSPECTIVE + ")", 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 kendo.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);
            }
            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);
        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(kendo.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);
            kendo.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);
            }
            for (;idx < length; idx++) {
                effects[idx].teardown();
            }
            if (options.completeCallback) {
                options.completeCallback(element);
            }
        }
    });
    kendo.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 = Effects[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();
        }
    };
    kendo.fx.transitionPromise = function(element, destination, options) {
        kendo.fx.animateTo(element, destination, options);
        return element;
    };
    extend(kendo.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
                    });
                } 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
                        });
                    });
                }
            }
        },
        animateTo: function(element, destination, options) {
            var direction, commonParent = element.parents().filter(destination.parents()).first(), originalOverflow;
            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[0].style.cssText = "";
                // Removing the whole style attribute breaks Android.
                if (!support.mobileOS.android) {
                    commonParent.css(OVERFLOW, originalOverflow);
                }
                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);
            kendo.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);
            }
            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 toUpperCase(letter) {
        return letter.toUpperCase();
    }
    function capitalize(word) {
        return word.replace(/^./, toUpperCase);
    }
    function createEffect(name, definition) {
        var effectClass = Effect.extend(definition), directions = effectClass.prototype.directions;
        Effects[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,
        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
            };
        },
        children: function() {
            var that = this, reverse = that._reverse, previous = that.options.previous, dir = that._direction;
            var children = [ fx(that.element).slideIn(dir).setReverse(reverse) ];
            if (previous) {
                children.push(fx(previous).slideIn(directions[dir].reverse).setReverse(!reverse));
            }
            return children;
        }
    });
    function createToggleEffect(name, property, endValue) {
        createEffect(name, {
            directions: IN_OUT,
            restore: [ property ],
            startValue: function(value) {
                this._startValue = value;
                return this;
            },
            endValue: function(value) {
                this._endValue = value;
                return this;
            },
            shouldHide: function() {
                return this._direction === "out" && this._end() === endValue ? !this._reverse : this._reverse;
            },
            _end: function() {
                return this._endValue || endValue;
            },
            _start: function() {
                return this._startValue || 1;
            },
            prepare: function(start, end) {
                var that = this, opacity = that.element.data(property), out = that.shouldHide(), value = isNaN(opacity) || opacity === "" ? that._start() : opacity;
                start[property] = end[property] = that._end();
                if (out) {
                    start[property] = value;
                } else {
                    end[property] = value;
                }
            }
        });
    }
    createToggleEffect("fade", "opacity", 0);
    createToggleEffect("zoom", "scale", .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);
            }
        }
    });
    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(value) {
            this._temporary = value;
            return this;
        },
        teardown: function() {
            if (this._temporary) {
                this.element.remove();
            }
        }
    });
    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) {
            var that = this, direction = that._reverse ? directions[that._direction].reverse : that._direction;
            start.clip = clipInHalf(that._container, direction);
        },
        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 [ fx(options.face).staticPage(direction, element).face(true).setReverse(reverse), fx(options.back).staticPage(reverseDirection, element).setReverse(reverse), fx(faceClone).turningPage(direction, element).face(true).clipInHalf(true).temporary(true).setReverse(reverse), fx(backClone).turningPage(reverseDirection, element).clipInHalf(true).temporary(true).setReverse(reverse) ];
        },
        prepare: function(start) {
            start[PERSPECTIVE] = DEFAULT_PERSPECTIVE;
            start.transformStyle = "preserve-3d";
        },
        teardown: function() {
            this.element.find(".temp-pages").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 [ fx(options.face).turningPage(direction, element).face(true).setReverse(reverse), fx(options.back).turningPage(reverseDirection, element).setReverse(reverse) ];
        },
        prepare: function(start) {
            start[PERSPECTIVE] = DEFAULT_PERSPECTIVE;
            start.transformStyle = "preserve-3d";
        }
    });
    var animationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
        setTimeout(callback, 1e3 / 60);
    };
    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;
                animationFrame(this._tickProxy);
            }
        },
        cancel: function() {
            this._started = false;
            this.onCancel();
        },
        _tick: function() {
            var that = this;
            if (!that._started) {
                return;
            }
            that.tick();
            if (!that.done()) {
                animationFrame(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, Date.now() - this.startDate);
        },
        moveTo: function(options) {
            var that = this, movable = that.movable;
            that.initial = movable[that.axis];
            that.delta = options.location - that.initial;
            that.duration = options.duration || 300;
            that.tick = that._easeProxy(options.ease);
            that.startDate = Date.now();
            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;
    fx.Effects = Effects;
})(window.kendo.jQuery);

(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"
                },
                create: {
                    cache: true,
                    dataType: "json",
                    contentType: "application/json",
                    type: "POST"
                },
                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);

(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, 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);
                };
            }
        },
        total: function(result) {
            return this.data(result).length;
        },
        errors: function(data) {
            return data ? data.errors : null;
        },
        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(/^\//, "").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);

(function($, undefined) {
    var extend = $.extend, proxy = $.proxy, isFunction = $.isFunction, isPlainObject = $.isPlainObject, isEmptyObject = $.isEmptyObject, isArray = $.isArray, grep = $.grep, ajax = $.ajax, map, each = $.each, noop = $.noop, kendo = window.kendo, 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);
        }
    });
    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;
            }
            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;
            if (field.indexOf(".")) {
                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;
                    }
                    path += ".";
                }
            }
            kendo.setter(field)(that, value);
        },
        set: function(field, value) {
            var that = this, current = kendo.getter(field, true)(that);
            if (current !== value) {
                if (!that.trigger("set", {
                    field: field,
                    value: value
                })) {
                    that._set(field, that.wrap(value, field, function() {
                        return that;
                    }));
                    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;
        },
        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) {
                that._set(field, that.wrap(data[field], field, parent));
            }
            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 = {}, 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;
            name = typeof field.field === STRING ? field.field : name;
            if (!field.nullable) {
                value = proto.defaults[name] = field.defaultValue !== undefined ? field.defaultValue : defaultValues[type.toLowerCase()];
            }
            if (options.id === name) {
                proto._defaultId = value;
            }
            proto.defaults[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);
        },
        asc: 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 && !b && a > 0 || b == null) {
                    return 1;
                }
                if (b && !a && b > 0) {
                    return -1;
                }
                return a > b ? 1 : a < b ? -1 : 0;
            };
        },
        desc: 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 && !b && a > 0 || b == null) {
                    return -1;
                }
                if (b && !a && b > 0 || a == null) {
                    return 1;
                }
                return a < b ? 1 : a > b ? -1 : 0;
            };
        },
        create: function(descriptor) {
            return this[descriptor.dir.toLowerCase()](descriptor.field);
        },
        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 PositionComparer = 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 (valueB == null) {
                    return 1;
                }
                return valueA > valueB ? 1 : valueA < valueB ? -1 : 0;
            };
        },
        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;
                }
                return valueA < valueB ? 1 : valueA > valueB ? -1 : 0;
            };
        }
    });
    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));
        },
        orderBy: function(selector) {
            var result = this.data.slice(0), comparer = isFunction(selector) || !selector ? Comparer.asc(selector) : selector.compare;
            return new Query(result.sort(comparer));
        },
        orderByDescending: function(selector) {
            return new Query(this.data.slice(0).sort(Comparer.desc(selector)));
        },
        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
                    } ]);
                    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, PositionComparer).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);
            accumulator = accumulator || value;
            if (accumulator > value) {
                accumulator = value;
            }
            return accumulator;
        }
    };
    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 convertRecords(data, getters, modelInstance) {
        var record, getter, 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));
            }
        }
    }
    function convertGroup(data, getters, modelInstance) {
        var record, idx, length;
        for (idx = 0, length = data.length; idx < length; idx++) {
            record = data[idx];
            record.value = modelInstance._parse(record.field, record.value);
            if (record.hasSubgroups) {
                convertGroup(record.items, getters, modelInstance);
            } else {
                convertRecords(record.items, getters, modelInstance);
            }
        }
    }
    function wrapDataAccess(originalFunction, model, converter, getters) {
        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());
            }
            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), getters = {};
                model = that.model;
                if (model.fields) {
                    each(model.fields, function(field, value) {
                        if (isPlainObject(value) && value.field) {
                            getters[value.field] = getter(value.field);
                        } else {
                            getters[field] = getter(field);
                        }
                    });
                }
                that.data = wrapDataAccess(dataFunction, model, convertRecords, getters);
                that.groups = wrapDataAccess(groupsFunction, model, convertGroup, getters);
            }
        },
        errors: function(data) {
            return data ? data.errors : null;
        },
        parse: identity,
        data: identity,
        total: function(data) {
            return data.length;
        },
        groups: identity,
        status: function(data) {
            return data.status;
        },
        aggregates: function() {
            return {};
        }
    });
    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);
                }
            }
            start = 0;
            if (++idx >= dest.length) {
                break;
            }
        }
    }
    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;
    }
    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._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);
        },
        insert: function(index, model) {
            if (!model) {
                model = index;
                index = 0;
            }
            if (!(model instanceof Model)) {
                if (this.reader.model) {
                    model = new this.reader.model(model);
                } else {
                    model = new ObservableObject(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, pristine = that._readData(that._pristine);
            if (model instanceof kendo.data.Model) {
                that._cancelModel(model);
            } else {
                that._destroyed = [];
                that._data = that._observe(pristine);
                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._readData(that._pristine), type = result.type, length;
            that.trigger(REQUESTEND, {
                response: response,
                type: type
            });
            if (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) {
                extend(true, 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._readData(this._pristine), 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) {
                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 = [];
            if (that.options.batch) {
                if (data.length) {
                    promises.push(that._promise({
                        data: {
                            models: toJSON(data)
                        }
                    }, data, method));
                }
            } else {
                for (idx = 0, length = data.length; idx < length; idx++) {
                    promises.push(that._promise({
                        data: data[idx].toJSON()
                    }, [ data[idx] ], method));
                }
            }
            return promises;
        },
        read: function(data) {
            var that = this, params = that._params(data);
            that._queueRequest(params, function() {
                if (!that.trigger(REQUESTSTART)) {
                    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._data = that._observe(data);
            that._addRange(that._data);
            that._dequeueRequest();
            that._process(that._data);
        },
        _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;
            }
            if (!that.options.serverFiltering) {
                delete options.filter;
            }
            if (!that.options.serverSorting) {
                delete options.sort;
            }
            if (!that.options.serverAggregates) {
                delete options.aggregate;
            }
            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);
            }
            return data.bind(CHANGE, proxy(that._change, that));
        },
        _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 = that._total || that.reader.total(that._pristine);
                if (action === "add") {
                    total++;
                } else if (action === "remove") {
                    total--;
                } 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.read(that._mergeState(options));
            } else {
                if (!that.trigger(REQUESTSTART)) {
                    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;
            if (callback && isFunction(callback)) {
                that.one(CHANGE, callback);
            }
            that._query();
        },
        _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 this._total || 0;
        },
        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;
        },
        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;
                    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);
                }
                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() {
                        that.transport.read({
                            data: options,
                            success: that._prefetchSuccessHandler(skip, size, callback)
                        });
                    });
                }, 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]);
        },
        _initChildren: function() {
            var that = this;
            if (!(that.children instanceof HierarchicalDataSource)) {
                that.children = new HierarchicalDataSource(that._childrenOptions);
                that.children.parent = function() {
                    return that;
                };
                that.children.bind(CHANGE, function(e) {
                    e.node = e.node || that;
                    that.trigger(CHANGE, 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);
            }
        },
        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";
        }
    });
    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));
        },
        remove: function(node) {
            var parentNode = node.parentNode(), dataSource = this, result;
            if (parentNode) {
                dataSource = parentNode.children;
            }
            result = DataSource.fn.remove.call(dataSource, node);
            if (parentNode && !dataSource.data().length) {
                parentNode.hasChildren = false;
            }
            return result;
        },
        insert: function(index, model) {
            var parentNode = this.parent();
            if (parentNode) {
                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;
        for (idx = 0, length = items.length; idx < length; idx++) {
            record = {};
            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] = children.find("a").attr("href");
            }
            if (imageUrlField) {
                record[imageUrlField] = children.find("img").attr("src");
            }
            if (spriteCssClassField) {
                className = children.find(".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);
    };
    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
    });
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, Observable = kendo.Observable, ObservableObject = kendo.data.ObservableObject, ObservableArray = kendo.data.ObservableArray, toString = {}.toString, binders = {}, 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);
            }
        },
        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);
                }
            }
            // 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;
            this.bindings[VALUE].set(this.element.value);
            this._initChange = false;
        },
        refresh: function() {
            if (!this._initChange) {
                var value = this.bindings[VALUE].get();
                if (value == null) {
                    value = "";
                }
                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);
        },
        refresh: function(e) {
            var that = this, source = that.bindings.source.get();
            if (source instanceof ObservableArray) {
                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('<option value="#:{0}#">#:{1}#</option>', options.valueField || options.textField, options.textField || options.valueField);
                    } else {
                        template = "<option>#:data#</option>";
                    }
                } else if (nodeName == "tbody") {
                    template = "<tr><td>#:data#</td></tr>";
                } else if (nodeName == "ul" || nodeName == "ol") {
                    template = "<li>#:data#</li>";
                } 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(), idx, length, child, clone = element.cloneNode(false), reference = element.children[index];
            $(clone).html(kendo.render(this.template(), items));
            if (clone.children.length) {
                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(this.bindings.source.parents));
                }
            }
        },
        remove: function(index, items) {
            var idx, element = this.container();
            for (idx = 0; idx < items.length; idx++) {
                element.removeChild(element.children[index]);
            }
        },
        render: function() {
            var source = this.bindings.source.get(), idx, length, element = this.container(), template = this.template(), parent;
            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) {
                $(element).html(this.bindings.template.render(source));
                if (element.children.length) {
                    for (idx = 0, length = source.length; idx < length; idx++) {
                        bindElement(element.children[idx], source[idx], this.options.roles, [ source[idx] ].concat(this.bindings.source.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, 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 (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(), groups = dataSource.group() || [];
                if (items.length) {
                    if (groups.length) {
                        view = flattenGroups(view);
                    }
                    for (idx = 0, length = view.length; idx < length; idx++) {
                        bindElement(items[idx], view[idx], this._ns(e.ns), [ view[idx] ].concat(this.bindings.source.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 = 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) {
                        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);
            }
        })
    };
    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;
            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);
            }
        },
        applyBinding: function(name, bindings) {
            var binder = 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(data[idx].items);
            }
        }
        return result;
    }
    function bindingTargetForRole(role, element, roles) {
        var type = roles[role];
        if (type) {
            return new WidgetBindingTarget(kendo.initWidget(element, type.options, roles));
        }
    }
    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(role, element, roles);
        }
        if (bind) {
            bind = parseBindings(bind.replace(whiteSpaceRegExp, ""));
            if (!target) {
                options = kendo.parseOptions(element, {
                    textField: "",
                    valueField: "",
                    template: "",
                    valueUpdate: CHANGE
                });
                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, roles = kendo.rolesFromNamespaces([].slice.call(arguments, 2));
        object = kendo.observable(object);
        dom = $(dom);
        for (idx = 0, length = dom.length; idx < length; idx++) {
            bindElement(dom[idx], 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) {
        var idx, length, children = element.children;
        unbindElement(element);
        if (children) {
            for (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);

(function($, undefined) {
    var kendo = window.kendo, Widget = kendo.ui.Widget, NS = ".kendoValidator", INVALIDMSG = "k-invalid-msg", 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] !== undefined;
        }
        return false;
    }, nameSpecialCharRegExp = /("|'|\[|\]|\$|\.|\:|\+)/g;
    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(/&amp/g, "&amp;").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">");
    }
    function numberOfDecimalDigits(value) {
        value = (value + "").split(".");
        if (value.length > 1) {
            return value[1].length;
        }
        return 0;
    }
    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();
        },
        options: {
            name: "Validator",
            errorTemplate: '<span class="k-widget k-tooltip k-tooltip-validation">' + '<span class="k-icon k-warning"> </span> #=message#</span>',
            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.attr("checked") !== "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 = 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 = 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 that = this, inputs, idx, invalid = false, length;
            that._errors = {};
            if (!that.element.is(INPUTSELECTOR)) {
                inputs = that.element.find(INPUTSELECTOR);
                for (idx = 0, length = inputs.length; idx < length; idx++) {
                    if (!that.validateInput(inputs.eq(idx))) {
                        invalid = true;
                    }
                }
                return !invalid;
            }
            return that.validateInput(that.element);
        },
        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 = $(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 = this.element.find("." + INVALIDMSG + "[" + kendo.attr("for") + "=" + fieldName.replace(nameSpecialCharRegExp, "\\$1") + "]");
            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 = $.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);

(function($, undefined) {
    var kendo = window.kendo, support = kendo.support, pointers = support.pointers, document = window.document, SURFACE = $(document.documentElement), Class = kendo.Class, Observable = kendo.Observable, now = $.now, extend = $.extend, OS = support.mobileOS, invalidZeroEvents = OS && OS.android, // UserEvents events
    PRESS = "press", SELECT = "select", START = "start", MOVE = "move", END = "end", CANCEL = "cancel", TAP = "tap", 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) {
            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.notifyInit = function() {
                that._trigger(PRESS, touchInfo);
            };
        },
        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);
            }
            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) {
            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();
        }
        parent.trigger($.Event(e.type, {
            target: target[0]
        }));
    }
    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 || 0;
            that.touches = [];
            that._maxTouches = options.multiTouch ? 2 : 1;
            that.allowSelection = options.allowSelection;
            that.eventNS = ns;
            element = $(element).handler(that);
            Observable.fn.init.call(that);
            extend(that, {
                element: element,
                surface: options.global ? SURFACE : 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 (pointers) {
                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 (support.eventCapture) {
                var downEvents = kendo.eventMap.up.split(" "), idx = 0, length = downEvents.length, surfaceElement = that.surface[0], preventIfMoving = function(e) {
                    if (that._isMoved()) {
                        e.preventDefault();
                    }
                };
                for (;idx < length; idx++) {
                    surfaceElement.addEventListener(downEvents[idx], preventIfMoving, true);
                }
            }
            that.bind([ PRESS, TAP, START, MOVE, END, CANCEL, GESTURESTART, GESTURECHANGE, GESTUREEND, GESTURETAP, SELECT ], options);
        },
        destroy: function() {
            var that = this;
            that.element.kendoDestroy(that.eventNS);
            that.surface.kendoDestroy(that.eventNS);
            that._disposeAll();
            that.unbind();
        },
        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() {
            $.each(this.touches, function() {
                this.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);
                } else {
                    target = that.element;
                }
                if (!target.length) {
                    continue;
                }
                touch = new Touch(that, target, touch);
                that.touches.push(touch);
                touch.notifyInit();
                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,
                target: target || this.element,
                stopPropagation: $.noop,
                preventDefault: $.noop
            });
        }
    });
    kendo.getTouches = getTouches;
    kendo.touchDelta = touchDelta;
    kendo.UserEvents = UserEvents;
})(window.kendo.jQuery);

(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", 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;
            $.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);
            });
            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;
            that.max = 0;
            if (that.horizontal) {
                that.measure = "width";
                that.scrollSize = "scrollWidth";
                that.axis = "x";
            } else {
                that.measure = "height";
                that.scrollSize = "scrollHeight";
                that.axis = "y";
            }
        },
        outOfBounds: function(offset) {
            return offset > this.max || offset < this.min;
        },
        forceEnabled: function() {
            this.forcedEnabled = true;
        },
        getSize: function() {
            return this.container[this.measure]();
        },
        getTotal: function() {
            return this.element[0][this.scrollSize];
        },
        rescale: function(scale) {
            this.scale = scale;
        },
        update: function(silent) {
            var that = this, total = that.getTotal(), scaledTotal = total * that.scale, size = that.getSize();
            that.size = size;
            that.total = scaledTotal;
            that.min = Math.min(that.max, that.size - scaledTotal);
            that.minScale = that.size / total;
            that.enabled = that.forcedEnabled || scaledTotal > size;
            if (!silent) {
                that.trigger(CHANGE, that);
            }
        }
    });
    var PaneDimensions = Observable.extend({
        init: function(options) {
            var that = this, refresh = proxy(that.refresh, that);
            Observable.fn.init.call(that);
            that.x = new PaneDimension(extend({
                horizontal: true
            }, options));
            that.y = new PaneDimension(extend({
                horizontal: false
            }, options));
            that.forcedMinScale = options.minScale;
            that.bind(CHANGE, options);
            kendo.onResize(refresh);
        },
        rescale: function(newScale) {
            this.x.rescale(newScale);
            this.y.rescale(newScale);
            this.refresh();
        },
        refresh: function() {
            var that = this;
            that.x.update();
            that.y.update();
            that.enabled = that.x.enabled || that.y.enabled;
            that.minScale = that.forcedMinScale || 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 ? .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;
                },
                gesturechange: function(e) {
                    var previousGesture = that.gesture, previousCenter = previousGesture.center, center = e.center, scaleDelta = e.distance / previousGesture.distance, minScale = that.dimensions.minScale, 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) * .8;
                    }
                    coordinates = {
                        x: (movable.x - previousCenter.x) * scaleDelta + center.x - movable.x,
                        y: (movable.y - previousCenter.y) * scaleDelta + center.y - movable.y
                    };
                    movable.scaleWith(scaleDelta);
                    x.dragMove(coordinates.x);
                    y.dragMove(coordinates.y);
                    that.dimensions.rescale(movable.scale);
                    that.gesture = e;
                },
                move: function(e) {
                    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", round = Math.round, translate;
    if (support.hasHW3D) {
        translate = function(x, y, scale) {
            return "translate3d(" + round(x) + "px," + round(y) + "px,0) scale(" + scale + ")";
        };
    } else {
        translate = function(x, y, scale) {
            return "translate(" + round(x) + "px," + round(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, newCoordinates = translate(that.x, that.y, that.scale);
            if (newCoordinates != that.coordinates) {
                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.userEvents = new UserEvents(that.element, {
                global: true,
                stopPropagation: true,
                filter: that.options.filter,
                threshold: that.options.distance,
                start: proxy(that._start, 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: [ DRAGSTART, DRAG, DRAGEND, DRAGCANCEL ],
        options: {
            name: "Draggable",
            distance: 5,
            group: "default",
            cursorOffset: null,
            axis: null,
            container: null,
            dropped: 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;
            that.currentTarget = e.target;
            that.currentTargetOffset = getOffset(that.currentTarget);
            if (hint) {
                if (that.hint) {
                    that.hint.stop(true, true).remove();
                }
                that.hint = $.isFunction(hint) ? $(hint.call(that, that.currentTarget)) : hint;
                var offset = getOffset(that.currentTarget);
                that.hintOffset = offset;
                that.hint.css({
                    position: "absolute",
                    zIndex: 2e4,
                    // 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);
        },
        _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;
            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);

(function($, undefined) {
    var kendo = window.kendo, mobile = kendo.mobile, fx = kendo.fx, 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 = .7, FRICTION = .93, OUT_OF_BOUNDS_FRICTION = .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 < .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 = e.touch[that.axis].velocity * 16;
                if (that.velocity) {
                    that.tapCapture.captureNext();
                    Animation.fn.start.call(that);
                }
            }
        },
        tick: function() {
            var that = this, dimension = that.dimension, friction = that._outOfBounds() ? OUT_OF_BOUNDS_FRICTION : 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 ScrollBar = Class.extend({
        init: function(options) {
            var that = this, horizontal = options.axis === "x", element = $('<div class="km-touch-scrollbar km-' + (horizontal ? "horizontal" : "vertical") + '-scrollbar" />');
            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;
            if (that.options.useNative && kendo.support.hasNativeScrolling) {
                element.addClass("km-native-scroller").prepend('<div class="km-scroll-header"/>');
                extend(that, {
                    scrollElement: element,
                    fixedContainer: element.children().first()
                });
                return;
            }
            element.css("overflow", "hidden").addClass("km-scroll-wrapper").wrapInner('<div class="km-scroll-container"/>').prepend('<div class="km-scroll-header"/>');
            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,
                change: function() {
                    that.trigger(RESIZE);
                }
            }), userEvents = new kendo.UserEvents(element, {
                allowSelection: true,
                preventDragEvent: true,
                multiTouch: that.options.zoom,
                start: function(e) {
                    dimensions.refresh();
                    var velocityX = abs(e.x.velocity), velocityY = abs(e.y.velocity);
                    if (dimensions.x.enabled && velocityX * 2 >= velocityY || dimensions.y.enabled && velocityY * 2 >= velocityX) {
                        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
            });
            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,
                userEvents: userEvents,
                pane: pane,
                tapCapture: tapCapture,
                pulled: false,
                scrollElement: inner,
                fixedContainer: element.children().first()
            });
            that._initAxis("x");
            that._initAxis("y");
            dimensions.refresh();
            if (that.options.pullToRefresh) {
                that._initPullToRefresh();
            }
            kendo.onResize($.proxy(that.reset, that));
        },
        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,
            pullTemplate: "Pull to refresh",
            releaseTemplate: "Release to refresh",
            refreshTemplate: "Refreshing"
        },
        events: [ PULL, SCROLL, RESIZE ],
        setOptions: function(options) {
            var that = this;
            Widget.fn.setOptions.call(that, options);
            if (options.pullToRefresh) {
                that._initPullToRefresh();
            }
        },
        reset: function() {
            this.movable.moveTo({
                x: 0,
                y: 0
            });
        },
        scrollTo: function(x, y) {
            this.movable.moveTo({
                x: x,
                y: y
            });
        },
        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);
            this.userEvents.destroy();
        },
        _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('<span class="km-scroller-pull"><span class="km-icon"></span><span class="km-template">' + that.pullTemplate({}) + "</span></span>");
            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
            }), inertia = new DragInertia({
                axis: axis,
                movable: movable,
                tapCapture: tapCapture,
                userEvents: that.userEvents,
                dimension: dimension,
                elastic: that.options.elastic,
                end: function() {
                    scrollBar.hide();
                }
            });
            that[axis + "inertia"] = inertia;
            that.pane[axis].bind(CHANGE, function() {
                scrollBar.show();
            });
        }
    });
    ui.plugin(Scroller);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, Widget = kendo.ui.Widget, proxy = $.proxy, NS = ".kendoGroupable", indicatorTmpl = kendo.template('<div class="k-group-indicator" data-#=data.ns#field="${data.field}" data-#=data.ns#title="${data.title || ""}" data-#=data.ns#dir="${data.dir || "asc"}">' + '<a href="\\#" class="k-link">' + '<span class="k-icon k-si-arrow-${(data.dir || "asc") == "asc" ? "n" : "s"}">(sorted ${(data.dir || "asc") == "asc" ? "ascending": "descending"})</span>' + "${data.title ? data.title: data.field}" + "</a>" + '<a class="k-button k-button-icon k-button-bare">' + '<span class="k-icon k-group-delete"></span>' + "</a>" + "</div>", {
        useWithBlock: false
    }), hint = function(target) {
        return $('<div class="k-header k-drag-clue" />').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('<span class="k-icon k-drag-status k-denied" />');
    }, dropCue = $('<div class="k-grouping-dropclue"/>'), 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, dropCuePositions = that._dropCuePositions = [];
            Widget.fn.init.call(that, element, options);
            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),
                            left: 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) + 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), left = element.position().left - marginLeft;
                    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], right = lastCuePosition.right, marginLeft = parseInt(lastCuePosition.element.css("marginLeft"), 10), marginRight = parseInt(lastCuePosition.element.css("marginRight"), 10);
            if (position >= right) {
                position = {
                    left: lastCuePosition.element.position().left + lastCuePosition.element.outerWidth() + marginRight,
                    element: lastCuePosition.element,
                    before: false
                };
            } else {
                position = $.grep(dropCuePositions, function(item) {
                    return item.left <= position && position <= item.right;
                })[0];
                if (position) {
                    position = {
                        left: 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
                });
            }
        },
        _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();
            return source[0] !== target[0] && (!next[0] || target[0] !== next[0] || position > next.position().left);
        },
        _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);

(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 = $('<div class="k-reorder-cue"><div class="k-icon k-i-arrow-s"></div><div class="k-icon k-i-arrow-n"></div></div>');
            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);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, proxy = $.proxy, isFunction = $.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
        },
        _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);

(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('<a class="k-link" href="#"/>').find(TLINK);
            }
            that.link = link;
            that.element.on("click" + NS, proxy(that._click, that));
        },
        options: {
            name: "Sortable",
            mode: SINGLE,
            allowUnsort: true
        },
        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) {
                $('<span class="k-icon k-i-arrow-n" />').appendTo(that.link);
                element.attr(ARIASORT, "ascending");
            } else if (dir === DESC) {
                $('<span class="k-icon k-i-arrow-s" />').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, sort = that.dataSource.sort() || [], idx, length;
            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
                } ];
            } 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
                });
            }
            e.preventDefault();
            that.dataSource.sort(sort);
        }
    });
    kendo.ui.plugin(Sortable);
})(window.kendo.jQuery);

(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() {
            $('<div class="parent"><span /></div>').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 = $("<div class='k-marquee'></div>");
            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, 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;
            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.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) {
            if ($(e.event.target).is("input,a,textarea")) {
                this.userEvents.cancel();
                this._downTarget = null;
            } else {
                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);

(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('<a href="\\#" title="#=text#" class="k-link"><span class="k-icon #= className #">#=text#</span></a>');
    function button(template, idx, text, numeric) {
        return template({
            idx: idx,
            text: text,
            ns: kendo.ns,
            numeric: numeric
        });
    }
    function icon(className, text) {
        return iconTemplate({
            className: className.substring(1),
            text: text
        });
    }
    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));
                    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 = $('<ul class="k-pager-numbers k-reset" />').appendTo(that.element);
                }
            }
            if (options.input) {
                if (!that.element.find(".k-pager-input").length) {
                    that.element.append('<span class="k-pager-input k-label">' + options.messages.page + '<input class="k-textbox">' + kendo.format(options.messages.of, totalPages) + "</span>");
                }
                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));
                    last(that.element, page, totalPages);
                }
            }
            if (options.pageSizes) {
                if (!that.element.find(".k-pager-sizes").length) {
                    $('<span class="k-pager-sizes k-label"><select/>' + options.messages.itemsPerPage + "</span>").appendTo(that.element).find("select").html($.map($.isArray(options.pageSizes) ? options.pageSizes : [ 5, 10, 20 ], function(page) {
                        return "<option>" + page + "</option>";
                    }).join("")).end().appendTo(that.element);
                }
                that.element.find(".k-pager-sizes select").val(that.pageSize());
                if (kendo.ui.DropDownList) {
                    that.element.find(".k-pager-sizes select").show().kendoDropDownList();
                }
                that.element.on(CHANGE + NS, ".k-pager-sizes select", proxy(that._change, that));
            }
            if (options.refresh) {
                if (!that.element.find(".k-pager-refresh").length) {
                    that.element.append('<a href="#" class="k-pager-refresh k-link"  title="' + options.messages.refresh + '"><span class="k-icon k-i-refresh">' + options.messages.refresh + "</span></a>");
                }
                that.element.on(CLICK + NS, ".k-pager-refresh", proxy(that._refreshClick, that));
            }
            if (options.info) {
                if (!that.element.find(".k-pager-info").length) {
                    that.element.append('<span class="k-pager-info k-label" />');
                }
            }
            that.element.on(CLICK + NS, "a", proxy(that._click, that)).addClass("k-pager-wrap k-widget");
            if (options.autoBind) {
                that.refresh();
            }
            kendo.notify(that);
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that.element.off(NS);
            that.dataSource.unbind(CHANGE, that._refreshHandler);
        },
        events: [ CHANGE ],
        options: {
            name: "Pager",
            selectTemplate: '<li><span class="k-state-selected">#=text#</span></li>',
            linkTemplate: '<li><a tabindex="-1" href="\\#" class="k-link" data-#=ns#page="#=idx#">#=text#</a></li>',
            buttonCount: 10,
            autoBind: true,
            numeric: true,
            info: true,
            input: false,
            previousNext: true,
            pageSizes: false,
            refresh: false,
            messages: {
                display: "{0} - {1} of {2} items",
                empty: "No items to display",
                page: "Page",
                of: "of {0}",
                itemsPerPage: "items per page",
                first: "Go to the first page",
                previous: "Go to the previous page",
                next: "Go to the next page",
                last: "Go to the last page",
                refresh: "Refresh"
            }
        },
        setDataSource: function(dataSource) {
            var that = this;
            that.dataSource.unbind(CHANGE, that._refreshHandler);
            that.dataSource = that.options.dataSource = dataSource;
            dataSource.bind(CHANGE, that._refreshHandler);
            if (that.options.autoBind) {
                dataSource.fetch();
            }
        },
        refresh: function() {
            var that = this, idx, end, start = 1, html = "", reminder, page = that.page(), options = that.options, pageSize = that.pageSize(), total = that.dataSource.total(), totalPages = that.totalPages(), linkTemplate = that.linkTemplate, buttonCount = options.buttonCount;
            if (options.numeric) {
                if (page > buttonCount) {
                    reminder = page % buttonCount;
                    start = reminder === 0 ? page - buttonCount + 1 : page - reminder + 1;
                }
                end = Math.min(start + buttonCount - 1, totalPages);
                if (start > 1) {
                    html += button(linkTemplate, start - 1, "...", false);
                }
                for (idx = start; idx <= end; idx++) {
                    html += button(idx == page ? that.selectTemplate : linkTemplate, idx, idx, true);
                }
                if (end < totalPages) {
                    html += button(linkTemplate, idx, "...", false);
                }
                if (html === "") {
                    html = that.selectTemplate({
                        text: 0
                    });
                }
                that.list.html(html);
            }
            if (options.info) {
                if (total > 0) {
                    html = kendo.format(options.messages.display, (page - 1) * pageSize + 1, // first item in the page
                    Math.min(page * pageSize, total), // last item in the page
                    total);
                } else {
                    html = options.messages.empty;
                }
                that.element.find(".k-pager-info").html(html);
            }
            if (options.input) {
                that.element.find(".k-pager-input").html(that.options.messages.page + '<input class="k-textbox">' + 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);
            }
        },
        _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);

(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", 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.hasClass(ACTIVEBORDER + "-down") ? "down" : "up";
                        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,
            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);
            }
            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;
                }
                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).css({
                    overflow: HIDDEN,
                    display: "block",
                    position: ABSOLUTE
                });
                if (support.mobileOS.android) {
                    wrapper.add(anchor).css(TRANSFORM, "translatez(0)");
                }
                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")[0];
            if (popup && popup !== that.element[0]) {
                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);

(function($, undefined) {
    var kendo = window.kendo, Widget = kendo.ui.Widget, Popup = kendo.ui.Popup, isFunction = $.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 = '<div role="tooltip" class="k-widget k-tooltip#if (!autoHide) {# k-tooltip-closable#}#">#if (!autoHide) {# <div class="k-tooltip-button"><a href="\\#" class="k-icon k-i-close">close</a></div> #}#' + '<div class="k-tooltip-content"></div>' + '#if (callout){ #<div class="k-callout k-callout-#=dir#"></div>#}#' + "</div>", IFRAMETEMPLATE = kendo.template("<iframe frameborder='0' class='" + KCONTENTFRAME + "' " + "src='#= content.url #'>" + "This page requires frames in order to show content" + "</iframe>"), 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: "height"
        },
        vertical: {
            offset: "left",
            size: "width"
        }
    }, 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) {
            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,
            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;
            if (that.options.showOn && that.options.showOn.match(/click|focus/)) {
                that._show($(e.currentTarget));
            } else {
                clearTimeout(that.timeout);
                that.timeout = setTimeout(function() {
                    that._show($(e.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({
                    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();
            }
        },
        hide: function() {
            if (this.popup) {
                this.popup.close();
            }
        },
        show: function(target) {
            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);
            }
            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("borderWidth"), 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);

(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", 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 = $('<ul unselectable="on" class="k-list k-reset"/>').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 = $("<div class='k-list-container'/>").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._initValue();
        },
        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);
        },
        _initValue: function() {
            var that = this, value = that.options.value;
            if (value) {
                that.element.val(value);
            } else {
                value = that.element.val();
            }
            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);
            }
            width = computedWidth - (list.outerWidth() - list.width());
            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;
            ul.scrollTop = ulScrollTop > itemOffsetTop ? itemOffsetTop : bottomDistance > ulScrollTop + ulOffsetHeight ? bottomDistance - ulOffsetHeight : 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('<li tabindex="-1" role="option" unselectable="on" class="k-item">${' + kendo.expr(options.dataTextField, "data") + "}</li>", {
                    useWithBlock: false
                });
            } else {
                template = kendo.template(template);
                that.template = function(data) {
                    return '<li tabindex="-1" role="option" unselectable="on" class="k-item">' + template(data) + "</li>";
                };
            }
        },
        _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);
        }
    });
    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) {}
        },
        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();
            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, isSelect = this._isSelect, option, selectedIndex;
            element = element[0];
            if (value === undefined) {
                if (isSelect) {
                    selectedIndex = element.selectedIndex;
                    if (selectedIndex > -1) {
                        option = element.options[selectedIndex];
                        if (option) {
                            value = option.value;
                        }
                    }
                } else {
                    value = element.value;
                }
                return value;
            } else {
                if (isSelect) {
                    element.selectedIndex = idx;
                } 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.dataSource = kendo.data.DataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind(PROGRESS, that._progressHandler).bind(REQUESTEND, that._requestEndHandler);
        },
        _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, pressed;
            if (key === keys.UP || down) {
                if (e.altKey) {
                    that.toggle(down);
                } else if (down) {
                    if (!current || that.selectedIndex === -1 && !that.value() && current[0] === ul.firstChild) {
                        current = ul.firstChild;
                    } else {
                        current = current[0].nextSibling;
                    }
                    that[methodName](current);
                } else {
                    that[methodName](current ? current[0].previousSibling : ul.lastChild);
                }
                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(value) {
            var that = this, options = that.options;
            value = that._selectedValue || options.value || that._accessor();
            if (value) {
                that.value(value);
            } else if (!that._bound) {
                that.select(options.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) {
                options = optionLabel;
                selectedIndex += 1;
                idx = 1;
            }
            for (;idx < length; idx++) {
                option = "<option";
                dataItem = data[idx];
                dataText = that._text(dataItem);
                dataValue = that._value(dataItem);
                if (dataValue !== undefined) {
                    dataValue += "";
                    if (dataValue.indexOf('"') !== -1) {
                        dataValue = dataValue.replace(quotRegExp, "&quot;");
                    }
                    option += ' value="' + dataValue + '"';
                }
                option += ">";
                if (dataText !== undefined) {
                    option += htmlEncode(dataText);
                }
                option += "</option>";
                options += option;
            }
            element.html(options);
            element[0].selectedIndex = selectedIndex;
        },
        _reset: function() {
            var that = this, element = that.element, form = element.closest("form");
            if (form[0]) {
                that._resetHandler = function() {
                    setTimeout(function() {
                        that.value(that._initial);
                    });
                };
                that._form = form.on("reset", that._resetHandler);
            }
        },
        _cascade: function() {
            var that = this, options = that.options, cascade = options.cascadeFrom, parent, parentElement, select, valueField, change;
            if (cascade) {
                that._selectedValue = options.value || that._accessor();
                parentElement = $("#" + cascade);
                parent = parentElement.data("kendo" + options.name);
                if (!parent) {
                    parent = parentElement.data("kendo" + alternativeNames[options.name]);
                }
                if (!parent) {
                    return;
                }
                valueField = parent.options.dataValueField;
                change = function() {
                    var value = that._selectedValue || that.value();
                    if (value) {
                        that.value(value);
                        if (!that.dataSource.view()[0] || that.selectedIndex === -1) {
                            that._clearSelection(parent, true);
                        }
                    } else {
                        that.select(options.index);
                    }
                    that.enable();
                };
                select = function() {
                    var dataItem = parent.dataItem(), filterValue = dataItem ? parent._value(dataItem) : null, expressions, filters;
                    if (filterValue) {
                        expressions = that.dataSource.filter() || {};
                        removeFiltersForField(expressions, valueField);
                        filters = expressions.filters || [];
                        filters.push({
                            field: valueField,
                            operator: "eq",
                            value: filterValue
                        });
                        that.dataSource.one(CHANGE, change).filter(filters);
                    } else {
                        that.enable(false);
                        that._clearSelection(parent);
                    }
                    that._triggerCascade();
                };
                parent.bind("cascade", function() {
                    select();
                });
                //refresh was called
                if (parent._bound) {
                    select();
                } else if (!parent.value()) {
                    that.enable(false);
                }
            }
        }
    });
    function removeFiltersForField(expression, field) {
        if (expression.filters) {
            expression.filters = $.grep(expression.filters, function(filter) {
                removeFiltersForField(filter, field);
                if (filter.filters) {
                    return filter.filters.length;
                } else {
                    return filter.field != field;
                }
            });
        }
    }
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, support = kendo.support, ui = kendo.ui, Widget = ui.Widget, parse = kendo.parseDate, adjustDate = kendo._adjustDate, keys = kendo.keys, extractFormat = kendo._extractFormat, template = kendo.template, getCulture = kendo.getCulture, transitions = kendo.support.transitions, transitionOrigin = transitions ? transitions.css + "transform-origin" : "", cellTemplate = template('<td#=data.cssClass# role="gridcell"><a tabindex="-1" class="k-link" href="\\#" data-#=data.ns#value="#=data.dateString#">#=data.value#</a></td>', {
        useWithBlock: false
    }), emptyCellTemplate = template('<td role="gridcell">&nbsp;</td>', {
        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 = 6e4, MS_PER_DAY = 864e5, 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";
            }
            value = options.value;
            normalize(options);
            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._view);
            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;
            } else {
                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("<div/>");
                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]);
            adjustDate(value);
            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, element = that.element, today = new DATE(), footer = element.find(".k-footer");
            if (!template) {
                that._toggle(false);
                footer.hide();
                return;
            }
            if (!footer[0]) {
                footer = $('<div class="k-footer"><a href="#" class="k-link k-nav-today"></a></div>').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('<div class="k-header">' + '<a href="#" role="button" class="k-link k-nav-prev"><span class="k-icon k-i-arrow-w"></span></a>' + '<a href="#" role="button" aria-live="assertive" aria-atomic="true" class="k-link k-nav-fast"></a>' + '<a href="#" role="button" class="k-link k-nav-next"><span class="k-icon k-i-arrow-e"></span></a>' + "</div>");
            }
            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, selectedValue = +that._value, bigger, navigate, arrow = NEXTARROW;
            if (value === undefined) {
                return options[option];
            }
            value = parse(value, options.format, options.culture);
            if (!value) {
                return;
            }
            options[option] = new DATE(+value);
            navigate = that._view.compare(value, that._current);
            if (option === MIN) {
                bigger = +value > selectedValue;
                navigate = navigate > -1;
                arrow = PREVARROW;
            } else {
                bigger = selectedValue > +value;
                navigate = navigate < 1;
            }
            if (bigger) {
                that.value(null);
            } else if (navigate) {
                that.navigate();
            } else {
                that[arrow].toggleClass(DISABLED, false).attr(ARIA_DISABLED, false);
            }
            that._toggle();
        },
        _toggle: function(toggle) {
            var that = this, options = that.options, link = that._today;
            if (toggle === undefined) {
                toggle = isInRange(new DATE(), 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 = new DATE();
            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('<td#=data.cssClass# role="gridcell"><a tabindex="-1" class="k-link#=data.linkClass#" href="#=data.url#" ' + kendo.attr("value") + '="#=data.dateString#" title="#=data.title#">' + (content || "#=data.value#") + "</a></td>", {
                    useWithBlock: !!content
                }),
                empty: template('<td role="gridcell">' + (empty || "&nbsp;") + "</td>", {
                    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 = '<table tabindex="0" role="grid" class="k-content" cellspacing="0"><thead><tr role="row">';
                for (;idx < 7; idx++) {
                    html += '<th scope="col" title="' + names[idx] + '">' + shortNames[idx] + "</th>";
                }
                today = new DATE(today.getFullYear(), today.getMonth(), today.getDate());
                adjustDate(today);
                today = +today;
                return view({
                    cells: 42,
                    perRow: 7,
                    html: html += '</tr></thead><tbody><tr role="row">',
                    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);
                }
                adjustDate(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);
                    }
                }
                adjustDate(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 || '<table tabindex="0" role="grid" class="k-content k-meta-view" cellspacing="0"><tbody><tr role="row">';
        for (;idx < length; idx++) {
            if (idx > 0 && idx % cellsPerRow === 0) {
                html += '</tr><tr role="row">';
            }
            data = build(start, idx);
            html += isInRange(start, min, max) ? content(data) : empty(data);
            setter(start, 1);
        }
        return html + "</tr></tbody></table>";
    }
    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 restrictValue(value, min, max) {
        var today = new DATE();
        today = new DATE(today.getFullYear(), today.getMonth(), today.getDate());
        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;
    }
    calendar.isEqualDatePart = isEqualDatePart;
    calendar.makeUnselectable = makeUnselectable;
    calendar.restrictValue = restrictValue;
    calendar.isInRange = isInRange;
    calendar.normalize = normalize;
    calendar.viewsEnum = views;
    kendo.calendar = calendar;
})(window.kendo.jQuery);

(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 = "<div />", SPAN = "<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.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('<td#=data.cssClass#><a tabindex="-1" class="k-link" href="\\#" ' + kendo.attr("value") + '="#=data.dateString#" title="#=data.title#">' + (content || "#=data.value#") + "</a></td>", {
                    useWithBlock: !!content
                }),
                empty: template("<td>" + (empty || "&nbsp;") + "</td>", {
                    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();
            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.value(options.value || that.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 = $('<span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-i-calendar">select</span></span>').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, form = 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);

(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._accessors();
            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: {}
        },
        _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 || ""));
            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);
            }
        },
        _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 = $('<span class="k-icon k-loading" style="display:none"></span>').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("<span />").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);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, Select = ui.Select, os = kendo.support.mobileOS, ns = ".kendoDropDownList", DISABLED = "disabled", READONLY = "readonly", CHANGE = "change", SELECT = "select", 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);
            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._accessors();
            that._dataSource();
            that._ignoreCase();
            that._enable();
            that._cascade();
            that._oldIndex = that.selectedIndex = -1;
            if (index !== undefined) {
                options.index = index;
            }
            if (options.autoBind) {
                that.dataSource.fetch();
            } else {
                text = options.text || "";
                if (!text) {
                    optionLabel = that._optionLabelText(options.optionLabel), useOptionLabel = optionLabel && options.index === 0;
                    if (element.is(SELECT)) {
                        if (useOptionLabel) {
                            text = optionLabel;
                        } else {
                            text = element.children(":selected").text();
                        }
                    } else if (!element[0].value && useOptionLabel) {
                        text = optionLabel;
                    }
                }
                that.text(text);
            }
            kendo.notify(that);
        },
        options: {
            name: "DropDownList",
            enabled: true,
            autoBind: true,
            index: 0,
            text: null,
            value: null,
            template: "",
            delay: 500,
            height: 200,
            dataTextField: "",
            dataValueField: "",
            optionLabel: "",
            cascadeFrom: "",
            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();
        },
        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.element.is(SELECT)) {
                if (optionLabel && length) {
                    optionLabel = that._optionLabelText(optionLabel);
                    optionLabel = '<option value="">' + optionLabel + "</option>";
                }
                that._options(data, optionLabel);
            }
            if (that._open) {
                that._open = false;
                that.toggle(!!length);
            }
            that._hideBusy();
            that._makeUnselectable();
            if (!that._fetch && length) {
                that._selectItem();
            }
            that._bound = true;
            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 span = this.span;
            if (text !== undefined) {
                span.text(text);
            } else {
                return span.text();
            }
        },
        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);
            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, function() {
                    dropDownWrapper.addClass(FOCUSED);
                    that._blured = false;
                }).on("focusout" + ns, function() {
                    if (!that._blured) {
                        that._triggerCascade();
                        that._blur();
                        dropDownWrapper.removeClass(FOCUSED);
                        that._blured = true;
                        element.blur();
                    }
                });
            } else {
                if (disable) {
                    wrapper.removeAttr(TABINDEX);
                    dropDownWrapper.addClass(STATEDISABLED).removeClass(DEFAULT);
                } else {
                    dropDownWrapper.addClass(DEFAULT).removeClass(STATEDISABLED);
                }
                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(character, index) {
            var that = this, ignoreCase = that.options.ignoreCase, data = that._data(), length = data.length, text;
            for (;index < length; index++) {
                text = that._text(data[index]);
                if (text) {
                    text = text + "";
                    if (ignoreCase) {
                        text = text.toLowerCase();
                    }
                    if (text.indexOf(character) === 0) {
                        that._select(index);
                        that._triggerEvents();
                        return true;
                    }
                }
            }
            return false;
        },
        _keypress: function(e) {
            var that = this;
            setTimeout(function() {
                var character = String.fromCharCode(e.keyCode || e.charCode), index = that.selectedIndex;
                if (that.options.ignoreCase) {
                    character = character.toLowerCase();
                }
                if (character === that._last && index > -1) {
                    that._word = character;
                    if (that._selectNext(character, index + 1)) {
                        return;
                    }
                } else {
                    that._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, 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.search(word);
                    }
                }).fetch();
                return;
            }
            that.search(word);
            that._triggerEvents();
        },
        _select: function(li) {
            var that = this, current = that._current, data = that._data(), value, text, 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 = data[idx];
                    text = that._text(data);
                    value = that._value(data);
                    that.selectedIndex = idx;
                    that.text(text);
                    that._accessor(value !== undefined ? value : text, idx);
                    that._selectedValue = that._accessor();
                    that.current(li.addClass(SELECTED));
                    if (that._optionID) {
                        that._current.attr("aria-selected", true);
                    }
                }
            }
        },
        _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('<span unselectable="on" class="k-dropdown-wrap k-state-default"><span unselectable="on" class="k-input">&nbsp;</span><span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-i-arrow-s">select</span></span></span>').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("<span />").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.text(optionLabel);
            that.element.val("");
            that.selectedIndex = -1;
        }
    });
    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);

(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._accessors();
            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);
                }
            }
            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,
            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, 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) {
                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());
                }
            }
            if (length) {
                if (options.highlightFirst) {
                    that.current($(ul.firstChild));
                }
                if (options.suggest && that.input.val() && that._request !== undefined) {
                    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;
                List.selectText(element, caret, value.length);
            }
        },
        text: function(text) {
            text = text === null ? "" : text;
            var that = this, textAccessor = that._text, input = that.input[0], ignoreCase = that.options.ignoreCase, loweredText = text, dataItem;
            if (text !== undefined) {
                dataItem = that.dataItem();
                if (dataItem && textAccessor(dataItem) === text) {
                    that._triggerCascade();
                    return;
                }
                if (ignoreCase) {
                    loweredText = loweredText.toLowerCase();
                }
                that._select(function(data) {
                    data = textAccessor(data);
                    if (ignoreCase) {
                        data = (data + "").toLowerCase();
                    }
                    return data === loweredText;
                });
                if (that.selectedIndex < 0) {
                    that._custom(text);
                    input.value = text;
                }
                that._triggerCascade();
            } else {
                return input.value;
            }
        },
        toggle: function(toggle) {
            var that = this;
            that._toggle(toggle);
        },
        value: function(value) {
            var that = this, 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);
                    that.text(value);
                    that._placeholder();
                }
                that._prev = 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 = $("<option/>");
                    element.append(custom);
                }
                custom.text(value);
                custom[0].selected = true;
            } else {
                element.val(value);
            }
            that._selectedValue = value;
        },
        _filter: function(word) {
            var that = this, options = that.options, dataSource = that.dataSource, ignoreCase = options.ignoreCase, predicate = function(dataItem) {
                var text = that._text(dataItem);
                if (text !== undefined) {
                    text = text + "";
                    if (text !== "" && word === "") {
                        return false;
                    }
                    if (ignoreCase) {
                        text = text.toLowerCase();
                    }
                    return text.indexOf(word) === 0;
                }
            };
            if (ignoreCase) {
                word = word.toLowerCase();
            }
            if (!that.ul[0].firstChild) {
                dataSource.one(CHANGE, function() {
                    if (dataSource.data()[0]) {
                        that.search(word);
                    }
                }).fetch();
                return;
            }
            if (that._highlight(predicate) !== -1) {
                if (options.suggest && that._current) {
                    that.suggest(that._current);
                }
                that.open();
            }
            that._hideBusy();
        },
        _highlight: function(li) {
            var that = this, idx;
            if (li === undefined || li === null) {
                return -1;
            }
            li = that._get(li);
            idx = List.inArray(li[0], that.ul[0]);
            if (idx == -1) {
                if (that.options.highlightFirst && !that.text()) {
                    li = $(that.ul[0].firstChild);
                } else {
                    li = NULL;
                }
            }
            that.current(li);
            return idx;
        },
        _input: function() {
            var that = this, element = that.element.removeClass("k-input")[0], accessKey = element.accessKey, wrapper = that.wrapper, SELECTOR = "input.k-input", name = element.name || "", input;
            if (name) {
                name = 'name="' + name + '_input" ';
            }
            input = wrapper.find(SELECTOR);
            if (!input[0]) {
                wrapper.append('<span tabindex="-1" unselectable="on" class="k-dropdown-wrap k-state-default"><input ' + name + 'class="k-input" type="text" autocomplete="off"/><span tabindex="-1" unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-i-arrow-s">select</span></span></span>').append(that.element);
                input = wrapper.find(SELECTOR);
            }
            input[0].style.cssText = element.style.cssText;
            if (element.maxLength > -1) {
                input[0].maxLength = element.maxLength;
            }
            input.addClass(element.className).val(element.value).css({
                width: "100%",
                height: element.style.height
            }).attr({
                role: "combobox",
                "aria-expanded": false
            }).show();
            if (placeholderSupported) {
                input.attr("placeholder", that.options.placeholder);
            }
            if (accessKey) {
                element.accessKey = "";
                input[0].accessKey = accessKey;
            }
            that._focused = that.input = input;
            that._inputWrapper = $(wrapper[0].firstChild);
            that._arrow = wrapper.find(".k-icon").attr({
                role: "button",
                tabIndex: -1
            });
            if (element.id) {
                that._arrow.attr("aria-controls", that.ul[0].id);
            }
        },
        _keydown: function(e) {
            var that = this, key = e.keyCode;
            that._last = key;
            clearTimeout(that._typing);
            if (key != keys.TAB && !that._move(e)) {
                that._search();
            }
        },
        _placeholder: function(show) {
            if (placeholderSupported) {
                return;
            }
            var that = this, input = that.input, placeholder = that.options.placeholder, value;
            if (placeholder) {
                value = that.value();
                if (show === undefined) {
                    show = !value;
                }
                input.toggleClass("k-readonly", show);
                if (!show) {
                    if (!value) {
                        placeholder = "";
                    } else {
                        return;
                    }
                }
                input.val(placeholder);
            }
        },
        _search: function() {
            var that = this;
            that._typing = setTimeout(function() {
                var value = that.text();
                if (that._prev !== value) {
                    that._prev = value;
                    that.search(value);
                }
            }, that.options.delay);
        },
        _select: function(li) {
            var that = this, text, value, data = that._data(), idx = that._highlight(li);
            that.selectedIndex = idx;
            if (idx !== -1) {
                if (that._state === STATE_FILTER) {
                    that._state = STATE_ACCEPT;
                }
                that._current.addClass(STATE_SELECTED);
                data = data[idx];
                text = that._text(data);
                value = that._value(data);
                that._prev = that.input[0].value = text;
                that._accessor(value !== undefined ? value : text, idx);
                that._selectedValue = that._accessor();
                that._placeholder();
                if (that._optionID) {
                    that._current.attr("aria-selected", true);
                }
            }
        },
        _wrapper: function() {
            var that = this, element = that.element, wrapper = element.parent();
            if (!wrapper.is("span.k-widget")) {
                wrapper = element.hide().wrap("<span />").parent();
                wrapper[0].style.cssText = element[0].style.cssText;
            }
            that.wrapper = wrapper.addClass("k-widget k-combobox k-header").addClass(element[0].className).css("display", "");
        },
        _clearSelection: function(parent, isFiltered) {
            var that = this, hasValue = parent._selectedValue || parent.value(), custom = hasValue && parent.selectedIndex === -1;
            if (isFiltered || !hasValue || custom) {
                that.value("");
            }
        }
    });
    ui.plugin(ComboBox);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, List = ui.List, keys = kendo.keys, activeElement = kendo._activeElement, ObservableArray = kendo.data.ObservableArray, proxy = $.proxy, ID = "id", LI = "li", ACCEPT = "accept", FILTER = "filter", OPEN = "open", CLOSE = "close", CHANGE = "change", PROGRESS = "progress", SELECT = "select", NEXT = "nextSibling", PREV = "previousSibling", HIDE = ' style="display:none"', ARIA_DISABLED = "aria-disabled", ARIA_READONLY = "aria-readonly", FOCUSEDCLASS = "k-state-focused", HIDDENCLASS = "k-loading-hidden", HOVERCLASS = "k-state-hover", STATEDISABLED = "k-state-disabled", DISABLED = "disabled", READONLY = "readonly", ns = ".kendoMultiSelect", CLICK = "click" + ns, KEYDOWN = "keydown" + ns, MOUSEENTER = "mouseenter" + ns, MOUSELEAVE = "mouseleave" + ns, HOVEREVENTS = MOUSEENTER + " " + MOUSELEAVE, quotRegExp = /"/g, styles = [ "font-family", "font-size", "font-stretch", "font-style", "font-weight", "letter-spacing", "text-transform", "line-height" ];
    var MultiSelect = List.extend({
        init: function(element, options) {
            var that = this, id;
            that.ns = ns;
            List.fn.init.call(that, element, options);
            that._wrapper();
            that._tagList();
            that._input();
            that._textContainer();
            that._loader();
            that._tabindex(that.input);
            options = that.options;
            element = that.element.attr("multiple", "multiple").hide();
            if (!options.placeholder) {
                options.placeholder = element.data("placeholder");
            }
            id = element.attr(ID);
            if (id) {
                that._tagID = id + "_tag_active";
                id = id + "_taglist";
                that.tagList.attr(ID, id);
            }
            that._aria(id);
            that._dataSource();
            that._ignoreCase();
            that._accessors();
            that._popup();
            that._values = [];
            that._dataItems = [];
            that._reset();
            that._enable();
            that._placeholder();
            if (options.autoBind) {
                that.dataSource.fetch();
            }
            kendo.notify(that);
        },
        options: {
            name: "MultiSelect",
            enabled: true,
            autoBind: true,
            highlightFirst: true,
            dataTextField: "",
            dataValueField: "",
            filter: "startswith",
            ignoreCase: true,
            minLength: 0,
            delay: 100,
            value: null,
            maxSelectedItems: null,
            itemTemplate: "",
            tagTemplate: "",
            placeholder: "",
            height: 200,
            animation: {}
        },
        events: [ OPEN, CLOSE, CHANGE, SELECT, "dataBinding", "dataBound" ],
        setDataSource: function(dataSource) {
            this.options.dataSource = dataSource;
            this._dataSource();
            if (this.options.autoBind) {
                this.dataSource.fetch();
            }
        },
        setOptions: function(options) {
            List.fn.setOptions.call(this, options);
            this._template();
            this._accessors();
            this._aria(this.tagList.attr(ID));
        },
        current: function(candidate) {
            this.currentTag(null);
            return List.fn.current.call(this, candidate);
        },
        currentTag: function(candidate) {
            var that = this;
            if (candidate !== undefined) {
                if (that._currentTag) {
                    that._currentTag.removeClass(FOCUSEDCLASS).removeAttr(ID);
                    that.input.removeAttr("aria-activedescendant");
                }
                if (candidate) {
                    candidate.addClass(FOCUSEDCLASS).attr(ID, that._tagID);
                    that.input.attr("aria-activedescendant", that._tagID);
                }
                that._currentTag = candidate;
            } else {
                return that._currentTag;
            }
        },
        dataItems: function() {
            return this._dataItems;
        },
        destroy: function() {
            var that = this, ns = that.ns;
            that.wrapper.off(ns);
            that.tagList.off(ns);
            that.input.off(ns);
            List.fn.destroy.call(that);
        },
        _editable: function(options) {
            var that = this, disable = options.disable, readonly = options.readonly, wrapper = that.wrapper.off(ns), tagList = that.tagList.off(ns), input = that.element.add(that.input.off(ns));
            if (!readonly && !disable) {
                wrapper.removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover).on("mousedown" + ns, function(e) {
                    e.preventDefault();
                    if (e.target.className.indexOf("k-delete") == -1) {
                        that.open();
                    }
                    if (that.input[0] !== activeElement()) {
                        that.input.focus();
                    }
                });
                that.input.on(KEYDOWN, proxy(that._keydown, that)).on("paste" + ns, proxy(that._search, that)).on("focus" + ns, function() {
                    that._placeholder(false);
                }).on("blur" + ns, function() {
                    clearTimeout(that._typing);
                    that._placeholder();
                    that.close();
                    if (that._state === FILTER) {
                        that._state = ACCEPT;
                    }
                });
                input.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false);
                tagList.on(MOUSEENTER, LI, function() {
                    $(this).addClass(HOVERCLASS);
                }).on(MOUSELEAVE, LI, function() {
                    $(this).removeClass(HOVERCLASS);
                }).on(CLICK, ".k-delete", function(e) {
                    that._unselect($(e.target).closest(LI));
                    that._change();
                    that.close();
                });
            } else {
                if (disable) {
                    wrapper.addClass(STATEDISABLED);
                } else {
                    wrapper.removeClass(STATEDISABLED);
                }
                input.attr(DISABLED, disable).attr(READONLY, readonly).attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
            }
        },
        close: function() {
            this.popup.close();
            this.current(null);
        },
        open: function() {
            var that = this;
            if (!that.ul[0].firstChild || that._state === ACCEPT) {
                that._state = "";
                that._open = true;
                that._filterSource();
            } else if (that._visibleItems && that._allowSelection()) {
                that.popup.open();
                that.current(that.options.highlightFirst ? $(first(that.ul[0])) : null);
            }
        },
        toggle: function(toggle) {
            this[toggle ? OPEN : CLOSE]();
        },
        refresh: function() {
            var that = this, li = null, length;
            that.trigger("dataBinding");
            length = that._render(that.dataSource.view());
            that._height(length);
            if (that._setInitialValues) {
                that._setInitialValues = false;
                that.value(that._initialValues);
            }
            if (that._open) {
                that._open = false;
                that.toggle(length);
            }
            if (that.popup.visible()) {
                that.popup._position();
                if (that.options.highlightFirst) {
                    li = $(first(that.ul[0]));
                }
            }
            that.current(li);
            if (that._touchScroller) {
                that._touchScroller.reset();
            }
            that._makeUnselectable();
            that._hideBusy();
            that.trigger("dataBound");
        },
        search: function(word) {
            var that = this, options = that.options, ignoreCase = options.ignoreCase, filter = options.filter, field = options.dataTextField;
            clearTimeout(that._typing);
            word = typeof word === "string" ? word : that.input.val();
            if (word.length >= options.minLength) {
                that._state = FILTER;
                that._open = true;
                that._filterSource({
                    value: ignoreCase ? word.toLowerCase() : word,
                    field: field,
                    operator: filter,
                    ignoreCase: ignoreCase
                });
            }
        },
        value: function(value) {
            var that = this, tags = $(that.tagList[0].children), length = tags.length, dataItemIndex, idx = 0;
            if (value === undefined) {
                return that._values;
            }
            if (that._fetchItems(value)) {
                return;
            }
            for (;idx < length; idx++) {
                that._unselect(tags.eq(idx));
            }
            if (value !== null) {
                value = $.isArray(value) || value instanceof ObservableArray ? value : [ value ];
                for (idx = 0, length = value.length; idx < length; idx++) {
                    dataItemIndex = that._index(value[idx]);
                    if (dataItemIndex > -1) {
                        that._select(dataItemIndex);
                    }
                }
                that._old = that._values.slice();
            }
        },
        _dataSource: function() {
            var that = this, element = that.element, options = that.options, dataSource = options.dataSource || {};
            dataSource = $.isArray(dataSource) ? {
                data: dataSource
            } : dataSource;
            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.dataSource = kendo.data.DataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind(PROGRESS, that._progressHandler);
        },
        _fetchItems: function(value) {
            var that = this;
            if (!that._fetch && !that.ul[0].firstChild) {
                that.dataSource.one(CHANGE, function() {
                    that.value(value);
                    that._fetch = false;
                });
                that._fetch = true;
                that.dataSource.fetch();
                return true;
            }
        },
        _reset: function() {
            var that = this, element = that.element, form = element.closest("form");
            if (form[0]) {
                that._resetHandler = function() {
                    setTimeout(function() {
                        that.value(that._initialValues);
                    });
                };
                that._form = form.on("reset", that._resetHandler);
            }
        },
        _initValue: function() {
            var that = this, value = that.options.value || that.element.val();
            if (value === null) {
                value = [];
            }
            that._old = that._initialValues = value;
            that._setInitialValues = !!value[0];
        },
        _change: function() {
            var that = this, value = that.value();
            if (!compare(value, that._old)) {
                that._old = value.slice();
                that.trigger(CHANGE);
                // trigger the DOM change event so any subscriber gets notified
                that.element.trigger(CHANGE);
            }
        },
        _click: function(e) {
            var that = this, li = $(e.currentTarget);
            if (!e.isDefaultPrevented()) {
                if (that.trigger(SELECT, {
                    item: li
                })) {
                    that.close();
                    return;
                }
                that._select(li);
                that._change();
                that.close();
            }
        },
        _item: function(item, direction) {
            item = item[direction]();
            if (item[0] && !item.is(":visible")) {
                item = this._item(item, direction);
            }
            return item;
        },
        _keydown: function(e) {
            var that = this, key = e.keyCode, tag = that._currentTag, current = that._current, hasValue = that.input.val(), isRtl = kendo.support.isRtl(that.wrapper), visible = that.popup.visible();
            if (key === keys.DOWN) {
                e.preventDefault();
                if (!visible) {
                    that.open();
                    return;
                }
                if (current) {
                    current = sibling(current[0], NEXT);
                } else {
                    current = that.ul[0].firstChild;
                }
                if (current) {
                    that.current($(current));
                }
            } else if (key === keys.UP) {
                if (visible) {
                    if (current) {
                        current = sibling(current[0], PREV);
                    } else {
                        current = that.ul[0].lastChild;
                    }
                    that.current($(current));
                    if (!that._current[0]) {
                        that.close();
                    }
                }
                e.preventDefault();
            } else if (key === keys.LEFT && !isRtl || key === keys.RIGHT && isRtl) {
                if (!hasValue) {
                    tag = tag ? tag.prev() : $(that.tagList[0].lastChild);
                    if (tag[0]) {
                        that.currentTag(tag);
                    }
                }
            } else if (key === keys.RIGHT && !isRtl || key === keys.LEFT && isRtl) {
                if (!hasValue && tag) {
                    tag = tag.next();
                    that.currentTag(tag[0] ? tag : null);
                }
            } else if (key === keys.ENTER) {
                if (visible) {
                    if (current) {
                        that._select(current);
                    }
                    that._change();
                    that.close();
                    e.preventDefault();
                }
            } else if (key === keys.ESC) {
                if (visible) {
                    e.preventDefault();
                } else {
                    that.currentTag(null);
                }
                that.close();
            } else if (key === keys.HOME) {
                if (visible) {
                    that.current($(first(that.ul[0])));
                } else if (!hasValue) {
                    tag = that.tagList[0].firstChild;
                    if (tag) {
                        that.currentTag($(tag));
                    }
                }
            } else if (key === keys.END) {
                if (visible) {
                    that.current($(last(that.ul[0])));
                } else if (!hasValue) {
                    tag = that.tagList[0].lastChild;
                    if (tag) {
                        that.currentTag($(tag));
                    }
                }
            } else if ((key === keys.DELETE || key === keys.BACKSPACE) && !hasValue) {
                if (key === keys.BACKSPACE && !tag) {
                    tag = $(that.tagList[0].lastChild);
                }
                if (tag && tag[0]) {
                    that._unselect(tag);
                    that._change();
                    that.close();
                }
            } else {
                clearTimeout(that._typing);
                that._search();
            }
        },
        _hideBusy: function() {
            var that = this;
            clearTimeout(that._busy);
            that.input.attr("aria-busy", false);
            that._loading.addClass(HIDDENCLASS);
            that._busy = null;
        },
        _showBusy: function() {
            var that = this;
            if (that._busy) {
                return;
            }
            that._busy = setTimeout(function() {
                that.input.attr("aria-busy", true);
                that._loading.removeClass(HIDDENCLASS);
            }, 100);
        },
        _placeholder: function(show) {
            var that = this, input = that.input;
            if (show === undefined) {
                show = false;
                if (input[0] !== activeElement()) {
                    show = !that._dataItems[0];
                }
                that.wrapper.removeClass(FOCUSEDCLASS);
            } else {
                that.wrapper.addClass(FOCUSEDCLASS);
            }
            input.toggleClass("k-readonly", show).val(show ? that.options.placeholder : "");
            that._scale();
        },
        _scale: function() {
            var that = this, wrapperWidth = that.wrapper.width(), span = that._span.text(that.input.val()), textWidth;
            if (!wrapperWidth) {
                span.appendTo(document.documentElement);
                wrapperWidth = textWidth = span.width() + 25;
                span.appendTo(this.wrapper);
            } else {
                textWidth = span.width() + 25;
            }
            that.input.width(textWidth > wrapperWidth ? wrapperWidth : textWidth);
        },
        _option: function(dataItem, selected) {
            var option = "<option", dataText = this._text(dataItem), dataValue = this._value(dataItem);
            if (dataValue !== undefined) {
                dataValue += "";
                if (dataValue.indexOf('"') !== -1) {
                    dataValue = dataValue.replace(quotRegExp, "&quot;");
                }
                option += ' value="' + dataValue + '"';
            }
            if (selected) {
                option += ' selected="selected"';
            }
            option += ">";
            if (dataText !== undefined) {
                option += kendo.htmlEncode(dataText);
            }
            return option += "</option>";
        },
        _render: function(data) {
            var that = this, length = data.length, template = that.itemTemplate, values = that._dataItems.slice(0), visibleItems = 0, idx = 0, options = "", html = "", dataItem, selected;
            for (;idx < length; idx++) {
                dataItem = data[idx];
                selected = that._selected(values, dataItem);
                html += template(dataItem, idx, selected);
                options += that._option(dataItem, selected);
                if (!selected) {
                    visibleItems += 1;
                }
            }
            length = values.length;
            if (length) {
                for (idx = 0; idx < length; idx++) {
                    options += that._option(values[idx], true);
                }
            }
            that.ul[0].innerHTML = html;
            that.element.html(options);
            that._visibleItems = visibleItems;
            return visibleItems;
        },
        _selected: function(values, dataItem) {
            var that = this, textAccessor = that._text, valueAccessor = that._value, value = valueAccessor(dataItem), length = values.length, selected = false, dataValue, idx = 0;
            if (value === undefined) {
                value = textAccessor(dataItem);
            }
            for (;idx < length; idx++) {
                dataItem = values[idx];
                dataValue = valueAccessor(dataItem);
                if (dataValue === undefined) {
                    dataValue = textAccessor(dataItem);
                }
                if (dataValue !== undefined && dataValue === value) {
                    selected = true;
                    break;
                }
            }
            if (selected) {
                values.splice(idx, 1);
            }
            return selected;
        },
        _search: function() {
            var that = this;
            that._typing = setTimeout(function() {
                var value = that.input.val();
                if (that._prev !== value) {
                    that._scale();
                    that._prev = value;
                    that.search(value);
                }
            }, that.options.delay);
        },
        _allowSelection: function() {
            var max = this.options.maxSelectedItems;
            return max === null || max > this._values.length;
        },
        _select: function(li) {
            var that = this, values = that._values, dataItem, idx;
            if (!that._allowSelection()) {
                return;
            }
            if (!isNaN(li)) {
                idx = li;
                that.ul[0].children[idx].style.display = "none";
            } else {
                idx = li.hide().data("idx");
            }
            that.element[0].children[idx].selected = true;
            dataItem = that.dataSource.view()[idx];
            that.tagList.append(that.tagTemplate(dataItem));
            that._dataItems.push(dataItem);
            values.push(that._dataValue(dataItem));
            that._visibleItems -= 1;
            that.currentTag(null);
            that._placeholder();
            that._height(that._visibleItems);
            if (that._state === FILTER) {
                that._state = ACCEPT;
            }
        },
        _unselect: function(tag) {
            var that = this, index = tag.index(), dataItem, value, options, option, length;
            tag.remove();
            that.currentTag(null);
            that._values.splice(index, 1);
            dataItem = that._dataItems.splice(index, 1)[0];
            value = that._dataValue(dataItem);
            index = that._index(value);
            if (index !== -1) {
                $(that.ul[0].children[index]).show();
                that.element[0].children[index].selected = false;
                that._visibleItems += 1;
                that._height(that._visibleItems);
            } else {
                index = that.dataSource.view().length;
                options = that.element[0].children;
                length = options.length;
                for (;index < length; index++) {
                    option = options[index];
                    if (option.value == value) {
                        option.selected = false;
                        break;
                    }
                }
            }
            that._placeholder();
        },
        _template: function() {
            var that = this, options = that.options, itemTemplate = options.itemTemplate, tagTemplate = options.tagTemplate, hasDataSource = options.dataSource, textTemplate;
            if (that.element[0].length && !hasDataSource) {
                options.dataTextField = options.dataTextField || "text";
                options.dataValueField = options.dataValueField || "value";
            }
            textTemplate = kendo.template("#:" + kendo.expr(options.dataTextField, "data") + "#", {
                useWithBlock: false
            });
            itemTemplate = itemTemplate ? kendo.template(itemTemplate) : textTemplate;
            tagTemplate = tagTemplate ? kendo.template(tagTemplate) : textTemplate;
            that.itemTemplate = function(data, idx, hide) {
                return '<li tabindex="-1" role="option" data-idx="' + idx + '" unselectable="on" class="k-item"' + (hide ? HIDE : "") + ">" + itemTemplate(data) + "</li>";
            };
            that.tagTemplate = function(data) {
                return '<li class="k-button"><span>' + tagTemplate(data) + '</span><span class="k-icon k-delete">delete</span></li>';
            };
        },
        _input: function() {
            var that = this, accessKey = that.element[0].accessKey, input = that._innerWrapper.children("input.k-input");
            if (!input[0]) {
                input = $('<input class="k-input" style="width: 25px" />').appendTo(that._innerWrapper);
            }
            that.element.removeAttr("accesskey");
            that._focused = that.input = input.attr({
                accesskey: accessKey,
                role: "listbox",
                "aria-expanded": false
            });
        },
        _tagList: function() {
            var that = this, tagList = that._innerWrapper.children("ul");
            if (!tagList[0]) {
                tagList = $('<ul role="listbox" unselectable="on" class="k-reset"/>').appendTo(that._innerWrapper);
            }
            that.tagList = tagList;
        },
        _loader: function() {
            this._loading = $('<span class="k-icon k-loading ' + HIDDENCLASS + '"></span>').insertAfter(this.input);
        },
        _textContainer: function() {
            var computedStyles = kendo.getComputedStyles(this.input[0], styles);
            computedStyles.position = "absolute";
            computedStyles.visibility = "hidden";
            this._span = $("<span/>").css(computedStyles).appendTo(this.wrapper);
        },
        _wrapper: function() {
            var that = this, element = that.element, wrapper = element.parent("span.k-multiselect");
            if (!wrapper[0]) {
                wrapper = element.wrap('<div class="k-widget k-multiselect k-header" />').parent();
                wrapper[0].style.cssText = element[0].style.cssText;
                $('<div class="k-multiselect-wrap k-floatwrap" />').insertBefore(element);
            }
            that.wrapper = wrapper.addClass(element[0].className).css("display", "");
            that._innerWrapper = $(wrapper[0].firstChild);
        }
    });
    function compare(a, b) {
        var length;
        if (a === null && b !== null || a !== null && b === null) {
            return false;
        }
        length = a.length;
        if (length !== b.length) {
            return false;
        }
        while (length--) {
            if (a[length] !== b[length]) {
                return false;
            }
        }
        return true;
    }
    function first(ul) {
        var item = ul.firstChild;
        if (item && item.style.display === "none") {
            item = sibling(item, NEXT);
        }
        return item;
    }
    function last(ul) {
        var item = ul.lastChild;
        if (item && item.style.display === "none") {
            item = sibling(item, PREV);
        }
        return item;
    }
    function sibling(item, direction) {
        item = item[direction];
        if (item && item.style.display === "none") {
            item = sibling(item, direction);
        }
        return item;
    }
    ui.plugin(MultiSelect);
})(window.kendo.jQuery);

(function($, parseInt, undefined) {
    // WARNING: removing the following jshint declaration and turning
    // == into === to make JSHint happy will break functionality.
    /*jshint eqnull:true  */
    var kendo = window.kendo, Class = kendo.Class, ui = kendo.ui, Widget = ui.Widget, KEYS = kendo.keys, BACKGROUNDCOLOR = "background-color", UNSELECTABLE = "unselectable", ITEMSELECTEDCLASS = "k-state-selected", SIMPLEPALETTE = "000000,7f7f7f,880015,ed1c24,ff7f27,fff200,22b14c,00a2e8,3f48cc,a349a4,ffffff,c3c3c3,b97a57,ffaec9,ffc90e,efe4b0,b5e61d,99d9ea,7092be,c8bfe7", WEBPALETTE = "FFFFFF,FFCCFF,FF99FF,FF66FF,FF33FF,FF00FF,CCFFFF,CCCCFF,CC99FF,CC66FF,CC33FF,CC00FF,99FFFF,99CCFF,9999FF,9966FF,9933FF,9900FF,FFFFCC,FFCCCC,FF99CC,FF66CC,FF33CC,FF00CC,CCFFCC,CCCCCC,CC99CC,CC66CC,CC33CC,CC00CC,99FFCC,99CCCC,9999CC,9966CC,9933CC,9900CC,FFFF99,FFCC99,FF9999,FF6699,FF3399,FF0099,CCFF99,CCCC99,CC9999,CC6699,CC3399,CC0099,99FF99,99CC99,999999,996699,993399,990099,FFFF66,FFCC66,FF9966,FF6666,FF3366,FF0066,CCFF66,CCCC66,CC9966,CC6666,CC3366,CC0066,99FF66,99CC66,999966,996666,993366,990066,FFFF33,FFCC33,FF9933,FF6633,FF3333,FF0033,CCFF33,CCCC33,CC9933,CC6633,CC3333,CC0033,99FF33,99CC33,999933,996633,993333,990033,FFFF00,FFCC00,FF9900,FF6600,FF3300,FF0000,CCFF00,CCCC00,CC9900,CC6600,CC3300,CC0000,99FF00,99CC00,999900,996600,993300,990000,66FFFF,66CCFF,6699FF,6666FF,6633FF,6600FF,33FFFF,33CCFF,3399FF,3366FF,3333FF,3300FF,00FFFF,00CCFF,0099FF,0066FF,0033FF,0000FF,66FFCC,66CCCC,6699CC,6666CC,6633CC,6600CC,33FFCC,33CCCC,3399CC,3366CC,3333CC,3300CC,00FFCC,00CCCC,0099CC,0066CC,0033CC,0000CC,66FF99,66CC99,669999,666699,663399,660099,33FF99,33CC99,339999,336699,333399,330099,00FF99,00CC99,009999,006699,003399,000099,66FF66,66CC66,669966,666666,663366,660066,33FF66,33CC66,339966,336666,333366,330066,00FF66,00CC66,009966,006666,003366,000066,66FF33,66CC33,669933,666633,663333,660033,33FF33,33CC33,339933,336633,333333,330033,00FF33,00CC33,009933,006633,003333,000033,66FF00,66CC00,669900,666600,663300,660000,33FF00,33CC00,339900,336600,333300,330000,00FF00,00CC00,009900,006600,003300,000000", APPLY_CANCEL = {
        apply: "Apply",
        cancel: "Cancel"
    }, NS = ".kendoColorTools", CLICK_NS = "click" + NS, KEYDOWN_NS = "keydown" + NS, browser = kendo.support.browser, isIE8 = browser.msie && browser.version < 9;
    var ColorSelector = Widget.extend({
        init: function(element, options) {
            var that = this, ariaId;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            options = that.options;
            that._value = options.value = parse(options.value);
            ariaId = that._ariaId = options.ariaId;
            if (ariaId) {
                element.attr("aria-labelledby", ariaId);
            }
            if (options._standalone) {
                that._triggerSelect = that._triggerChange;
            }
        },
        options: {
            value: null,
            _standalone: true
        },
        events: [ "change", "select", "cancel" ],
        color: function(value) {
            if (value !== undefined) {
                this._updateUI(this._value = parse(value));
            }
            return this._value;
        },
        value: function(color) {
            color = this.color(color);
            if (color) {
                if (this.options.opacity) {
                    color = color.toCssRgba();
                } else {
                    color = color.toCss();
                }
            }
            return color || null;
        },
        enable: function(enable) {
            if (arguments.length === 0) {
                enable = true;
            }
            if (enable) {
                $(".k-disabled-overlay", this.wrapper).remove();
            } else {
                this.wrapper.append("<div class='k-disabled-overlay'></div>");
            }
            this._onEnable(enable);
        },
        _select: function(color, nohooks) {
            var prev = this._value;
            color = this.color(color);
            if (!nohooks) {
                if (!color.equals(prev)) {
                    this.trigger("change", {
                        value: this.value()
                    });
                } else if (!this._standalone) {
                    this.trigger("cancel");
                }
            }
        },
        _triggerSelect: function(color) {
            triggerEvent(this, "select", color);
        },
        _triggerChange: function(color) {
            triggerEvent(this, "change", color);
        },
        destroy: function() {
            if (this.element) {
                this.element.off(NS);
            }
            if (this.wrapper) {
                this.wrapper.off(NS).find("*").off(NS);
            }
            this.wrapper = null;
            Widget.fn.destroy.call(this);
        },
        _updateUI: $.noop,
        _selectOnHide: function() {
            return null;
        },
        _cancel: function() {
            this.trigger("cancel");
        }
    });
    function triggerEvent(self, type, color) {
        color = parse(color);
        if (color && !color.equals(self.color())) {
            if (type == "change") {
                // UI is already updated.  setting _value directly
                // rather than calling self.color(color) to avoid an
                // endless loop.
                self._value = color;
            }
            if (color.a != 1) {
                color = color.toCssRgba();
            } else {
                color = color.toCss();
            }
            self.trigger(type, {
                value: color
            });
        }
    }
    var ColorPalette = ColorSelector.extend({
        init: function(element, options) {
            var that = this;
            ColorSelector.fn.init.call(that, element, options);
            element = that.wrapper = that.element;
            options = that.options;
            var colors = options.palette;
            if (colors == "websafe") {
                colors = WEBPALETTE;
                options.columns = 18;
            } else if (colors == "basic") {
                colors = SIMPLEPALETTE;
            }
            if (typeof colors == "string") {
                colors = colors.split(",");
            }
            if ($.isArray(colors)) {
                colors = $.map(colors, function(x) {
                    return parse(x);
                });
            }
            element.addClass("k-widget k-colorpalette").append($(that._template({
                colors: colors,
                tileSize: options.tileSize,
                value: that._value,
                id: options.ariaId
            }))).on(CLICK_NS, ".k-item", function(ev) {
                that._select($(ev.currentTarget).find("div").css(BACKGROUNDCOLOR));
            }).find("*").attr(UNSELECTABLE, "on").end().attr("tabIndex", 0).on(KEYDOWN_NS, bind(that._keydown, that));
            var tileSize = options.tileSize, width, height;
            if (tileSize) {
                if (/number|string/.test(typeof tileSize)) {
                    width = height = parseFloat(tileSize);
                } else if (typeof tileSize == "object") {
                    width = parseFloat(tileSize.width);
                    height = parseFloat(tileSize.height);
                } else {
                    throw new Error("Unsupported value for the 'tileSize' argument");
                }
                element.find(".k-item").css({
                    width: width,
                    height: height
                });
            }
            if (options.columns) {
                element.css("width", options.columns * (width || 14));
            }
        },
        focus: function() {
            this.wrapper.focus();
        },
        options: {
            name: "ColorPalette",
            columns: 10,
            tileSize: null,
            palette: "basic"
        },
        _onEnable: function(enable) {
            if (enable) {
                this.wrapper.removeAttr("tabIndex");
            } else {
                this.wrapper.attr("tabIndex", 0);
            }
        },
        _keydown: function(e) {
            var selected, that = this, wrapper = that.wrapper, items = wrapper.find(".k-item"), current = items.filter("." + ITEMSELECTEDCLASS).get(0), keyCode = e.keyCode;
            if (keyCode == KEYS.LEFT) {
                selected = relative(items, current, -1);
            } else if (keyCode == KEYS.RIGHT) {
                selected = relative(items, current, 1);
            } else if (keyCode == KEYS.DOWN) {
                selected = relative(items, current, that.options.columns);
            } else if (keyCode == KEYS.UP) {
                selected = relative(items, current, -that.options.columns);
            } else if (keyCode == KEYS.ENTER) {
                preventDefault(e);
                if (current) {
                    this._select($("div", current).css(BACKGROUNDCOLOR));
                }
            } else if (keyCode == KEYS.ESC) {
                this._cancel();
            }
            if (selected) {
                preventDefault(e);
                selected = $(selected);
                $(current).removeClass(ITEMSELECTEDCLASS).removeAttr("aria-selected");
                selected.addClass(ITEMSELECTEDCLASS).attr("aria-selected", true);
                try {
                    var color = parse(selected.find("div").css(BACKGROUNDCOLOR));
                    that._triggerSelect(color);
                } catch (ex) {}
            }
        },
        _updateUI: function(color) {
            var that = this, el = null;
            that.wrapper.find(".k-item." + ITEMSELECTEDCLASS).removeClass(ITEMSELECTEDCLASS).removeAttr("aria-selected");
            that.wrapper.find(".k-item div").each(function() {
                var c = parse($(this).css(BACKGROUNDCOLOR));
                if (c && c.equals(color)) {
                    el = this.parentNode;
                }
            });
            $(el).addClass(ITEMSELECTEDCLASS).attr("aria-selected", true);
        },
        _template: kendo.template('<ul class="k-palette k-reset">' + "# for (var i = 0; i < colors.length; i++) { #" + '<li #=(id && i === 0) ? "id=\\""+id+"\\" aria-selected=\\"true\\"" : "" # class="k-item #= colors[i].equals(value) ? "' + ITEMSELECTEDCLASS + '" : "" #" aria-label="#= colors[i].toCss() #">' + '<div style="background-color:#= colors[i].toCss() #"></div>' + "</li>" + "# } #" + "</ul>")
    });
    var FlatColorPicker = ColorSelector.extend({
        init: function(element, options) {
            var that = this;
            ColorSelector.fn.init.call(that, element, options);
            options = that.options;
            element = that.element;
            that.wrapper = element.addClass("k-widget k-flatcolorpicker").append(that._template(options)).find("*").attr(UNSELECTABLE, "on").end();
            if (isIE8) {
                // IE filters require absolute URLs
                that._applyIEFilter();
            }
            that._hueElements = $(".k-hsv-rectangle, .k-transparency-slider .k-slider-track", element);
            that._selectedColor = $(".k-selected-color-display", element);
            that._colorAsText = $("input.k-color-value", element);
            that._sliders();
            that._hsvArea();
            that._updateUI(that._value || new _RGB(1, 0, 0, 1));
            element.find("input.k-color-value").on(KEYDOWN_NS, function(ev) {
                if (ev.keyCode == KEYS.ENTER) {
                    try {
                        var color = parse(this.value);
                        var val = that.color();
                        $(this).removeClass("k-state-error");
                        that._select(color, !color.equals(val));
                    } catch (ex) {
                        $(this).addClass("k-state-error");
                    }
                }
            }).end().on(CLICK_NS, ".k-controls button.apply", function() {
                // calling select for the currently displayed
                // color will trigger the "change" event.
                that._select(that._getHSV());
            }).on(CLICK_NS, ".k-controls button.cancel", function() {
                // but on cancel, we simply select the previous
                // value (again, triggers "change" event).
                that._updateUI(that.color());
                that._cancel();
            });
        },
        destroy: function() {
            this._hueSlider.destroy();
            if (this._opacitySlider) {
                this._opacitySlider.destroy();
            }
            this._hueSlider = this._opacitySlider = this._hsvRect = this._hsvHandle = this._hueElements = this._selectedColor = this._colorAsText = null;
            ColorSelector.fn.destroy.call(this);
        },
        options: {
            name: "FlatColorPicker",
            opacity: false,
            buttons: false,
            input: true,
            preview: true,
            messages: APPLY_CANCEL
        },
        _applyIEFilter: function() {
            var track = this.element.find(".k-hue-slider .k-slider-track")[0], url = track.currentStyle.backgroundImage;
            url = url.replace(/^url\([\'\"]?|[\'\"]?\)$/g, "");
            track.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url + "', sizingMethod='scale')";
        },
        _sliders: function() {
            var that = this, element = that.element;
            function hueChange(e) {
                that._updateUI(that._getHSV(e.value, null, null, null));
            }
            that._hueSlider = element.find(".k-hue-slider").kendoSlider({
                min: 0,
                max: 359,
                tickPlacement: "none",
                showButtons: false,
                slide: hueChange,
                change: hueChange
            }).data("kendoSlider");
            function opacityChange(e) {
                that._updateUI(that._getHSV(null, null, null, e.value / 100));
            }
            that._opacitySlider = element.find(".k-transparency-slider").kendoSlider({
                min: 0,
                max: 100,
                tickPlacement: "none",
                showButtons: false,
                slide: opacityChange,
                change: opacityChange
            }).data("kendoSlider");
        },
        _hsvArea: function() {
            var that = this, element = that.element, hsvRect = element.find(".k-hsv-rectangle"), hsvHandle = hsvRect.find(".k-draghandle").attr("tabIndex", 0).on(KEYDOWN_NS, bind(that._keydown, that));
            function update(x, y) {
                var offset = this.offset, dx = x - offset.left, dy = y - offset.top, rw = this.width, rh = this.height;
                dx = dx < 0 ? 0 : dx > rw ? rw : dx;
                dy = dy < 0 ? 0 : dy > rh ? rh : dy;
                that._svChange(dx / rw, 1 - dy / rh);
            }
            that._hsvEvents = new kendo.UserEvents(hsvRect, {
                global: true,
                press: function(e) {
                    this.offset = kendo.getOffset(hsvRect);
                    this.width = hsvRect.width();
                    this.height = hsvRect.height();
                    hsvHandle.focus();
                    update.call(this, e.x.location, e.y.location);
                },
                start: function() {
                    hsvRect.addClass("k-dragging");
                    hsvHandle.focus();
                },
                move: function(e) {
                    e.preventDefault();
                    update.call(this, e.x.location, e.y.location);
                },
                end: function() {
                    hsvRect.removeClass("k-dragging");
                }
            });
            that._hsvRect = hsvRect;
            that._hsvHandle = hsvHandle;
        },
        _onEnable: function(enable) {
            this._hueSlider.enable(enable);
            if (this._opacitySlider) {
                this._opacitySlider.enable(enable);
            }
            this.wrapper.find("input").attr("disabled", !enable);
            var handle = this._hsvRect.find(".k-draghandle");
            if (enable) {
                handle.attr("tabIndex", 0);
            } else {
                handle.removeAttr("tabIndex");
            }
        },
        _keydown: function(ev) {
            var that = this;
            function move(prop, d) {
                var c = that._getHSV();
                c[prop] += d * (ev.shiftKey ? .01 : .05);
                if (c[prop] < 0) {
                    c[prop] = 0;
                }
                if (c[prop] > 1) {
                    c[prop] = 1;
                }
                that._updateUI(c);
                preventDefault(ev);
            }
            function hue(d) {
                var c = that._getHSV();
                c.h += d * (ev.shiftKey ? 1 : 5);
                if (c.h < 0) {
                    c.h = 0;
                }
                if (c.h > 359) {
                    c.h = 359;
                }
                that._updateUI(c);
                preventDefault(ev);
            }
            switch (ev.keyCode) {
              case KEYS.LEFT:
                if (ev.ctrlKey) {
                    hue(-1);
                } else {
                    move("s", -1);
                }
                break;

              case KEYS.RIGHT:
                if (ev.ctrlKey) {
                    hue(1);
                } else {
                    move("s", 1);
                }
                break;

              case KEYS.UP:
                move(ev.ctrlKey && that._opacitySlider ? "a" : "v", 1);
                break;

              case KEYS.DOWN:
                move(ev.ctrlKey && that._opacitySlider ? "a" : "v", -1);
                break;

              case KEYS.ENTER:
                that._select(that._getHSV());
                break;

              case KEYS.F2:
                that.wrapper.find("input.k-color-value").focus().select();
                break;

              case KEYS.ESC:
                that._cancel();
                break;
            }
        },
        focus: function() {
            this._hsvHandle.focus();
        },
        _getHSV: function(h, s, v, a) {
            var rect = this._hsvRect, width = rect.width(), height = rect.height(), handlePosition = this._hsvHandle.position();
            if (h == null) {
                h = this._hueSlider.value();
            }
            if (s == null) {
                s = handlePosition.left / width;
            }
            if (v == null) {
                v = 1 - handlePosition.top / height;
            }
            if (a == null) {
                a = this._opacitySlider ? this._opacitySlider.value() / 100 : 1;
            }
            return new _HSV(h, s, v, a);
        },
        _svChange: function(s, v) {
            var color = this._getHSV(null, s, v, null);
            this._updateUI(color);
        },
        _updateUI: function(color) {
            var that = this, rect = that._hsvRect;
            if (!color) {
                return;
            }
            that._selectedColor.css(BACKGROUNDCOLOR, color.toDisplay());
            that._colorAsText.val(that._opacitySlider ? color.toCssRgba() : color.toCss());
            that._triggerSelect(color);
            color = color.toHSV();
            that._hsvHandle.css({
                // saturation is 0 on the left side, full (1) on the right
                left: color.s * rect.width() + "px",
                // value is 0 on the bottom, full on the top.
                top: (1 - color.v) * rect.height() + "px"
            });
            that._hueElements.css(BACKGROUNDCOLOR, new _HSV(color.h, 1, 1, 1).toCss());
            that._hueSlider.value(color.h);
            if (that._opacitySlider) {
                that._opacitySlider.value(100 * color.a);
            }
        },
        _selectOnHide: function() {
            return this.options.buttons ? null : this._getHSV();
        },
        _template: kendo.template("# if (preview) { #" + '<div class="k-selected-color"><div class="k-selected-color-display"><input spellcheck="false" class="k-color-value" #= !data.input ? \'style="visibility: hidden;"\' : "" #></div></div>' + "# } #" + '<div class="k-hsv-rectangle"><div class="k-hsv-gradient"></div><div class="k-draghandle"></div></div>' + '<input class="k-hue-slider" />' + "# if (opacity) { #" + '<input class="k-transparency-slider" />' + "# } #" + "# if (buttons) { #" + '<div class="k-controls"><button class="k-button apply">#: messages.apply #</button> <button class="k-button cancel">#: messages.cancel #</button></div>' + "# } #")
    });
    /* -----[ color utils ]----- */
    function hex(n, width, pad) {
        if (!pad) {
            pad = "0";
        }
        n = n.toString(16);
        while (width > n.length) {
            n = "0" + n;
        }
        return n;
    }
    function fixed(n) {
        return parseFloat((+n).toFixed(3));
    }
    var Color = Class.extend({
        toHSV: function() {
            return this;
        },
        toRGB: function() {
            return this;
        },
        toHex: function() {
            return this.toBytes().toHex();
        },
        toBytes: function() {
            return this;
        },
        toCss: function() {
            return "#" + this.toHex();
        },
        toCssRgba: function() {
            var rgb = this.toBytes();
            return "rgba(" + rgb.r + ", " + rgb.g + ", " + rgb.b + ", " + fixed(this.a) + ")";
        },
        toDisplay: function() {
            if (isIE8) {
                return this.toCss();
            }
            return this.toCssRgba();
        },
        equals: function(c) {
            return c === this || c !== null && this.toHex() == parse(c).toHex();
        },
        diff: function(c2) {
            if (c2 == null) {
                return NaN;
            }
            var c1 = this.toBytes();
            c2 = c2.toBytes();
            return Math.sqrt(Math.pow((c1.r - c2.r) * .3, 2) + Math.pow((c1.g - c2.g) * .59, 2) + Math.pow((c1.b - c2.b) * .11, 2));
        },
        clone: function() {
            var c = this.toBytes();
            if (c === this) {
                c = new _Bytes(c.r, c.g, c.b, c.a);
            }
            return c;
        }
    });
    var _RGB = Color.extend({
        init: function(r, g, b, a) {
            this.r = r;
            this.g = g;
            this.b = b;
            this.a = a;
        },
        toHSV: function() {
            var min, max, delta, h, s, v;
            var r = this.r, g = this.g, b = this.b;
            min = Math.min(r, g, b);
            max = Math.max(r, g, b);
            v = max;
            delta = max - min;
            if (delta === 0) {
                return new _HSV(0, 0, v, this.a);
            }
            if (max !== 0) {
                s = delta / max;
                if (r == max) {
                    h = (g - b) / delta;
                } else if (g == max) {
                    h = 2 + (b - r) / delta;
                } else {
                    h = 4 + (r - g) / delta;
                }
                h *= 60;
                if (h < 0) {
                    h += 360;
                }
            } else {
                s = 0;
                h = -1;
            }
            return new _HSV(h, s, v, this.a);
        },
        toBytes: function() {
            return new _Bytes(this.r * 255, this.g * 255, this.b * 255, this.a);
        }
    });
    var _Bytes = _RGB.extend({
        init: function(r, g, b, a) {
            this.r = Math.round(r);
            this.g = Math.round(g);
            this.b = Math.round(b);
            this.a = a;
        },
        toRGB: function() {
            return new _RGB(this.r / 255, this.g / 255, this.b / 255, this.a);
        },
        toHSV: function() {
            return this.toRGB().toHSV();
        },
        toHex: function() {
            return hex(this.r, 2) + hex(this.g, 2) + hex(this.b, 2);
        },
        toBytes: function() {
            return this;
        }
    });
    var _HSV = Color.extend({
        init: function(h, s, v, a) {
            this.h = h;
            this.s = s;
            this.v = v;
            this.a = a;
        },
        toRGB: function() {
            var h = this.h, s = this.s, v = this.v;
            var i, r, g, b, f, p, q, t;
            if (s === 0) {
                r = g = b = v;
            } else {
                h /= 60;
                i = Math.floor(h);
                f = h - i;
                p = v * (1 - s);
                q = v * (1 - s * f);
                t = v * (1 - s * (1 - f));
                switch (i) {
                  case 0:
                    r = v;
                    g = t;
                    b = p;
                    break;

                  case 1:
                    r = q;
                    g = v;
                    b = p;
                    break;

                  case 2:
                    r = p;
                    g = v;
                    b = t;
                    break;

                  case 3:
                    r = p;
                    g = q;
                    b = v;
                    break;

                  case 4:
                    r = t;
                    g = p;
                    b = v;
                    break;

                  default:
                    r = v;
                    g = p;
                    b = q;
                    break;
                }
            }
            return new _RGB(r, g, b, this.a);
        },
        toBytes: function() {
            return this.toRGB().toBytes();
        }
    });
    function parse(color, nothrow) {
        if (color == null || color == "transparent") {
            return null;
        }
        if (color instanceof Color) {
            return color;
        }
        var m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color);
        if (m) {
            return new _Bytes(parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16), 1);
        }
        m = /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color);
        if (m) {
            return new _Bytes(parseInt(m[1] + m[1], 16), parseInt(m[2] + m[2], 16), parseInt(m[3] + m[3], 16), 1);
        }
        m = /^rgb\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/.exec(color);
        if (m) {
            return new _Bytes(parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10), 1);
        }
        m = /^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9.]+)\s*\)/.exec(color);
        if (m) {
            return new _Bytes(parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10), parseFloat(m[4]));
        }
        m = /^rgb\(\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9]*\.?[0-9]+)%\s*\)/.exec(color);
        if (m) {
            return new _RGB(parseFloat(m[1]) / 100, parseFloat(m[2]) / 100, parseFloat(m[3]) / 100, 1);
        }
        m = /^rgba\(\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9]*\.?[0-9]+)%\s*,\s*([0-9.]+)\s*\)/.exec(color);
        if (m) {
            return new _RGB(parseFloat(m[1]) / 100, parseFloat(m[2]) / 100, parseFloat(m[3]) / 100, parseFloat(m[4]));
        }
        if (!nothrow) {
            throw new Error("Cannot parse color: " + color);
        }
        return undefined;
    }
    function relative(array, element, delta) {
        array = Array.prototype.slice.call(array);
        var n = array.length;
        var pos = array.indexOf(element);
        if (pos < 0) {
            return delta < 0 ? array[n - 1] : array[0];
        }
        pos += delta;
        if (pos < 0) {
            pos += n;
        } else {
            pos %= n;
        }
        return array[pos];
    }
    /* -----[ The ColorPicker widget ]----- */
    var ColorPicker = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            options = that.options;
            element = that.element;
            var value;
            if (element.val()) {
                value = parse(element.val(), true);
            } else {
                value = parse(options.value, true);
            }
            that._value = options.value = value;
            var content = that.wrapper = $(that._template(options));
            element.hide().after(content);
            if (element.is("input")) {
                element.appendTo(content);
            }
            that.enable(!element.attr("disabled"));
            var accesskey = element.attr("accesskey");
            if (accesskey) {
                element.attr("accesskey", null);
                content.attr("accesskey", accesskey);
            }
            that.bind("activate", function(ev) {
                if (!ev.isDefaultPrevented()) {
                    that.toggle();
                }
            });
            that._updateUI(value);
        },
        destroy: function() {
            this.wrapper.add("*").off(NS);
            if (this._popup) {
                this._selector.destroy();
                this._popup.destroy();
            }
            this._selector = this._popup = this.wrapper = null;
            Widget.fn.destroy.call(this);
        },
        enable: function(enable) {
            var that = this, wrapper = that.wrapper, innerWrapper = wrapper.children(".k-picker-wrap"), icon = innerWrapper.find(".k-select");
            if (arguments.length === 0) {
                enable = true;
            }
            that.element.attr("disabled", !enable);
            wrapper.attr("disabled", !enable);
            icon.off(NS).on("mousedown" + NS, preventDefault);
            if (enable) {
                wrapper.removeClass("k-state-disabled").attr("tabIndex", 0).on("mouseenter" + NS, function() {
                    innerWrapper.addClass("k-state-hover");
                }).on("mouseleave" + NS, function() {
                    innerWrapper.removeClass("k-state-hover");
                }).on("focus" + NS, function() {
                    innerWrapper.addClass("k-state-focused");
                }).on("blur" + NS, function() {
                    innerWrapper.removeClass("k-state-focused");
                }).on(KEYDOWN_NS, bind(that._keydown, that)).on(CLICK_NS, ".k-icon", bind(that.toggle, that)).on(CLICK_NS, that.options.toolIcon ? ".k-tool-icon" : ".k-selected-color", function() {
                    that.trigger("activate");
                });
            } else {
                wrapper.addClass("k-state-disabled").removeAttr("tabIndex").add("*", wrapper).off(NS);
            }
        },
        _template: kendo.template('<span class="k-widget k-colorpicker k-header">' + '<span class="k-picker-wrap k-state-default">' + "# if (toolIcon) { #" + '<span class="k-tool-icon #= toolIcon #">' + '<span class="k-selected-color"></span>' + "</span>" + "# } else { #" + '<span class="k-selected-color"></span>' + "# } #" + '<span class="k-select" unselectable="on">' + '<span class="k-icon k-i-arrow-s" unselectable="on"></span>' + "</span>" + "</span>" + "</span>"),
        options: {
            name: "ColorPicker",
            palette: null,
            columns: 10,
            toolIcon: null,
            value: null,
            messages: APPLY_CANCEL,
            opacity: false,
            buttons: true,
            preview: true
        },
        events: [ "activate", "change", "select", "open", "close" ],
        open: function() {
            this._getPopup().open();
        },
        close: function() {
            this._getPopup().close();
        },
        toggle: function() {
            this._getPopup().toggle();
        },
        color: ColorSelector.fn.color,
        value: ColorSelector.fn.value,
        _select: ColorSelector.fn._select,
        _triggerSelect: ColorSelector.fn._triggerSelect,
        _isInputTypeColor: function() {
            var el = this.element[0];
            return /^input$/i.test(el.tagName) && /^color$/i.test(el.type);
        },
        _updateUI: function(value) {
            if (value) {
                if (this._isInputTypeColor() || value.a == 1) {
                    // seems that input type="color" doesn't support opacity
                    // in colors; the only accepted format is hex #RRGGBB
                    this.element.val(value.toCss());
                } else {
                    this.element.val(value.toCssRgba());
                }
            }
            this._triggerSelect(value);
            this.wrapper.find(".k-selected-color").css(BACKGROUNDCOLOR, value ? value.toDisplay() : "transparent");
        },
        _keydown: function(ev) {
            var key = ev.keyCode;
            if (this._getPopup().visible()) {
                if (key == KEYS.ESC) {
                    this._selector._cancel();
                } else {
                    this._selector._keydown(ev);
                }
                preventDefault(ev);
            } else if (key == KEYS.ENTER || key == KEYS.DOWN) {
                this.open();
                preventDefault(ev);
            }
        },
        _getPopup: function() {
            var that = this, popup = that._popup;
            if (!popup) {
                var options = this.options;
                var selectorType;
                if (options.palette) {
                    selectorType = ColorPalette;
                } else {
                    selectorType = FlatColorPicker;
                }
                options._standalone = false;
                var selector = this._selector = new selectorType($("<div />").appendTo(document.body), options);
                that._popup = popup = selector.wrapper.kendoPopup({
                    anchor: that.wrapper
                }).data("kendoPopup");
                selector.bind({
                    select: function(ev) {
                        that._updateUI(parse(ev.value));
                    },
                    change: function() {
                        that._select(selector.color());
                        that.close();
                    },
                    cancel: function() {
                        that.close();
                    }
                });
                popup.bind({
                    close: function(ev) {
                        if (that.trigger("close")) {
                            ev.preventDefault();
                            return;
                        }
                        var color = selector._selectOnHide();
                        if (!color) {
                            that.wrapper.focus();
                            that._updateUI(that.color());
                        } else {
                            that._select(color);
                        }
                    },
                    open: function(ev) {
                        if (that.trigger("open")) {
                            ev.preventDefault();
                        }
                    },
                    activate: function() {
                        selector._select(that.color(), true);
                        selector.focus();
                    }
                });
            }
            return popup;
        }
    });
    function preventDefault(ev) {
        ev.preventDefault();
    }
    function bind(callback, obj) {
        return function() {
            return callback.apply(obj, arguments);
        };
    }
    ui.plugin(ColorPalette);
    ui.plugin(FlatColorPicker);
    ui.plugin(ColorPicker);
    kendo.parseColor = parse;
    kendo.Color = {
        fromBytes: function(r, g, b, a) {
            return new _Bytes(r, g, b, a != null ? a : 1);
        },
        fromRGB: function(r, g, b, a) {
            return new _RGB(r, g, b, a != null ? a : 1);
        },
        fromHSV: function(h, s, v, a) {
            return new _HSV(h, s, v, a != null ? a : 1);
        }
    };
})(jQuery, parseInt);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, proxy = $.proxy, extend = $.extend, grep = $.grep, map = $.map, inArray = $.inArray, ACTIVE = "k-state-selected", ASC = "asc", DESC = "desc", CHANGE = "change", INIT = "init", POPUP = "kendoPopup", FILTERMENU = "kendoFilterMenu", MENU = "kendoMenu", NS = ".kendoColumnMenu", nameSpecialCharRegExp = /(\[|\]|\$|\.|\:|\+)/g, Widget = ui.Widget;
    function trim(text) {
        return $.trim(text).replace(/&nbsp;/gi, "");
    }
    var ColumnMenu = Widget.extend({
        init: function(element, options) {
            var that = this, link;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            options = that.options;
            that.owner = options.owner;
            that.dataSource = options.dataSource;
            that.field = element.attr(kendo.attr("field"));
            link = element.find(".k-header-column-menu");
            if (!link[0]) {
                link = element.prepend('<a class="k-header-column-menu" href="#"><span class="k-icon k-i-arrowhead-s"/></a>').find(".k-header-column-menu");
            }
            that.link = link.attr("tabindex", -1).on("click" + NS, proxy(that._click, that));
            that.wrapper = $('<div class="k-column-menu"/>');
        },
        _init: function() {
            var that = this, options = that.options;
            that.wrapper.html(kendo.template(template)({
                ns: kendo.ns,
                messages: options.messages,
                sortable: options.sortable,
                filterable: options.filterable,
                columns: that._ownerColumns(),
                showColumns: options.columns
            }));
            that.popup = that.wrapper[POPUP]({
                anchor: that.link,
                open: proxy(that._open, that),
                activate: proxy(that._activate, that),
                close: that.options.closeCallback
            }).data(POPUP);
            that._menu();
            that._sort();
            that._columns();
            that._filter();
            that.trigger(INIT, {
                field: that.field,
                container: that.wrapper
            });
        },
        events: [ INIT ],
        options: {
            name: "ColumnMenu",
            messages: {
                sortAscending: "Sort Ascending",
                sortDescending: "Sort Descending",
                filter: "Filter",
                columns: "Columns"
            },
            columns: true,
            sortable: true,
            filterable: true
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            if (that.filterMenu) {
                that.filterMenu.destroy();
            }
            that.dataSource.unbind("refresh", that._refreshHandler);
            if (that.options.columns) {
                that.owner.unbind("columnShow", that._updateColumnsMenuHandler);
                that.owner.unbind("columnHide", that._updateColumnsMenuHandler);
            }
            if (that.menu) {
                that.menu.element.off(NS);
                that.menu.destroy();
            }
            that.wrapper.off(NS);
            if (that.popup) {
                that.popup.destroy();
            }
            that.link.off(NS);
        },
        close: function() {
            this.menu.close();
            this.popup.close();
            this.popup.element.off("keydown" + NS);
        },
        _click: function(e) {
            e.preventDefault();
            e.stopPropagation();
            if (!this.popup) {
                this._init();
            }
            this.popup.toggle();
        },
        _open: function() {
            var that = this;
            $(".k-column-menu").not(that.wrapper).each(function() {
                $(this).data(POPUP).close();
            });
            that.popup.element.on("keydown" + NS, function(e) {
                if (e.keyCode == kendo.keys.ESC) {
                    that.close();
                }
            });
        },
        _activate: function() {
            this.menu.element.focus();
        },
        _ownerColumns: function() {
            var columns = this.owner.columns, menuColumns = grep(columns, function(col) {
                var result = true, title = trim(col.title || "");
                if (col.menu === false || !col.field && !title.length) {
                    result = false;
                }
                return result;
            });
            return map(menuColumns, function(col) {
                return {
                    field: col.field || col.title,
                    title: col.title || col.field,
                    hidden: col.hidden,
                    index: inArray(col, columns)
                };
            });
        },
        _menu: function() {
            this.menu = this.wrapper.children()[MENU]({
                orientation: "vertical",
                closeOnClick: false
            }).data(MENU);
        },
        _sort: function() {
            var that = this;
            if (that.options.sortable) {
                that.refresh();
                that._refreshHandler = proxy(that.refresh, that);
                that.dataSource.bind(CHANGE, that._refreshHandler);
                that.menu.bind("select", function(e) {
                    var item = $(e.item), dir;
                    if (item.hasClass("k-sort-asc")) {
                        dir = ASC;
                    } else if (item.hasClass("k-sort-desc")) {
                        dir = DESC;
                    }
                    if (!dir) {
                        return;
                    }
                    item.parent().find(".k-sort-" + (dir == ASC ? DESC : ASC)).removeClass(ACTIVE);
                    that._sortDataSource(item, dir);
                    that.close();
                });
            }
        },
        _sortDataSource: function(item, dir) {
            var that = this, sortable = that.options.sortable, dataSource = that.dataSource, idx, length, sort = dataSource.sort() || [];
            if (item.hasClass(ACTIVE) && sortable && sortable.allowUnsort !== false) {
                item.removeClass(ACTIVE);
                dir = undefined;
            } else {
                item.addClass(ACTIVE);
            }
            if (sortable === true || sortable.mode === "single") {
                sort = [ {
                    field: that.field,
                    dir: dir
                } ];
            } else {
                for (idx = 0, length = sort.length; idx < length; idx++) {
                    if (sort[idx].field === that.field) {
                        sort.splice(idx, 1);
                        break;
                    }
                }
                sort.push({
                    field: that.field,
                    dir: dir
                });
            }
            dataSource.sort(sort);
        },
        _columns: function() {
            var that = this;
            if (that.options.columns) {
                that._updateColumnsMenu();
                that._updateColumnsMenuHandler = proxy(that._updateColumnsMenu, that);
                that.owner.bind([ "columnHide", "columnShow" ], that._updateColumnsMenuHandler);
                that.menu.bind("select", function(e) {
                    var item = $(e.item), input, index, column, columns = that.owner.columns, field;
                    if (!item.parent().closest("li.k-columns-item")[0]) {
                        return;
                    }
                    input = item.find(":checkbox");
                    if (input.attr("disabled")) {
                        return;
                    }
                    field = input.attr(kendo.attr("field"));
                    column = grep(columns, function(column) {
                        return column.field == field || column.title == field;
                    })[0];
                    index = inArray(column, columns);
                    if (column.hidden === true) {
                        that.owner.showColumn(index);
                    } else {
                        that.owner.hideColumn(index);
                    }
                });
            }
        },
        _updateColumnsMenu: function() {
            var attr = "[" + kendo.attr("field") + "=", columns = this._ownerColumns(), allselector = map(columns, function(col) {
                return attr + '"' + col.field.replace(nameSpecialCharRegExp, "\\$1") + '"]';
            }).join(","), visible = grep(columns, function(field) {
                return !field.hidden;
            }), selector = map(visible, function(col) {
                return attr + '"' + col.field.replace(nameSpecialCharRegExp, "\\$1") + '"]';
            }).join(",");
            this.wrapper.find(allselector).prop("checked", false);
            this.wrapper.find(selector).prop("checked", true).prop("disabled", visible.length == 1);
        },
        _filter: function() {
            var that = this, options = that.options;
            if (options.filterable !== false) {
                that.filterMenu = that.wrapper.find(".k-filterable")[FILTERMENU](extend(true, {}, {
                    appendToElement: true,
                    dataSource: options.dataSource,
                    values: options.values,
                    field: that.field
                }, options.filterable)).data(FILTERMENU);
            }
        },
        refresh: function() {
            var that = this, sort = that.options.dataSource.sort() || [], descriptor, field = that.field, idx, length;
            that.wrapper.find(".k-sort-asc, .k-sort-desc").removeClass(ACTIVE);
            for (idx = 0, length = sort.length; idx < length; idx++) {
                descriptor = sort[idx];
                if (field == descriptor.field) {
                    that.wrapper.find(".k-sort-" + descriptor.dir).addClass(ACTIVE);
                }
            }
        }
    });
    var template = "<ul>" + "#if(sortable){#" + '<li class="k-item k-sort-asc"><span class="k-link"><span class="k-sprite k-i-sort-asc"></span>${messages.sortAscending}</span></li>' + '<li class="k-item k-sort-desc"><span class="k-link"><span class="k-sprite k-i-sort-desc"></span>${messages.sortDescending}</span></li>' + "#if(showColumns || filterable){#" + '<li class="k-separator"></li>' + "#}#" + "#}#" + "#if(showColumns){#" + '<li class="k-item k-columns-item"><span class="k-link"><span class="k-sprite k-i-columns"></span>${messages.columns}</span><ul>' + "#for (var col in columns) {#" + '<li><input type="checkbox" data-#=ns#field="#=columns[col].field#" data-#=ns#index="#=columns[col].index#"/>#=columns[col].title#</li>' + "#}#" + "</ul></li>" + "#if(filterable){#" + '<li class="k-separator"></li>' + "#}#" + "#}#" + "#if(filterable){#" + '<li class="k-item k-filter-item"><span class="k-link"><span class="k-sprite k-filter"></span>${messages.filter}</span><ul>' + '<li><div class="k-filterable"></div></li>' + "</ul></li>" + "#}#" + "</ul>";
    ui.plugin(ColumnMenu);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, DataSource = kendo.data.DataSource, Groupable = ui.Groupable, tbodySupportsInnerHtml = kendo.support.tbodyInnerHtml, activeElement = kendo._activeElement, Widget = ui.Widget, keys = kendo.keys, isPlainObject = $.isPlainObject, extend = $.extend, map = $.map, grep = $.grep, isArray = $.isArray, inArray = $.inArray, proxy = $.proxy, isFunction = $.isFunction, isEmptyObject = $.isEmptyObject, math = Math, PROGRESS = "progress", ERROR = "error", DATA_CELL = ":not(.k-group-cell):not(.k-hierarchy-cell):visible", SELECTION_CELL_SELECTOR = "tbody>tr:not(.k-grouping-row):not(.k-detail-row):not(.k-group-footer) > td:not(.k-group-cell):not(.k-hierarchy-cell)", NAVROW = "tr:not(.k-footer-template):visible", NAVCELL = ":not(.k-group-cell):not(.k-hierarchy-cell):visible", FIRSTNAVITEM = NAVROW + ":first>" + NAVCELL + ":first", HEADERCELLS = "th.k-header:not(.k-group-cell,.k-hierarchy-cell)", GROUPINGDRAGGABLES = HEADERCELLS + ":visible[" + kendo.attr("field") + "]", GROUPINGFILTER = HEADERCELLS + "[" + kendo.attr("field") + "]", NS = ".kendoGrid", EDIT = "edit", SAVE = "save", REMOVE = "remove", DETAILINIT = "detailInit", FILTERMENUINIT = "filterMenuInit", COLUMNMENUINIT = "columnMenuInit", CHANGE = "change", COLUMNHIDE = "columnHide", COLUMNSHOW = "columnShow", SAVECHANGES = "saveChanges", DATABOUND = "dataBound", DETAILEXPAND = "detailExpand", DETAILCOLLAPSE = "detailCollapse", FOCUSED = "k-state-focused", SELECTED = "k-state-selected", COLUMNRESIZE = "columnResize", COLUMNREORDER = "columnReorder", CLICK = "click", HEIGHT = "height", TABINDEX = "tabIndex", FUNCTION = "function", STRING = "string", DELETECONFIRM = "Are you sure you want to delete this record?", formatRegExp = /(\}|\#)/gi, indicatorWidth = 3, templateHashRegExp = /#/gi, whitespaceRegExp = "[\\x20\\t\\r\\n\\f]", nonDataCellsRegExp = new RegExp("(^|" + whitespaceRegExp + ")" + "(k-group-cell|k-hierarchy-cell)" + "(" + whitespaceRegExp + "|$)"), COMMANDBUTTONTMPL = '<a class="k-button k-button-icontext #=className#" #=attr# href="\\#"><span class="#=iconClass# #=imageClass#"></span>#=text#</a>', isRtl = false;
    var VirtualScrollable = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            that._refreshHandler = proxy(that.refresh, that);
            that.setDataSource(options.dataSource);
            that.wrap();
        },
        setDataSource: function(dataSource) {
            var that = this;
            if (that.dataSource) {
                that.dataSource.unbind(CHANGE, that._refreshHandler);
            }
            that.dataSource = dataSource;
            that.dataSource.bind(CHANGE, that._refreshHandler);
        },
        options: {
            name: "VirtualScrollable",
            itemHeight: $.noop
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that.dataSource.unbind(CHANGE, that._refreshHandler);
            that.wrapper.add(that.verticalScrollbar).off(NS);
            if (that.drag) {
                that.drag.destroy();
            }
        },
        wrap: function() {
            var that = this, // workaround for IE issue where scroll is not raised if container is same width as the scrollbar
            scrollbar = kendo.support.scrollbar() + 1, element = that.element, wrapper;
            element.css({
                width: "auto",
                overflow: "hidden"
            }).css(isRtl ? "padding-left" : "padding-right", scrollbar);
            that.content = element.children().first();
            wrapper = that.wrapper = that.content.wrap('<div class="k-virtual-scrollable-wrap"/>').parent().bind("DOMMouseScroll" + NS + " mousewheel" + NS, proxy(that._wheelScroll, that));
            if (kendo.support.kineticScrollNeeded) {
                that.drag = new kendo.UserEvents(that.wrapper, {
                    global: true,
                    move: function(e) {
                        that.verticalScrollbar.scrollTop(that.verticalScrollbar.scrollTop() - e.y.delta);
                        wrapper.scrollLeft(wrapper.scrollLeft() - e.x.delta);
                        e.preventDefault();
                    }
                });
            }
            that.verticalScrollbar = $('<div class="k-scrollbar k-scrollbar-vertical" />').css({
                width: scrollbar
            }).appendTo(element).bind("scroll" + NS, proxy(that._scroll, that));
        },
        _wheelScroll: function(e) {
            var that = this, scrollTop = that.verticalScrollbar.scrollTop(), originalEvent = e.originalEvent, deltaY = originalEvent.wheelDeltaY, delta;
            if (originalEvent.wheelDelta) {
                // Webkit and IE
                if (deltaY === undefined || deltaY) {
                    // IE does not have deltaY, thus always scroll (horizontal scrolling is treated as vertical)
                    delta = originalEvent.wheelDelta;
                }
            } else if (originalEvent.detail && originalEvent.axis === originalEvent.VERTICAL_AXIS) {
                // Firefox and Opera
                delta = -originalEvent.detail * 10;
            }
            if (delta) {
                e.preventDefault();
                that.verticalScrollbar.scrollTop(scrollTop + -delta);
            }
        },
        _scroll: function(e) {
            var that = this, scrollTop = e.currentTarget.scrollTop, dataSource = that.dataSource, rowHeight = that.itemHeight, skip = dataSource.skip() || 0, start = that._rangeStart || skip, height = that.element.innerHeight(), isScrollingUp = !!(that._scrollbarTop && that._scrollbarTop > scrollTop), firstItemIndex = math.max(math.floor(scrollTop / rowHeight), 0), lastItemIndex = math.max(firstItemIndex + math.floor(height / rowHeight), 0);
            that._scrollTop = scrollTop - start * rowHeight;
            that._scrollbarTop = scrollTop;
            if (!that._fetch(firstItemIndex, lastItemIndex, isScrollingUp)) {
                that.wrapper[0].scrollTop = that._scrollTop;
            }
        },
        _fetch: function(firstItemIndex, lastItemIndex, scrollingUp) {
            var that = this, dataSource = that.dataSource, itemHeight = that.itemHeight, take = dataSource.take(), rangeStart = that._rangeStart || dataSource.skip() || 0, currentSkip = math.floor(firstItemIndex / take) * take, fetching = false, prefetchAt = .33;
            if (firstItemIndex < rangeStart) {
                fetching = true;
                rangeStart = math.max(0, lastItemIndex - take);
                that._scrollTop = (firstItemIndex - rangeStart) * itemHeight;
                that._page(rangeStart, take);
            } else if (lastItemIndex >= rangeStart + take && !scrollingUp) {
                fetching = true;
                rangeStart = firstItemIndex;
                that._scrollTop = itemHeight;
                that._page(rangeStart, take);
            } else if (!that._fetching) {
                if (firstItemIndex < currentSkip + take - take * prefetchAt && firstItemIndex > take) {
                    dataSource.prefetch(currentSkip - take, take);
                }
                if (lastItemIndex > currentSkip + take * prefetchAt) {
                    dataSource.prefetch(currentSkip + take, take);
                }
            }
            return fetching;
        },
        _page: function(skip, take) {
            var that = this, dataSource = that.dataSource;
            clearTimeout(that._timeout);
            that._fetching = true;
            that._rangeStart = skip;
            if (dataSource.inRange(skip, take)) {
                dataSource.range(skip, take);
            } else {
                kendo.ui.progress(that.wrapper.parent(), true);
                that._timeout = setTimeout(function() {
                    dataSource.range(skip, take);
                }, 100);
            }
        },
        refresh: function() {
            var that = this, html = "", maxHeight = 25e4, dataSource = that.dataSource, rangeStart = that._rangeStart, scrollbar = !kendo.support.kineticScrollNeeded ? kendo.support.scrollbar() : 0, wrapperElement = that.wrapper[0], totalHeight, idx, itemHeight;
            kendo.ui.progress(that.wrapper.parent(), false);
            clearTimeout(that._timeout);
            itemHeight = that.itemHeight = that.options.itemHeight() || 0;
            var addScrollBarHeight = wrapperElement.scrollWidth > wrapperElement.offsetWidth ? scrollbar : 0;
            totalHeight = dataSource.total() * itemHeight + addScrollBarHeight;
            for (idx = 0; idx < math.floor(totalHeight / maxHeight); idx++) {
                html += '<div style="width:1px;height:' + maxHeight + 'px"></div>';
            }
            if (totalHeight % maxHeight) {
                html += '<div style="width:1px;height:' + totalHeight % maxHeight + 'px"></div>';
            }
            that.verticalScrollbar.html(html);
            wrapperElement.scrollTop = that._scrollTop;
            if (that.drag) {
                that.drag.cancel();
            }
            if (rangeStart && !that._fetching) {
                // we are rebound from outside local range should be reset
                that._rangeStart = dataSource.skip();
            }
            that._fetching = false;
        }
    });
    function groupCells(count) {
        return new Array(count + 1).join('<td class="k-group-cell">&nbsp;</td>');
    }
    function stringifyAttributes(attributes) {
        var attr, result = " ";
        if (attributes) {
            if (typeof attributes === STRING) {
                return attributes;
            }
            for (attr in attributes) {
                result += attr + '="' + attributes[attr] + '"';
            }
        }
        return result;
    }
    var defaultCommands = {
        create: {
            text: "Add new record",
            imageClass: "k-add",
            className: "k-grid-add",
            iconClass: "k-icon"
        },
        cancel: {
            text: "Cancel changes",
            imageClass: "k-cancel",
            className: "k-grid-cancel-changes",
            iconClass: "k-icon"
        },
        save: {
            text: "Save changes",
            imageClass: "k-update",
            className: "k-grid-save-changes",
            iconClass: "k-icon"
        },
        destroy: {
            text: "Delete",
            imageClass: "k-delete",
            className: "k-grid-delete",
            iconClass: "k-icon"
        },
        edit: {
            text: "Edit",
            imageClass: "k-edit",
            className: "k-grid-edit",
            iconClass: "k-icon"
        },
        update: {
            text: "Update",
            imageClass: "k-update",
            className: "k-grid-update",
            iconClass: "k-icon"
        },
        canceledit: {
            text: "Cancel",
            imageClass: "k-cancel",
            className: "k-grid-cancel",
            iconClass: "k-icon"
        }
    };
    function heightAboveHeader(context) {
        var top = 0;
        $("> .k-grouping-header, > .k-grid-toolbar", context).each(function() {
            top += this.offsetHeight;
        });
        return top;
    }
    function cursor(context, value) {
        $("th, th .k-grid-filter, th .k-link", context).add(document.body).css("cursor", value);
    }
    function buildEmptyAggregatesObject(aggregates) {
        var idx, length, aggregate = {}, fieldsMap = {};
        if (!isEmptyObject(aggregates)) {
            if (!isArray(aggregates)) {
                aggregates = [ aggregates ];
            }
            for (idx = 0, length = aggregates.length; idx < length; idx++) {
                aggregate[aggregates[idx].aggregate] = 0;
                fieldsMap[aggregates[idx].field] = aggregate;
            }
        }
        return fieldsMap;
    }
    function reorder(selector, sourceIndex, destIndex) {
        var source = selector.eq(sourceIndex), dest = selector.eq(destIndex);
        source[sourceIndex > destIndex ? "insertBefore" : "insertAfter"](dest);
    }
    function attachCustomCommandEvent(context, container, commands) {
        var idx, length, command, commandName;
        commands = !isArray(commands) ? [ commands ] : commands;
        for (idx = 0, length = commands.length; idx < length; idx++) {
            command = commands[idx];
            if (isPlainObject(command) && command.click) {
                commandName = command.name || command.text;
                container.on(CLICK + NS, "a.k-grid-" + (commandName || "").replace(/\s/g, ""), {
                    commandName: commandName
                }, proxy(command.click, context));
            }
        }
    }
    function visibleColumns(columns) {
        return grep(columns, function(column) {
            return !column.hidden;
        });
    }
    function addHiddenStyle(attr) {
        attr = attr || {};
        var style = attr.style;
        if (!style) {
            style = "display:none";
        } else {
            style = style.replace(/((.*)?display)(.*)?:([^;]*)/i, "$1:none");
            if (style === attr.style) {
                style = style.replace(/(.*)?/i, "display:none;$1");
            }
        }
        return extend({}, attr, {
            style: style
        });
    }
    function removeHiddenStyle(attr) {
        attr = attr || {};
        var style = attr.style;
        if (style) {
            attr.style = style.replace(/(display\s*:\s*none\s*;?)*/gi, "");
        }
        return attr;
    }
    function normalizeCols(table, visibleColumns, hasDetails, groups) {
        var colgroup = table.find(">colgroup"), width, browser = kendo.support.browser, cols = map(visibleColumns, function(column) {
            width = column.width;
            if (width && parseInt(width, 10) !== 0) {
                return kendo.format('<col style="width:{0}"/>', typeof width === STRING ? width : width + "px");
            }
            return "<col />";
        });
        if (hasDetails || colgroup.find(".k-hierarchy-col").length) {
            cols.splice(0, 0, '<col class="k-hierarchy-col" />');
        }
        if (colgroup.length) {
            colgroup.remove();
        }
        colgroup = $("<colgroup/>").append($(new Array(groups + 1).join('<col class="k-group-col">') + cols.join("")));
        table.prepend(colgroup);
        // fill gap after column hiding
        if (browser.msie && browser.version == 8) {
            table.css("display", "inline-table");
            window.setTimeout(function() {
                table.css("display", "");
            }, 1);
        }
    }
    function convertToObject(array) {
        var result = {}, item, idx, length;
        for (idx = 0, length = array.length; idx < length; idx++) {
            item = array[idx];
            result[item.value] = item.text;
        }
        return result;
    }
    function formatGroupValue(value, format, columnValues) {
        var isForiegnKey = columnValues && columnValues.length && isPlainObject(columnValues[0]) && "value" in columnValues[0], groupValue = isForiegnKey ? convertToObject(columnValues)[value] : value;
        groupValue = groupValue != null ? groupValue : "";
        return format ? kendo.format(format, groupValue) : groupValue;
    }
    function setCellVisibility(cells, index, visible) {
        var pad = 0, state, cell = cells[pad];
        while (cell) {
            state = visible ? true : cell.style.display !== "none";
            if (state && !nonDataCellsRegExp.test(cell.className) && --index < 0) {
                cell.style.display = visible ? "" : "none";
                break;
            }
            cell = cells[++pad];
        }
    }
    var Grid = Widget.extend({
        init: function(element, options) {
            var that = this;
            options = isArray(options) ? {
                dataSource: options
            } : options;
            Widget.fn.init.call(that, element, options);
            isRtl = kendo.support.isRtl(element);
            that._element();
            that._aria();
            that._columns(that.options.columns);
            that._dataSource();
            that._tbody();
            that._pageable();
            that._thead();
            that._groupable();
            that._toolbar();
            that._setContentHeight();
            that._templates();
            that._navigatable();
            that._selectable();
            that._details();
            that._editable();
            that._attachCustomCommandsEvent();
            if (that.options.autoBind) {
                that.dataSource.fetch();
            } else {
                that._footer();
            }
            kendo.notify(that);
        },
        events: [ CHANGE, "dataBinding", "cancel", DATABOUND, DETAILEXPAND, DETAILCOLLAPSE, DETAILINIT, FILTERMENUINIT, COLUMNMENUINIT, EDIT, SAVE, REMOVE, SAVECHANGES, COLUMNRESIZE, COLUMNREORDER, COLUMNSHOW, COLUMNHIDE ],
        setDataSource: function(dataSource) {
            var that = this;
            that.options.dataSource = dataSource;
            that._dataSource();
            that._pageable();
            if (that.options.groupable) {
                that._groupable();
            }
            that._thead();
            if (that.virtualScrollable) {
                that.virtualScrollable.setDataSource(that.options.dataSource);
            }
            if (that.options.autoBind) {
                dataSource.fetch();
            }
        },
        options: {
            name: "Grid",
            columns: [],
            toolbar: null,
            autoBind: true,
            filterable: false,
            scrollable: true,
            sortable: false,
            selectable: false,
            navigatable: false,
            pageable: false,
            editable: false,
            groupable: false,
            rowTemplate: "",
            altRowTemplate: "",
            dataSource: {},
            height: null,
            resizable: false,
            reorderable: false,
            columnMenu: false,
            detailTemplate: null
        },
        destroy: function() {
            var that = this, element;
            Widget.fn.destroy.call(that);
            if (that.pager) {
                that.pager.destroy();
            }
            if (that.groupable) {
                that.groupable.destroy();
            }
            if (that.virtualScrollable) {
                that.virtualScrollable.destroy();
            }
            that._destroyColumnAttachments();
            that._destroyEditable();
            that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(PROGRESS, that._progressHandler).unbind(ERROR, that._errorHandler);
            element = that.element.add(that.wrapper).add(that.table).add(that.thead).add(that.wrapper.find(">.k-grid-toolbar"));
            if (that.content) {
                element = element.add(that.content).add(that.content.find(">.k-virtual-scrollable-wrap"));
            }
            element.off(NS);
            kendo.destroy(that.wrapper);
        },
        setOptions: function(options) {
            var that = this;
            Widget.fn.setOptions.call(this, options);
            that._templates();
        },
        items: function() {
            return this.tbody.children(":not(.k-grouping-row,.k-detail-row,.k-group-footer)");
        },
        _destroyColumnAttachments: function() {
            var that = this;
            that.thead.find("th").each(function() {
                var th = $(this), filterMenu = th.data("kendoFilterMenu"), sortable = th.data("kendoSortable"), columnMenu = th.data("kendoColumnMenu");
                if (filterMenu) {
                    filterMenu.destroy();
                }
                if (sortable) {
                    sortable.destroy();
                }
                if (columnMenu) {
                    columnMenu.destroy();
                }
            });
        },
        _attachCustomCommandsEvent: function() {
            var that = this, columns = that.columns || [], command, idx, length;
            for (idx = 0, length = columns.length; idx < length; idx++) {
                command = columns[idx].command;
                if (command) {
                    attachCustomCommandEvent(that, that.wrapper, command);
                }
            }
        },
        _aria: function() {
            var id = this.element.attr("id") || "aria";
            if (id) {
                this._cellId = id + "_active_cell";
            }
        },
        _element: function() {
            var that = this, table = that.element;
            if (!table.is("table")) {
                if (that.options.scrollable) {
                    table = that.element.find("> .k-grid-content > table");
                } else {
                    table = that.element.children("table");
                }
                if (!table.length) {
                    table = $("<table />").appendTo(that.element);
                }
            }
            that.table = table.attr("cellspacing", 0).attr("role", that._hasDetails() ? "treegrid" : "grid");
            that._wrapper();
        },
        _positionColumnResizeHandle: function(container) {
            var that = this, scrollable = that.options.scrollable, resizeHandle = that.resizeHandle, left;
            that.thead.on("mousemove" + NS, "th:not(.k-group-cell,.k-hierarchy-cell)", function(e) {
                var th = $(this), clientX = e.clientX, winScrollLeft = $(window).scrollLeft(), position = th.offset().left + (!isRtl ? this.offsetWidth : 0);
                if (clientX + winScrollLeft > position - indicatorWidth && clientX + winScrollLeft < position + indicatorWidth) {
                    if (!resizeHandle) {
                        resizeHandle = that.resizeHandle = $('<div class="k-resize-handle"/>');
                        container.append(resizeHandle);
                    }
                    if (!isRtl) {
                        left = this.offsetWidth;
                        th.prevAll(":visible").each(function() {
                            left += this.offsetWidth;
                        });
                    } else {
                        var headerWrap = th.closest(".k-grid-header-wrap"), ieCorrection = kendo.support.browser.msie ? headerWrap.scrollLeft() : 0, webkitCorrection = kendo.support.browser.webkit ? headerWrap[0].scrollWidth - headerWrap[0].offsetWidth - headerWrap.scrollLeft() : 0, firefoxCorrection = kendo.support.browser.mozilla ? headerWrap[0].scrollWidth - headerWrap[0].offsetWidth - (headerWrap[0].scrollWidth - headerWrap[0].offsetWidth - headerWrap.scrollLeft()) : 0;
                        left = th.position().left - webkitCorrection + firefoxCorrection - ieCorrection;
                    }
                    resizeHandle.css({
                        top: scrollable ? 0 : heightAboveHeader(that.wrapper),
                        left: left - indicatorWidth,
                        height: th.outerHeight(),
                        width: indicatorWidth * 3
                    }).data("th", th).show();
                } else {
                    if (resizeHandle) {
                        resizeHandle.hide();
                    } else {
                        cursor(that.wrapper, "");
                    }
                }
            });
        },
        _resizable: function() {
            var that = this, options = that.options, container, columnStart, columnWidth, gridWidth, col;
            if (options.resizable) {
                container = options.scrollable ? that.wrapper.find(".k-grid-header-wrap:first") : that.wrapper;
                that._positionColumnResizeHandle(container);
                container.kendoResizable({
                    handle: ".k-resize-handle",
                    hint: function(handle) {
                        return $('<div class="k-grid-resize-indicator" />').css({
                            height: handle.data("th").outerHeight() + that.tbody.attr("clientHeight")
                        });
                    },
                    start: function(e) {
                        var th = $(e.currentTarget).data("th"), index = $.inArray(th[0], th.parent().children(":visible")), contentTable = that.tbody.parent(), footer = that.footer || $();
                        cursor(that.wrapper, "col-resize");
                        if (options.scrollable) {
                            col = that.thead.parent().find("col:eq(" + index + ")").add(contentTable.children("colgroup").find("col:eq(" + index + ")")).add(footer.find("colgroup").find("col:eq(" + index + ")"));
                        } else {
                            col = contentTable.children("colgroup").find("col:eq(" + index + ")");
                        }
                        columnStart = e.x.location;
                        columnWidth = th.outerWidth();
                        gridWidth = that.tbody.outerWidth();
                    },
                    resize: function(e) {
                        var rtlMultiplier = isRtl ? -1 : 1, width = columnWidth + e.x.location * rtlMultiplier - columnStart * rtlMultiplier, footer = that.footer || $();
                        if (width > 10) {
                            col.css("width", width);
                            if (options.scrollable) {
                                that._footerWidth = gridWidth + e.x.location * rtlMultiplier - columnStart * rtlMultiplier;
                                that.tbody.parent().add(that.thead.parent()).add(footer.find("table")).css("width", that._footerWidth);
                            }
                        }
                    },
                    resizeend: function(e) {
                        var th = $(e.currentTarget).data("th"), newWidth = th.outerWidth(), column;
                        cursor(that.wrapper, "");
                        if (columnWidth != newWidth) {
                            column = that.columns[th.parent().find("th:not(.k-group-cell,.k-hierarchy-cell)").index(th)];
                            column.width = newWidth;
                            that.trigger(COLUMNRESIZE, {
                                column: column,
                                oldWidth: columnWidth,
                                newWidth: newWidth
                            });
                        }
                        that.resizeHandle.hide();
                    }
                });
            }
        },
        _draggable: function() {
            var that = this;
            if (that.options.reorderable) {
                if (that._draggableInstance) {
                    that._draggableInstance.destroy();
                }
                that._draggableInstance = that.wrapper.kendoDraggable({
                    group: kendo.guid(),
                    filter: that.content ? ">.k-grid-header " + HEADERCELLS : ">table>.k-grid-header " + HEADERCELLS,
                    hint: function(target) {
                        return $('<div class="k-header k-drag-clue" />').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")) || target.text()).prepend('<span class="k-icon k-drag-status k-denied" />');
                    }
                }).data("kendoDraggable");
            }
        },
        _reorderable: function() {
            var that = this;
            if (that.options.reorderable) {
                that.wrapper.kendoReorderable({
                    draggable: that._draggableInstance,
                    change: function(e) {
                        var newIndex = inArray(that.columns[e.newIndex], that.columns), column = that.columns[e.oldIndex];
                        that.trigger(COLUMNREORDER, {
                            newIndex: newIndex,
                            oldIndex: inArray(column, that.columns),
                            column: column
                        });
                        that.reorderColumn(newIndex, column);
                    }
                });
            }
        },
        reorderColumn: function(destIndex, column) {
            var that = this, sourceIndex = inArray(column, that.columns), colSourceIndex = inArray(column, visibleColumns(that.columns)), colDestIndex = inArray(that.columns[destIndex], visibleColumns(that.columns)), rows, idx, length, footer = that.footer || that.wrapper.find(".k-grid-footer");
            if (sourceIndex === destIndex) {
                return;
            }
            that.columns.splice(sourceIndex, 1);
            that.columns.splice(destIndex, 0, column);
            that._templates();
            reorder(that.thead.prev().find("col:not(.k-group-col,.k-hierarchy-col)"), colSourceIndex, colDestIndex);
            if (that.options.scrollable) {
                reorder(that.tbody.prev().find("col:not(.k-group-col,.k-hierarchy-col)"), colSourceIndex, colDestIndex);
            }
            reorder(that.thead.find(".k-header:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex);
            if (footer && footer.length) {
                reorder(footer.find(".k-grid-footer-wrap>table>colgroup>col:not(.k-group-col,.k-hierarchy-col)"), colSourceIndex, colDestIndex);
                reorder(footer.find(".k-footer-template>td:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex);
            }
            rows = that.tbody.children(":not(.k-grouping-row,.k-detail-row)");
            for (idx = 0, length = rows.length; idx < length; idx += 1) {
                reorder(rows.eq(idx).find(">td:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex);
            }
        },
        cellIndex: function(td) {
            return $(td).parent().children("td:not(.k-group-cell,.k-hierarchy-cell)").index(td);
        },
        _modelForContainer: function(container) {
            container = $(container);
            if (!container.is("tr") && this._editMode() !== "popup") {
                container = container.closest("tr");
            }
            var id = container.attr(kendo.attr("uid"));
            return this.dataSource.getByUid(id);
        },
        _editable: function() {
            var that = this, selectable = that.selectable && that.selectable.options.multiple, editable = that.options.editable, handler = function() {
                var target = activeElement(), cell = that._editContainer;
                if (cell && !$.contains(cell[0], target) && cell[0] !== target && !$(target).closest(".k-animation-container").length) {
                    if (that.editable.end()) {
                        that.closeCell();
                    }
                }
            };
            if (editable) {
                var mode = that._editMode();
                if (mode === "incell") {
                    if (editable.update !== false) {
                        that.wrapper.on(CLICK + NS, "tr:not(.k-grouping-row) > td", function(e) {
                            var td = $(this);
                            if (td.hasClass("k-hierarchy-cell") || td.hasClass("k-detail-cell") || td.hasClass("k-group-cell") || td.hasClass("k-edit-cell") || td.has("a.k-grid-delete").length || td.has("button.k-grid-delete").length || td.closest("tbody")[0] !== that.tbody[0] || $(e.target).is(":input")) {
                                return;
                            }
                            if (that.editable) {
                                if (that.editable.end()) {
                                    if (selectable) {
                                        $(activeElement()).blur();
                                    }
                                    that.closeCell();
                                    that.editCell(td);
                                }
                            } else {
                                that.editCell(td);
                            }
                        }).on("focusin" + NS, function() {
                            clearTimeout(that.timer);
                            that.timer = null;
                        }).on("focusout" + NS, function() {
                            that.timer = setTimeout(handler, 1);
                        });
                    }
                } else {
                    if (editable.update !== false) {
                        that.wrapper.on(CLICK + NS, "tbody>tr:not(.k-detail-row,.k-grouping-row):visible a.k-grid-edit", function(e) {
                            e.preventDefault();
                            that.editRow($(this).closest("tr"));
                        });
                    }
                }
                if (editable.destroy !== false) {
                    that.wrapper.on(CLICK + NS, "tbody>tr:not(.k-detail-row,.k-grouping-row):visible .k-grid-delete", function(e) {
                        e.preventDefault();
                        e.stopPropagation();
                        that.removeRow($(this).closest("tr"));
                    });
                } else {
                    //Required for the MVC server wrapper delete button
                    that.wrapper.on(CLICK + NS, "tbody>tr:not(.k-detail-row,.k-grouping-row):visible button.k-grid-delete", function(e) {
                        if (!that._confirmation()) {
                            e.preventDefault();
                        }
                    });
                }
            }
        },
        editCell: function(cell) {
            var that = this, column = that.columns[that.cellIndex(cell)], model = that._modelForContainer(cell);
            if (model && (!model.editable || model.editable(column.field)) && !column.command && column.field) {
                that._attachModelChange(model);
                that._editContainer = cell;
                that.editable = cell.addClass("k-edit-cell").kendoEditable({
                    fields: {
                        field: column.field,
                        format: column.format,
                        editor: column.editor,
                        values: column.values
                    },
                    model: model,
                    change: function(e) {
                        if (that.trigger(SAVE, {
                            values: e.values,
                            container: cell,
                            model: model
                        })) {
                            e.preventDefault();
                        }
                    }
                }).data("kendoEditable");
                cell.parent().addClass("k-grid-edit-row");
                that.trigger(EDIT, {
                    container: cell,
                    model: model
                });
            }
        },
        _destroyEditable: function() {
            var that = this;
            var destroy = function() {
                if (that.editable) {
                    that._detachModelChange();
                    that.editable.destroy();
                    that.editable = null;
                    that._editContainer = null;
                }
            };
            if (that.editable) {
                if (that._editMode() === "popup") {
                    that._editContainer.data("kendoWindow").bind("deactivate", destroy).close();
                } else {
                    destroy();
                }
            }
        },
        _attachModelChange: function(model) {
            var that = this;
            that._modelChangeHandler = function(e) {
                that._modelChange({
                    field: e.field,
                    model: this
                });
            };
            model.bind("change", that._modelChangeHandler);
        },
        _detachModelChange: function() {
            var that = this, container = that._editContainer, model = that._modelForContainer(container);
            if (model) {
                model.unbind(CHANGE, that._modelChangeHandler);
            }
        },
        closeCell: function() {
            var that = this, cell, id, column, model;
            if (!that._editContainer) {
                return;
            }
            cell = that._editContainer.removeClass("k-edit-cell");
            id = cell.closest("tr").attr(kendo.attr("uid"));
            column = that.columns[that.cellIndex(cell)];
            model = that.dataSource.getByUid(id);
            cell.parent().removeClass("k-grid-edit-row");
            that._destroyEditable();
            // editable should be destoryed before content of the container is changed
            that._displayCell(cell, column, model);
            if (cell.hasClass("k-dirty-cell")) {
                $('<span class="k-dirty"/>').prependTo(cell);
            }
        },
        _displayCell: function(cell, column, dataItem) {
            var that = this, state = {
                storage: {},
                count: 0
            }, settings = extend({}, kendo.Template, that.options.templateSettings), tmpl = kendo.template(that._cellTmpl(column, state), settings);
            if (state.count > 0) {
                tmpl = proxy(tmpl, state.storage);
            }
            cell.empty().html(tmpl(dataItem));
        },
        removeRow: function(row) {
            var that = this, model, mode;
            if (!that._confirmation()) {
                return;
            }
            row = $(row).hide();
            model = that._modelForContainer(row);
            if (model && !that.trigger(REMOVE, {
                row: row,
                model: model
            })) {
                mode = that._editMode();
                if (mode !== "incell") {
                    that.cancelRow();
                }
                that.dataSource.remove(model);
                if (mode === "inline" || mode === "popup") {
                    that.dataSource.sync();
                }
            }
        },
        _editMode: function() {
            var mode = "incell", editable = this.options.editable;
            if (editable !== true) {
                if (typeof editable == "string") {
                    mode = editable;
                } else {
                    mode = editable.mode || mode;
                }
            }
            return mode;
        },
        editRow: function(row) {
            var that = this, model = that._modelForContainer(row), mode = that._editMode(), navigatable = that.options.navigatable, container;
            that.cancelRow();
            if (model) {
                that._attachModelChange(model);
                if (mode === "popup") {
                    that._createPopupEditor(model);
                } else if (mode === "inline") {
                    that._createInlineEditor(row, model);
                } else if (mode === "incell") {
                    $(row).children(DATA_CELL).each(function() {
                        var cell = $(this);
                        var column = that.columns[cell.index()];
                        model = that._modelForContainer(cell);
                        if (model && (!model.editable || model.editable(column.field)) && column.field) {
                            that.editCell(cell);
                            return false;
                        }
                    });
                }
                container = that._editContainer;
                container.on(CLICK + NS, "a.k-grid-cancel", function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    if (that.trigger("cancel", {
                        container: container,
                        model: model
                    })) {
                        return;
                    }
                    var currentIndex = that.items().index($(that.current()).parent());
                    that.cancelRow();
                    if (navigatable) {
                        that.current(that.items().eq(currentIndex).children().filter(NAVCELL).first());
                        focusTable(that.table, true);
                    }
                });
                container.on(CLICK + NS, "a.k-grid-update", function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    that.saveRow();
                });
            }
        },
        _createPopupEditor: function(model) {
            var that = this, html = "<div " + kendo.attr("uid") + '="' + model.uid + '"><div class="k-edit-form-container">', column, command, fields = [], idx, length, tmpl, updateText, cancelText, tempCommand, attr, editable = that.options.editable, template = editable.template, options = isPlainObject(editable) ? editable.window : {}, settings = extend({}, kendo.Template, that.options.templateSettings);
            if (template) {
                if (typeof template === STRING) {
                    template = window.unescape(template);
                }
                html += kendo.template(template, settings)(model);
                for (idx = 0, length = that.columns.length; idx < length; idx++) {
                    column = that.columns[idx];
                    if (column.command) {
                        tempCommand = getCommand(column.command, "edit");
                        if (tempCommand) {
                            command = tempCommand;
                        }
                    }
                }
            } else {
                for (idx = 0, length = that.columns.length; idx < length; idx++) {
                    column = that.columns[idx];
                    if (!column.command) {
                        html += '<div class="k-edit-label"><label for="' + column.field + '">' + (column.title || column.field || "") + "</label></div>";
                        if ((!model.editable || model.editable(column.field)) && column.field) {
                            fields.push({
                                field: column.field,
                                format: column.format,
                                editor: column.editor,
                                values: column.values
                            });
                            html += "<div " + kendo.attr("container-for") + '="' + column.field + '" class="k-edit-field"></div>';
                        } else {
                            var state = {
                                storage: {},
                                count: 0
                            };
                            tmpl = kendo.template(that._cellTmpl(column, state), settings);
                            if (state.count > 0) {
                                tmpl = proxy(tmpl, state.storage);
                            }
                            html += '<div class="k-edit-field">' + tmpl(model) + "</div>";
                        }
                    } else if (column.command) {
                        tempCommand = getCommand(column.command, "edit");
                        if (tempCommand) {
                            command = tempCommand;
                        }
                    }
                }
            }
            if (command) {
                if (isPlainObject(command)) {
                    if (command.text && isPlainObject(command.text)) {
                        updateText = command.text.update;
                        cancelText = command.text.cancel;
                    }
                    if (command.attr) {
                        attr = command.attr;
                    }
                }
            }
            html += that._createButton({
                name: "update",
                text: updateText,
                attr: attr
            }) + that._createButton({
                name: "canceledit",
                text: cancelText,
                attr: attr
            });
            html += "</div></div>";
            var container = that._editContainer = $(html).appendTo(that.wrapper).eq(0).kendoWindow(extend({
                modal: true,
                resizable: false,
                draggable: true,
                title: "Edit",
                visible: false,
                close: function(e) {
                    if (e.userTriggered) {
                        if (that.trigger("cancel", {
                            container: container,
                            model: model
                        })) {
                            e.preventDefault();
                            return;
                        }
                        var currentIndex = that.items().index($(that.current()).parent());
                        that.cancelRow();
                        if (that.options.navigatable) {
                            that.current(that.items().eq(currentIndex).children().filter(NAVCELL).first());
                            focusTable(that.table, true);
                        }
                    }
                }
            }, options));
            that.editable = that._editContainer.kendoEditable({
                fields: fields,
                model: model,
                clearContainer: false
            }).data("kendoEditable");
            container.data("kendoWindow").center().open();
            that.trigger(EDIT, {
                container: container,
                model: model
            });
        },
        _createInlineEditor: function(row, model) {
            var that = this, column, cell, command, fields = [];
            row.children(":not(.k-group-cell,.k-hierarchy-cell)").each(function() {
                cell = $(this);
                column = that.columns[that.cellIndex(cell)];
                if (!column.command && column.field && (!model.editable || model.editable(column.field))) {
                    fields.push({
                        field: column.field,
                        format: column.format,
                        editor: column.editor,
                        values: column.values
                    });
                    cell.attr(kendo.attr("container-for"), column.field);
                    cell.empty();
                } else if (column.command) {
                    command = getCommand(column.command, "edit");
                    if (command) {
                        cell.empty();
                        var updateText, cancelText, attr;
                        if (isPlainObject(command)) {
                            if (command.text && isPlainObject(command.text)) {
                                updateText = command.text.update;
                                cancelText = command.text.cancel;
                            }
                            if (command.attr) {
                                attr = command.attr;
                            }
                        }
                        $(that._createButton({
                            name: "update",
                            text: updateText,
                            attr: attr
                        }) + that._createButton({
                            name: "canceledit",
                            text: cancelText,
                            attr: attr
                        })).appendTo(cell);
                    }
                }
            });
            that._editContainer = row;
            that.editable = row.addClass("k-grid-edit-row").kendoEditable({
                fields: fields,
                model: model,
                clearContainer: false
            }).data("kendoEditable");
            that.trigger(EDIT, {
                container: row,
                model: model
            });
        },
        cancelRow: function() {
            var that = this, container = that._editContainer, model;
            if (container) {
                model = that._modelForContainer(container);
                that.dataSource.cancelChanges(model);
                if (that._editMode() !== "popup") {
                    that._displayRow(container);
                } else {
                    that._displayRow(that.items().filter("[" + kendo.attr("uid") + "=" + model.uid + "]"));
                }
                that._destroyEditable();
            }
        },
        saveRow: function() {
            var that = this, container = that._editContainer, model = that._modelForContainer(container), editable = that.editable;
            if (container && editable && editable.end() && !that.trigger(SAVE, {
                container: container,
                model: model
            })) {
                that.dataSource.sync();
            }
        },
        _displayRow: function(row) {
            var that = this, model = that._modelForContainer(row);
            if (model) {
                row.replaceWith($((row.hasClass("k-alt") ? that.altRowTemplate : that.rowTemplate)(model)));
            }
        },
        _showMessage: function(text) {
            return window.confirm(text);
        },
        _confirmation: function() {
            var that = this, editable = that.options.editable, confirmation = editable === true || typeof editable === STRING ? DELETECONFIRM : editable.confirmation;
            return confirmation !== false && confirmation != null ? that._showMessage(confirmation) : true;
        },
        cancelChanges: function() {
            this.dataSource.cancelChanges();
        },
        saveChanges: function() {
            var that = this;
            if ((that.editable && that.editable.end() || !that.editable) && !that.trigger(SAVECHANGES)) {
                that.dataSource.sync();
            }
        },
        addRow: function() {
            var that = this, index, dataSource = that.dataSource, mode = that._editMode(), createAt = that.options.editable.createAt || "", pageSize = dataSource.pageSize(), view = dataSource.view() || [];
            if (that.editable && that.editable.end() || !that.editable) {
                if (mode != "incell") {
                    that.cancelRow();
                }
                index = dataSource.indexOf(view[0]);
                if (createAt.toLowerCase() == "bottom") {
                    index += view.length;
                    if (pageSize && !dataSource.options.serverPaging && pageSize <= view.length) {
                        index -= 1;
                    }
                }
                if (index < 0) {
                    if (dataSource.page() > dataSource.totalPages()) {
                        index = (dataSource.page() - 1) * pageSize;
                    } else {
                        index = 0;
                    }
                }
                var model = dataSource.insert(index, {}), id = model.uid, row = that.table.find("tr[" + kendo.attr("uid") + "=" + id + "]"), cell = row.children("td:not(.k-group-cell,.k-hierarchy-cell)").eq(that._firstEditableColumnIndex(row));
                if ((mode === "inline" || mode === "popup") && row.length) {
                    that.editRow(row);
                } else if (cell.length) {
                    that.editCell(cell);
                }
            }
        },
        _firstEditableColumnIndex: function(container) {
            var that = this, column, columns = that.columns, idx, length, model = that._modelForContainer(container);
            for (idx = 0, length = columns.length; idx < length; idx++) {
                column = columns[idx];
                if (model && (!model.editable || model.editable(column.field)) && !column.command && column.field) {
                    return idx;
                }
            }
            return -1;
        },
        _toolbar: function() {
            var that = this, wrapper = that.wrapper, toolbar = that.options.toolbar, editable = that.options.editable, container;
            if (toolbar) {
                container = that.wrapper.find(".k-grid-toolbar");
                if (!container.length) {
                    if (!isFunction(toolbar)) {
                        toolbar = typeof toolbar === STRING ? toolbar : that._toolbarTmpl(toolbar).replace(templateHashRegExp, "\\#");
                        toolbar = proxy(kendo.template(toolbar), that);
                    }
                    container = $('<div class="k-toolbar k-grid-toolbar" />').html(toolbar({})).prependTo(wrapper);
                }
                if (editable && editable.create !== false) {
                    container.on(CLICK + NS, ".k-grid-add", function(e) {
                        e.preventDefault();
                        that.addRow();
                    }).on(CLICK + NS, ".k-grid-cancel-changes", function(e) {
                        e.preventDefault();
                        that.cancelChanges();
                    }).on(CLICK + NS, ".k-grid-save-changes", function(e) {
                        e.preventDefault();
                        that.saveChanges();
                    });
                }
            }
        },
        _toolbarTmpl: function(commands) {
            var that = this, idx, length, html = "";
            if (isArray(commands)) {
                for (idx = 0, length = commands.length; idx < length; idx++) {
                    html += that._createButton(commands[idx]);
                }
            }
            return html;
        },
        _createButton: function(command) {
            var template = command.template || COMMANDBUTTONTMPL, commandName = typeof command === STRING ? command : command.name || command.text, options = {
                className: "k-grid-" + (commandName || "").replace(/\s/g, ""),
                text: commandName,
                imageClass: "",
                attr: "",
                iconClass: ""
            };
            if (!commandName && !(isPlainObject(command) && command.template)) {
                throw new Error("Custom commands should have name specified");
            }
            if (isPlainObject(command)) {
                if (command.className) {
                    command.className += " " + options.className;
                }
                if (commandName === "edit" && isPlainObject(command.text)) {
                    command = extend(true, {}, command);
                    command.text = command.text.edit;
                }
                if (command.attr && isPlainObject(command.attr)) {
                    command.attr = stringifyAttributes(command.attr);
                }
                options = extend(true, options, defaultCommands[commandName], command);
            } else {
                options = extend(true, options, defaultCommands[commandName]);
            }
            return kendo.template(template)(options);
        },
        _groupable: function() {
            var that = this;
            that.table.on(CLICK + NS, ".k-grouping-row .k-i-collapse, .k-grouping-row .k-i-expand", function(e) {
                var element = $(this), group = element.closest("tr");
                if (element.hasClass("k-i-collapse")) {
                    that.collapseGroup(group);
                } else {
                    that.expandGroup(group);
                }
                e.preventDefault();
                e.stopPropagation();
            });
            that._attachGroupable();
        },
        _attachGroupable: function() {
            var that = this, wrapper = that.wrapper, groupable = that.options.groupable;
            if (groupable) {
                if (!wrapper.has("div.k-grouping-header")[0]) {
                    $("<div>&nbsp;</div>").addClass("k-grouping-header").prependTo(wrapper);
                }
                if (that.groupable) {
                    that.groupable.destroy();
                }
                that.groupable = new Groupable(wrapper, extend({}, groupable, {
                    draggable: that._draggableInstance,
                    groupContainer: ">div.k-grouping-header",
                    dataSource: that.dataSource,
                    draggableElements: that.content ? ">.k-grid-header " + GROUPINGDRAGGABLES : ">table>.k-grid-header " + GROUPINGDRAGGABLES,
                    filter: that.content ? ">.k-grid-header " + GROUPINGFILTER : ">table>.k-grid-header " + GROUPINGFILTER,
                    allowDrag: that.options.reorderable
                }));
            }
        },
        _selectable: function() {
            var that = this, multi, cell, selectable = that.options.selectable;
            if (selectable) {
                multi = typeof selectable === STRING && selectable.toLowerCase().indexOf("multiple") > -1;
                cell = typeof selectable === STRING && selectable.toLowerCase().indexOf("cell") > -1;
                that.selectable = new kendo.ui.Selectable(that.table, {
                    filter: ">" + (cell ? SELECTION_CELL_SELECTOR : "tbody>tr:not(.k-grouping-row,.k-detail-row,.k-group-footer)"),
                    aria: true,
                    multiple: multi,
                    change: function() {
                        that.trigger(CHANGE);
                    }
                });
                if (that.options.navigatable) {
                    that.table.on("keydown" + NS, function(e) {
                        var current = that.current();
                        if (e.keyCode === keys.SPACEBAR && e.target == that.table[0] && !current.is(".k-edit-cell,.k-header") && current.parent().is(":not(.k-grouping-row,.k-detail-row,.k-group-footer)")) {
                            e.preventDefault();
                            e.stopPropagation();
                            current = cell ? current : current.parent();
                            if (multi) {
                                if (!e.ctrlKey) {
                                    that.selectable.clear();
                                } else {
                                    if (current.hasClass(SELECTED)) {
                                        current.removeClass(SELECTED);
                                        that.trigger(CHANGE);
                                        return;
                                    }
                                }
                            } else {
                                that.selectable.clear();
                            }
                            that.selectable.value(current);
                        }
                    });
                }
            }
        },
        clearSelection: function() {
            var that = this;
            that.selectable.clear();
            that.trigger(CHANGE);
        },
        select: function(items) {
            var that = this, selectable = that.selectable;
            items = $(items);
            if (items.length) {
                if (!selectable.options.multiple) {
                    selectable.clear();
                    items = items.first();
                }
                selectable.value(items);
                return;
            }
            return selectable.value();
        },
        current: function(element) {
            var that = this, scrollable = that.options.scrollable, current = that._current, table = that.table.add(that.thead.parent());
            if (element !== undefined && element.length) {
                if (!current || current[0] !== element[0]) {
                    if (current) {
                        current.removeClass(FOCUSED).removeAttr("id");
                        table.removeAttr("aria-activedescendant");
                    }
                    element.attr("id", that._cellId);
                    that._current = element.addClass(FOCUSED);
                    table.attr("aria-activedescendant", that._cellId);
                    if (element.length && scrollable) {
                        if ($.contains(that.content[0], element[0])) {
                            that._scrollTo(element.parent()[0], that.content[0]);
                        }
                        if (scrollable.virtual) {
                            that._scrollTo(element[0], that.content.find(">.k-virtual-scrollable-wrap")[0]);
                        } else {
                            that._scrollTo(element[0], that.content[0]);
                        }
                    }
                }
            }
            return that._current;
        },
        _removeCurrent: function() {
            if (this._current) {
                this._current.removeClass(FOCUSED);
                this._current = null;
            }
        },
        _scrollTo: function(element, container) {
            var elementToLowercase = element.tagName.toLowerCase(), isHorizontal = elementToLowercase === "td" || elementToLowercase === "th", elementOffset = element[isHorizontal ? "offsetLeft" : "offsetTop"], elementOffsetDir = element[isHorizontal ? "offsetWidth" : "offsetHeight"], containerScroll = container[isHorizontal ? "scrollLeft" : "scrollTop"], containerOffsetDir = container[isHorizontal ? "clientWidth" : "clientHeight"], bottomDistance = elementOffset + elementOffsetDir, result = 0;
            if (containerScroll > elementOffset) {
                result = elementOffset;
            } else if (bottomDistance > containerScroll + containerOffsetDir) {
                if (elementOffsetDir <= containerOffsetDir) {
                    result = bottomDistance - containerOffsetDir;
                } else {
                    result = elementOffset;
                }
            } else {
                result = containerScroll;
            }
            container[isHorizontal ? "scrollLeft" : "scrollTop"] = result;
        },
        _navigatable: function() {
            var that = this, currentProxy = proxy(that.current, that), table = that.table, headerTable = that.thead.parent(), dataTable = table, isRtl = kendo.support.isRtl(that.element);
            if (!that.options.navigatable) {
                return;
            }
            if (that.options.scrollable) {
                dataTable = table.add(headerTable);
                headerTable.attr(TABINDEX, -1);
            }
            headerTable.on("keydown" + NS, function(e) {
                if (e.altKey && e.keyCode == keys.DOWN) {
                    currentProxy().find(".k-grid-filter, .k-header-column-menu").click();
                    e.stopImmediatePropagation();
                }
            }).find("a.k-link").attr("tabIndex", -1);
            table.attr(TABINDEX, math.max(table.attr(TABINDEX) || 0, 0)).on("mousedown" + NS + " keydown" + NS, ".k-detail-cell", function(e) {
                if (e.target !== e.currentTarget) {
                    e.stopImmediatePropagation();
                }
            });
            dataTable.on(kendo.support.touch ? "touchstart" + NS : "mousedown" + NS, NAVROW + ">" + NAVCELL, proxy(tableClick, that)).on("focus" + NS, function() {
                var current = currentProxy();
                if (current && current.is(":visible")) {
                    current.addClass(FOCUSED);
                } else {
                    currentProxy($(this).find(FIRSTNAVITEM));
                }
                if (this == table[0]) {
                    headerTable.attr(TABINDEX, -1);
                    table.attr(TABINDEX, 0);
                } else {
                    table.attr(TABINDEX, -1);
                    headerTable.attr(TABINDEX, 0);
                }
            }).on("focusout" + NS, function() {
                var current = currentProxy();
                if (current) {
                    current.removeClass(FOCUSED);
                }
            }).on("keydown" + NS, function(e) {
                var key = e.keyCode, handled = false, canHandle = !e.isDefaultPrevented() && !$(e.target).is(":button,a,:input,a>.k-icon"), pageable = that.options.pageable, dataSource = that.dataSource, isInCell = that._editMode() == "incell", active, currentIndex, row, index, tableToFocus, shiftKey = e.shiftKey, browser = kendo.support.browser, current = currentProxy();
                if (current && current.is("th")) {
                    canHandle = true;
                }
                if (canHandle && key == keys.UP) {
                    if (current) {
                        row = current.parent().prevAll(NAVROW).first();
                        if (!row[0]) {
                            tableToFocus = that.thead.parent();
                            focusTable(tableToFocus, true);
                            row = tableToFocus.find(NAVROW).first();
                        }
                        index = current.index();
                        current = row.children().eq(index);
                        if (!current[0] || !current.is(NAVCELL)) {
                            current = row.children(NAVCELL).first();
                        }
                    } else {
                        current = table.find(FIRSTNAVITEM);
                    }
                    handled = true;
                    currentProxy(current);
                } else if (canHandle && key == keys.DOWN) {
                    if (current) {
                        row = current.parent().nextAll(NAVROW).first();
                        if (!row[0] && current.is("th")) {
                            focusTable(that.tbody.parent());
                            row = that.tbody.find(NAVROW).first();
                        }
                        index = current.index();
                        current = row.children().eq(index);
                        if (!current[0] || !current.is(NAVCELL)) {
                            current = row.children(NAVCELL).first();
                        }
                    } else {
                        current = table.find(FIRSTNAVITEM);
                    }
                    handled = true;
                    currentProxy(current);
                } else if (canHandle && key == (isRtl ? keys.RIGHT : keys.LEFT)) {
                    currentProxy(current ? current.prevAll(DATA_CELL + ":first") : table.find(FIRSTNAVITEM));
                    handled = true;
                } else if (canHandle && key == (isRtl ? keys.LEFT : keys.RIGHT)) {
                    if (current) {
                        if (current.next()[0]) {
                            current = current.nextAll(DATA_CELL + ":first");
                        }
                    } else {
                        current = table.find(FIRSTNAVITEM);
                    }
                    handled = true;
                    currentProxy(current);
                } else if (canHandle && pageable && keys.PAGEDOWN == key) {
                    dataSource.page(dataSource.page() + 1);
                    handled = true;
                } else if (canHandle && pageable && keys.PAGEUP == key) {
                    dataSource.page(dataSource.page() - 1);
                    handled = true;
                } else if (key == keys.ENTER || keys.F2 == key) {
                    current = current ? current : table.find(FIRSTNAVITEM);
                    if (current.is("th")) {
                        current.find(".k-link").click();
                        handled = true;
                    } else if (current.parent().is(".k-master-row,.k-grouping-row")) {
                        current.parent().find(".k-icon:first").click();
                        handled = true;
                    } else {
                        var focusable = current.find(":focusable:first");
                        if (!current.hasClass("k-edit-cell") && focusable[0] && current.hasClass("k-state-focused")) {
                            focusable.focus();
                            handled = true;
                        } else if (that.options.editable && !$(e.target).is(":button,.k-button")) {
                            that._handleEditing(current);
                            handled = true;
                        }
                    }
                } else if (keys.ESC == key) {
                    active = activeElement();
                    if (current && $.contains(current[0], active) && !current.hasClass("k-edit-cell") && !current.parent().hasClass("k-grid-edit-row")) {
                        focusTable(that.table[0], true);
                        handled = true;
                    } else if (that._editContainer && (!current || that._editContainer.has(current[0]) || current[0] === that._editContainer[0])) {
                        if (isInCell) {
                            that.closeCell();
                        } else {
                            currentIndex = that.items().index($(current).parent());
                            if (active) {
                                active.blur();
                            }
                            that.cancelRow();
                            if (currentIndex >= 0) {
                                that.current(that.items().eq(currentIndex).children().filter(NAVCELL).first());
                            }
                        }
                        if (browser.msie && browser.version < 9) {
                            document.body.focus();
                        }
                        focusTable(table, true);
                        handled = true;
                    }
                } else if (keys.TAB == key) {
                    var cell;
                    current = $(current);
                    if (that.options.editable && isInCell) {
                        cell = $(activeElement()).closest(".k-edit-cell");
                        if (cell[0] && cell[0] !== current[0]) {
                            current = cell;
                        }
                    }
                    cell = shiftKey ? current.prevAll(DATA_CELL + ":first") : current.nextAll(":visible:first");
                    if (!cell.length) {
                        cell = current.parent()[shiftKey ? "prevAll" : "nextAll"]("tr:not(.k-grouping-row):not(.k-detail-row):visible:first").children(DATA_CELL + (shiftKey ? ":last" : ":first"));
                    }
                    if (!current.is("th") && cell.length && that.options.editable && isInCell) {
                        that._handleEditing(current, cell);
                        handled = true;
                    }
                }
                if (handled) {
                    //prevent browser scrolling
                    e.preventDefault();
                    //required in hierarchy
                    e.stopPropagation();
                }
            });
        },
        _handleEditing: function(current, next) {
            var that = this, active = $(activeElement()), mode = that._editMode(), editContainer = that._editContainer, focusable, isEdited;
            if (mode == "incell") {
                isEdited = current.hasClass("k-edit-cell");
            } else {
                isEdited = current.parent().hasClass("k-grid-edit-row");
            }
            if (that.editable) {
                if ($.contains(editContainer[0], active[0])) {
                    active.blur();
                    if (kendo.support.browser.opera) {
                        active.change();
                    }
                }
                if (!that.editable) {
                    focusTable(that.table);
                    return;
                }
                if (that.editable.end()) {
                    if (mode == "incell") {
                        that.closeCell();
                    } else {
                        that.saveRow();
                        isEdited = true;
                    }
                } else {
                    if (mode == "incell") {
                        that.current(editContainer);
                    } else {
                        that.current(editContainer.children().filter(DATA_CELL).first());
                    }
                    focusable = editContainer.find(":focusable:first")[0];
                    if (focusable) {
                        focusable.focus();
                    }
                    return;
                }
            }
            if (next) {
                that.current(next);
            }
            focusTable(that.table, true);
            if (!isEdited && !next || next) {
                if (mode == "incell") {
                    that.editCell(that.current());
                } else {
                    that.editRow(that.current().parent());
                }
            }
        },
        _wrapper: function() {
            var that = this, table = that.table, height = that.options.height, wrapper = that.element;
            if (!wrapper.is("div")) {
                wrapper = wrapper.wrap("<div/>").parent();
            }
            that.wrapper = wrapper.addClass("k-grid k-widget");
            /*
                                  .attr(TABINDEX, math.max(table.attr(TABINDEX) || 0, 0));

            table.removeAttr(TABINDEX);
            */
            if (height) {
                that.wrapper.css(HEIGHT, height);
                table.css(HEIGHT, "auto");
            }
        },
        _tbody: function() {
            var that = this, table = that.table, tbody;
            tbody = table.find(">tbody");
            if (!tbody.length) {
                tbody = $("<tbody/>").appendTo(table);
            }
            that.tbody = tbody;
        },
        _scrollable: function() {
            var that = this, header, table, options = that.options, scrollable = options.scrollable, hasVirtualScroll = scrollable !== true && scrollable.virtual && !that.virtualScrollable, scrollbar = !kendo.support.kineticScrollNeeded || hasVirtualScroll ? kendo.support.scrollbar() : 0;
            if (scrollable) {
                header = that.wrapper.children(".k-grid-header");
                if (!header[0]) {
                    header = $('<div class="k-grid-header" />').insertBefore(that.table);
                }
                // workaround for IE issue where scroll is not raised if container is same width as the scrollbar
                header.css(isRtl ? "padding-left" : "padding-right", scrollable.virtual ? scrollbar + 1 : scrollbar);
                table = $('<table role="grid" cellspacing="0" />');
                table.append(that.thead);
                header.empty().append($('<div class="k-grid-header-wrap" />').append(table));
                that.content = that.table.parent();
                if (that.content.is(".k-virtual-scrollable-wrap")) {
                    that.content = that.content.parent();
                }
                if (!that.content.is(".k-grid-content, .k-virtual-scrollable-wrap")) {
                    that.content = that.table.wrap('<div class="k-grid-content" />').parent();
                }
                if (hasVirtualScroll) {
                    that.virtualScrollable = new VirtualScrollable(that.content, {
                        dataSource: that.dataSource,
                        itemHeight: proxy(that._averageRowHeight, that)
                    });
                }
                that.scrollables = header.children(".k-grid-header-wrap");
                // the footer may exists if rendered from the server
                var footer = that.wrapper.find(".k-grid-footer"), webKitRtlCorrection = isRtl && kendo.support.browser.webkit ? scrollbar : 0;
                if (footer.length) {
                    that.scrollables = that.scrollables.add(footer.children(".k-grid-footer-wrap"));
                }
                if (scrollable.virtual) {
                    that.content.find(">.k-virtual-scrollable-wrap").bind("scroll" + NS, function() {
                        that.scrollables.scrollLeft(this.scrollLeft + webKitRtlCorrection);
                    });
                } else {
                    that.content.bind("scroll" + NS, function() {
                        that.scrollables.scrollLeft(this.scrollLeft + webKitRtlCorrection);
                    });
                    var touchScroller = kendo.touchScroller(that.content);
                    if (touchScroller && touchScroller.movable) {
                        touchScroller.movable.bind("change", function(e) {
                            that.scrollables.scrollLeft(-e.sender.x);
                        });
                    }
                }
            }
        },
        _setContentHeight: function() {
            var that = this, options = that.options, height = that.wrapper.innerHeight(), header = that.wrapper.children(".k-grid-header"), scrollbar = kendo.support.scrollbar();
            if (options.scrollable) {
                height -= header.outerHeight();
                if (that.pager) {
                    height -= that.pager.element.outerHeight();
                }
                if (options.groupable) {
                    height -= that.wrapper.children(".k-grouping-header").outerHeight();
                }
                if (options.toolbar) {
                    height -= that.wrapper.children(".k-grid-toolbar").outerHeight();
                }
                if (that.footerTemplate) {
                    height -= that.wrapper.children(".k-grid-footer").outerHeight();
                }
                var isGridHeightSet = function(el) {
                    var initialHeight, newHeight;
                    if (el[0].style.height) {
                        return true;
                    } else {
                        initialHeight = el.height();
                    }
                    el.height("auto");
                    newHeight = el.height();
                    if (initialHeight != newHeight) {
                        el.height("");
                        return true;
                    }
                    el.height("");
                    return false;
                };
                if (isGridHeightSet(that.wrapper)) {
                    // set content height only if needed
                    if (height > scrollbar * 2) {
                        // do not set height if proper scrollbar cannot be displayed
                        that.content.height(height);
                    } else {
                        that.content.height(scrollbar * 2 + 1);
                    }
                }
            }
        },
        _averageRowHeight: function() {
            var that = this, rowHeight = that._rowHeight;
            if (!that._rowHeight) {
                that._rowHeight = rowHeight = that.table.outerHeight() / that.items().length;
                that._sum = rowHeight;
                that._measures = 1;
            }
            var currentRowHeight = that.table.outerHeight() / that.items().length;
            if (rowHeight !== currentRowHeight) {
                that._measures++;
                that._sum += currentRowHeight;
                that._rowHeight = that._sum / that._measures;
            }
            return rowHeight;
        },
        _dataSource: function() {
            var that = this, options = that.options, pageable, dataSource = options.dataSource;
            dataSource = isArray(dataSource) ? {
                data: dataSource
            } : dataSource;
            if (isPlainObject(dataSource)) {
                extend(dataSource, {
                    table: that.table,
                    fields: that.columns
                });
                pageable = options.pageable;
                if (isPlainObject(pageable) && pageable.pageSize !== undefined) {
                    dataSource.pageSize = pageable.pageSize;
                }
            }
            if (that.dataSource && that._refreshHandler) {
                that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(PROGRESS, that._progressHandler).unbind(ERROR, that._errorHandler);
            } else {
                that._refreshHandler = proxy(that.refresh, that);
                that._progressHandler = proxy(that._requestStart, that);
                that._errorHandler = proxy(that._error, that);
            }
            that.dataSource = DataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind(PROGRESS, that._progressHandler).bind(ERROR, that._errorHandler);
        },
        _error: function() {
            this._progress(false);
        },
        _requestStart: function() {
            this._progress(true);
        },
        _modelChange: function(e) {
            var that = this, model = e.model, row = that.tbody.find("tr[" + kendo.attr("uid") + "=" + model.uid + "]"), cell, column, isAlt = row.hasClass("k-alt"), tmp, idx = that.items().index(row), length;
            if (row.children(".k-edit-cell").length && !that.options.rowTemplate) {
                row.children(":not(.k-group-cell,.k-hierarchy-cell)").each(function() {
                    cell = $(this);
                    column = that.columns[that.cellIndex(cell)];
                    if (column.field === e.field) {
                        if (!cell.hasClass("k-edit-cell")) {
                            that._displayCell(cell, column, model);
                            $('<span class="k-dirty"/>').prependTo(cell);
                        } else {
                            cell.addClass("k-dirty-cell");
                        }
                    }
                });
            } else if (!row.hasClass("k-grid-edit-row")) {
                tmp = (isAlt ? that.altRowTemplate : that.rowTemplate)(model);
                row.replaceWith(tmp);
                tmp = that.items().eq(idx);
                for (idx = 0, length = that.columns.length; idx < length; idx++) {
                    column = that.columns[idx];
                    if (column.field === e.field) {
                        cell = tmp.children(":not(.k-group-cell,.k-hierarchy-cell)").eq(idx);
                        $('<span class="k-dirty"/>').prependTo(cell);
                    }
                }
                that.trigger("itemChange", {
                    item: tmp,
                    data: model,
                    ns: ui
                });
            }
        },
        _pageable: function() {
            var that = this, wrapper, pageable = that.options.pageable;
            if (pageable) {
                wrapper = that.wrapper.children("div.k-grid-pager");
                if (!wrapper.length) {
                    wrapper = $('<div class="k-pager-wrap k-grid-pager"/>').appendTo(that.wrapper);
                }
                if (that.pager) {
                    that.pager.destroy();
                }
                if (typeof pageable === "object" && pageable instanceof kendo.ui.Pager) {
                    that.pager = pageable;
                } else {
                    that.pager = new kendo.ui.Pager(wrapper, extend({}, pageable, {
                        dataSource: that.dataSource
                    }));
                }
            }
        },
        _footer: function() {
            var that = this, aggregates = that.dataSource.aggregates(), html = "", footerTemplate = that.footerTemplate, options = that.options, footerWrap, footer = that.footer || that.wrapper.find(".k-grid-footer");
            if (footerTemplate) {
                aggregates = !isEmptyObject(aggregates) ? aggregates : buildEmptyAggregatesObject(that.dataSource.aggregate());
                html = $(that._wrapFooter(footerTemplate(aggregates)));
                if (footer.length) {
                    var tmp = html;
                    footer.replaceWith(tmp);
                    footer = that.footer = tmp;
                } else {
                    if (options.scrollable) {
                        footer = that.footer = options.pageable ? html.insertBefore(that.wrapper.children("div.k-grid-pager")) : html.appendTo(that.wrapper);
                    } else {
                        footer = that.footer = html.insertBefore(that.tbody);
                    }
                }
            } else if (footer && !that.footer) {
                that.footer = footer;
            }
            if (footer.length) {
                if (options.scrollable) {
                    footerWrap = footer.attr("tabindex", -1).children(".k-grid-footer-wrap");
                    that.scrollables = that.scrollables.not(".k-grid-footer-wrap").add(footerWrap);
                }
                if (that._footerWidth) {
                    footer.find("table").css("width", that._footerWidth);
                }
                if (footerWrap) {
                    footerWrap.scrollLeft(that.content.scrollLeft());
                }
            }
        },
        _wrapFooter: function(footerRow) {
            var that = this, html = "", scrollbar = !kendo.support.mobileOS ? kendo.support.scrollbar() : 0;
            if (that.options.scrollable) {
                html = $('<div class="k-grid-footer"><div class="k-grid-footer-wrap"><table cellspacing="0"><tbody>' + footerRow + "</tbody></table></div></div>");
                that._appendCols(html.find("table"));
                html.css(isRtl ? "padding-left" : "padding-right", scrollbar);
                // Update inner fix.
                return html;
            }
            return '<tfoot class="k-grid-footer">' + footerRow + "</tfoot>";
        },
        _columnMenu: function() {
            var that = this, menu, columns = that.columns, column, options = that.options, columnMenu = options.columnMenu, menuOptions, sortable, filterable, closeCallback = function() {
                focusTable(that.thead.parent(), true);
            }, initCallback = function(e) {
                that.trigger(COLUMNMENUINIT, {
                    field: e.field,
                    container: e.container
                });
            }, cell;
            if (columnMenu) {
                if (typeof columnMenu == "boolean") {
                    columnMenu = {};
                }
                that.thead.find("th:not(.k-hierarchy-cell,.k-group-cell)").each(function(index) {
                    column = columns[index];
                    cell = $(this);
                    if (!column.command && (column.field || cell.attr("data-" + kendo.ns + "field"))) {
                        menu = cell.data("kendoColumnMenu");
                        if (menu) {
                            menu.destroy();
                        }
                        sortable = column.sortable !== false && columnMenu.sortable !== false ? options.sortable : false;
                        filterable = options.filterable && column.filterable !== false && columnMenu.filterable !== false ? extend({}, column.filterable, options.filterable) : false;
                        menuOptions = {
                            dataSource: that.dataSource,
                            values: column.values,
                            columns: columnMenu.columns,
                            sortable: sortable,
                            filterable: filterable,
                            messages: columnMenu.messages,
                            owner: that,
                            closeCallback: closeCallback,
                            init: initCallback
                        };
                        cell.kendoColumnMenu(menuOptions);
                    }
                });
            }
        },
        _filterable: function() {
            var that = this, columns = that.columns, cell, filterMenu, closeCallback = function() {
                focusTable(that.thead.parent(), true);
            }, filterable = that.options.filterable;
            if (filterable && !that.options.columnMenu) {
                that.thead.find("th:not(.k-hierarchy-cell,.k-group-cell)").each(function(index) {
                    cell = $(this);
                    if (columns[index].filterable !== false && !columns[index].command && (columns[index].field || cell.attr("data-" + kendo.ns + "field"))) {
                        filterMenu = cell.data("kendoFilterMenu");
                        if (filterMenu) {
                            filterMenu.destroy();
                        }
                        cell.kendoFilterMenu(extend(true, {}, filterable, columns[index].filterable, {
                            dataSource: that.dataSource,
                            values: columns[index].values,
                            closeCallback: closeCallback,
                            init: function(e) {
                                that.trigger(FILTERMENUINIT, {
                                    field: e.field,
                                    container: e.container
                                });
                            }
                        }));
                    }
                });
            }
        },
        _sortable: function() {
            var that = this, columns = that.columns, column, cell, sortableInstance, sortable = that.options.sortable;
            if (sortable) {
                that.thead.find("th:not(.k-hierarchy-cell,.k-group-cell)").each(function(index) {
                    column = columns[index];
                    if (column.sortable !== false && !column.command && column.field) {
                        cell = $(this);
                        sortableInstance = cell.data("kendoSortable");
                        if (sortableInstance) {
                            sortableInstance.destroy();
                        }
                        cell.attr("data-" + kendo.ns + "field", column.field).kendoSortable(extend({}, sortable, {
                            dataSource: that.dataSource,
                            aria: true
                        }));
                    }
                });
            }
        },
        _columns: function(columns) {
            var that = this, table = that.table, encoded, cols = table.find("col"), dataSource = that.options.dataSource;
            // using HTML5 data attributes as a configuration option e.g. <th data-field="foo">Foo</foo>
            columns = columns.length ? columns : map(table.find("th"), function(th, idx) {
                th = $(th);
                var sortable = th.attr(kendo.attr("sortable")), filterable = th.attr(kendo.attr("filterable")), type = th.attr(kendo.attr("type")), groupable = th.attr(kendo.attr("groupable")), field = th.attr(kendo.attr("field")), menu = th.attr(kendo.attr("menu"));
                if (!field) {
                    field = th.text().replace(/\s|[^A-z0-9]/g, "");
                }
                return {
                    field: field,
                    type: type,
                    sortable: sortable !== "false",
                    filterable: filterable !== "false",
                    groupable: groupable !== "false",
                    menu: menu,
                    template: th.attr(kendo.attr("template")),
                    width: cols.eq(idx).css("width")
                };
            });
            encoded = !(that.table.find("tbody tr").length > 0 && (!dataSource || !dataSource.transport));
            that.columns = map(columns, function(column) {
                column = typeof column === STRING ? {
                    field: column
                } : column;
                if (column.hidden) {
                    column.attributes = addHiddenStyle(column.attributes);
                    column.footerAttributes = addHiddenStyle(column.footerAttributes);
                    column.headerAttributes = addHiddenStyle(column.headerAttributes);
                }
                return extend({
                    encoded: encoded
                }, column);
            });
        },
        _groups: function() {
            var group = this.dataSource.group();
            return group ? group.length : 0;
        },
        _tmpl: function(rowTemplate, alt) {
            var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), idx, length = that.columns.length, template, state = {
                storage: {},
                count: 0
            }, column, type, hasDetails = that._hasDetails(), className = [], groups = that._groups();
            if (!rowTemplate) {
                rowTemplate = "<tr";
                if (alt) {
                    className.push("k-alt");
                }
                if (hasDetails) {
                    className.push("k-master-row");
                }
                if (className.length) {
                    rowTemplate += ' class="' + className.join(" ") + '"';
                }
                if (length) {
                    // data item is an object
                    rowTemplate += " " + kendo.attr("uid") + '="#=' + kendo.expr("uid", settings.paramName) + '#"';
                }
                rowTemplate += " role='row'>";
                if (groups > 0) {
                    rowTemplate += groupCells(groups);
                }
                if (hasDetails) {
                    rowTemplate += '<td class="k-hierarchy-cell"><a class="k-icon k-plus" href="\\#" tabindex="-1"></a></td>';
                }
                for (idx = 0; idx < length; idx++) {
                    column = that.columns[idx];
                    template = column.template;
                    type = typeof template;
                    rowTemplate += "<td" + stringifyAttributes(column.attributes) + " role='gridcell'>";
                    rowTemplate += that._cellTmpl(column, state);
                    rowTemplate += "</td>";
                }
                rowTemplate += "</tr>";
            }
            rowTemplate = kendo.template(rowTemplate, settings);
            if (state.count > 0) {
                return proxy(rowTemplate, state.storage);
            }
            return rowTemplate;
        },
        _headerCellText: function(column) {
            var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), template = column.headerTemplate, type = typeof template, text = column.title || column.field || "";
            if (type === FUNCTION) {
                text = kendo.template(template, settings)({});
            } else if (type === STRING) {
                text = template;
            }
            return text;
        },
        _cellTmpl: function(column, state) {
            var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), template = column.template, paramName = settings.paramName, field = column.field, html = "", idx, length, format = column.format, type = typeof template, columnValues = column.values;
            if (column.command) {
                if (isArray(column.command)) {
                    for (idx = 0, length = column.command.length; idx < length; idx++) {
                        html += that._createButton(column.command[idx]);
                    }
                    return html.replace(templateHashRegExp, "\\#");
                }
                return that._createButton(column.command).replace(templateHashRegExp, "\\#");
            }
            if (type === FUNCTION) {
                state.storage["tmpl" + state.count] = template;
                html += "#=this.tmpl" + state.count + "(" + paramName + ")#";
                state.count++;
            } else if (type === STRING) {
                html += template;
            } else if (columnValues && columnValues.length && isPlainObject(columnValues[0]) && "value" in columnValues[0] && field) {
                html += "#var v =" + kendo.stringify(convertToObject(columnValues)) + "#";
                html += "#var f = v[";
                if (!settings.useWithBlock) {
                    html += paramName + ".";
                }
                html += field + "]#";
                html += "${f != null ? f : ''}";
            } else {
                html += column.encoded ? "#:" : "#=";
                if (format) {
                    html += 'kendo.format("' + format.replace(formatRegExp, "\\$1") + '",';
                }
                if (field) {
                    field = kendo.expr(field, paramName);
                    html += field + "==null?'':" + field;
                } else {
                    html += "''";
                }
                if (format) {
                    html += ")";
                }
                html += "#";
            }
            return html;
        },
        _templates: function() {
            var that = this, options = that.options, dataSource = that.dataSource, groups = dataSource.group(), footer = that.footer || that.wrapper.find(".k-grid-footer"), aggregates = dataSource.aggregate();
            that.rowTemplate = that._tmpl(options.rowTemplate);
            that.altRowTemplate = that._tmpl(options.altRowTemplate || options.rowTemplate, true);
            if (that._hasDetails()) {
                that.detailTemplate = that._detailTmpl(options.detailTemplate || "");
            }
            if (that._group && !isEmptyObject(aggregates) || !isEmptyObject(aggregates) && !footer.length || grep(that.columns, function(column) {
                return column.footerTemplate;
            }).length) {
                that.footerTemplate = that._footerTmpl(aggregates, "footerTemplate", "k-footer-template");
            }
            if (groups && grep(that.columns, function(column) {
                return column.groupFooterTemplate;
            }).length) {
                aggregates = $.map(groups, function(g) {
                    return g.aggregates;
                });
                that.groupFooterTemplate = that._footerTmpl(aggregates, "groupFooterTemplate", "k-group-footer");
            }
        },
        _footerTmpl: function(aggregates, templateName, rowClass) {
            var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), paramName = settings.paramName, html = "", idx, length, columns = that.columns, template, type, storage = {}, count = 0, scope = {}, groups = that._groups(), fieldsMap = buildEmptyAggregatesObject(aggregates), column;
            html += '<tr class="' + rowClass + '">';
            if (groups > 0) {
                html += groupCells(groups);
            }
            if (that._hasDetails()) {
                html += '<td class="k-hierarchy-cell">&nbsp;</td>';
            }
            for (idx = 0, length = that.columns.length; idx < length; idx++) {
                column = columns[idx];
                template = column[templateName];
                type = typeof template;
                html += "<td" + stringifyAttributes(column.footerAttributes) + ">";
                if (template) {
                    if (type !== FUNCTION) {
                        scope = fieldsMap[column.field] ? extend({}, settings, {
                            paramName: paramName + "." + column.field
                        }) : {};
                        template = kendo.template(template, scope);
                    }
                    storage["tmpl" + count] = template;
                    html += "#=this.tmpl" + count + "(" + paramName + ")#";
                    count++;
                } else {
                    html += "&nbsp;";
                }
                html += "</td>";
            }
            html += "</tr>";
            html = kendo.template(html, settings);
            if (count > 0) {
                return proxy(html, storage);
            }
            return html;
        },
        _detailTmpl: function(template) {
            var that = this, html = "", settings = extend({}, kendo.Template, that.options.templateSettings), paramName = settings.paramName, templateFunctionStorage = {}, templateFunctionCount = 0, groups = that._groups(), colspan = visibleColumns(that.columns).length, type = typeof template;
            html += '<tr class="k-detail-row">';
            if (groups > 0) {
                html += groupCells(groups);
            }
            html += '<td class="k-hierarchy-cell"></td><td class="k-detail-cell"' + (colspan ? ' colspan="' + colspan + '"' : "") + ">";
            if (type === FUNCTION) {
                templateFunctionStorage["tmpl" + templateFunctionCount] = template;
                html += "#=this.tmpl" + templateFunctionCount + "(" + paramName + ")#";
                templateFunctionCount++;
            } else {
                html += template;
            }
            html += "</td></tr>";
            html = kendo.template(html, settings);
            if (templateFunctionCount > 0) {
                return proxy(html, templateFunctionStorage);
            }
            return html;
        },
        _hasDetails: function() {
            var that = this;
            return that.options.detailTemplate !== null || (that._events[DETAILINIT] || []).length;
        },
        _details: function() {
            var that = this;
            that.table.on(CLICK + NS, ".k-hierarchy-cell .k-plus, .k-hierarchy-cell .k-minus", function(e) {
                var button = $(this), expanding = button.hasClass("k-plus"), masterRow = button.closest("tr.k-master-row"), detailRow, detailTemplate = that.detailTemplate, data, hasDetails = that._hasDetails();
                button.toggleClass("k-plus", !expanding).toggleClass("k-minus", expanding);
                if (hasDetails && !masterRow.next().hasClass("k-detail-row")) {
                    data = that.dataItem(masterRow);
                    $(detailTemplate(data)).addClass(masterRow.hasClass("k-alt") ? "k-alt" : "").insertAfter(masterRow);
                    that.trigger(DETAILINIT, {
                        masterRow: masterRow,
                        detailRow: masterRow.next(),
                        data: data,
                        detailCell: masterRow.next().find(".k-detail-cell")
                    });
                }
                detailRow = masterRow.next();
                that.trigger(expanding ? DETAILEXPAND : DETAILCOLLAPSE, {
                    masterRow: masterRow,
                    detailRow: detailRow
                });
                detailRow.toggle(expanding);
                if (that._current) {
                    that._current.attr("aria-expanded", expanding);
                }
                e.preventDefault();
                return false;
            });
        },
        dataItem: function(tr) {
            return this._data[this.tbody.find("> tr:not(.k-grouping-row,.k-detail-row,.k-group-footer)").index($(tr))];
        },
        expandRow: function(tr) {
            $(tr).find("> td .k-plus, > td .k-i-expand").click();
        },
        collapseRow: function(tr) {
            $(tr).find("> td .k-minus, > td .k-i-collapse").click();
        },
        _thead: function() {
            var that = this, columns = that.columns, hasDetails = that._hasDetails() && columns.length, idx, length, html = "", thead = that.table.find(">thead"), tr, text, th;
            if (!thead.length) {
                thead = $("<thead/>").insertBefore(that.tbody);
            }
            tr = that.element.find("tr:has(th):first");
            if (!tr.length) {
                tr = thead.children().first();
                if (!tr.length) {
                    tr = $("<tr/>");
                }
            }
            if (!tr.children().length) {
                if (hasDetails) {
                    html += '<th class="k-hierarchy-cell">&nbsp;</th>';
                }
                for (idx = 0, length = columns.length; idx < length; idx++) {
                    th = columns[idx];
                    text = that._headerCellText(th);
                    if (!th.command) {
                        html += "<th role='columnheader' " + kendo.attr("field") + "='" + (th.field || "") + "' ";
                        if (th.title) {
                            html += kendo.attr("title") + '="' + th.title.replace(/'/g, "'") + '" ';
                        }
                        if (th.groupable !== undefined) {
                            html += kendo.attr("groupable") + "='" + th.groupable + "' ";
                        }
                        if (th.aggregates) {
                            html += kendo.attr("aggregates") + "='" + th.aggregates + "'";
                        }
                        html += stringifyAttributes(th.headerAttributes);
                        html += ">" + text + "</th>";
                    } else {
                        html += "<th" + stringifyAttributes(th.headerAttributes) + ">" + text + "</th>";
                    }
                }
                tr.html(html);
            } else if (hasDetails && !tr.find(".k-hierarchy-cell")[0]) {
                tr.prepend('<th class="k-hierarchy-cell">&nbsp;</th>');
            }
            tr.find("th").addClass("k-header");
            if (!that.options.scrollable) {
                thead.addClass("k-grid-header");
            }
            tr.find("script").remove().end().appendTo(thead);
            if (that.thead) {
                that._destroyColumnAttachments();
            }
            that.thead = thead;
            that._sortable();
            that._filterable();
            that._scrollable();
            that._updateCols();
            that._resizable();
            that._draggable();
            that._reorderable();
            if (that.groupable) {
                that._attachGroupable();
            }
            that._columnMenu();
        },
        _updateCols: function() {
            var that = this;
            that._appendCols(that.thead.parent().add(that.table));
        },
        _appendCols: function(table) {
            var that = this;
            normalizeCols(table, visibleColumns(that.columns), that._hasDetails(), that._groups());
        },
        _autoColumns: function(schema) {
            if (schema && schema.toJSON) {
                var that = this, field;
                schema = schema.toJSON();
                for (field in schema) {
                    that.columns.push({
                        field: field
                    });
                }
                that._thead();
                that._templates();
            }
        },
        _rowsHtml: function(data) {
            var that = this, html = "", idx, length, rowTemplate = that.rowTemplate, altRowTemplate = that.altRowTemplate;
            for (idx = 0, length = data.length; idx < length; idx++) {
                if (idx % 2) {
                    html += altRowTemplate(data[idx]);
                } else {
                    html += rowTemplate(data[idx]);
                }
                that._data.push(data[idx]);
            }
            return html;
        },
        _groupRowHtml: function(group, colspan, level) {
            var that = this, html = "", idx, length, field = group.field, column = grep(that.columns, function(column) {
                return column.field == field;
            })[0] || {}, template = column.groupHeaderTemplate, text = (column.title || field) + ": " + formatGroupValue(group.value, column.format, column.values), data = extend({}, {
                field: group.field,
                value: group.value
            }, group.aggregates[group.field]), footerDefaults = that._groupAggregatesDefaultObject || {}, groupItems = group.items;
            if (template) {
                text = typeof template === FUNCTION ? template(data) : kendo.template(template)(data);
            }
            html += '<tr class="k-grouping-row">' + groupCells(level) + '<td colspan="' + colspan + '" aria-expanded="true">' + '<p class="k-reset">' + '<a class="k-icon k-i-collapse" href="#" tabindex="-1"></a>' + text + "</p></td></tr>";
            if (group.hasSubgroups) {
                for (idx = 0, length = groupItems.length; idx < length; idx++) {
                    html += that._groupRowHtml(groupItems[idx], colspan - 1, level + 1);
                }
            } else {
                html += that._rowsHtml(groupItems);
            }
            if (that.groupFooterTemplate) {
                html += that.groupFooterTemplate(extend(footerDefaults, group.aggregates));
            }
            return html;
        },
        collapseGroup: function(group) {
            group = $(group).find(".k-icon").addClass("k-i-expand").removeClass("k-i-collapse").end();
            var level = group.find(".k-group-cell").length, footerCount = 1, offset, tr;
            group.find("td:first").attr("aria-expanded", false);
            group.nextAll("tr").each(function() {
                tr = $(this);
                offset = tr.find(".k-group-cell").length;
                if (tr.hasClass("k-grouping-row")) {
                    footerCount++;
                } else if (tr.hasClass("k-group-footer")) {
                    footerCount--;
                }
                if (offset <= level || tr.hasClass("k-group-footer") && footerCount < 0) {
                    return false;
                }
                tr.hide();
            });
        },
        expandGroup: function(group) {
            group = $(group).find(".k-icon").addClass("k-i-collapse").removeClass("k-i-expand").end();
            var that = this, level = group.find(".k-group-cell").length, tr, offset, groupsCount = 1;
            group.find("td:first").attr("aria-expanded", true);
            group.nextAll("tr").each(function() {
                tr = $(this);
                offset = tr.find(".k-group-cell").length;
                if (offset <= level) {
                    return false;
                }
                if (offset == level + 1 && !tr.hasClass("k-detail-row")) {
                    tr.show();
                    if (tr.hasClass("k-grouping-row") && tr.find(".k-icon").hasClass("k-i-collapse")) {
                        that.expandGroup(tr);
                    }
                    if (tr.hasClass("k-master-row") && tr.find(".k-icon").hasClass("k-minus")) {
                        tr.next().show();
                    }
                }
                if (tr.hasClass("k-grouping-row")) {
                    groupsCount++;
                }
                if (tr.hasClass("k-group-footer")) {
                    if (groupsCount == 1) {
                        tr.show();
                    } else {
                        groupsCount--;
                    }
                }
            });
        },
        _updateHeader: function(groups) {
            var that = this, cells = that.thead.find("th.k-group-cell"), length = cells.length;
            if (groups > length) {
                $(new Array(groups - length + 1).join('<th class="k-group-cell k-header">&nbsp;</th>')).prependTo(that.thead.find("tr"));
            } else if (groups < length) {
                length = length - groups;
                $(grep(cells, function(item, index) {
                    return length > index;
                })).remove();
            }
        },
        _firstDataItem: function(data, grouped) {
            if (data && grouped) {
                if (data.hasSubgroups) {
                    data = this._firstDataItem(data.items[0], grouped);
                } else {
                    data = data.items[0];
                }
            }
            return data;
        },
        hideColumn: function(column) {
            var that = this, rows, row, cell, tables, idx, cols, colWidth, width = 0, length, footer = that.footer || that.wrapper.find(".k-grid-footer"), columns = that.columns, columnIndex, browser = kendo.support.browser;
            if (typeof column == "number") {
                column = columns[column];
            } else {
                column = grep(columns, function(item) {
                    return item.field === column;
                })[0];
            }
            if (!column || column.hidden) {
                return;
            }
            columnIndex = inArray(column, visibleColumns(columns));
            column.hidden = true;
            column.attributes = addHiddenStyle(column.attributes);
            column.footerAttributes = addHiddenStyle(column.footerAttributes);
            column.headerAttributes = addHiddenStyle(column.headerAttributes);
            that._templates();
            that._updateCols();
            that.thead.find(">tr>th:not(.k-hierarchy-cell,.k-group-cell):visible").eq(columnIndex).hide();
            if (footer) {
                that._appendCols(footer.find("table:first"));
                footer.find(".k-footer-template>td:not(.k-hierarchy-cell,.k-group-cell):visible").eq(columnIndex).hide();
            }
            rows = that.tbody.children();
            for (idx = 0, length = rows.length; idx < length; idx += 1) {
                row = rows.eq(idx);
                if (row.is(".k-grouping-row,.k-detail-row")) {
                    cell = row.children(":not(.k-group-cell):first,.k-detail-cell").last();
                    cell.attr("colspan", parseInt(cell.attr("colspan"), 10) - 1);
                } else {
                    if (row.hasClass("k-grid-edit-row") && (cell = row.children(".k-edit-container")[0])) {
                        cell = $(cell);
                        cell.attr("colspan", parseInt(cell.attr("colspan"), 10) - 1);
                        cell.find("col").eq(columnIndex).remove();
                        row = cell.find("tr:first");
                    }
                    setCellVisibility(row[0].cells, columnIndex, false);
                }
            }
            cols = that.thead.prev().find("col");
            for (idx = 0, length = cols.length; idx < length; idx += 1) {
                colWidth = cols[idx].style.width;
                if (colWidth && colWidth.indexOf("%") == -1) {
                    width += parseInt(colWidth, 10);
                } else {
                    width = 0;
                    break;
                }
            }
            tables = $(">.k-grid-header table:first,>.k-grid-footer table:first", that.wrapper).add(that.table);
            that._footerWidth = null;
            if (width) {
                tables.width(width);
                that._footerWidth = width;
            }
            if (browser.msie && browser.version == 8) {
                tables.css("display", "inline-table");
                setTimeout(function() {
                    tables.css("display", "table");
                }, 1);
            }
            that.trigger(COLUMNHIDE, {
                column: column
            });
        },
        showColumn: function(column) {
            var that = this, rows, idx, length, row, cell, tables, width, colWidth, cols, columns = that.columns, footer = that.footer || that.wrapper.find(".k-grid-footer"), columnIndex;
            if (typeof column == "number") {
                column = columns[column];
            } else {
                column = grep(columns, function(item) {
                    return item.field === column;
                })[0];
            }
            if (!column || !column.hidden) {
                return;
            }
            columnIndex = inArray(column, columns);
            column.hidden = false;
            column.attributes = removeHiddenStyle(column.attributes);
            column.footerAttributes = removeHiddenStyle(column.footerAttributes);
            column.headerAttributes = removeHiddenStyle(column.headerAttributes);
            that._templates();
            that._updateCols();
            that.thead.find(">tr>th:not(.k-hierarchy-cell,.k-group-cell)").eq(columnIndex).show();
            if (footer) {
                that._appendCols(footer.find("table:first"));
                footer.find(".k-footer-template>td:not(.k-hierarchy-cell,.k-group-cell)").eq(columnIndex).show();
            }
            rows = that.tbody.children();
            for (idx = 0, length = rows.length; idx < length; idx += 1) {
                row = rows.eq(idx);
                if (row.is(".k-grouping-row,.k-detail-row")) {
                    cell = row.children(":not(.k-group-cell):first,.k-detail-cell").last();
                    cell.attr("colspan", parseInt(cell.attr("colspan"), 10) + 1);
                } else {
                    if (row.hasClass("k-grid-edit-row") && (cell = row.children(".k-edit-container")[0])) {
                        cell = $(cell);
                        cell.attr("colspan", parseInt(cell.attr("colspan"), 10) + 1);
                        normalizeCols(cell.find(">form>table"), visibleColumns(columns), false, 0);
                        row = cell.find("tr:first");
                    }
                    setCellVisibility(row[0].cells, columnIndex, true);
                }
            }
            tables = $(">.k-grid-header table:first,>.k-grid-footer table:first", that.wrapper).add(that.table);
            if (!column.width) {
                tables.width("");
            } else {
                width = 0;
                cols = that.thead.prev().find("col");
                for (idx = 0, length = cols.length; idx < length; idx += 1) {
                    colWidth = cols[idx].style.width;
                    if (colWidth.indexOf("%") > -1) {
                        width = 0;
                        break;
                    }
                    width += parseInt(colWidth, 10);
                }
                that._footerWidth = null;
                if (width) {
                    tables.width(width);
                    that._footerWidth = width;
                }
            }
            that.trigger(COLUMNSHOW, {
                column: column
            });
        },
        _progress: function(toggle) {
            var that = this, element = that.element.is("table") ? that.element.parent() : that.content && that.content.length ? that.content : that.element;
            kendo.ui.progress(element, toggle);
        },
        refresh: function(e) {
            var that = this, length, idx, html = "", data = that.dataSource.view(), navigatable = that.options.navigatable, tbody, placeholder, currentIndex, current = $(that.current()), isCurrentInHeader = false, groups = (that.dataSource.group() || []).length, colspan = groups + visibleColumns(that.columns).length, active;
            if (e && e.action === "itemchange" && that.editable) {
                // skip rebinding if editing is in progress
                return;
            }
            e = e || {};
            if (that.trigger("dataBinding", {
                action: e.action || "rebind",
                index: e.index,
                items: e.items
            })) {
                return;
            }
            active = activeElement();
            if (navigatable && (that.table[0] === active || $.contains(that.table[0], active) || that._editContainer && that._editContainer.data("kendoWindow"))) {
                isCurrentInHeader = current.is("th");
                currentIndex = 0;
                if (isCurrentInHeader) {
                    currentIndex = that.thead.find("th:not(.k-group-cell)").index(current);
                }
            }
            that._destroyEditable();
            that._progress(false);
            that._data = [];
            if (!that.columns.length) {
                that._autoColumns(that._firstDataItem(data[0], groups));
                colspan = groups + that.columns.length;
            }
            that._group = groups > 0 || that._group;
            if (that._group) {
                that._templates();
                that._updateCols();
                that._updateHeader(groups);
                that._group = groups > 0;
            }
            if (groups > 0) {
                if (that.detailTemplate) {
                    colspan++;
                }
                if (that.groupFooterTemplate) {
                    that._groupAggregatesDefaultObject = buildEmptyAggregatesObject(that.dataSource.aggregate());
                }
                for (idx = 0, length = data.length; idx < length; idx++) {
                    html += that._groupRowHtml(data[idx], colspan, 0);
                }
            } else {
                html += that._rowsHtml(data);
            }
            if (tbodySupportsInnerHtml) {
                that.tbody[0].innerHTML = html;
            } else {
                placeholder = document.createElement("div");
                placeholder.innerHTML = "<table><tbody>" + html + "</tbody></table>";
                tbody = placeholder.firstChild.firstChild;
                that.table[0].replaceChild(tbody, that.tbody[0]);
                that.tbody = $(tbody);
            }
            that._footer();
            that._setContentHeight();
            if (currentIndex >= 0) {
                that._removeCurrent();
                if (!isCurrentInHeader) {
                    that.current(that.items().eq(currentIndex).children().filter(DATA_CELL).first());
                } else {
                    that.current(that.thead.find("th:not(.k-group-cell)").eq(currentIndex));
                }
                if (that._current) {
                    focusTable(that._current.closest("table")[0], true);
                }
            }
            that.trigger(DATABOUND);
        }
    });
    function getCommand(commands, name) {
        var idx, length, command;
        if (typeof commands === STRING && commands === name) {
            return commands;
        }
        if (isPlainObject(commands) && commands.name === name) {
            return commands;
        }
        if (isArray(commands)) {
            for (idx = 0, length = commands.length; idx < length; idx++) {
                command = commands[idx];
                if (typeof command === STRING && command === name || command.name === name) {
                    return command;
                }
            }
        }
        return null;
    }
    function focusTable(table, direct) {
        var msie = kendo.support.browser.msie;
        if (direct === true) {
            table = $(table);
            var condition = msie && table.parent().is(".k-grid-content,.k-grid-header-wrap"), scrollTop, scrollLeft;
            if (condition) {
                scrollTop = table.parent().scrollTop();
                scrollLeft = table.parent().scrollLeft();
            }
            if (msie) {
                try {
                    //The setActive method does not cause the document to scroll to the active object in the current page
                    table[0].setActive();
                } catch (e) {
                    table[0].focus();
                }
            } else {
                table[0].focus();
            }
            if (condition) {
                table.parent().scrollTop(scrollTop);
                table.parent().scrollLeft(scrollLeft);
            }
        } else {
            $(table).one("focusin", function(e) {
                e.preventDefault();
            }).focus();
        }
    }
    function tableClick(e) {
        var currentTarget = $(e.currentTarget), isHeader = currentTarget.is("th"), currentTable = currentTarget.closest("table")[0];
        if (currentTable !== this.table[0] && currentTable !== this.thead.parent()[0]) {
            return;
        }
        this.current(currentTarget);
        if (isHeader || !$(e.target).is(":button,a,:input,a>.k-icon,textarea,span.k-icon,.k-input")) {
            setTimeout(function() {
                //DOMElement.focus() only for header, because IE doesn't really focus the table
                focusTable(currentTable, true);
            });
        }
        if (isHeader) {
            e.preventDefault();
        }
    }
    ui.plugin(Grid);
    ui.plugin(VirtualScrollable);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, CHANGE = "change", CANCEL = "cancel", DATABOUND = "dataBound", DATABINDING = "dataBinding", Widget = kendo.ui.Widget, keys = kendo.keys, FOCUSSELECTOR = ">*", PROGRESS = "progress", ERROR = "error", FOCUSED = "k-state-focused", SELECTED = "k-state-selected", KEDITITEM = "k-edit-item", STRING = "string", EDIT = "edit", REMOVE = "remove", SAVE = "save", CLICK = "click", NS = ".kendoListView", proxy = $.proxy, activeElement = kendo._activeElement, progress = kendo.ui.progress, DataSource = kendo.data.DataSource;
    var ListView = Widget.extend({
        init: function(element, options) {
            var that = this;
            options = $.isArray(options) ? {
                dataSource: options
            } : options;
            Widget.fn.init.call(that, element, options);
            options = that.options;
            that.wrapper = element = that.element;
            if (element[0].id) {
                that._itemId = element[0].id + "_lv_active";
            }
            that._element();
            that._dataSource();
            that.template = kendo.template(options.template || "");
            that.altTemplate = kendo.template(options.altTemplate || options.template);
            that.editTemplate = kendo.template(options.editTemplate || "");
            that._navigatable();
            that._selectable();
            that._pageable();
            that._crudHandlers();
            if (that.options.autoBind) {
                that.dataSource.fetch();
            }
            kendo.notify(that);
        },
        events: [ CHANGE, CANCEL, DATABINDING, DATABOUND, EDIT, REMOVE, SAVE ],
        options: {
            name: "ListView",
            autoBind: true,
            selectable: false,
            navigatable: false,
            template: "",
            altTemplate: "",
            editTemplate: ""
        },
        _item: function(action) {
            return this.element.children()[action]();
        },
        items: function() {
            return this.element.children();
        },
        setDataSource: function(dataSource) {
            this.options.dataSource = dataSource;
            this._dataSource();
            if (this.options.autoBind) {
                dataSource.fetch();
            }
        },
        _unbindDataSource: function() {
            var that = this;
            that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(PROGRESS, that._progressHandler).unbind(ERROR, that._errorHandler);
        },
        _dataSource: function() {
            var that = this;
            if (that.dataSource && that._refreshHandler) {
                that._unbindDataSource();
            } else {
                that._refreshHandler = proxy(that.refresh, that);
                that._progressHandler = proxy(that._progress, that);
                that._errorHandler = proxy(that._error, that);
            }
            that.dataSource = DataSource.create(that.options.dataSource).bind(CHANGE, that._refreshHandler).bind(PROGRESS, that._progressHandler).bind(ERROR, that._errorHandler);
        },
        _progress: function() {
            progress(this.element, true);
        },
        _error: function() {
            progress(this.element, false);
        },
        _element: function() {
            this.element.addClass("k-widget k-listview").attr("role", "listbox");
        },
        refresh: function(e) {
            var that = this, view = that.dataSource.view(), data, items, item, html = "", idx, length, template = that.template, altTemplate = that.altTemplate, active = activeElement();
            if (e && e.action === "itemchange") {
                if (!that.editable) {
                    data = e.items[0];
                    idx = $.inArray(data, view);
                    if (idx >= 0) {
                        that.items().eq(idx).replaceWith(template(data));
                        item = that.items().eq(idx);
                        item.attr(kendo.attr("uid"), data.uid);
                        that.trigger("itemChange", {
                            item: item,
                            data: data
                        });
                    }
                }
                return;
            }
            e = e || {};
            if (that.trigger(DATABINDING, {
                action: e.action || "rebind",
                items: e.items,
                index: e.index
            })) {
                return;
            }
            that._destroyEditable();
            for (idx = 0, length = view.length; idx < length; idx++) {
                if (idx % 2) {
                    html += altTemplate(view[idx]);
                } else {
                    html += template(view[idx]);
                }
            }
            that.element.html(html);
            items = that.items();
            for (idx = 0, length = view.length; idx < length; idx++) {
                items.eq(idx).attr(kendo.attr("uid"), view[idx].uid).attr("role", "option").attr("aria-selected", "false");
            }
            if (that.element[0] === active && that.options.navigatable) {
                that.current(items.eq(0));
            }
            that.trigger(DATABOUND);
        },
        _pageable: function() {
            var that = this, pageable = that.options.pageable, settings, pagerId;
            if ($.isPlainObject(pageable)) {
                pagerId = pageable.pagerId;
                settings = $.extend({}, pageable, {
                    dataSource: that.dataSource,
                    pagerId: null
                });
                that.pager = new kendo.ui.Pager($("#" + pagerId), settings);
            }
        },
        _selectable: function() {
            var that = this, multi, current, selectable = that.options.selectable, navigatable = that.options.navigatable;
            if (selectable) {
                multi = typeof selectable === STRING && selectable.toLowerCase().indexOf("multiple") > -1;
                if (multi) {
                    that.element.attr("aria-multiselectable", true);
                }
                that.selectable = new kendo.ui.Selectable(that.element, {
                    aria: true,
                    multiple: multi,
                    filter: FOCUSSELECTOR,
                    change: function() {
                        that.trigger(CHANGE);
                    }
                });
                if (navigatable) {
                    that.element.on("keydown" + NS, function(e) {
                        if (e.keyCode === keys.SPACEBAR) {
                            current = that.current();
                            if (e.target == e.currentTarget) {
                                e.preventDefault();
                            }
                            if (multi) {
                                if (!e.ctrlKey) {
                                    that.selectable.clear();
                                } else {
                                    if (current && current.hasClass(SELECTED)) {
                                        current.removeClass(SELECTED);
                                        return;
                                    }
                                }
                            } else {
                                that.selectable.clear();
                            }
                            that.selectable.value(current);
                        }
                    });
                }
            }
        },
        current: function(candidate) {
            var that = this, element = that.element, current = that._current, id = that._itemId;
            if (candidate === undefined) {
                return current;
            }
            if (current) {
                if (current[0].id === id) {
                    current.removeAttr("id");
                }
                current.removeClass(FOCUSED);
                element.removeAttr("aria-activedescendant");
            }
            if (candidate && candidate[0]) {
                id = candidate[0].id || id;
                that._scrollTo(candidate[0]);
                element.attr("aria-activedescendant", id);
                candidate.addClass(FOCUSED).attr("id", id);
            }
            that._current = candidate;
        },
        _scrollTo: function(element) {
            var that = this, container, UseJQueryoffset = false, SCROLL = "scroll";
            if (that.wrapper.css("overflow") == "auto" || that.wrapper.css("overflow") == SCROLL) {
                container = that.wrapper[0];
            } else {
                container = window;
                UseJQueryoffset = true;
            }
            var scrollDirectionFunc = function(direction, dimension) {
                var elementOffset = UseJQueryoffset ? $(element).offset()[direction.toLowerCase()] : element["offset" + direction], elementDimension = element["client" + dimension], containerScrollAmount = $(container)[SCROLL + direction](), containerDimension = $(container)[dimension.toLowerCase()]();
                if (elementOffset + elementDimension > containerScrollAmount + containerDimension) {
                    $(container)[SCROLL + direction](elementOffset + elementDimension - containerDimension);
                } else if (elementOffset < containerScrollAmount) {
                    $(container)[SCROLL + direction](elementOffset);
                }
            };
            scrollDirectionFunc("Top", "Height");
            scrollDirectionFunc("Left", "Width");
        },
        _navigatable: function() {
            var that = this, navigatable = that.options.navigatable, element = that.element, clickCallback = function(e) {
                that.current($(e.currentTarget));
                if (!$(e.target).is(":button,a,:input,a>.k-icon,textarea")) {
                    element.focus();
                }
            };
            if (navigatable) {
                that._tabindex();
                element.on("focus" + NS, function() {
                    var current = that._current;
                    if (!current || !current.is(":visible")) {
                        current = that._item("first");
                    }
                    that.current(current);
                }).on("focusout" + NS, function() {
                    if (that._current) {
                        that._current.removeClass(FOCUSED);
                    }
                }).on("keydown" + NS, function(e) {
                    var key = e.keyCode, current = that.current(), target = $(e.target), canHandle = !target.is(":button,textarea,a,a>.t-icon,input"), isTextBox = target.is(":text"), preventDefault = kendo.preventDefault, editItem = element.find("." + KEDITITEM), active = activeElement(), idx;
                    if (!canHandle && !isTextBox && keys.ESC != key || isTextBox && keys.ESC != key && keys.ENTER != key) {
                        return;
                    }
                    if (keys.UP === key || keys.LEFT === key) {
                        if (current) {
                            current = current.prev();
                        }
                        that.current(!current || !current[0] ? that._item("last") : current);
                        preventDefault(e);
                    } else if (keys.DOWN === key || keys.RIGHT === key) {
                        if (current) {
                            current = current.next();
                        }
                        that.current(!current || !current[0] ? that._item("first") : current);
                        preventDefault(e);
                    } else if (keys.PAGEUP === key) {
                        that.current(null);
                        that.dataSource.page(that.dataSource.page() - 1);
                        preventDefault(e);
                    } else if (keys.PAGEDOWN === key) {
                        that.current(null);
                        that.dataSource.page(that.dataSource.page() + 1);
                        preventDefault(e);
                    } else if (keys.HOME === key) {
                        that.current(that._item("first"));
                        preventDefault(e);
                    } else if (keys.END === key) {
                        that.current(that._item("last"));
                        preventDefault(e);
                    } else if (keys.ENTER === key) {
                        if (editItem.length !== 0 && (canHandle || isTextBox)) {
                            idx = that.items().index(editItem);
                            if (active) {
                                active.blur();
                            }
                            that.save();
                            var focusAgain = function() {
                                that.element.trigger("focus");
                                that.current(that.items().eq(idx));
                            };
                            that.one("dataBound", focusAgain);
                        } else if (that.options.editTemplate !== "") {
                            that.edit(current);
                        }
                    } else if (keys.ESC === key) {
                        editItem = element.find("." + KEDITITEM);
                        if (editItem.length === 0) {
                            return;
                        }
                        idx = that.items().index(editItem);
                        that.cancel();
                        that.element.trigger("focus");
                        that.current(that.items().eq(idx));
                    }
                });
                element.on("mousedown" + NS + " touchstart" + NS, FOCUSSELECTOR, proxy(clickCallback, that));
            }
        },
        clearSelection: function() {
            var that = this;
            that.selectable.clear();
            that.trigger(CHANGE);
        },
        select: function(items) {
            var that = this, selectable = that.selectable;
            items = $(items);
            if (items.length) {
                if (!selectable.options.multiple) {
                    selectable.clear();
                    items = items.first();
                }
                selectable.value(items);
                return;
            }
            return selectable.value();
        },
        _destroyEditable: function() {
            var that = this;
            if (that.editable) {
                that.editable.destroy();
                delete that.editable;
            }
        },
        _modelFromElement: function(element) {
            var uid = element.attr(kendo.attr("uid"));
            return this.dataSource.getByUid(uid);
        },
        _closeEditable: function(validate) {
            var that = this, editable = that.editable, data, index, template = that.template, valid = true;
            if (editable) {
                if (validate) {
                    valid = editable.end();
                }
                if (valid) {
                    if (editable.element.index() % 2) {
                        template = that.altTemplate;
                    }
                    data = that._modelFromElement(editable.element);
                    that._destroyEditable();
                    index = editable.element.index();
                    editable.element.replaceWith(template(data));
                    that.items().eq(index).attr(kendo.attr("uid"), data.uid);
                }
            }
            return valid;
        },
        edit: function(item) {
            var that = this, data = that._modelFromElement(item), container, index = item.index();
            that.cancel();
            item.replaceWith(that.editTemplate(data));
            container = that.items().eq(index).addClass(KEDITITEM).attr(kendo.attr("uid"), data.uid);
            that.editable = container.kendoEditable({
                model: data,
                clearContainer: false,
                errorTemplate: false
            }).data("kendoEditable");
            that.trigger(EDIT, {
                model: data,
                item: container
            });
        },
        save: function() {
            var that = this, editable = that.editable, model;
            if (!editable) {
                return;
            }
            editable = editable.element;
            model = that._modelFromElement(editable);
            if (!that.trigger(SAVE, {
                model: model,
                item: editable
            }) && that._closeEditable(true)) {
                that.dataSource.sync();
            }
        },
        remove: function(item) {
            var that = this, dataSource = that.dataSource, data = that._modelFromElement(item);
            if (!that.trigger(REMOVE, {
                model: data,
                item: item
            })) {
                item.hide();
                dataSource.remove(data);
                dataSource.sync();
            }
        },
        add: function() {
            var that = this, dataSource = that.dataSource, index = dataSource.indexOf((dataSource.view() || [])[0]);
            if (index < 0) {
                index = 0;
            }
            that.cancel();
            dataSource.insert(index, {});
            that.edit(that.element.children().first());
        },
        cancel: function() {
            var that = this, dataSource = that.dataSource;
            if (that.editable) {
                var container = that.editable.element;
                var model = that._modelFromElement(container);
                if (!that.trigger(CANCEL, {
                    model: model,
                    container: container
                })) {
                    dataSource.cancelChanges(model);
                    that._closeEditable(false);
                }
            }
        },
        _crudHandlers: function() {
            var that = this, clickNS = CLICK + NS;
            that.element.on(clickNS, ".k-edit-button", function(e) {
                var item = $(this).closest("[" + kendo.attr("uid") + "]");
                that.edit(item);
                e.preventDefault();
            });
            that.element.on(clickNS, ".k-delete-button", function(e) {
                var item = $(this).closest("[" + kendo.attr("uid") + "]");
                that.remove(item);
                e.preventDefault();
            });
            that.element.on(clickNS, ".k-update-button", function(e) {
                that.save();
                e.preventDefault();
            });
            that.element.on(clickNS, ".k-cancel-button", function(e) {
                that.cancel();
                e.preventDefault();
            });
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that._unbindDataSource();
            that._destroyEditable();
            that.element.off(NS);
            if (that.pager) {
                that.pager.destroy();
            }
            if (that.selectable) {
                that.selectable.destroy();
            }
            kendo.destroy(that.element);
        }
    });
    kendo.ui.plugin(ListView);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, Widget = kendo.ui.Widget, isPlainObject = $.isPlainObject, proxy = $.proxy, extend = $.extend, placeholderSupported = kendo.support.placeholder, browser = kendo.support.browser, isFunction = $.isFunction, trimSlashesRegExp = /(^\/|\/$)/g, CHANGE = "change", APPLY = "apply", ERROR = "error", CLICK = "click", NS = ".kendoImageBrowser", BREADCRUBMSNS = ".kendoBreadcrumbs", SEARCHBOXNS = ".kendoSearchBox", NAMEFIELD = "name", SIZEFIELD = "size", TYPEFIELD = "type", DEFAULTSORTORDER = {
        field: TYPEFIELD,
        dir: "asc"
    }, ARRANGEBYTMPL = kendo.template('<li data-#=ns#value="#=value#" class="k-item">${text}</li>'), EMPTYTILE = kendo.template('<li class="k-tile-empty"><strong>${text}</strong></li>'), TOOLBARTMPL = '<div class="k-widget k-toolbar k-floatwrap">' + '<div class="k-toolbar-wrap">' + "#if(showUpload) { # " + '<div class="k-widget k-upload"><div class="k-button k-button-icontext k-button-bare k-upload-button">' + '<span class="k-icon k-add"></span>#=messages.uploadFile#<input type="file" name="file" /></div></div>' + "#}#" + "#if(showCreate) {#" + '<button type="button" class="k-button k-button-icon k-button-bare"><span class="k-icon k-addfolder"></span></button>' + "#}#" + "#if(showDelete) {#" + '<button type="button" class="k-button k-button-icon k-button-bare k-state-disabled"><span class="k-icon k-delete"></span></button>&nbsp;' + "#}#" + "</div>" + '<div class="k-tiles-arrange">#=messages.orderBy#: <a href="\\#" class="k-link"><span>#=messages.orderByName#</span><span class="k-icon k-i-arrow-s"></span></a>' + "</div>" + "</div>";
    extend(true, kendo.data, {
        schemas: {
            imagebrowser: {
                data: function(data) {
                    return data.items || data || [];
                },
                model: {
                    id: "name",
                    fields: {
                        name: "name",
                        size: "size",
                        type: "type"
                    }
                }
            }
        }
    });
    extend(true, kendo.data, {
        transports: {
            imagebrowser: kendo.data.RemoteTransport.extend({
                init: function(options) {
                    kendo.data.RemoteTransport.fn.init.call(this, $.extend(true, {}, this.options, options));
                },
                _call: function(type, options) {
                    options.data = $.extend({}, options.data, {
                        path: this.options.path()
                    });
                    if (isFunction(this.options[type])) {
                        this.options[type].call(this, options);
                    } else {
                        kendo.data.RemoteTransport.fn[type].call(this, options);
                    }
                },
                read: function(options) {
                    this._call("read", options);
                },
                create: function(options) {
                    this._call("create", options);
                },
                destroy: function(options) {
                    this._call("destroy", options);
                },
                update: function() {},
                options: {
                    read: {
                        type: "POST"
                    },
                    update: {
                        type: "POST"
                    },
                    create: {
                        type: "POST"
                    },
                    destroy: {
                        type: "POST"
                    }
                }
            })
        }
    });
    function bindDragEventWrappers(element, onDragEnter, onDragLeave) {
        var hideInterval, lastDrag;
        element.on("dragenter" + NS, function() {
            onDragEnter();
            lastDrag = new Date();
            if (!hideInterval) {
                hideInterval = setInterval(function() {
                    var sinceLastDrag = new Date() - lastDrag;
                    if (sinceLastDrag > 100) {
                        onDragLeave();
                        clearInterval(hideInterval);
                        hideInterval = null;
                    }
                }, 100);
            }
        }).on("dragover" + NS, function() {
            lastDrag = new Date();
        });
    }
    var offsetTop;
    if (browser.msie && browser.version < 8) {
        offsetTop = function(element) {
            return element.offsetTop;
        };
    } else {
        offsetTop = function(element) {
            return element.offsetTop - $(element).height();
        };
    }
    function fieldName(fields, name) {
        var descriptor = fields[name];
        if (isPlainObject(descriptor)) {
            return descriptor.field || name;
        }
        return descriptor;
    }
    function concatPaths(path, name) {
        if (path === undefined || !path.match(/\/$/)) {
            path = (path || "") + "/";
        }
        return path + name;
    }
    function sizeFormatter(value) {
        if (!value) {
            return "";
        }
        var suffix = " bytes";
        if (value >= 1073741824) {
            suffix = " GB";
            value /= 1073741824;
        } else if (value >= 1048576) {
            suffix = " MB";
            value /= 1048576;
        } else if (value >= 1024) {
            suffix = " KB";
            value /= 1024;
        }
        return Math.round(value * 100) / 100 + suffix;
    }
    var ImageBrowser = Widget.extend({
        init: function(element, options) {
            var that = this;
            options = options || {};
            Widget.fn.init.call(that, element, options);
            that.element.addClass("k-imagebrowser");
            that.element.on(CLICK + NS, ".k-toolbar button:not(.k-state-disabled):has(.k-delete)", proxy(that._deleteClick, that)).on(CLICK + NS, ".k-toolbar button:not(.k-state-disabled):has(.k-addfolder)", proxy(that._addClick, that)).on("keydown" + NS, "li.k-state-selected input", proxy(that._directoryKeyDown, that)).on("blur" + NS, "li.k-state-selected input", proxy(that._directoryBlur, that));
            that._dataSource();
            that.refresh();
            that.path(that.options.path);
        },
        options: {
            name: "ImageBrowser",
            messages: {
                uploadFile: "Upload",
                orderBy: "Arrange by",
                orderByName: "Name",
                orderBySize: "Size",
                directoryNotFound: "A directory with this name was not found.",
                emptyFolder: "Empty Folder",
                deleteFile: 'Are you sure you want to delete "{0}"?',
                invalidFileType: 'The selected file "{0}" is not valid. Supported file types are {1}.',
                overwriteFile: 'A file with name "{0}" already exists in the current directory. Do you want to overwrite it?',
                dropFilesHere: "drop files here to upload",
                search: "Search"
            },
            transport: {},
            path: "/",
            fileTypes: "*.png,*.gif,*.jpg,*.jpeg"
        },
        events: [ ERROR, CHANGE, APPLY ],
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that.dataSource.unbind(ERROR, that._errorHandler);
            that.element.add(that.list).add(that.toolbar).off(NS);
            if (that.arrangeByPopup) {
                that.arrangeByPopup.destroy();
            }
            kendo.destroy(that.element);
        },
        value: function() {
            var that = this, selected = that._selectedItem(), path, imageUrl = that.options.transport.imageUrl;
            if (selected && selected.get(that._getFieldName(TYPEFIELD)) === "f") {
                path = concatPaths(that.path(), selected.get(that._getFieldName(NAMEFIELD))).replace(trimSlashesRegExp, "");
                if (imageUrl) {
                    path = isFunction(imageUrl) ? imageUrl(path) : kendo.format(imageUrl, path);
                }
                return path;
            }
        },
        _selectedItem: function() {
            var listView = this.listView, selected = listView.select();
            if (selected.length) {
                return this.dataSource.getByUid(selected.attr(kendo.attr("uid")));
            }
        },
        _toolbar: function() {
            var that = this, template = kendo.template(TOOLBARTMPL), messages = that.options.messages, link, popup, arrangeBy = [ {
                text: messages.orderByName,
                value: "name",
                ns: kendo.ns
            }, {
                text: messages.orderBySize,
                value: "size",
                ns: kendo.ns
            } ];
            that.toolbar = $(template({
                messages: messages,
                showUpload: that.options.transport.uploadUrl,
                showCreate: that.options.transport.create,
                showDelete: that.options.transport.destroy
            })).appendTo(that.element).find(".k-upload input").kendoUpload({
                multiple: false,
                localization: {
                    dropFilesHere: messages.dropFilesHere
                },
                async: {
                    saveUrl: that.options.transport.uploadUrl,
                    autoUpload: true
                },
                upload: proxy(that._fileUpload, that)
            }).end();
            that.upload = that.toolbar.find(".k-upload input").data("kendoUpload");
            link = that.toolbar.find(".k-tiles-arrange a");
            that.arrangeByPopup = popup = $("<ul>" + kendo.render(ARRANGEBYTMPL, arrangeBy) + "</ul>").kendoPopup({
                anchor: link
            }).on(CLICK + NS, "li", function() {
                var item = $(this), field = item.attr(kendo.attr("value"));
                that.toolbar.find(".k-tiles-arrange a span:first").html(item.text());
                popup.close();
                that.orderBy(field);
            }).data("kendoPopup");
            link.on(CLICK + NS, function(e) {
                e.preventDefault();
                popup.toggle();
            });
            that._attachDropzoneEvents();
        },
        _attachDropzoneEvents: function() {
            var that = this;
            if (that.options.transport.uploadUrl) {
                bindDragEventWrappers($(document.documentElement), $.proxy(that._dropEnter, that), $.proxy(that._dropLeave, that));
                that._scrollHandler = proxy(that._positionDropzone, that);
            }
        },
        _dropEnter: function() {
            this._positionDropzone();
            $(document).on("scroll" + NS, this._scrollHandler);
        },
        _dropLeave: function() {
            this._removeDropzone();
            $(document).off("scroll" + NS, this._scrollHandler);
        },
        _positionDropzone: function() {
            var that = this, element = that.element, offset = element.offset();
            that.toolbar.find(".k-dropzone").addClass("k-imagebrowser-dropzone").offset(offset).css({
                width: element[0].clientWidth,
                height: element[0].clientHeight,
                lineHeight: element[0].clientHeight + "px"
            });
        },
        _removeDropzone: function() {
            this.toolbar.find(".k-dropzone").removeClass("k-imagebrowser-dropzone").css({
                width: "",
                height: "",
                lineHeight: "",
                top: "",
                left: ""
            });
        },
        _deleteClick: function() {
            var that = this, item = that.listView.select(), message = kendo.format(that.options.messages.deleteFile, item.find("strong").text());
            if (item.length && that._showMessage(message, "confirm")) {
                that.listView.remove(item);
            }
        },
        _addClick: function() {
            this.createDirectory();
        },
        _fileUpload: function(e) {
            var that = this, options = that.options, fileTypes = options.fileTypes, filterRegExp = new RegExp(("(" + fileTypes.split(",").join(")|(") + ")").replace(/\*\./g, ".*."), "i"), fileName = e.files[0].name, fileNameField = that._getFieldName(NAMEFIELD), sizeField = that._getFieldName(SIZEFIELD), model;
            if (filterRegExp.test(fileName)) {
                e.data = {
                    path: that.path()
                };
                model = that._createFile(fileName);
                if (!model) {
                    e.preventDefault();
                } else {
                    that.upload.one("success", function(e) {
                        model.set(fileNameField, e.response[fileNameField]);
                        model.set(sizeField, e.response[sizeField]);
                        that._tiles = that.listView.items().filter("[" + kendo.attr("type") + "=f]");
                        that._scroll();
                    });
                }
            } else {
                e.preventDefault();
                that._showMessage(kendo.format(options.messages.invalidFileType, fileName, fileTypes));
            }
        },
        _findFile: function(name) {
            var data = this.dataSource.data(), idx, result, typeField = this._getFieldName(TYPEFIELD), nameField = this._getFieldName(NAMEFIELD), length;
            name = name.toLowerCase();
            for (idx = 0, length = data.length; idx < length; idx++) {
                if (data[idx].get(typeField) === "f" && data[idx].get(nameField).toLowerCase() === name) {
                    result = data[idx];
                    break;
                }
            }
            return result;
        },
        _createFile: function(fileName) {
            var that = this, idx, length, index = 0, model = {}, typeField = that._getFieldName(TYPEFIELD), view = that.dataSource.view(), file = that._findFile(fileName);
            if (file && !that._showMessage(kendo.format(that.options.messages.overwriteFile, fileName), "confirm")) {
                return null;
            }
            if (file) {
                return file;
            }
            for (idx = 0, length = view.length; idx < length; idx++) {
                if (view[idx].get(typeField) === "f") {
                    index = idx;
                    break;
                }
            }
            model[typeField] = "f";
            model[that._getFieldName(NAMEFIELD)] = fileName;
            model[that._getFieldName(SIZEFIELD)] = 0;
            return that.dataSource.insert(++index, model);
        },
        createDirectory: function() {
            var that = this, idx, length, lastDirectoryIdx = 0, typeField = that._getFieldName(TYPEFIELD), nameField = that._getFieldName(NAMEFIELD), view = that.dataSource.data(), name = that._nameDirectory(), model = new that.dataSource.reader.model();
            for (idx = 0, length = view.length; idx < length; idx++) {
                if (view[idx].get(typeField) === "d") {
                    lastDirectoryIdx = idx;
                }
            }
            model.set(typeField, "d");
            model.set(nameField, name);
            that.listView.one("dataBound", function() {
                var selected = that.listView.items().filter("[" + kendo.attr("uid") + "=" + model.uid + "]"), input = selected.find("input");
                if (selected.length) {
                    this.edit(selected);
                }
                this.element.scrollTop(selected.attr("offsetTop") - this.element[0].offsetHeight);
                setTimeout(function() {
                    input.select();
                });
            }).one("save", function(e) {
                var value = e.model.get(nameField);
                if (!value) {
                    e.model.set(nameField, name);
                } else {
                    e.model.set(nameField, that._nameExists(value, model.uid) ? that._nameDirectory() : value);
                }
            });
            that.dataSource.insert(++lastDirectoryIdx, model);
        },
        _directoryKeyDown: function(e) {
            if (e.keyCode == 13) {
                e.currentTarget.blur();
            }
        },
        _directoryBlur: function() {
            this.listView.save();
        },
        _nameExists: function(name, uid) {
            var data = this.dataSource.data(), typeField = this._getFieldName(TYPEFIELD), nameField = this._getFieldName(NAMEFIELD), idx, length;
            for (idx = 0, length = data.length; idx < length; idx++) {
                if (data[idx].get(typeField) === "d" && data[idx].get(nameField).toLowerCase() === name.toLowerCase() && data[idx].uid !== uid) {
                    return true;
                }
            }
            return false;
        },
        _nameDirectory: function() {
            var name = "New folder", data = this.dataSource.data(), directoryNames = [], typeField = this._getFieldName(TYPEFIELD), nameField = this._getFieldName(NAMEFIELD), candidate, idx, length;
            for (idx = 0, length = data.length; idx < length; idx++) {
                if (data[idx].get(typeField) === "d" && data[idx].get(nameField).toLowerCase().indexOf(name.toLowerCase()) > -1) {
                    directoryNames.push(data[idx].get(nameField));
                }
            }
            if ($.inArray(name, directoryNames) > -1) {
                idx = 2;
                do {
                    candidate = name + " (" + idx + ")";
                    idx++;
                } while ($.inArray(candidate, directoryNames) > -1);
                name = candidate;
            }
            return name;
        },
        orderBy: function(field) {
            this.dataSource.sort([ {
                field: this._getFieldName(TYPEFIELD),
                dir: "asc"
            }, {
                field: this._getFieldName(field),
                dir: "asc"
            } ]);
        },
        search: function(name) {
            this.dataSource.filter({
                field: this._getFieldName(NAMEFIELD),
                operator: "contains",
                value: name
            });
        },
        _content: function() {
            var that = this;
            that.list = $('<ul class="k-reset k-floats k-tiles" />').appendTo(that.element).on("scroll" + NS, proxy(that._scroll, that)).on("dblclick" + NS, "li", proxy(that._dblClick, that));
            that.listView = new kendo.ui.ListView(that.list, {
                dataSource: that.dataSource,
                template: that._itemTmpl(),
                editTemplate: that._editTmpl(),
                selectable: true,
                autoBind: false,
                dataBinding: function(e) {
                    that.toolbar.find(".k-delete").parent().addClass("k-state-disabled");
                    if (e.action === "remove" || e.action === "sync") {
                        e.preventDefault();
                    }
                },
                dataBound: function() {
                    if (that.dataSource.view().length) {
                        that._tiles = this.items().filter("[" + kendo.attr("type") + "=f]");
                        that._scroll();
                    } else {
                        this.wrapper.append(EMPTYTILE({
                            text: that.options.messages.emptyFolder
                        }));
                    }
                },
                change: proxy(that._listViewChange, that)
            });
        },
        _dblClick: function(e) {
            var that = this, li = $(e.currentTarget);
            if (li.filter("[" + kendo.attr("type") + "=d]").length) {
                var folder = that.dataSource.getByUid(li.attr(kendo.attr("uid")));
                if (folder) {
                    that.path(concatPaths(that.path(), folder.get(that._getFieldName(NAMEFIELD))));
                    that.breadcrumbs.value(that.path());
                }
            } else if (li.filter("[" + kendo.attr("type") + "=f]").length) {
                that.trigger(APPLY);
            }
        },
        _listViewChange: function() {
            var selected = this._selectedItem();
            if (selected) {
                this.toolbar.find(".k-delete").parent().removeClass("k-state-disabled");
                if (selected.get(this._getFieldName(TYPEFIELD)) === "f") {
                    this.trigger(CHANGE);
                }
            }
        },
        _dataSource: function() {
            var that = this, options = that.options, transport = options.transport, typeSortOrder = extend({}, DEFAULTSORTORDER), nameSortOrder = {
                field: NAMEFIELD,
                dir: "asc"
            }, schema, dataSource = {
                type: transport.type || "imagebrowser",
                sort: [ typeSortOrder, nameSortOrder ]
            };
            if (isPlainObject(transport)) {
                transport.path = proxy(that.path, that);
                dataSource.transport = transport;
            }
            if (isPlainObject(options.schema)) {
                dataSource.schema = options.schema;
                if (isPlainObject(options.schema.model) && options.schema.model.fields) {
                    typeSortOrder.field = fieldName(options.schema.model.fields, TYPEFIELD);
                    nameSortOrder.field = fieldName(options.schema.model.fields, NAMEFIELD);
                }
            } else if (transport.type && isPlainObject(kendo.data.schemas[transport.type])) {
                schema = kendo.data.schemas[transport.type];
                if (isPlainObject(schema.model) && schema.model.fields) {
                    typeSortOrder.field = fieldName(schema.model.fields, TYPEFIELD);
                    nameSortOrder.field = fieldName(schema.model.fields, NAMEFIELD);
                }
            }
            if (that.dataSource && that._errorHandler) {
                that.dataSource.unbind(ERROR, that._errorHandler);
            } else {
                that._errorHandler = proxy(that._error, that);
            }
            that.dataSource = kendo.data.DataSource.create(dataSource).bind(ERROR, that._errorHandler);
        },
        _navigation: function() {
            var that = this, navigation = $('<div class="k-floatwrap"><input/><input/></div>').appendTo(this.element);
            that.breadcrumbs = navigation.find("input:first").kendoBreadcrumbs({
                value: that.options.path,
                change: function() {
                    that.path(this.value());
                }
            }).data("kendoBreadcrumbs");
            that.searchBox = navigation.parent().find("input:last").kendoSearchBox({
                label: that.options.messages.search,
                change: function() {
                    that.search(this.value());
                }
            }).data("kendoSearchBox");
        },
        _error: function(e) {
            var that = this, status;
            if (!that.trigger(ERROR, e)) {
                status = e.xhr.status;
                if (e.status == "error") {
                    if (status == "404") {
                        that._showMessage(that.options.messages.directoryNotFound);
                    } else if (status != "0") {
                        that._showMessage("Error! The requested URL returned " + status + " - " + e.xhr.statusText);
                    }
                } else if (status == "timeout") {
                    that._showMessage("Error! Server timeout.");
                }
            }
        },
        _showMessage: function(message, type) {
            return window[type || "alert"](message);
        },
        refresh: function() {
            var that = this;
            that._navigation();
            that._toolbar();
            that._content();
        },
        _loadImage: function(li) {
            var that = this, element = $(li), dataItem = that.dataSource.getByUid(element.attr(kendo.attr("uid"))), name = dataItem.get(that._getFieldName(NAMEFIELD)), thumbnailUrl = that.options.transport.thumbnailUrl, img = $("<img />", {
                alt: name
            }).hide().on("load" + NS, function() {
                $(this).prev().remove().end().addClass("k-image").fadeIn();
            }), urlJoin = "?";
            element.find(".k-loading").after(img);
            if (isFunction(thumbnailUrl)) {
                thumbnailUrl = thumbnailUrl(that.path(), encodeURIComponent(name));
            } else {
                if (thumbnailUrl.indexOf("?") >= 0) {
                    urlJoin = "&";
                }
                thumbnailUrl = thumbnailUrl + urlJoin + "path=" + that.path() + encodeURIComponent(name);
            }
            // IE8 will trigger the load event immediately when the src is assign
            // if the image is loaded from the cache
            img.attr("src", thumbnailUrl);
            li.loaded = true;
        },
        _scroll: function() {
            var that = this;
            if (that.options.transport && that.options.transport.thumbnailUrl) {
                clearTimeout(that._timeout);
                that._timeout = setTimeout(function() {
                    var height = that.list.outerHeight(), viewTop = that.list.scrollTop(), viewBottom = viewTop + height;
                    that._tiles.each(function() {
                        var top = offsetTop(this), bottom = top + this.offsetHeight;
                        if (top >= viewTop && top < viewBottom || bottom >= viewTop && bottom < viewBottom) {
                            that._loadImage(this);
                        }
                        if (top > viewBottom) {
                            return false;
                        }
                    });
                    that._tiles = that._tiles.filter(function() {
                        return !this.loaded;
                    });
                }, 250);
            }
        },
        _editTmpl: function() {
            var that = this, html = '<li class="k-tile k-state-selected" ' + kendo.attr("uid") + '="#=uid#" ';
            html += kendo.attr("type") + '="${' + that._getFieldName(TYPEFIELD) + '}">';
            html += "#if(" + that._getFieldName(TYPEFIELD) + ' == "d") { #';
            html += '<div class="k-thumb"><span class="k-icon k-folder"></span></div>';
            html += "#}else{#";
            html += '<div class="k-thumb"><span class="k-icon k-loading"></span></div>';
            html += "#}#";
            html += "#if(" + that._getFieldName(TYPEFIELD) + ' == "d") { #';
            html += '<input class="k-input" ' + kendo.attr("bind") + '="value:' + that._getFieldName(NAMEFIELD) + '"/>';
            html += "#}#";
            html += "</li>";
            return proxy(kendo.template(html), {
                sizeFormatter: sizeFormatter
            });
        },
        _itemTmpl: function() {
            var that = this, html = '<li class="k-tile" ' + kendo.attr("uid") + '="#=uid#" ';
            html += kendo.attr("type") + '="${' + that._getFieldName(TYPEFIELD) + '}">';
            html += "#if(" + that._getFieldName(TYPEFIELD) + ' == "d") { #';
            html += '<div class="k-thumb"><span class="k-icon k-folder"></span></div>';
            html += "#}else{#";
            if (that.options.transport && that.options.transport.thumbnailUrl) {
                html += '<div class="k-thumb"><span class="k-icon k-loading"></span></div>';
            } else {
                html += '<div class="k-thumb"><span class="k-icon k-file"></span></div>';
            }
            html += "#}#";
            html += "<strong>${" + that._getFieldName(NAMEFIELD) + "}</strong>";
            html += "#if(" + that._getFieldName(TYPEFIELD) + ' == "f") { # <span class="k-filesize">${this.sizeFormatter(' + that._getFieldName(SIZEFIELD) + ")}</span> #}#";
            html += "</li>";
            return proxy(kendo.template(html), {
                sizeFormatter: sizeFormatter
            });
        },
        _getFieldName: function(name) {
            return fieldName(this.dataSource.reader.model.fields, name);
        },
        path: function(value) {
            var that = this, path = that._path || "";
            if (value !== undefined) {
                that._path = value.replace(trimSlashesRegExp, "") + "/";
                that.dataSource.read({
                    path: that._path
                });
                return;
            }
            if (path) {
                path = path.replace(trimSlashesRegExp, "");
            }
            return path === "/" || path === "" ? "" : path + "/";
        }
    });
    var SearchBox = Widget.extend({
        init: function(element, options) {
            var that = this;
            options = options || {};
            Widget.fn.init.call(that, element, options);
            if (placeholderSupported) {
                that.element.attr("placeholder", that.options.label);
            }
            that._wrapper();
            that.element.on("keydown" + SEARCHBOXNS, proxy(that._keydown, that)).on("change" + SEARCHBOXNS, proxy(that._updateValue, that));
            that.wrapper.on(CLICK + SEARCHBOXNS, "a", proxy(that._click, that));
            if (!placeholderSupported) {
                that.element.on("focus" + SEARCHBOXNS, proxy(that._focus, that)).on("blur" + SEARCHBOXNS, proxy(that._blur, that));
            }
        },
        options: {
            name: "SearchBox",
            label: "Search",
            value: ""
        },
        events: [ CHANGE ],
        destroy: function() {
            var that = this;
            that.wrapper.add(that.element).add(that.label).off(SEARCHBOXNS);
            Widget.fn.destroy.call(that);
        },
        _keydown: function(e) {
            if (e.keyCode === 13) {
                this._updateValue();
            }
        },
        _click: function(e) {
            e.preventDefault();
            this._updateValue();
        },
        _updateValue: function() {
            var that = this, value = that.element.val();
            if (value !== that.value()) {
                that.value(value);
                that.trigger(CHANGE);
            }
        },
        _blur: function() {
            this._updateValue();
            this._toggleLabel();
        },
        _toggleLabel: function() {
            if (!placeholderSupported) {
                this.label.toggle(!this.element.val());
            }
        },
        _focus: function() {
            this.label.hide();
        },
        _wrapper: function() {
            var element = this.element, wrapper = element.parents(".k-search-wrap");
            element[0].style.width = "";
            element.addClass("k-input k-textbox");
            if (!wrapper.length) {
                wrapper = element.wrap($('<div class="k-widget k-search-wrap k-textbox"/>')).parent();
                if (!placeholderSupported) {
                    $('<label style="display:block">' + this.options.label + "</label>").insertBefore(element);
                }
                $('<a href="#" class="k-icon k-i-search k-search"/>').appendTo(wrapper);
            }
            this.wrapper = wrapper;
            this.label = wrapper.find(">label");
        },
        value: function(value) {
            var that = this;
            if (value !== undefined) {
                that.options.value = value;
                that.element.val(value);
                that._toggleLabel();
                return;
            }
            return that.options.value;
        }
    });
    var Breadcrumbs = Widget.extend({
        init: function(element, options) {
            var that = this;
            options = options || {};
            Widget.fn.init.call(that, element, options);
            that._wrapper();
            that.wrapper.on("focus" + BREADCRUBMSNS, "input", proxy(that._focus, that)).on("blur" + BREADCRUBMSNS, "input", proxy(that._blur, that)).on("keydown" + BREADCRUBMSNS, "input", proxy(that._keydown, that)).on(CLICK + BREADCRUBMSNS, "a.k-i-arrow-n:first", proxy(that._rootClick, that)).on(CLICK + BREADCRUBMSNS, "a:not(.k-i-arrow-n)", proxy(that._click, that));
            that.value(that.options.value);
        },
        options: {
            name: "Breadcrumbs",
            gap: 50
        },
        events: [ CHANGE ],
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that.wrapper.add(that.wrapper.find("input")).add(that.wrapper.find("a")).off(BREADCRUBMSNS);
        },
        _update: function(val) {
            val = (val || "").charAt(0) === "/" ? val : "/" + (val || "");
            if (val !== this.value()) {
                this.value(val);
                this.trigger(CHANGE);
            }
        },
        _click: function(e) {
            e.preventDefault();
            this._update(this._path($(e.target).prevAll("a:not(.k-i-arrow-n)").andSelf()));
        },
        _rootClick: function(e) {
            e.preventDefault();
            this._update("");
        },
        _focus: function() {
            var that = this, element = that.element;
            that.overlay.hide();
            that.element.val(that.value());
            setTimeout(function() {
                element.select();
            });
        },
        _blur: function() {
            if (this.overlay.is(":visible")) {
                return;
            }
            var that = this, element = that.element, val = element.val().replace(/\/{2,}/g, "/");
            that.overlay.show();
            element.val("");
            that._update(val);
        },
        _keydown: function(e) {
            var that = this;
            if (e.keyCode === 13) {
                that._blur();
                setTimeout(function() {
                    that.overlay.find("a:first").focus();
                });
            }
        },
        _wrapper: function() {
            var element = this.element, wrapper = element.parents(".k-breadcrumbs"), overlay;
            element[0].style.width = "";
            element.addClass("k-input");
            if (!wrapper.length) {
                wrapper = element.wrap($('<div class="k-widget k-breadcrumbs k-header k-state-default"/>')).parent();
            }
            overlay = wrapper.find(".k-breadcrumbs-wrap");
            if (!overlay.length) {
                overlay = $('<div class="k-breadcrumbs-wrap"/>').appendTo(wrapper);
            }
            this.wrapper = wrapper;
            this.overlay = overlay;
        },
        refresh: function() {
            var html = "", value = this.value(), segments, segment, idx, length;
            if (value === undefined || !value.match(/^\//)) {
                value = "/" + (value || "");
            }
            segments = value.split("/");
            for (idx = 0, length = segments.length; idx < length; idx++) {
                segment = segments[idx];
                if (segment) {
                    if (!html) {
                        html += '<a href="#" class="k-icon k-i-arrow-n">root</a>';
                    }
                    html += '<a class="k-link" href="#">' + segments[idx] + "</a>";
                    html += '<span class="k-icon k-i-arrow-e">&gt;</span>';
                }
            }
            this.overlay.empty().append($(html));
            this._adjustSectionWidth();
        },
        _adjustSectionWidth: function() {
            var that = this, wrapper = that.wrapper, width = wrapper.width() - that.options.gap, links = that.overlay.find("a"), a;
            links.each(function(index) {
                a = $(this);
                if (a.parent().width() > width) {
                    if (index == links.length - 1) {
                        a.width(width);
                    } else {
                        a.prev().andSelf().hide();
                    }
                }
            });
        },
        value: function(val) {
            if (val !== undefined) {
                this._value = val.replace(/\/{2,}/g, "/");
                this.refresh();
                return;
            }
            return this._value;
        },
        _path: function(trail) {
            return "/" + $.map(trail, function(b) {
                return $(b).text();
            }).join("/");
        }
    });
    kendo.ui.plugin(ImageBrowser);
    kendo.ui.plugin(Breadcrumbs);
    kendo.ui.plugin(SearchBox);
})(window.kendo.jQuery);

(function($, undefined) {
    // Imports ================================================================
    var kendo = window.kendo, Class = kendo.Class, Widget = kendo.ui.Widget, os = kendo.support.mobileOS, browser = kendo.support.browser, extend = $.extend, deepExtend = kendo.deepExtend, NS = ".kendoEditor", keys = kendo.keys;
    // options can be: template (as string), cssClass, title, defaultValue
    var ToolTemplate = Class.extend({
        init: function(options) {
            this.options = options;
        },
        getHtml: function() {
            var options = this.options;
            return kendo.template(options.template)({
                cssClass: options.cssClass,
                tooltip: options.title,
                initialValue: options.initialValue
            });
        }
    });
    var EditorUtils = {
        select: function(editor) {
            editor.trigger("select", {});
        },
        editorWrapperTemplate: '<table cellspacing="4" cellpadding="0" class="k-widget k-editor k-header" role="presentation"><tbody>' + '<tr role="presentation"><td class="k-editor-toolbar-wrap" role="presentation"><ul class="k-editor-toolbar" role="toolbar"></ul></td></tr>' + '<tr><td class="k-editable-area"></td></tr>' + "</tbody></table>",
        buttonTemplate: '<li class="k-editor-button" role="presentation">' + '<a href="" role="button" class="k-tool-icon #= cssClass #" unselectable="on" title="#= tooltip #">#= tooltip #</a>' + "</li>",
        colorPickerTemplate: '<li class="k-editor-colorpicker" role="presentation"><div class="k-colorpicker #= cssClass #"></div></li>',
        comboBoxTemplate: '<li class="k-editor-combobox">' + '<select title="#= tooltip #" class="#= cssClass #"></select>' + "</li>",
        dropDownListTemplate: '<li class="k-editor-selectbox">' + '<select title="#= tooltip #" class="#= cssClass #"></select>' + "</li>",
        separatorTemplate: '<li class="k-separator"></li>',
        focusable: ".k-colorpicker,a.k-tool-icon:not(.k-state-disabled),.k-selectbox, .k-combobox .k-input",
        wrapTextarea: function(textarea) {
            var w = textarea[0].style.width, h = textarea[0].style.height, template = EditorUtils.editorWrapperTemplate, editorWrap = $(template).insertBefore(textarea).width(w).height(h), editArea = editorWrap.find(".k-editable-area");
            textarea.appendTo(editArea).addClass("k-content k-raw-content").hide();
            return textarea.closest(".k-editor");
        },
        renderTools: function(editor, tools) {
            var editorTools = {}, currentTool, tool, i, nativeTools = editor._nativeTools, template, options, toolsArea = $(editor.element).closest(".k-editor").find(".k-editor-toolbar");
            if (tools) {
                for (i = 0; i < tools.length; i++) {
                    currentTool = tools[i];
                    options = null;
                    if ($.isPlainObject(currentTool)) {
                        if (currentTool.name && editor.tools[currentTool.name]) {
                            $.extend(editor.tools[currentTool.name].options, currentTool);
                            editorTools[currentTool.name] = editor.tools[currentTool.name];
                            options = editorTools[currentTool.name].options;
                        } else {
                            options = extend({
                                cssClass: "k-i-custom",
                                type: "button",
                                tooltip: ""
                            }, currentTool);
                            if (options.name) {
                                options.cssClass = "k-" + (options.name == "custom" ? "i-custom" : options.name);
                            }
                            if (!options.template) {
                                if (options.type == "button") {
                                    options.template = EditorUtils.buttonTemplate;
                                }
                            }
                        }
                    } else if (editor.tools[currentTool]) {
                        editorTools[currentTool] = editor.tools[currentTool];
                        options = editorTools[currentTool].options;
                    }
                    if (!options) {
                        continue;
                    }
                    template = options.template;
                    if (template) {
                        if (template.getHtml) {
                            template = template.getHtml();
                        } else {
                            if (!$.isFunction(template)) {
                                template = kendo.template(template);
                            }
                            template = template(options);
                        }
                        if (template.indexOf("<li") !== 0) {
                            template = "<li class='k-editor-template'>" + template + "</li>";
                        }
                        tool = $(template).appendTo(toolsArea);
                        if (options.type == "button" && options.exec) {
                            tool.find(".k-tool-icon").click($.proxy(options.exec, editor.element[0]));
                        }
                    }
                }
            }
            for (i = 0; i < nativeTools.length; i++) {
                if (!editorTools[nativeTools[i]]) {
                    editorTools[nativeTools[i]] = editor.tools[nativeTools[i]];
                }
            }
            editor.options.tools = editorTools;
        },
        decorateStyleToolItems: function(textarea) {
            var selectBox = textarea.data.closest(".k-editor").find(".k-style").data("kendoSelectBox");
            if (!selectBox) {
                return;
            }
            var classes = selectBox.dataSource.view();
            selectBox.list.find(".k-item").each(function(idx, element) {
                var item = $(element), text = item.text(), style = kendo.ui.editor.Dom.inlineStyle(textarea.data.data("kendoEditor").document, "span", {
                    className: classes[idx].value
                });
                item.html('<span unselectable="on" style="display:block;' + style + '">' + text + "</span>");
            });
        },
        createContentElement: function(textarea, stylesheets) {
            var iframe, wnd, doc, rtlStyle = kendo.support.isRtl(textarea) ? "direction:rtl;" : "";
            textarea.hide();
            iframe = $("<iframe />", {
                src: 'javascript:""',
                frameBorder: "0"
            }).css("display", "").addClass("k-content").insertBefore(textarea)[0];
            wnd = iframe.contentWindow || iframe;
            if (stylesheets.length > 0) {
                $(iframe).one("load", textarea, EditorUtils.decorateStyleToolItems);
            }
            doc = wnd.document || iframe.contentDocument;
            doc.open();
            doc.write("<!DOCTYPE html><html><head>" + "<meta charset='utf-8' />" + "<style>" + "html,body{padding:0;margin:0;background:#fff;height:100%;min-height:100%;}" + "body{font: 12px/1.5 Verdana,Geneva,sans-serif;padding-top:1px;margin-top:-1px;" + "word-wrap: break-word;-webkit-nbsp-mode: space;-webkit-line-break: after-white-space;" + rtlStyle + "}" + "h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em}h3{font-size:1.16em}h4{font-size:1em}h5{font-size:.83em}h6{font-size:.7em}" + "p{margin:0 0 1em;padding:0 .2em}.k-marker{display:none;}.k-paste-container,.Apple-style-span{position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden}" + "ul,ol{padding-left:2.5em}" + "a{color:#00a}" + "code{font-size:1.23em}" + "</style>" + $.map(stylesheets, function(href) {
                return "<link rel='stylesheet' href='" + href + "'>";
            }).join("") + "</head><body contenteditable='true'></body></html>");
            doc.close();
            return wnd;
        },
        initializeContentElement: function(editor) {
            var isFirstKeyDown = true;
            editor.window = EditorUtils.createContentElement($(editor.textarea), editor.options.stylesheets);
            editor.document = editor.window.contentDocument || editor.window.document;
            editor.body = editor.document.body;
            $(editor.document).on("keydown" + NS, function(e) {
                var range;
                if (e.keyCode === keys.F10) {
                    // Handling with timeout to avoid the default IE menu
                    setTimeout(function() {
                        var TABINDEX = "tabIndex", element = editor.wrapper, tabIndex = element.attr(TABINDEX);
                        // Chrome can't focus something which has already been focused
                        element.attr(TABINDEX, tabIndex || 0).focus().find("li:has(" + focusable + ")").first().focus();
                        if (!tabIndex && tabIndex !== 0) {
                            element.removeAttr(TABINDEX);
                        }
                    }, 100);
                    e.preventDefault();
                    return;
                } else if (e.keyCode === keys.BACKSPACE) {
                    range = editor.getRange();
                    var ancestor, emptyParagraphContent = kendo.support.browser.msie ? "" : '<br _moz_dirty="" />', dom = kendo.ui.editor.Dom;
                    range.deleteContents();
                    ancestor = range.commonAncestorContainer;
                    if (dom.name(ancestor) === "p" && ancestor.innerHTML === "") {
                        ancestor.innerHTML = emptyParagraphContent;
                        range.setStart(ancestor, 0);
                        range.collapse(true);
                        editor.selectRange(range);
                    }
                }
                var toolName = editor.keyboard.toolFromShortcut(editor.options.tools, e);
                if (toolName) {
                    e.preventDefault();
                    if (!/undo|redo/.test(toolName)) {
                        editor.keyboard.endTyping(true);
                    }
                    editor.exec(toolName);
                    return false;
                }
                if (editor.keyboard.isTypingKey(e) && editor.pendingFormats.hasPending()) {
                    if (isFirstKeyDown) {
                        isFirstKeyDown = false;
                    } else {
                        range = editor.getRange();
                        editor.pendingFormats.apply(range);
                        editor.selectRange(range);
                    }
                }
                editor.keyboard.clearTimeout();
                editor.keyboard.keydown(e);
            }).on("keyup" + NS, function(e) {
                var selectionCodes = [ 8, 9, 33, 34, 35, 36, 37, 38, 39, 40, 40, 45, 46 ];
                if ($.inArray(e.keyCode, selectionCodes) > -1 || e.keyCode == 65 && e.ctrlKey && !e.altKey && !e.shiftKey) {
                    editor.pendingFormats.clear();
                    select(editor);
                }
                if (editor.keyboard.isTypingKey(e)) {
                    if (editor.pendingFormats.hasPending()) {
                        var range = editor.getRange();
                        editor.pendingFormats.apply(range);
                        editor.selectRange(range);
                    }
                } else {
                    isFirstKeyDown = true;
                }
                editor.keyboard.keyup(e);
            }).on("mousedown" + NS, function(e) {
                editor.pendingFormats.clear();
                var target = $(e.target);
                if (!browser.gecko && e.which == 2 && target.is("a[href]")) {
                    window.open(target.attr("href"), "_new");
                }
            }).on("mouseup" + NS, function() {
                select(editor);
            }).on("click" + NS, function(e) {
                var dom = kendo.ui.editor.Dom, range;
                if (dom.name(e.target) === "img") {
                    range = editor.createRange();
                    range.selectNode(e.target);
                    editor.selectRange(range);
                }
            });
            $(editor.window).on("blur" + NS, function() {
                var old = editor.textarea.value, value = editor.encodedValue();
                editor.update();
                if (value != old) {
                    editor.trigger("change");
                }
            });
            $(editor.body).on("cut" + NS + " paste" + NS, function(e) {
                editor.clipboard["on" + e.type](e);
            });
        },
        formatByName: function(name, format) {
            for (var i = 0; i < format.length; i++) {
                if ($.inArray(name, format[i].tags) >= 0) {
                    return format[i];
                }
            }
        },
        registerTool: function(toolName, tool) {
            if (tool.options && tool.options.template) {
                tool.options.template.options.cssClass = "k-" + toolName;
            }
            Editor.fn._tools[toolName] = tool;
        },
        registerFormat: function(formatName, format) {
            Editor.fn.options.formats[formatName] = format;
        },
        createDialog: function(windowContent, editor, initOptions) {
            var isRtl = kendo.support.isRtl(editor.wrapper), win = $(windowContent).appendTo(document.body).kendoWindow(initOptions);
            if (isRtl) {
                win.closest(".k-window").addClass("k-rtl");
            }
            return win;
        }
    };
    var select = EditorUtils.select, focusable = EditorUtils.focusable, wrapTextarea = EditorUtils.wrapTextarea, renderTools = EditorUtils.renderTools, initializeContentElement = EditorUtils.initializeContentElement;
    var messages = {
        bold: "Bold",
        italic: "Italic",
        underline: "Underline",
        strikethrough: "Strikethrough",
        superscript: "Superscript",
        subscript: "Subscript",
        justifyCenter: "Center text",
        justifyLeft: "Align text left",
        justifyRight: "Align text right",
        justifyFull: "Justify",
        insertUnorderedList: "Insert unordered list",
        insertOrderedList: "Insert ordered list",
        indent: "Indent",
        outdent: "Outdent",
        createLink: "Insert hyperlink",
        unlink: "Remove hyperlink",
        insertImage: "Insert image",
        insertHtml: "Insert HTML",
        viewHtml: "View HTML",
        fontName: "Select font family",
        fontNameInherit: "(inherited font)",
        fontSize: "Select font size",
        fontSizeInherit: "(inherited size)",
        formatBlock: "Format",
        foreColor: "Color",
        backColor: "Background color",
        style: "Styles",
        emptyFolder: "Empty Folder",
        uploadFile: "Upload",
        orderBy: "Arrange by:",
        orderBySize: "Size",
        orderByName: "Name",
        invalidFileType: 'The selected file "{0}" is not valid. Supported file types are {1}.',
        deleteFile: 'Are you sure you want to delete "{0}"?',
        overwriteFile: 'A file with name "{0}" already exists in the current directory. Do you want to overwrite it?',
        directoryNotFound: "A directory with this name was not found.",
        imageWebAddress: "Web address",
        imageAltText: "Alternate text",
        linkWebAddress: "Web address",
        linkText: "Text",
        linkToolTip: "ToolTip",
        linkOpenInNewWindow: "Open link in new window",
        dialogInsert: "Insert",
        dialogButtonSeparator: "or",
        dialogCancel: "Cancel"
    };
    var supportedBrowser = !os || os.ios && os.flatVersion >= 500 || !os.ios && typeof document.documentElement.contentEditable != "undefined";
    var Editor = Widget.extend({
        init: function(element, options) {
            /* suppress initialization in mobile webkit devices (w/o proper contenteditable support) */
            if (!supportedBrowser) {
                return;
            }
            var that = this, wrapper, value, editorNS = kendo.ui.editor;
            Widget.fn.init.call(that, element, options);
            that.tools = deepExtend({}, kendo.ui.Editor.fn._tools);
            that.options = deepExtend({}, that.options, options);
            element = $(element);
            element.closest("form").on("submit" + NS, function() {
                that.update();
            });
            for (var id in that.tools) {
                that.tools[id].name = id.toLowerCase();
            }
            that.textarea = element.attr("autocomplete", "off")[0];
            wrapper = that.wrapper = wrapTextarea(element);
            if (that.textarea.id) {
                wrapper.find(".k-editor-toolbar").attr("aria-controls", that.textarea.id);
            }
            renderTools(that, that.options.tools);
            initializeContentElement(that);
            that.keyboard = new editorNS.Keyboard([ new editorNS.TypingHandler(that), new editorNS.SystemHandler(that) ]);
            that.clipboard = new editorNS.Clipboard(this);
            that.pendingFormats = new editorNS.PendingFormats(this);
            that.undoRedoStack = new editorNS.UndoRedoStack();
            if (options && options.value) {
                value = options.value;
            } else {
                // indented HTML introduces problematic ranges in IE
                value = element.val().replace(/[\r\n\v\f\t ]+/gi, " ");
            }
            that.value(value);
            function toolFromClassName(element) {
                var tool = $.grep(element.className.split(" "), function(x) {
                    return !/^k-(widget|tool-icon|state-hover|header|combobox|dropdown|selectbox|colorpicker)$/i.test(x);
                });
                return tool[0] ? tool[0].substring(tool[0].lastIndexOf("-") + 1) : "custom";
            }
            function appendShortcutSequence(localizedText, tool) {
                if (!tool.key) {
                    return localizedText;
                }
                var res = localizedText + " (";
                if (tool.ctrl) {
                    res += "Ctrl + ";
                }
                if (tool.shift) {
                    res += "Shift + ";
                }
                if (tool.alt) {
                    res += "Alt + ";
                }
                res += tool.key + ")";
                return res;
            }
            var toolbarItems = ".k-editor-toolbar > li > *, .k-editor-toolbar > li select", buttons = ".k-editor-button .k-tool-icon", enabledButtons = buttons + ":not(.k-state-disabled)", disabledButtons = buttons + ".k-state-disabled";
            wrapper.find(".k-combobox .k-input").keydown(function(e) {
                var combobox = $(this).closest(".k-combobox").data("kendoComboBox"), key = e.keyCode;
                if (key == keys.RIGHT || key == keys.LEFT) {
                    combobox.close();
                } else if (key == keys.DOWN) {
                    if (!combobox.dropDown.isOpened()) {
                        e.stopImmediatePropagation();
                        combobox.open();
                    }
                }
            });
            wrapper.on("mouseenter" + NS, enabledButtons, function() {
                $(this).addClass("k-state-hover");
            }).on("mouseleave" + NS, enabledButtons, function() {
                $(this).removeClass("k-state-hover");
            }).on("mousedown" + NS, buttons, false).on("keydown" + NS, focusable, function(e) {
                var closestLi = $(this).closest("li"), focusableTool = "li:has(" + focusable + ")", focusElement, keyCode = e.keyCode;
                if (keyCode == keys.RIGHT) {
                    focusElement = closestLi.nextAll(focusableTool).first().find(focusable);
                } else if (keyCode == keys.LEFT) {
                    focusElement = closestLi.prevAll(focusableTool).first().find(focusable);
                } else if (keyCode == keys.ESC) {
                    focusElement = that;
                } else if (keyCode == keys.TAB && !(e.ctrlKey || e.altKey)) {
                    // skip tabbing to disabled tools, and focus the editing area when running out of tools
                    if (e.shiftKey) {
                        focusElement = closestLi.prevAll(focusableTool).first().find(focusable);
                        if (focusElement.length) {
                            e.preventDefault();
                        } else {
                            return;
                        }
                    } else {
                        e.preventDefault();
                        focusElement = closestLi.nextAll(focusableTool).first().find(focusable);
                        if (!focusElement.length) {
                            focusElement = that;
                        }
                    }
                }
                if (focusElement) {
                    focusElement.focus();
                }
            }).on("click" + NS, enabledButtons, function(e) {
                e.preventDefault();
                e.stopPropagation();
                that.exec(toolFromClassName(this));
            }).on("click" + NS, disabledButtons, function(e) {
                e.preventDefault();
            }).find(toolbarItems).each(function() {
                var toolName = toolFromClassName(this), options = that.options, tool = options.tools[toolName], description = options.messages[toolName], $this = $(this);
                if (!tool) {
                    return;
                }
                if (toolName == "fontSize" || toolName == "fontName") {
                    var inheritText = options.messages[toolName + "Inherit"] || messages[toolName + "Inherit"];
                    $this.find("input").val(inheritText).end().find("span.k-input").text(inheritText).end();
                }
                tool.initialize($this, {
                    title: appendShortcutSequence(description, tool),
                    editor: that
                });
            });
            that.bind("select", function() {
                var range = that.getRange();
                var nodes = editorNS.RangeUtils.textNodes(range);
                if (!nodes.length) {
                    nodes = [ range.startContainer ];
                }
                wrapper.find(toolbarItems).each(function() {
                    var tool = that.options.tools[toolFromClassName(this)];
                    if (tool) {
                        tool.update($(this), nodes, that.pendingFormats);
                    }
                });
            });
            that._DOMNodeInsertedHandler = function(e) {
                that._DOMNodeInserted(e);
            };
            that._endTypingHandler = function() {
                that._endTyping();
            };
            $(document).on("DOMNodeInserted", that._DOMNodeInsertedHandler).on("mousedown", that._endTypingHandler);
            kendo.notify(that);
        },
        _endTyping: function() {
            var that = this;
            try {
                if (that.keyboard.isTypingInProgress()) {
                    that.keyboard.endTyping(true);
                }
                if (!that.selectionRestorePoint) {
                    that.selectionRestorePoint = new kendo.ui.editor.RestorePoint(that.getRange());
                }
            } catch (e) {}
        },
        _DOMNodeInserted: function(e) {
            var that = this, wrapper = that.wrapper;
            if ($.contains(e.target, wrapper[0]) || wrapper[0] == e.target) {
                // preserve updated value before re-initializing
                // don't use update() to prevent the editor from encoding the content too early
                that.textarea.value = that.value();
                wrapper.find("iframe").remove();
                initializeContentElement(that);
                that.value(that.textarea.value);
            }
        },
        events: [ "select", "change", "execute", "error", "paste", "keydown", "keyup" ],
        options: {
            name: "Editor",
            messages: messages,
            formats: {},
            encoded: true,
            stylesheets: [],
            dialogOptions: {
                modal: true,
                resizable: false,
                draggable: true,
                animation: false
            },
            fontName: [ {
                text: "Arial",
                value: "Arial,Helvetica,sans-serif"
            }, {
                text: "Courier New",
                value: "'Courier New',Courier,monospace"
            }, {
                text: "Georgia",
                value: "Georgia,serif"
            }, {
                text: "Impact",
                value: "Impact,Charcoal,sans-serif"
            }, {
                text: "Lucida Console",
                value: "'Lucida Console',Monaco,monospace"
            }, {
                text: "Tahoma",
                value: "Tahoma,Geneva,sans-serif"
            }, {
                text: "Times New Roman",
                value: "'Times New Roman',Times,serif"
            }, {
                text: "Trebuchet MS",
                value: "'Trebuchet MS',Helvetica,sans-serif"
            }, {
                text: "Verdana",
                value: "Verdana,Geneva,sans-serif"
            } ],
            fontSize: [ {
                text: "1 (8pt)",
                value: "xx-small"
            }, {
                text: "2 (10pt)",
                value: "x-small"
            }, {
                text: "3 (12pt)",
                value: "small"
            }, {
                text: "4 (14pt)",
                value: "medium"
            }, {
                text: "5 (18pt)",
                value: "large"
            }, {
                text: "6 (24pt)",
                value: "x-large"
            }, {
                text: "7 (36pt)",
                value: "xx-large"
            } ],
            formatBlock: [ {
                text: "Paragraph",
                value: "p"
            }, {
                text: "Quotation",
                value: "blockquote"
            }, {
                text: "Heading 1",
                value: "h1"
            }, {
                text: "Heading 2",
                value: "h2"
            }, {
                text: "Heading 3",
                value: "h3"
            }, {
                text: "Heading 4",
                value: "h4"
            }, {
                text: "Heading 5",
                value: "h5"
            }, {
                text: "Heading 6",
                value: "h6"
            } ],
            tools: [ "bold", "italic", "underline", "strikethrough", "fontName", "fontSize", "foreColor", "backColor", "justifyLeft", "justifyCenter", "justifyRight", "justifyFull", "insertUnorderedList", "insertOrderedList", "indent", "outdent", "formatBlock", "createLink", "unlink", "insertImage" ]
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            $(that.window).add(that.document).add(that.wrapper).add(that.element.closest("form")).off(NS);
            $(document).off("DOMNodeInserted", that._DOMNodeInsertedHandler).off("mousedown", that._endTypingHandler);
            kendo.destroy(that.wrapper);
        },
        _nativeTools: [ "insertLineBreak", "insertParagraph", "redo", "undo", "insertHtml" ],
        _tools: {
            undo: {
                options: {
                    key: "Z",
                    ctrl: true
                }
            },
            redo: {
                options: {
                    key: "Y",
                    ctrl: true
                }
            }
        },
        tools: {},
        // tools collection is copied from _tools during initialization
        value: function(html) {
            var body = this.body, dom = kendo.ui.editor.Dom, currentHtml = kendo.ui.editor.Serializer.domToXhtml(body);
            if (html === undefined) {
                return currentHtml;
            }
            if (html == currentHtml) {
                return;
            }
            this.pendingFormats.clear();
            var onerrorRe = /onerror\s*=\s*(?:'|")?([^'">\s]*)(?:'|")?/i;
            // handle null value passed as a parameter
            html = (html || "").replace(/<!\[CDATA\[(.*)?\]\]>/g, "<!--[CDATA[$1]]-->").replace(/<script([^>]*)>(.*)?<\/script>/gi, "<telerik:script $1>$2</telerik:script>").replace(/<img([^>]*)>/gi, function(match) {
                return match.replace(onerrorRe, "");
            }).replace(/(<\/?img[^>]*>)[\r\n\v\f\t ]+/gi, "$1");
            if (!browser.msie) {
                // Add <br/>s to empty paragraphs in mozilla/chrome, to make them focusable
                html = html.replace(/<p([^>]*)>(\s*)?<\/p>/gi, '<p $1><br _moz_dirty="" /></p>');
            }
            if (browser.msie && browser.version < 9) {
                // Internet Explorer removes comments from the beginning of the html
                html = "<br/>" + html;
                var originalSrc = "originalsrc", originalHref = "originalhref";
                // IE < 8 makes href and src attributes absolute
                html = html.replace(/href\s*=\s*(?:'|")?([^'">\s]*)(?:'|")?/, originalHref + '="$1"');
                html = html.replace(/src\s*=\s*(?:'|")?([^'">\s]*)(?:'|")?/, originalSrc + '="$1"');
                body.innerHTML = html;
                dom.remove(body.firstChild);
                $(body).find("telerik\\:script,script,link,img,a").each(function() {
                    var node = this;
                    if (node[originalHref]) {
                        node.setAttribute("href", node[originalHref]);
                        node.removeAttribute(originalHref);
                    }
                    if (node[originalSrc]) {
                        node.setAttribute("src", node[originalSrc]);
                        node.removeAttribute(originalSrc);
                    }
                });
            } else {
                body.innerHTML = html;
                if (browser.msie) {
                    // having unicode characters creates denormalized DOM tree in IE9
                    dom.normalize(body);
                    setTimeout(function() {
                        // fix for IE9 OL bug -- https://connect.microsoft.com/IE/feedback/details/657695/ordered-list-numbering-changes-from-correct-to-0-0
                        var ols = body.getElementsByTagName("ol"), i, ol, originalStart;
                        for (i = 0; i < ols.length; i++) {
                            ol = ols[i];
                            originalStart = ol.getAttribute("start");
                            ol.setAttribute("start", 1);
                            if (originalStart) {
                                ol.setAttribute("start", originalStart);
                            } else {
                                ol.removeAttribute(originalStart);
                            }
                        }
                    }, 1);
                }
            }
            this.selectionRestorePoint = null;
            this.update();
        },
        focus: function() {
            this.window.focus();
        },
        update: function(value) {
            this.textarea.value = value || this.options.encoded ? this.encodedValue() : this.value();
        },
        encodedValue: function() {
            return kendo.ui.editor.Dom.encode(this.value());
        },
        createRange: function(document) {
            return kendo.ui.editor.RangeUtils.createRange(document || this.document);
        },
        getSelection: function() {
            return kendo.ui.editor.SelectionUtils.selectionFromDocument(this.document);
        },
        selectRange: function(range) {
            this.focus();
            var selection = this.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
        },
        getRange: function() {
            var selection = this.getSelection(), range = selection.rangeCount > 0 ? selection.getRangeAt(0) : this.createRange(), doc = this.document;
            if (range.startContainer == doc && range.endContainer == doc && !range.startOffset && !range.endOffset) {
                range.setStart(this.body, 0);
                range.collapse(true);
            }
            return range;
        },
        selectedHtml: function() {
            return kendo.ui.editor.Serializer.domToXhtml(this.getRange().cloneContents());
        },
        paste: function(html) {
            this.clipboard.paste(html);
        },
        exec: function(name, params) {
            var that = this, range, body, id, tool = "", pendingTool;
            name = name.toLowerCase();
            // restore selection
            if (!that.keyboard.isTypingInProgress()) {
                that.focus();
                range = that.getRange();
                body = that.document.body;
            }
            // exec tool
            for (id in that.options.tools) {
                if (id.toLowerCase() == name) {
                    tool = that.options.tools[id];
                    break;
                }
            }
            if (tool) {
                range = that.getRange();
                if (!/undo|redo/i.test(name) && tool.willDelayExecution(range)) {
                    // clone our tool to apply params only once
                    pendingTool = $.extend({}, tool);
                    $.extend(pendingTool.options, {
                        params: params
                    });
                    that.pendingFormats.toggle(pendingTool);
                    select(that);
                    return;
                }
                var command = tool.command ? tool.command(extend({
                    range: range
                }, params)) : null;
                that.trigger("execute", {
                    name: name,
                    command: command
                });
                if (/undo|redo/i.test(name)) {
                    that.undoRedoStack[name]();
                } else if (command) {
                    if (!command.managesUndoRedo) {
                        that.undoRedoStack.push(command);
                    }
                    command.editor = that;
                    command.exec();
                    if (command.async) {
                        command.change = $.proxy(function() {
                            select(that);
                        }, that);
                        return;
                    }
                }
                select(that);
            }
        }
    });
    kendo.ui.plugin(Editor);
    var Tool = Class.extend({
        init: function(options) {
            this.options = options;
        },
        initialize: function(ui, options) {
            ui.attr({
                unselectable: "on",
                title: options.title
            });
        },
        command: function(commandArguments) {
            return new this.options.command(commandArguments);
        },
        update: function() {},
        willDelayExecution: function() {
            return false;
        }
    });
    Tool.exec = function(editor, name, value) {
        editor.exec(name, {
            value: value
        });
    };
    var FormatTool = Tool.extend({
        init: function(options) {
            Tool.fn.init.call(this, options);
        },
        command: function(commandArguments) {
            var that = this;
            return new kendo.ui.editor.FormatCommand(extend(commandArguments, {
                formatter: that.options.formatter
            }));
        },
        update: function($ui, nodes, pendingFormats) {
            var isPending = pendingFormats.isPending(this.name), isFormatted = this.options.finder.isFormatted(nodes), isActive = isPending ? !isFormatted : isFormatted;
            $ui.toggleClass("k-state-active", isActive);
            $ui.attr("aria-pressed", isActive);
        }
    });
    EditorUtils.registerTool("separator", new Tool({
        template: new ToolTemplate({
            template: EditorUtils.separatorTemplate
        })
    }));
    // Exports ================================================================
    extend(kendo.ui, {
        editor: {
            ToolTemplate: ToolTemplate,
            EditorUtils: EditorUtils,
            Tool: Tool,
            FormatTool: FormatTool
        }
    });
})(window.jQuery);

(function($) {
    var kendo = window.kendo, map = $.map, extend = $.extend, browser = kendo.support.browser, STYLE = "style", FLOAT = "float", CSSFLOAT = "cssFloat", STYLEFLOAT = "styleFloat", CLASS = "class", KMARKER = "k-marker";
    function makeMap(items) {
        var obj = {}, i, len;
        for (i = 0, len = items.length; i < len; i++) {
            obj[items[i]] = true;
        }
        return obj;
    }
    var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed".split(",")), blockElements = "div,p,h1,h2,h3,h4,h5,h6,address,applet,blockquote,button,center,dd,dir,dl,dt,fieldset,form,frameset,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,pre,script,table,tbody,td,tfoot,th,thead,tr,ul".split(","), block = makeMap(blockElements), inlineElements = "span,em,a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,strike,strong,sub,sup,textarea,tt,u,var".split(","), inline = makeMap(inlineElements), fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected".split(","));
    var normalize = function(node) {
        if (node.nodeType == 1) {
            node.normalize();
        }
    };
    if (browser.msie && browser.version >= 8) {
        normalize = function(parent) {
            if (parent.nodeType == 1 && parent.firstChild) {
                var prev = parent.firstChild, node = prev;
                while (true) {
                    node = node.nextSibling;
                    if (!node) {
                        break;
                    }
                    if (node.nodeType == 3 && prev.nodeType == 3) {
                        node.nodeValue = prev.nodeValue + node.nodeValue;
                        Dom.remove(prev);
                    }
                    prev = node;
                }
            }
        };
    }
    var whitespace = /^\s+$/, rgb = /rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i, amp = /&/g, openTag = /</g, closeTag = />/g, nbsp = /\u00a0/g, bom = /\ufeff/g, persistedScrollTop;
    var cssAttributes = ("color,padding-left,padding-right,padding-top,padding-bottom," + "background-color,background-attachment,background-image,background-position,background-repeat," + "border-top-style,border-top-width,border-top-color," + "border-bottom-style,border-bottom-width,border-bottom-color," + "border-left-style,border-left-width,border-left-color," + "border-right-style,border-right-width,border-right-color," + "font-family,font-size,font-style,font-variant,font-weight,line-height").split(",");
    var Dom = {
        findNodeIndex: function(node) {
            var i = 0;
            while (true) {
                node = node.previousSibling;
                if (!node) {
                    break;
                }
                i++;
            }
            return i;
        },
        isDataNode: function(node) {
            return node && node.nodeValue !== null && node.data !== null;
        },
        isAncestorOf: function(parent, node) {
            try {
                return !Dom.isDataNode(parent) && ($.contains(parent, Dom.isDataNode(node) ? node.parentNode : node) || node.parentNode == parent);
            } catch (e) {
                return false;
            }
        },
        isAncestorOrSelf: function(root, node) {
            return Dom.isAncestorOf(root, node) || root == node;
        },
        findClosestAncestor: function(root, node) {
            if (Dom.isAncestorOf(root, node)) {
                while (node && node.parentNode != root) {
                    node = node.parentNode;
                }
            }
            return node;
        },
        getNodeLength: function(node) {
            return Dom.isDataNode(node) ? node.length : node.childNodes.length;
        },
        splitDataNode: function(node, offset) {
            var newNode = node.cloneNode(false), denormalizedText = "", iterator = node;
            while (iterator.nextSibling && iterator.nextSibling.nodeType == 3 && iterator.nextSibling.nodeValue) {
                denormalizedText += iterator.nextSibling.nodeValue;
                iterator = iterator.nextSibling;
            }
            node.deleteData(offset, node.length);
            newNode.deleteData(0, offset);
            newNode.nodeValue += denormalizedText;
            Dom.insertAfter(newNode, node);
        },
        attrEquals: function(node, attributes) {
            for (var key in attributes) {
                var value = node[key];
                if (key == FLOAT) {
                    value = node[$.support.cssFloat ? CSSFLOAT : STYLEFLOAT];
                }
                if (typeof value == "object") {
                    if (!Dom.attrEquals(value, attributes[key])) {
                        return false;
                    }
                } else if (value != attributes[key]) {
                    return false;
                }
            }
            return true;
        },
        blockParentOrBody: function(node) {
            return Dom.parentOfType(node, blockElements) || node.ownerDocument.body;
        },
        blockParents: function(nodes) {
            var blocks = [], i, len;
            for (i = 0, len = nodes.length; i < len; i++) {
                var block = Dom.parentOfType(nodes[i], Dom.blockElements);
                if (block && $.inArray(block, blocks) < 0) {
                    blocks.push(block);
                }
            }
            return blocks;
        },
        windowFromDocument: function(document) {
            return document.defaultView || document.parentWindow;
        },
        normalize: normalize,
        blockElements: blockElements,
        inlineElements: inlineElements,
        empty: empty,
        fillAttrs: fillAttrs,
        toHex: function(color) {
            var matches = rgb.exec(color);
            if (!matches) {
                return color;
            }
            return "#" + map(matches.slice(1), function(x) {
                x = parseInt(x, 10).toString(16);
                return x.length > 1 ? x : "0" + x;
            }).join("");
        },
        encode: function(value) {
            return value.replace(amp, "&amp;").replace(openTag, "&lt;").replace(closeTag, "&gt;").replace(nbsp, "&nbsp;");
        },
        name: function(node) {
            return node.nodeName.toLowerCase();
        },
        significantChildNodes: function(node) {
            return $.grep(node.childNodes, function(child) {
                return child.nodeType != 3 || !Dom.isWhitespace(child);
            });
        },
        lastTextNode: function(node) {
            var result = null;
            if (node.nodeType == 3) {
                return node;
            }
            for (var child = node.lastChild; child; child = child.previousSibling) {
                result = Dom.lastTextNode(child);
                if (result) {
                    return result;
                }
            }
            return result;
        },
        is: function(node, nodeName) {
            return Dom.name(node) == nodeName;
        },
        isMarker: function(node) {
            return node.className == KMARKER;
        },
        isWhitespace: function(node) {
            return whitespace.test(node.nodeValue);
        },
        isBlock: function(node) {
            return block[Dom.name(node)];
        },
        isEmpty: function(node) {
            return empty[Dom.name(node)];
        },
        isInline: function(node) {
            return inline[Dom.name(node)];
        },
        scrollContainer: function(doc) {
            var wnd = Dom.windowFromDocument(doc), scrollContainer = (wnd.contentWindow || wnd).document || wnd.ownerDocument || wnd;
            if (kendo.support.browser.webkit || scrollContainer.compatMode == "BackCompat") {
                scrollContainer = scrollContainer.body;
            } else {
                scrollContainer = scrollContainer.documentElement;
            }
            return scrollContainer;
        },
        scrollTo: function(node) {
            var element = $(Dom.isDataNode(node) ? node.parentNode : node), wnd = Dom.windowFromDocument(node.ownerDocument), windowHeight = wnd.innerHeight, elementTop, elementHeight, scrollContainer = Dom.scrollContainer(node.ownerDocument);
            if (Dom.name(element[0]) == "br") {
                element = element.parent();
            }
            elementTop = element.offset().top;
            elementHeight = element[0].offsetHeight;
            if (elementHeight + elementTop > scrollContainer.scrollTop + windowHeight) {
                scrollContainer.scrollTop = elementHeight + elementTop - windowHeight;
            }
        },
        persistScrollTop: function(doc) {
            persistedScrollTop = Dom.scrollContainer(doc).scrollTop;
        },
        restoreScrollTop: function(doc) {
            Dom.scrollContainer(doc).scrollTop = persistedScrollTop;
        },
        insertAt: function(parent, newElement, position) {
            parent.insertBefore(newElement, parent.childNodes[position] || null);
        },
        insertBefore: function(newElement, referenceElement) {
            if (referenceElement.parentNode) {
                return referenceElement.parentNode.insertBefore(newElement, referenceElement);
            } else {
                return referenceElement;
            }
        },
        insertAfter: function(newElement, referenceElement) {
            return referenceElement.parentNode.insertBefore(newElement, referenceElement.nextSibling);
        },
        remove: function(node) {
            node.parentNode.removeChild(node);
        },
        trim: function(parent) {
            for (var i = parent.childNodes.length - 1; i >= 0; i--) {
                var node = parent.childNodes[i];
                if (Dom.isDataNode(node)) {
                    if (!node.nodeValue.replace(bom, "").length) {
                        Dom.remove(node);
                    }
                    if (Dom.isWhitespace(node)) {
                        Dom.insertBefore(node, parent);
                    }
                } else if (node.className != KMARKER) {
                    Dom.trim(node);
                    if (!node.childNodes.length && !Dom.isEmpty(node)) {
                        Dom.remove(node);
                    }
                }
            }
            return parent;
        },
        parentOfType: function(node, tags) {
            do {
                node = node.parentNode;
            } while (node && !Dom.ofType(node, tags));
            return node;
        },
        ofType: function(node, tags) {
            return $.inArray(Dom.name(node), tags) >= 0;
        },
        changeTag: function(referenceElement, tagName) {
            var newElement = Dom.create(referenceElement.ownerDocument, tagName), attributes = referenceElement.attributes, i, len, name, value, attribute;
            for (i = 0, len = attributes.length; i < len; i++) {
                attribute = attributes[i];
                if (attribute.specified) {
                    // IE < 8 cannot set class or style via setAttribute
                    name = attribute.nodeName;
                    value = attribute.nodeValue;
                    if (name == CLASS) {
                        newElement.className = value;
                    } else if (name == STYLE) {
                        newElement.style.cssText = referenceElement.style.cssText;
                    } else {
                        newElement.setAttribute(name, value);
                    }
                }
            }
            while (referenceElement.firstChild) {
                newElement.appendChild(referenceElement.firstChild);
            }
            Dom.insertBefore(newElement, referenceElement);
            Dom.remove(referenceElement);
            return newElement;
        },
        wrap: function(node, wrapper) {
            Dom.insertBefore(wrapper, node);
            wrapper.appendChild(node);
            return wrapper;
        },
        unwrap: function(node) {
            var parent = node.parentNode;
            while (node.firstChild) {
                parent.insertBefore(node.firstChild, node);
            }
            parent.removeChild(node);
        },
        create: function(document, tagName, attributes) {
            return Dom.attr(document.createElement(tagName), attributes);
        },
        attr: function(element, attributes) {
            attributes = extend({}, attributes);
            if (attributes && STYLE in attributes) {
                Dom.style(element, attributes.style);
                delete attributes.style;
            }
            for (var attr in attributes) {
                if (attributes[attr] === null) {
                    element.removeAttribute(attr);
                    delete attributes[attr];
                }
            }
            return extend(element, attributes);
        },
        style: function(node, value) {
            $(node).css(value || {});
        },
        unstyle: function(node, value) {
            for (var key in value) {
                if (key == FLOAT) {
                    key = $.support.cssFloat ? CSSFLOAT : STYLEFLOAT;
                }
                node.style[key] = "";
            }
            if (node.style.cssText === "") {
                node.removeAttribute(STYLE);
            }
        },
        inlineStyle: function(document, name, attributes) {
            var span = $(Dom.create(document, name, attributes)), style;
            document.body.appendChild(span[0]);
            style = map(cssAttributes, function(value) {
                if (browser.msie && value == "line-height" && span.css(value) == "1px") {
                    return "line-height:1.5";
                } else {
                    return value + ":" + span.css(value);
                }
            }).join(";");
            span.remove();
            return style;
        },
        removeClass: function(node, classNames) {
            var className = " " + node.className + " ", classes = classNames.split(" "), i, len;
            for (i = 0, len = classes.length; i < len; i++) {
                className = className.replace(" " + classes[i] + " ", " ");
            }
            className = $.trim(className);
            if (className.length) {
                node.className = className;
            } else {
                node.removeAttribute(CLASS);
            }
        },
        commonAncestor: function() {
            var count = arguments.length, paths = [], minPathLength = Infinity, output = null, i, ancestors, node, first, j;
            if (!count) {
                return null;
            }
            if (count == 1) {
                return arguments[0];
            }
            for (i = 0; i < count; i++) {
                ancestors = [];
                node = arguments[i];
                while (node) {
                    ancestors.push(node);
                    node = node.parentNode;
                }
                paths.push(ancestors.reverse());
                minPathLength = Math.min(minPathLength, ancestors.length);
            }
            if (count == 1) {
                return paths[0][0];
            }
            for (i = 0; i < minPathLength; i++) {
                first = paths[0][i];
                for (j = 1; j < count; j++) {
                    if (first != paths[j][i]) {
                        return output;
                    }
                }
                output = first;
            }
            return output;
        }
    };
    kendo.ui.editor.Dom = Dom;
})(window.kendo.jQuery);

(function($, undefined) {
    // Imports ================================================================
    var kendo = window.kendo, Editor = kendo.ui.editor, dom = Editor.Dom, extend = $.extend;
    var fontSizeMappings = "xx-small,x-small,small,medium,large,x-large,xx-large".split(","), quoteRe = /"/g, brRe = /<br[^>]*>/i, emptyPRe = /<p><\/p>/i, cssDeclaration = /([\w|\-]+)\s*:\s*([^;]+);?/i;
    var Serializer = {
        domToXhtml: function(root) {
            var result = [];
            var tagMap = {
                "telerik:script": {
                    start: function(node) {
                        result.push("<script");
                        attr(node);
                        result.push(">");
                    },
                    end: function() {
                        result.push("</script>");
                    }
                },
                b: {
                    start: function() {
                        result.push("<strong>");
                    },
                    end: function() {
                        result.push("</strong>");
                    }
                },
                i: {
                    start: function() {
                        result.push("<em>");
                    },
                    end: function() {
                        result.push("</em>");
                    }
                },
                u: {
                    start: function() {
                        result.push('<span style="text-decoration:underline;">');
                    },
                    end: function() {
                        result.push("</span>");
                    }
                },
                iframe: {
                    start: function(node) {
                        result.push("<iframe");
                        attr(node);
                        result.push(">");
                    },
                    end: function() {
                        result.push("</iframe>");
                    }
                },
                font: {
                    start: function(node) {
                        result.push('<span style="');
                        var color = node.getAttribute("color");
                        var size = fontSizeMappings[node.getAttribute("size")];
                        var face = node.getAttribute("face");
                        if (color) {
                            result.push("color:");
                            result.push(dom.toHex(color));
                            result.push(";");
                        }
                        if (face) {
                            result.push("font-face:");
                            result.push(face);
                            result.push(";");
                        }
                        if (size) {
                            result.push("font-size:");
                            result.push(size);
                            result.push(";");
                        }
                        result.push('">');
                    },
                    end: function() {
                        result.push("</span>");
                    }
                }
            };
            function attr(node) {
                var specifiedAttributes = [], attributes = node.attributes, attribute, i, l, trim = $.trim;
                if (dom.is(node, "img")) {
                    var width = node.style.width, height = node.style.height, $node = $(node);
                    if (width) {
                        $node.attr("width", parseInt(width, 10));
                        dom.unstyle(node, {
                            width: undefined
                        });
                    }
                    if (height) {
                        $node.attr("height", parseInt(height, 10));
                        dom.unstyle(node, {
                            height: undefined
                        });
                    }
                }
                for (i = 0, l = attributes.length; i < l; i++) {
                    attribute = attributes[i];
                    var name = attribute.nodeName;
                    // In IE < 8 the 'value' attribute is not returned as 'specified'. The same goes for type="text"
                    if (attribute.specified || name == "value" && !node.value || name == "type" && attribute.nodeValue == "text") {
                        // altHtml is injected by IE8 when an <object> tag is used in the Editor
                        if (name.indexOf("_moz") < 0 && name != "complete" && name != "altHtml") {
                            specifiedAttributes.push(attribute);
                        }
                    }
                }
                if (!specifiedAttributes.length) {
                    return;
                }
                specifiedAttributes.sort(function(a, b) {
                    return a.nodeName > b.nodeName ? 1 : a.nodeName < b.nodeName ? -1 : 0;
                });
                for (i = 0, l = specifiedAttributes.length; i < l; i++) {
                    attribute = specifiedAttributes[i];
                    var attributeName = attribute.nodeName;
                    var attributeValue = attribute.nodeValue;
                    result.push(" ");
                    result.push(attributeName);
                    result.push('="');
                    if (attributeName == "style") {
                        // In IE < 8 the style attribute does not return proper nodeValue
                        var css = trim(attributeValue || node.style.cssText).split(";");
                        for (var cssIndex = 0, len = css.length; cssIndex < len; cssIndex++) {
                            var pair = css[cssIndex];
                            if (pair.length) {
                                var match = cssDeclaration.exec(pair);
                                var property = trim(match[1].toLowerCase()), value = trim(match[2]);
                                if (property == "font-size-adjust" || property == "font-stretch") {
                                    continue;
                                }
                                if (property.indexOf("color") >= 0) {
                                    value = dom.toHex(value);
                                }
                                if (property.indexOf("font") >= 0) {
                                    value = value.replace(quoteRe, "'");
                                }
                                result.push(property);
                                result.push(":");
                                result.push(value);
                                result.push(";");
                            }
                        }
                    } else if (attributeName == "src" || attributeName == "href") {
                        result.push(node.getAttribute(attributeName, 2));
                    } else {
                        result.push(dom.fillAttrs[attributeName] ? attributeName : attributeValue);
                    }
                    result.push('"');
                }
            }
            function children(node, skip) {
                for (var childNode = node.firstChild; childNode; childNode = childNode.nextSibling) {
                    child(childNode, skip);
                }
            }
            function child(node, skip) {
                var nodeType = node.nodeType, tagName, mapper, parent, value, previous;
                if (nodeType == 1) {
                    tagName = dom.name(node);
                    if (!tagName || (node.attributes._moz_dirty || node.attributes._moz_editor_bogus_node) && dom.is(node, "br") || node.className == "k-marker") {
                        return;
                    }
                    mapper = tagMap[tagName];
                    if (mapper) {
                        mapper.start(node);
                        children(node);
                        mapper.end(node);
                        return;
                    }
                    result.push("<");
                    result.push(tagName);
                    attr(node);
                    if (dom.empty[tagName]) {
                        result.push(" />");
                    } else {
                        result.push(">");
                        children(node, skip || dom.is(node, "pre"));
                        result.push("</");
                        result.push(tagName);
                        result.push(">");
                    }
                } else if (nodeType == 3) {
                    value = node.nodeValue;
                    if (!skip && $.support.leadingWhitespace) {
                        parent = node.parentNode;
                        previous = node.previousSibling;
                        if (!previous) {
                            previous = (dom.isInline(parent) ? parent : node).previousSibling;
                        }
                        if (!previous || previous.innerHTML === "" || dom.isBlock(previous)) {
                            value = value.replace(/^[\r\n\v\f\t ]+/, "");
                        }
                        value = value.replace(/ +/, " ");
                    }
                    result.push(dom.encode(value));
                } else if (nodeType == 4) {
                    result.push("<![CDATA[");
                    result.push(node.data);
                    result.push("]]>");
                } else if (nodeType == 8) {
                    if (node.data.indexOf("[CDATA[") < 0) {
                        result.push("<!--");
                        result.push(node.data);
                        result.push("-->");
                    } else {
                        result.push("<!");
                        result.push(node.data);
                        result.push(">");
                    }
                }
            }
            children(root);
            result = result.join("");
            // if serialized dom contains only whitespace elements, consider it empty (required field validation)
            if (result.replace(brRe, "").replace(emptyPRe, "") === "") {
                return "";
            }
            return result;
        }
    };
    extend(Editor, {
        Serializer: Serializer
    });
})(window.kendo.jQuery);

(function($) {
    // Imports ================================================================
    var kendo = window.kendo, Class = kendo.Class, extend = $.extend, Editor = kendo.ui.editor, browser = kendo.support.browser, dom = Editor.Dom, findNodeIndex = dom.findNodeIndex, isDataNode = dom.isDataNode, findClosestAncestor = dom.findClosestAncestor, getNodeLength = dom.getNodeLength, normalize = dom.normalize;
    var SelectionUtils = {
        selectionFromWindow: function(window) {
            if (!("getSelection" in window)) {
                return new W3CSelection(window.document);
            }
            return window.getSelection();
        },
        selectionFromRange: function(range) {
            var rangeDocument = RangeUtils.documentFromRange(range);
            return SelectionUtils.selectionFromDocument(rangeDocument);
        },
        selectionFromDocument: function(document) {
            return SelectionUtils.selectionFromWindow(dom.windowFromDocument(document));
        }
    };
    var W3CRange = Class.extend({
        init: function(doc) {
            $.extend(this, {
                ownerDocument: doc,
                /* not part of the spec; used when cloning ranges, traversing the dom and creating fragments */
                startContainer: doc,
                endContainer: doc,
                commonAncestorContainer: doc,
                startOffset: 0,
                endOffset: 0,
                collapsed: true
            });
        },
        // Positioning Methods
        setStart: function(node, offset) {
            this.startContainer = node;
            this.startOffset = offset;
            updateRangeProperties(this);
            fixIvalidRange(this, true);
        },
        setEnd: function(node, offset) {
            this.endContainer = node;
            this.endOffset = offset;
            updateRangeProperties(this);
            fixIvalidRange(this, false);
        },
        setStartBefore: function(node) {
            this.setStart(node.parentNode, findNodeIndex(node));
        },
        setStartAfter: function(node) {
            this.setStart(node.parentNode, findNodeIndex(node) + 1);
        },
        setEndBefore: function(node) {
            this.setEnd(node.parentNode, findNodeIndex(node));
        },
        setEndAfter: function(node) {
            this.setEnd(node.parentNode, findNodeIndex(node) + 1);
        },
        selectNode: function(node) {
            this.setStartBefore(node);
            this.setEndAfter(node);
        },
        selectNodeContents: function(node) {
            this.setStart(node, 0);
            this.setEnd(node, node[node.nodeType === 1 ? "childNodes" : "nodeValue"].length);
        },
        collapse: function(toStart) {
            var that = this;
            if (toStart) {
                that.setEnd(that.startContainer, that.startOffset);
            } else {
                that.setStart(that.endContainer, that.endOffset);
            }
        },
        // Editing Methods
        deleteContents: function() {
            var that = this, range = that.cloneRange();
            if (that.startContainer != that.commonAncestorContainer) {
                that.setStartAfter(findClosestAncestor(that.commonAncestorContainer, that.startContainer));
            }
            that.collapse(true);
            (function deleteSubtree(iterator) {
                while (iterator.next()) {
                    if (iterator.hasPartialSubtree()) {
                        deleteSubtree(iterator.getSubtreeIterator());
                    } else {
                        iterator.remove();
                    }
                }
            })(new RangeIterator(range));
        },
        cloneContents: function() {
            // clone subtree
            var document = RangeUtils.documentFromRange(this);
            return function cloneSubtree(iterator) {
                var node, frag = document.createDocumentFragment();
                while (node = iterator.next()) {
                    node = node.cloneNode(!iterator.hasPartialSubtree());
                    if (iterator.hasPartialSubtree()) {
                        node.appendChild(cloneSubtree(iterator.getSubtreeIterator()));
                    }
                    frag.appendChild(node);
                }
                return frag;
            }(new RangeIterator(this));
        },
        extractContents: function() {
            var that = this, range = that.cloneRange();
            if (that.startContainer != that.commonAncestorContainer) {
                that.setStartAfter(findClosestAncestor(that.commonAncestorContainer, that.startContainer));
            }
            that.collapse(true);
            var document = RangeUtils.documentFromRange(that);
            return function extractSubtree(iterator) {
                var node, frag = document.createDocumentFragment();
                while (node = iterator.next()) {
                    if (iterator.hasPartialSubtree()) {
                        node = node.cloneNode(false);
                        node.appendChild(extractSubtree(iterator.getSubtreeIterator()));
                    } else {
                        iterator.remove(that.originalRange);
                    }
                    frag.appendChild(node);
                }
                return frag;
            }(new RangeIterator(range));
        },
        insertNode: function(node) {
            var that = this;
            if (isDataNode(that.startContainer)) {
                if (that.startOffset != that.startContainer.nodeValue.length) {
                    dom.splitDataNode(that.startContainer, that.startOffset);
                }
                dom.insertAfter(node, that.startContainer);
            } else {
                dom.insertAt(that.startContainer, node, that.startOffset);
            }
            that.setStart(that.startContainer, that.startOffset);
        },
        cloneRange: function() {
            // fast copy
            return $.extend(new W3CRange(this.ownerDocument), {
                startContainer: this.startContainer,
                endContainer: this.endContainer,
                commonAncestorContainer: this.commonAncestorContainer,
                startOffset: this.startOffset,
                endOffset: this.endOffset,
                collapsed: this.collapsed,
                originalRange: this
            });
        },
        // used for debug purposes
        toString: function() {
            var startNodeName = this.startContainer.nodeName, endNodeName = this.endContainer.nodeName;
            return [ startNodeName == "#text" ? this.startContainer.nodeValue : startNodeName, "(", this.startOffset, ") : ", endNodeName == "#text" ? this.endContainer.nodeValue : endNodeName, "(", this.endOffset, ")" ].join("");
        }
    });
    /* can be used in Range.compareBoundaryPoints if we need it one day */
    function compareBoundaries(start, end, startOffset, endOffset) {
        if (start == end) {
            return endOffset - startOffset;
        }
        // end is child of start
        var container = end;
        while (container && container.parentNode != start) {
            container = container.parentNode;
        }
        if (container) {
            return findNodeIndex(container) - startOffset;
        }
        // start is child of end
        container = start;
        while (container && container.parentNode != end) {
            container = container.parentNode;
        }
        if (container) {
            return endOffset - findNodeIndex(container) - 1;
        }
        // deep traversal
        var root = dom.commonAncestor(start, end);
        var startAncestor = start;
        while (startAncestor && startAncestor.parentNode != root) {
            startAncestor = startAncestor.parentNode;
        }
        if (!startAncestor) {
            startAncestor = root;
        }
        var endAncestor = end;
        while (endAncestor && endAncestor.parentNode != root) {
            endAncestor = endAncestor.parentNode;
        }
        if (!endAncestor) {
            endAncestor = root;
        }
        if (startAncestor == endAncestor) {
            return 0;
        }
        return findNodeIndex(endAncestor) - findNodeIndex(startAncestor);
    }
    function fixIvalidRange(range, toStart) {
        function isInvalidRange(range) {
            try {
                return compareBoundaries(range.startContainer, range.endContainer, range.startOffset, range.endOffset) < 0;
            } catch (ex) {
                // range was initially invalid (e.g. when cloned from invalid range) - it must be fixed
                return true;
            }
        }
        if (isInvalidRange(range)) {
            if (toStart) {
                range.commonAncestorContainer = range.endContainer = range.startContainer;
                range.endOffset = range.startOffset;
            } else {
                range.commonAncestorContainer = range.startContainer = range.endContainer;
                range.startOffset = range.endOffset;
            }
            range.collapsed = true;
        }
    }
    function updateRangeProperties(range) {
        range.collapsed = range.startContainer == range.endContainer && range.startOffset == range.endOffset;
        var node = range.startContainer;
        while (node && node != range.endContainer && !dom.isAncestorOf(node, range.endContainer)) {
            node = node.parentNode;
        }
        range.commonAncestorContainer = node;
    }
    var RangeIterator = Class.extend({
        init: function(range) {
            $.extend(this, {
                range: range,
                _current: null,
                _next: null,
                _end: null
            });
            if (range.collapsed) {
                return;
            }
            var root = range.commonAncestorContainer;
            this._next = range.startContainer == root && !isDataNode(range.startContainer) ? range.startContainer.childNodes[range.startOffset] : findClosestAncestor(root, range.startContainer);
            this._end = range.endContainer == root && !isDataNode(range.endContainer) ? range.endContainer.childNodes[range.endOffset] : findClosestAncestor(root, range.endContainer).nextSibling;
        },
        hasNext: function() {
            return !!this._next;
        },
        next: function() {
            var that = this, current = that._current = that._next;
            that._next = that._current && that._current.nextSibling != that._end ? that._current.nextSibling : null;
            if (isDataNode(that._current)) {
                if (that.range.endContainer == that._current) {
                    current = current.cloneNode(true);
                    current.deleteData(that.range.endOffset, current.length - that.range.endOffset);
                }
                if (that.range.startContainer == that._current) {
                    current = current.cloneNode(true);
                    current.deleteData(0, that.range.startOffset);
                }
            }
            return current;
        },
        traverse: function(callback) {
            var that = this, current;
            function next() {
                that._current = that._next;
                that._next = that._current && that._current.nextSibling != that._end ? that._current.nextSibling : null;
                return that._current;
            }
            while (current = next()) {
                if (that.hasPartialSubtree()) {
                    that.getSubtreeIterator().traverse(callback);
                } else {
                    callback(current);
                }
            }
            return current;
        },
        remove: function(originalRange) {
            var that = this, inStartContainer = that.range.startContainer == that._current, inEndContainer = that.range.endContainer == that._current, start, end, delta;
            if (isDataNode(that._current) && (inStartContainer || inEndContainer)) {
                start = inStartContainer ? that.range.startOffset : 0;
                end = inEndContainer ? that.range.endOffset : that._current.length;
                delta = end - start;
                if (originalRange && (inStartContainer || inEndContainer)) {
                    if (that._current == originalRange.startContainer && start <= originalRange.startOffset) {
                        originalRange.startOffset -= delta;
                    }
                    if (that._current == originalRange.endContainer && end <= originalRange.endOffset) {
                        originalRange.endOffset -= delta;
                    }
                }
                that._current.deleteData(start, delta);
            } else {
                var parent = that._current.parentNode;
                if (originalRange && (that.range.startContainer == parent || that.range.endContainer == parent)) {
                    var nodeIndex = findNodeIndex(that._current);
                    if (parent == originalRange.startContainer && nodeIndex <= originalRange.startOffset) {
                        originalRange.startOffset -= 1;
                    }
                    if (parent == originalRange.endContainer && nodeIndex < originalRange.endOffset) {
                        originalRange.endOffset -= 1;
                    }
                }
                dom.remove(that._current);
            }
        },
        hasPartialSubtree: function() {
            return !isDataNode(this._current) && (dom.isAncestorOrSelf(this._current, this.range.startContainer) || dom.isAncestorOrSelf(this._current, this.range.endContainer));
        },
        getSubtreeIterator: function() {
            var that = this, subRange = that.range.cloneRange();
            subRange.selectNodeContents(that._current);
            if (dom.isAncestorOrSelf(that._current, that.range.startContainer)) {
                subRange.setStart(that.range.startContainer, that.range.startOffset);
            }
            if (dom.isAncestorOrSelf(that._current, that.range.endContainer)) {
                subRange.setEnd(that.range.endContainer, that.range.endOffset);
            }
            return new RangeIterator(subRange);
        }
    });
    var W3CSelection = Class.extend({
        init: function(doc) {
            this.ownerDocument = doc;
            this.rangeCount = 1;
        },
        addRange: function(range) {
            var textRange = this.ownerDocument.body.createTextRange();
            // end container should be adopted first in order to prevent selection with negative length
            adoptContainer(textRange, range, false);
            adoptContainer(textRange, range, true);
            textRange.select();
        },
        removeAllRanges: function() {
            this.ownerDocument.selection.empty();
        },
        getRangeAt: function() {
            var textRange, range = new W3CRange(this.ownerDocument), selection = this.ownerDocument.selection, element;
            try {
                textRange = selection.createRange();
                element = textRange.item ? textRange.item(0) : textRange.parentElement();
                if (element.ownerDocument != this.ownerDocument) {
                    return range;
                }
            } catch (ex) {
                return range;
            }
            if (selection.type == "Control") {
                range.selectNode(textRange.item(0));
            } else {
                adoptEndPoint(textRange, range, true);
                adoptEndPoint(textRange, range, false);
                if (range.startContainer.nodeType == 9) {
                    range.setStart(range.endContainer, range.startOffset);
                }
                if (range.endContainer.nodeType == 9) {
                    range.setEnd(range.startContainer, range.endOffset);
                }
                if (textRange.compareEndPoints("StartToEnd", textRange) === 0) {
                    range.collapse(false);
                }
                var startContainer = range.startContainer, endContainer = range.endContainer, body = this.ownerDocument.body;
                if (!range.collapsed && range.startOffset === 0 && range.endOffset == getNodeLength(range.endContainer) && // check for full body selection
                !(startContainer == endContainer && isDataNode(startContainer) && startContainer.parentNode == body)) {
                    // but not when single textnode is selected
                    var movedStart = false, movedEnd = false;
                    while (findNodeIndex(startContainer) === 0 && startContainer == startContainer.parentNode.firstChild && startContainer != body) {
                        startContainer = startContainer.parentNode;
                        movedStart = true;
                    }
                    while (findNodeIndex(endContainer) == getNodeLength(endContainer.parentNode) - 1 && endContainer == endContainer.parentNode.lastChild && endContainer != body) {
                        endContainer = endContainer.parentNode;
                        movedEnd = true;
                    }
                    if (startContainer == body && endContainer == body && movedStart && movedEnd) {
                        range.setStart(startContainer, 0);
                        range.setEnd(endContainer, getNodeLength(body));
                    }
                }
            }
            return range;
        }
    });
    function adoptContainer(textRange, range, start) {
        // find anchor node and offset
        var container = range[start ? "startContainer" : "endContainer"];
        var offset = range[start ? "startOffset" : "endOffset"], textOffset = 0;
        var anchorNode = isDataNode(container) ? container : container.childNodes[offset] || null;
        var anchorParent = isDataNode(container) ? container.parentNode : container;
        // visible data nodes need a text offset
        if (container.nodeType == 3 || container.nodeType == 4) {
            textOffset = offset;
        }
        // create a cursor element node to position range (since we can't select text nodes)
        var cursorNode = anchorParent.insertBefore(dom.create(range.ownerDocument, "a"), anchorNode);
        var cursor = range.ownerDocument.body.createTextRange();
        cursor.moveToElementText(cursorNode);
        dom.remove(cursorNode);
        cursor[start ? "moveStart" : "moveEnd"]("character", textOffset);
        cursor.collapse(false);
        textRange.setEndPoint(start ? "StartToStart" : "EndToStart", cursor);
    }
    function adoptEndPoint(textRange, range, start) {
        var cursorNode = dom.create(range.ownerDocument, "a"), cursor = textRange.duplicate();
        cursor.collapse(start);
        var parent = cursor.parentElement();
        do {
            parent.insertBefore(cursorNode, cursorNode.previousSibling);
            cursor.moveToElementText(cursorNode);
        } while (cursor.compareEndPoints(start ? "StartToStart" : "StartToEnd", textRange) > 0 && cursorNode.previousSibling);
        cursor.setEndPoint(start ? "EndToStart" : "EndToEnd", textRange);
        var target = cursorNode.nextSibling;
        if (!target) {
            // at end of text node
            target = cursorNode.previousSibling;
            if (target && isDataNode(target)) {
                // in case of collapsed range in empty tag
                range.setEnd(target, target.nodeValue.length);
                dom.remove(cursorNode);
            } else {
                range.selectNodeContents(parent);
                dom.remove(cursorNode);
                range.endOffset -= 1;
            }
            return;
        }
        dom.remove(cursorNode);
        if (isDataNode(target)) {
            range[start ? "setStart" : "setEnd"](target, cursor.text.length);
        } else {
            range[start ? "setStartBefore" : "setEndBefore"](target);
        }
    }
    var RangeEnumerator = Class.extend({
        init: function(range) {
            this.enumerate = function() {
                var nodes = [];
                function visit(node) {
                    if (dom.is(node, "img") || node.nodeType == 3 && !dom.isWhitespace(node)) {
                        nodes.push(node);
                    } else {
                        node = node.firstChild;
                        while (node) {
                            visit(node);
                            node = node.nextSibling;
                        }
                    }
                }
                new RangeIterator(range).traverse(visit);
                return nodes;
            };
        }
    });
    var RestorePoint = Class.extend({
        init: function(range) {
            var that = this;
            that.range = range;
            that.rootNode = RangeUtils.documentFromRange(range);
            that.body = that.rootNode.body;
            that.html = that.body.innerHTML;
            that.startContainer = that.nodeToPath(range.startContainer);
            that.endContainer = that.nodeToPath(range.endContainer);
            that.startOffset = that.offset(range.startContainer, range.startOffset);
            that.endOffset = that.offset(range.endContainer, range.endOffset);
        },
        index: function(node) {
            var result = 0, lastType = node.nodeType;
            while (node = node.previousSibling) {
                var nodeType = node.nodeType;
                if (nodeType != 3 || lastType != nodeType) {
                    result++;
                }
                lastType = nodeType;
            }
            return result;
        },
        offset: function(node, value) {
            if (node.nodeType == 3) {
                while ((node = node.previousSibling) && node.nodeType == 3) {
                    value += node.nodeValue.length;
                }
            }
            return value;
        },
        nodeToPath: function(node) {
            var path = [];
            while (node != this.rootNode) {
                path.push(this.index(node));
                node = node.parentNode;
            }
            return path;
        },
        toRangePoint: function(range, start, path, denormalizedOffset) {
            var node = this.rootNode, length = path.length, offset = denormalizedOffset;
            while (length--) {
                node = node.childNodes[path[length]];
            }
            while (node.nodeType == 3 && node.nodeValue.length < offset) {
                offset -= node.nodeValue.length;
                node = node.nextSibling;
            }
            range[start ? "setStart" : "setEnd"](node, offset);
        },
        toRange: function() {
            var that = this, result = that.range.cloneRange();
            that.toRangePoint(result, true, that.startContainer, that.startOffset);
            that.toRangePoint(result, false, that.endContainer, that.endOffset);
            return result;
        }
    });
    var Marker = Class.extend({
        init: function() {
            this.caret = null;
        },
        addCaret: function(range) {
            var that = this;
            that.caret = dom.create(RangeUtils.documentFromRange(range), "span", {
                className: "k-marker"
            });
            range.insertNode(that.caret);
            range.selectNode(that.caret);
            return that.caret;
        },
        removeCaret: function(range) {
            var that = this, previous = that.caret.previousSibling, startOffset = 0;
            if (previous) {
                startOffset = isDataNode(previous) ? previous.nodeValue.length : findNodeIndex(previous);
            }
            var container = that.caret.parentNode;
            var containerIndex = previous ? findNodeIndex(previous) : 0;
            dom.remove(that.caret);
            normalize(container);
            var node = container.childNodes[containerIndex];
            if (isDataNode(node)) {
                range.setStart(node, startOffset);
            } else if (node) {
                var textNode = dom.lastTextNode(node);
                if (textNode) {
                    range.setStart(textNode, textNode.nodeValue.length);
                } else {
                    range[previous ? "setStartAfter" : "setStartBefore"](node);
                }
            } else {
                if (!browser.msie && !container.innerHTML) {
                    container.innerHTML = '<br _moz_dirty="" />';
                }
                range.selectNodeContents(container);
            }
            range.collapse(true);
        },
        add: function(range, expand) {
            var that = this;
            if (expand && range.collapsed) {
                that.addCaret(range);
                range = RangeUtils.expand(range);
            }
            var rangeBoundary = range.cloneRange();
            rangeBoundary.collapse(false);
            that.end = dom.create(RangeUtils.documentFromRange(range), "span", {
                className: "k-marker"
            });
            rangeBoundary.insertNode(that.end);
            rangeBoundary = range.cloneRange();
            rangeBoundary.collapse(true);
            that.start = that.end.cloneNode(true);
            rangeBoundary.insertNode(that.start);
            range.setStartBefore(that.start);
            range.setEndAfter(that.end);
            normalize(range.commonAncestorContainer);
            return range;
        },
        remove: function(range) {
            var that = this, start = that.start, end = that.end, shouldNormalizeStart, shouldNormalizeEnd, shouldNormalize;
            normalize(range.commonAncestorContainer);
            while (!start.nextSibling && start.parentNode) {
                start = start.parentNode;
            }
            while (!end.previousSibling && end.parentNode) {
                end = end.parentNode;
            }
            // merely accessing the siblings will solve range issues in IE
            shouldNormalizeStart = start.previousSibling && start.previousSibling.nodeType == 3 && start.nextSibling && start.nextSibling.nodeType == 3;
            shouldNormalizeEnd = end.previousSibling && end.previousSibling.nodeType == 3 && end.nextSibling && end.nextSibling.nodeType == 3;
            shouldNormalize = shouldNormalizeStart && shouldNormalizeEnd;
            start = start.nextSibling;
            end = end.previousSibling;
            var collapsed = false;
            var collapsedToStart = false;
            // collapsed range
            if (start == that.end) {
                collapsedToStart = !!that.start.previousSibling;
                start = end = that.start.previousSibling || that.end.nextSibling;
                collapsed = true;
            }
            dom.remove(that.start);
            dom.remove(that.end);
            if (!start || !end) {
                range.selectNodeContents(range.commonAncestorContainer);
                range.collapse(true);
                return;
            }
            var startOffset = collapsed ? isDataNode(start) ? start.nodeValue.length : start.childNodes.length : 0;
            var endOffset = isDataNode(end) ? end.nodeValue.length : end.childNodes.length;
            if (start.nodeType == 3) {
                while (start.previousSibling && start.previousSibling.nodeType == 3) {
                    start = start.previousSibling;
                    startOffset += start.nodeValue.length;
                }
            }
            if (end.nodeType == 3) {
                while (end.previousSibling && end.previousSibling.nodeType == 3) {
                    end = end.previousSibling;
                    endOffset += end.nodeValue.length;
                }
            }
            var startIndex = findNodeIndex(start), startParent = start.parentNode;
            var endIndex = findNodeIndex(end), endParent = end.parentNode;
            for (var startPointer = start; startPointer.previousSibling; startPointer = startPointer.previousSibling) {
                if (startPointer.nodeType == 3 && startPointer.previousSibling.nodeType == 3) {
                    startIndex--;
                }
            }
            for (var endPointer = end; endPointer.previousSibling; endPointer = endPointer.previousSibling) {
                if (endPointer.nodeType == 3 && endPointer.previousSibling.nodeType == 3) {
                    endIndex--;
                }
            }
            normalize(startParent);
            if (start.nodeType == 3) {
                start = startParent.childNodes[startIndex];
            }
            normalize(endParent);
            if (end.nodeType == 3) {
                end = endParent.childNodes[endIndex];
            }
            if (collapsed) {
                if (start.nodeType == 3) {
                    range.setStart(start, startOffset);
                } else {
                    range[collapsedToStart ? "setStartAfter" : "setStartBefore"](start);
                }
                range.collapse(true);
            } else {
                if (start.nodeType == 3) {
                    range.setStart(start, startOffset);
                } else {
                    range.setStartBefore(start);
                }
                if (end.nodeType == 3) {
                    range.setEnd(end, endOffset);
                } else {
                    range.setEndAfter(end);
                }
            }
            if (that.caret) {
                that.removeCaret(range);
            }
        }
    });
    var boundary = /[\u0009-\u000d]|\u0020|\u00a0|\ufeff|\.|,|;|:|!|\(|\)|\?/;
    var RangeUtils = {
        nodes: function(range) {
            var nodes = RangeUtils.textNodes(range);
            if (!nodes.length) {
                range.selectNodeContents(range.commonAncestorContainer);
                nodes = RangeUtils.textNodes(range);
                if (!nodes.length) {
                    nodes = dom.significantChildNodes(range.commonAncestorContainer);
                }
            }
            return nodes;
        },
        textNodes: function(range) {
            return new RangeEnumerator(range).enumerate();
        },
        documentFromRange: function(range) {
            var startContainer = range.startContainer;
            return startContainer.nodeType == 9 ? startContainer : startContainer.ownerDocument;
        },
        createRange: function(document) {
            if (browser.msie && browser.version < 9) {
                return new W3CRange(document);
            }
            return document.createRange();
        },
        selectRange: function(range) {
            var image = RangeUtils.image(range);
            if (image) {
                range.setStartAfter(image);
                range.setEndAfter(image);
            }
            var selection = SelectionUtils.selectionFromRange(range);
            selection.removeAllRanges();
            selection.addRange(range);
        },
        split: function(range, node, trim) {
            function partition(start) {
                var partitionRange = range.cloneRange();
                partitionRange.collapse(start);
                partitionRange[start ? "setStartBefore" : "setEndAfter"](node);
                var contents = partitionRange.extractContents();
                if (trim) {
                    contents = dom.trim(contents);
                }
                dom[start ? "insertBefore" : "insertAfter"](contents, node);
            }
            partition(true);
            partition(false);
        },
        getMarkers: function(range) {
            var markers = [];
            new RangeIterator(range).traverse(function(node) {
                if (node.className == "k-marker") {
                    markers.push(node);
                }
            });
            return markers;
        },
        image: function(range) {
            var nodes = [];
            new RangeIterator(range).traverse(function(node) {
                if (dom.is(node, "img")) {
                    nodes.push(node);
                }
            });
            if (nodes.length == 1) {
                return nodes[0];
            }
        },
        expand: function(range) {
            var result = range.cloneRange();
            var startContainer = result.startContainer.childNodes[result.startOffset === 0 ? 0 : result.startOffset - 1];
            var endContainer = result.endContainer.childNodes[result.endOffset];
            if (!isDataNode(startContainer) || !isDataNode(endContainer)) {
                return result;
            }
            var beforeCaret = startContainer.nodeValue;
            var afterCaret = endContainer.nodeValue;
            if (!beforeCaret || !afterCaret) {
                return result;
            }
            var startOffset = beforeCaret.split("").reverse().join("").search(boundary);
            var endOffset = afterCaret.search(boundary);
            if (!startOffset || !endOffset) {
                return result;
            }
            endOffset = endOffset == -1 ? afterCaret.length : endOffset;
            startOffset = startOffset == -1 ? 0 : beforeCaret.length - startOffset;
            result.setStart(startContainer, startOffset);
            result.setEnd(endContainer, endOffset);
            return result;
        },
        isExpandable: function(range) {
            var node = range.startContainer;
            var rangeDocument = RangeUtils.documentFromRange(range);
            if (node == rangeDocument || node == rangeDocument.body) {
                return false;
            }
            var result = range.cloneRange();
            var value = node.nodeValue;
            if (!value) {
                return false;
            }
            var beforeCaret = value.substring(0, result.startOffset);
            var afterCaret = value.substring(result.startOffset);
            var startOffset = 0, endOffset = 0;
            if (beforeCaret) {
                startOffset = beforeCaret.split("").reverse().join("").search(boundary);
            }
            if (afterCaret) {
                endOffset = afterCaret.search(boundary);
            }
            return startOffset && endOffset;
        }
    };
    extend(Editor, {
        SelectionUtils: SelectionUtils,
        W3CRange: W3CRange,
        RangeIterator: RangeIterator,
        W3CSelection: W3CSelection,
        RangeEnumerator: RangeEnumerator,
        RestorePoint: RestorePoint,
        Marker: Marker,
        RangeUtils: RangeUtils
    });
})(window.kendo.jQuery);

(function($) {
    // Imports ================================================================
    var kendo = window.kendo, Class = kendo.Class, Editor = kendo.ui.editor, EditorUtils = Editor.EditorUtils, registerTool = EditorUtils.registerTool, dom = Editor.Dom, RangeUtils = Editor.RangeUtils, selectRange = RangeUtils.selectRange, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate, RestorePoint = Editor.RestorePoint, Marker = Editor.Marker, extend = $.extend;
    var Command = Class.extend({
        init: function(options) {
            var that = this;
            that.options = options;
            that.restorePoint = new RestorePoint(options.range);
            that.marker = new Marker();
            that.formatter = options.formatter;
        },
        getRange: function() {
            return this.restorePoint.toRange();
        },
        lockRange: function(expand) {
            return this.marker.add(this.getRange(), expand);
        },
        releaseRange: function(range) {
            this.marker.remove(range);
            selectRange(range);
        },
        undo: function() {
            var point = this.restorePoint;
            point.body.innerHTML = point.html;
            selectRange(point.toRange());
        },
        redo: function() {
            this.exec();
        },
        exec: function() {
            var that = this, range = that.lockRange(true);
            that.formatter.editor = that.editor;
            that.formatter.toggle(range);
            that.releaseRange(range);
        }
    });
    var GenericCommand = Class.extend({
        init: function(startRestorePoint, endRestorePoint) {
            this.body = startRestorePoint.body;
            this.startRestorePoint = startRestorePoint;
            this.endRestorePoint = endRestorePoint;
        },
        redo: function() {
            this.body.innerHTML = this.endRestorePoint.html;
            selectRange(this.endRestorePoint.toRange());
        },
        undo: function() {
            this.body.innerHTML = this.startRestorePoint.html;
            selectRange(this.startRestorePoint.toRange());
        }
    });
    var InsertHtmlCommand = Command.extend({
        init: function(options) {
            Command.fn.init.call(this, options);
            this.managesUndoRedo = true;
        },
        exec: function() {
            var editor = this.editor;
            var range = editor.getRange();
            var startRestorePoint = new RestorePoint(range);
            editor.clipboard.paste(this.options.value || "");
            editor.undoRedoStack.push(new GenericCommand(startRestorePoint, new RestorePoint(editor.getRange())));
            editor.focus();
        }
    });
    var InsertHtmlTool = Tool.extend({
        initialize: function(ui, initOptions) {
            var editor = initOptions.editor, options = this.options, dataSource = options.items ? options.items : editor.options.insertHtml;
            new Editor.SelectBox(ui, {
                dataSource: dataSource,
                dataTextField: "text",
                dataValueField: "value",
                change: function() {
                    Tool.exec(editor, "insertHtml", this.value());
                },
                title: editor.options.messages.insertHtml,
                highlightFirst: false
            });
        },
        command: function(commandArguments) {
            return new InsertHtmlCommand(commandArguments);
        },
        update: function(ui) {
            var selectbox = ui.data("kendoSelectBox") || ui.find("select").data("kendoSelectBox");
            selectbox.close();
            selectbox.value(selectbox.options.title);
        }
    });
    var UndoRedoStack = Class.extend({
        init: function() {
            this.stack = [];
            this.currentCommandIndex = -1;
        },
        push: function(command) {
            var that = this;
            that.stack = that.stack.slice(0, that.currentCommandIndex + 1);
            that.currentCommandIndex = that.stack.push(command) - 1;
        },
        undo: function() {
            if (this.canUndo()) {
                this.stack[this.currentCommandIndex--].undo();
            }
        },
        redo: function() {
            if (this.canRedo()) {
                this.stack[++this.currentCommandIndex].redo();
            }
        },
        canUndo: function() {
            return this.currentCommandIndex >= 0;
        },
        canRedo: function() {
            return this.currentCommandIndex != this.stack.length - 1;
        }
    });
    var TypingHandler = Class.extend({
        init: function(editor) {
            this.editor = editor;
        },
        keydown: function(e) {
            var that = this, editor = that.editor, keyboard = editor.keyboard, isTypingKey = keyboard.isTypingKey(e), evt = extend($.Event(), e);
            that.editor.trigger("keydown", evt);
            if (evt.isDefaultPrevented()) {
                e.preventDefault();
            }
            if (!evt.isDefaultPrevented() && isTypingKey && !keyboard.isTypingInProgress()) {
                var range = editor.getRange();
                that.startRestorePoint = new RestorePoint(range);
                keyboard.startTyping(function() {
                    editor.selectionRestorePoint = that.endRestorePoint = new RestorePoint(editor.getRange());
                    editor.undoRedoStack.push(new GenericCommand(that.startRestorePoint, that.endRestorePoint));
                });
                return true;
            }
            return false;
        },
        keyup: function(e) {
            var keyboard = this.editor.keyboard;
            this.editor.trigger("keyup", e);
            if (keyboard.isTypingInProgress()) {
                keyboard.endTyping();
                return true;
            }
            return false;
        }
    });
    var SystemHandler = Class.extend({
        init: function(editor) {
            this.editor = editor;
            this.systemCommandIsInProgress = false;
        },
        createUndoCommand: function() {
            var that = this;
            that.endRestorePoint = new RestorePoint(that.editor.getRange());
            that.editor.undoRedoStack.push(new GenericCommand(that.startRestorePoint, that.endRestorePoint));
            that.startRestorePoint = that.endRestorePoint;
        },
        changed: function() {
            if (this.startRestorePoint) {
                return this.startRestorePoint.html != this.editor.body.innerHTML;
            }
            return false;
        },
        keydown: function(e) {
            var that = this, editor = that.editor, keyboard = editor.keyboard;
            if (keyboard.isModifierKey(e)) {
                if (keyboard.isTypingInProgress()) {
                    keyboard.endTyping(true);
                }
                that.startRestorePoint = new RestorePoint(editor.getRange());
                return true;
            }
            if (keyboard.isSystem(e)) {
                that.systemCommandIsInProgress = true;
                if (that.changed()) {
                    that.systemCommandIsInProgress = false;
                    that.createUndoCommand();
                }
                return true;
            }
            return false;
        },
        keyup: function(e) {
            var that = this;
            if (that.systemCommandIsInProgress && that.changed()) {
                that.systemCommandIsInProgress = false;
                that.createUndoCommand(e);
                return true;
            }
            return false;
        }
    });
    var Keyboard = Class.extend({
        init: function(handlers) {
            this.handlers = handlers;
            this.typingInProgress = false;
        },
        isCharacter: function(keyCode) {
            return keyCode >= 48 && keyCode <= 90 || keyCode >= 96 && keyCode <= 111 || keyCode >= 186 && keyCode <= 192 || keyCode >= 219 && keyCode <= 222;
        },
        toolFromShortcut: function(tools, e) {
            var key = String.fromCharCode(e.keyCode), toolName, toolOptions;
            for (toolName in tools) {
                toolOptions = $.extend({
                    ctrl: false,
                    alt: false,
                    shift: false
                }, tools[toolName].options);
                if ((toolOptions.key == key || toolOptions.key == e.keyCode) && toolOptions.ctrl == e.ctrlKey && toolOptions.alt == e.altKey && toolOptions.shift == e.shiftKey) {
                    return toolName;
                }
            }
        },
        isTypingKey: function(e) {
            var keyCode = e.keyCode;
            return this.isCharacter(keyCode) && !e.ctrlKey && !e.altKey || keyCode == 32 || keyCode == 13 || keyCode == 8 || keyCode == 46 && !e.shiftKey && !e.ctrlKey && !e.altKey;
        },
        isModifierKey: function(e) {
            var keyCode = e.keyCode;
            return keyCode == 17 && !e.shiftKey && !e.altKey || keyCode == 16 && !e.ctrlKey && !e.altKey || keyCode == 18 && !e.ctrlKey && !e.shiftKey;
        },
        isSystem: function(e) {
            return e.keyCode == 46 && e.ctrlKey && !e.altKey && !e.shiftKey;
        },
        startTyping: function(callback) {
            this.onEndTyping = callback;
            this.typingInProgress = true;
        },
        stopTyping: function() {
            this.typingInProgress = false;
            if (this.onEndTyping) {
                this.onEndTyping();
            }
        },
        endTyping: function(force) {
            var that = this;
            that.clearTimeout();
            if (force) {
                that.stopTyping();
            } else {
                that.timeout = window.setTimeout($.proxy(that.stopTyping, that), 1e3);
            }
        },
        isTypingInProgress: function() {
            return this.typingInProgress;
        },
        clearTimeout: function() {
            window.clearTimeout(this.timeout);
        },
        notify: function(e, what) {
            var i, handlers = this.handlers;
            for (i = 0; i < handlers.length; i++) {
                if (handlers[i][what](e)) {
                    break;
                }
            }
        },
        keydown: function(e) {
            this.notify(e, "keydown");
        },
        keyup: function(e) {
            this.notify(e, "keyup");
        }
    });
    var Clipboard = Class.extend({
        init: function(editor) {
            this.editor = editor;
            this.cleaners = [ new MSWordFormatCleaner(), new WebkitFormatCleaner() ];
        },
        htmlToFragment: function(html) {
            var editor = this.editor, doc = editor.document, container = dom.create(doc, "div"), fragment = doc.createDocumentFragment();
            container.innerHTML = html;
            while (container.firstChild) {
                fragment.appendChild(container.firstChild);
            }
            return fragment;
        },
        isBlock: function(html) {
            return /<(div|p|ul|ol|table|h[1-6])/i.test(html);
        },
        oncut: function() {
            var editor = this.editor, startRestorePoint = new RestorePoint(editor.getRange());
            setTimeout(function() {
                editor.undoRedoStack.push(new GenericCommand(startRestorePoint, new RestorePoint(editor.getRange())));
            });
        },
        onpaste: function(e) {
            var editor = this.editor, range = editor.getRange(), bom = "", startRestorePoint = new RestorePoint(range), clipboardNode = dom.create(editor.document, "div", {
                className: "k-paste-container",
                innerHTML: bom
            });
            dom.persistScrollTop(editor.document);
            editor.body.appendChild(clipboardNode);
            if (editor.body.createTextRange) {
                e.preventDefault();
                var r = editor.createRange();
                r.selectNodeContents(clipboardNode);
                editor.selectRange(r);
                var textRange = editor.body.createTextRange();
                textRange.moveToElementText(clipboardNode);
                $(editor.body).unbind("paste");
                textRange.execCommand("Paste");
                $(editor.body).bind("paste", $.proxy(arguments.callee, this));
            } else {
                var clipboardRange = editor.createRange();
                clipboardRange.selectNodeContents(clipboardNode);
                selectRange(clipboardRange);
            }
            range.deleteContents();
            setTimeout(function() {
                var html, args = {
                    html: ""
                };
                selectRange(range);
                // iOS5 will substitute the paste container with its own element
                if (!clipboardNode.parentNode) {
                    clipboardNode = editor.body.lastChild;
                }
                dom.remove(clipboardNode);
                if (clipboardNode.lastChild && dom.is(clipboardNode.lastChild, "br")) {
                    dom.remove(clipboardNode.lastChild);
                }
                html = clipboardNode.innerHTML;
                if (html != bom) {
                    args.html = html;
                }
                editor.trigger("paste", args);
                editor.clipboard.paste(args.html, true);
                editor.undoRedoStack.push(new GenericCommand(startRestorePoint, new RestorePoint(editor.getRange())));
                Editor.EditorUtils.select(editor);
            });
        },
        splittableParent: function(block, node) {
            var parentNode, body;
            if (block) {
                return dom.parentOfType(node, [ "p", "ul", "ol" ]) || node.parentNode;
            }
            parentNode = node.parentNode;
            body = node.ownerDocument.body;
            if (dom.isInline(parentNode)) {
                while (parentNode.parentNode != body && !dom.isBlock(parentNode.parentNode)) {
                    parentNode = parentNode.parentNode;
                }
            }
            return parentNode;
        },
        paste: function(html, clean) {
            var editor = this.editor, i, l;
            for (i = 0, l = this.cleaners.length; i < l; i++) {
                if (this.cleaners[i].applicable(html)) {
                    html = this.cleaners[i].clean(html);
                }
            }
            if (clean) {
                // remove br elements which immediately precede block elements
                html = html.replace(/(<br>(\s|&nbsp;)*)+(<\/?(div|p|li|col|t))/gi, "$3");
                // remove empty inline elements
                html = html.replace(/<(a|span)[^>]*><\/\1>/gi, "");
            }
            // It is possible in IE to copy just <li> tags
            html = html.replace(/^<li/i, "<ul><li").replace(/li>$/g, "li></ul>");
            var block = this.isBlock(html);
            var range = editor.getRange();
            range.deleteContents();
            if (range.startContainer == editor.document) {
                range.selectNodeContents(editor.body);
            }
            var marker = new Marker();
            var caret = marker.addCaret(range);
            var parent = this.splittableParent(block, caret);
            var unwrap = false;
            if (!/body|td/.test(dom.name(parent)) && (block || dom.isInline(parent))) {
                range.selectNode(caret);
                RangeUtils.split(range, parent, true);
                unwrap = true;
            }
            var fragment = this.htmlToFragment(html);
            if (fragment.firstChild && fragment.firstChild.className === "k-paste-container") {
                var fragmentsHtml = [];
                for (i = 0, l = fragment.childNodes.length; i < l; i++) {
                    fragmentsHtml.push(fragment.childNodes[i].innerHTML);
                }
                fragment = this.htmlToFragment(fragmentsHtml.join("<br />"));
            }
            range.insertNode(fragment);
            parent = this.splittableParent(block, caret);
            if (unwrap) {
                while (caret.parentNode != parent) {
                    dom.unwrap(caret.parentNode);
                }
                dom.unwrap(caret.parentNode);
            }
            dom.normalize(range.commonAncestorContainer);
            caret.style.display = "inline";
            dom.restoreScrollTop(editor.document);
            dom.scrollTo(caret);
            marker.removeCaret(range);
            selectRange(range);
        }
    });
    var MSWordFormatCleaner = Class.extend({
        init: function() {
            this.replacements = [ /<\?xml[^>]*>/gi, "", /<!--(.|\n)*?-->/g, "", /* comments */
            /&quot;/g, "'", /* encoded quotes (in attributes) */
            /(?:<br>&nbsp;[\s\r\n]+|<br>)*(<\/?(h[1-6]|hr|p|div|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|address|pre|form|blockquote|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br>&nbsp;[\s\r\n]+|<br>)*/g, "$1", /<br><br>/g, "<BR><BR>", /<br>/g, " ", /<table([^>]*)>(\s|&nbsp;)+<t/gi, "<table$1><t", /<tr[^>]*>(\s|&nbsp;)*<\/tr>/gi, "", /<tbody[^>]*>(\s|&nbsp;)*<\/tbody>/gi, "", /<table[^>]*>(\s|&nbsp;)*<\/table>/gi, "", /<BR><BR>/g, "<br>", /^\s*(&nbsp;)+/gi, "", /(&nbsp;|<br[^>]*>)+\s*$/gi, "", /mso-[^;"]*;?/gi, "", /* office-related CSS attributes */
            /<(\/?)b(\s[^>]*)?>/gi, "<$1strong$2>", /<(\/?)i(\s[^>]*)?>/gi, "<$1em$2>", /<\/?(meta|link|style|o:|v:|x:)[^>]*>((?:.|\n)*?<\/(meta|link|style|o:|v:|x:)[^>]*>)?/gi, "", /* external references and namespaced tags */
            /style=(["|'])\s*\1/g, "" ];
        },
        applicable: function(html) {
            return /class="?Mso|style="[^"]*mso-/i.test(html);
        },
        listType: function(html) {
            if (/^[\u2022\u00b7\u00a7\u00d8o]\u00a0+/.test(html)) {
                return "ul";
            }
            if (/^\s*\w+[\.\)]\u00a0{2,}/.test(html)) {
                return "ol";
            }
        },
        lists: function(html) {
            var placeholder = dom.create(document, "div", {
                innerHTML: html
            });
            var blockChildren = $(dom.blockElements.join(","), placeholder);
            var lastMargin = -1, lastType, levels = {
                ul: {},
                ol: {}
            }, li = placeholder;
            for (var i = 0; i < blockChildren.length; i++) {
                var p = blockChildren[i];
                html = p.innerHTML.replace(/<\/?\w+[^>]*>/g, "").replace(/&nbsp;/g, " ");
                var type = this.listType(html);
                if (!type || dom.name(p) != "p") {
                    if (!p.innerHTML) {
                        dom.remove(p);
                    } else {
                        levels = {
                            ul: {},
                            ol: {}
                        };
                        li = placeholder;
                        lastMargin = -1;
                    }
                    continue;
                }
                var margin = parseFloat(p.style.marginLeft || 0);
                var list = levels[type][margin];
                if (margin > lastMargin || !list) {
                    list = dom.create(document, type);
                    if (li == placeholder) {
                        dom.insertBefore(list, p);
                    } else {
                        li.appendChild(list);
                    }
                    levels[type][margin] = list;
                }
                if (lastType != type) {
                    for (var key in levels) {
                        for (var child in levels[key]) {
                            if ($.contains(list, levels[key][child])) {
                                delete levels[key][child];
                            }
                        }
                    }
                }
                dom.remove(p.firstChild);
                li = dom.create(document, "li", {
                    innerHTML: p.innerHTML
                });
                list.appendChild(li);
                dom.remove(p);
                lastMargin = margin;
                lastType = type;
            }
            return placeholder.innerHTML;
        },
        stripEmptyAnchors: function(html) {
            return html.replace(/<a([^>]*)>\s*<\/a>/gi, function(a, attributes) {
                if (!attributes || attributes.indexOf("href") < 0) {
                    return "";
                }
                return a;
            });
        },
        clean: function(html) {
            var that = this, replacements = that.replacements, i, l;
            for (i = 0, l = replacements.length; i < l; i += 2) {
                html = html.replace(replacements[i], replacements[i + 1]);
            }
            html = that.stripEmptyAnchors(html);
            html = that.lists(html);
            html = html.replace(/\s+class="?[^"\s>]*"?/gi, "");
            return html;
        }
    });
    var WebkitFormatCleaner = Class.extend({
        init: function() {
            this.replacements = [ /\s+class="Apple-style-span[^"]*"/gi, "", /<(div|p|h[1-6])\s+style="[^"]*"/gi, "<$1", /^<div>(.*)<\/div>$/, "$1" ];
        },
        applicable: function(html) {
            return /class="?Apple-style-span|style="[^"]*-webkit-nbsp-mode/i.test(html);
        },
        clean: function(html) {
            var that = this, replacements = that.replacements, i, l;
            for (i = 0, l = replacements.length; i < l; i += 2) {
                html = html.replace(replacements[i], replacements[i + 1]);
            }
            return html;
        }
    });
    extend(Editor, {
        Command: Command,
        GenericCommand: GenericCommand,
        InsertHtmlCommand: InsertHtmlCommand,
        InsertHtmlTool: InsertHtmlTool,
        UndoRedoStack: UndoRedoStack,
        TypingHandler: TypingHandler,
        SystemHandler: SystemHandler,
        Keyboard: Keyboard,
        Clipboard: Clipboard,
        MSWordFormatCleaner: MSWordFormatCleaner,
        WebkitFormatCleaner: WebkitFormatCleaner
    });
    registerTool("insertHtml", new InsertHtmlTool({
        template: new ToolTemplate({
            template: EditorUtils.dropDownListTemplate,
            title: "Insert HTML",
            initialValue: "Insert HTML"
        })
    }));
})(window.kendo.jQuery);

(function($) {
    var kendo = window.kendo, Class = kendo.Class, Editor = kendo.ui.editor, formats = kendo.ui.Editor.fn.options.formats, EditorUtils = Editor.EditorUtils, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate, FormatTool = Editor.FormatTool, dom = Editor.Dom, RangeUtils = Editor.RangeUtils, extend = $.extend, registerTool = Editor.EditorUtils.registerTool, registerFormat = Editor.EditorUtils.registerFormat, KMARKER = "k-marker";
    var InlineFormatFinder = Class.extend({
        init: function(format) {
            this.format = format;
        },
        numberOfSiblings: function(referenceNode) {
            var textNodesCount = 0, elementNodesCount = 0, markerCount = 0, parentNode = referenceNode.parentNode, node;
            for (node = parentNode.firstChild; node; node = node.nextSibling) {
                if (node != referenceNode) {
                    if (node.className == KMARKER) {
                        markerCount++;
                    } else if (node.nodeType == 3) {
                        textNodesCount++;
                    } else {
                        elementNodesCount++;
                    }
                }
            }
            if (markerCount > 1 && parentNode.firstChild.className == KMARKER && parentNode.lastChild.className == KMARKER) {
                // full node selection
                return 0;
            } else {
                return elementNodesCount + textNodesCount;
            }
        },
        findSuitable: function(sourceNode, skip) {
            if (!skip && this.numberOfSiblings(sourceNode) > 0) {
                return null;
            }
            return dom.parentOfType(sourceNode, this.format[0].tags);
        },
        findFormat: function(sourceNode) {
            var format = this.format, attrEquals = dom.attrEquals, i, len, node, tags, attributes;
            for (i = 0, len = format.length; i < len; i++) {
                node = sourceNode;
                tags = format[i].tags;
                attributes = format[i].attr;
                if (node && dom.ofType(node, tags) && attrEquals(node, attributes)) {
                    return node;
                }
                while (node) {
                    node = dom.parentOfType(node, tags);
                    if (node && attrEquals(node, attributes)) {
                        return node;
                    }
                }
            }
            return null;
        },
        isFormatted: function(nodes) {
            var i, len;
            for (i = 0, len = nodes.length; i < len; i++) {
                if (this.findFormat(nodes[i])) {
                    return true;
                }
            }
            return false;
        }
    });
    var InlineFormatter = Class.extend({
        init: function(format, values) {
            var that = this;
            that.finder = new InlineFormatFinder(format);
            that.attributes = extend({}, format[0].attr, values);
            that.tag = format[0].tags[0];
        },
        wrap: function(node) {
            return dom.wrap(node, dom.create(node.ownerDocument, this.tag, this.attributes));
        },
        activate: function(range, nodes) {
            var that = this;
            if (that.finder.isFormatted(nodes)) {
                that.split(range);
                that.remove(nodes);
            } else {
                that.apply(nodes);
            }
        },
        toggle: function(range) {
            var nodes = RangeUtils.textNodes(range);
            if (nodes.length > 0) {
                this.activate(range, nodes);
            }
        },
        apply: function(nodes) {
            var that = this, formatNodes = [], i, l, node, formatNode;
            for (i = 0, l = nodes.length; i < l; i++) {
                node = nodes[i];
                formatNode = that.finder.findSuitable(node);
                if (formatNode) {
                    dom.attr(formatNode, that.attributes);
                } else {
                    formatNode = that.wrap(node);
                }
                formatNodes.push(formatNode);
            }
            that.consolidate(formatNodes);
        },
        remove: function(nodes) {
            var that = this, i, l, formatNode;
            for (i = 0, l = nodes.length; i < l; i++) {
                formatNode = that.finder.findFormat(nodes[i]);
                if (formatNode) {
                    if (that.attributes && that.attributes.style) {
                        dom.unstyle(formatNode, that.attributes.style);
                        if (!formatNode.style.cssText) {
                            dom.unwrap(formatNode);
                        }
                    } else {
                        dom.unwrap(formatNode);
                    }
                }
            }
        },
        split: function(range) {
            var nodes = RangeUtils.textNodes(range), l = nodes.length, i, formatNode;
            if (l > 0) {
                for (i = 0; i < l; i++) {
                    formatNode = this.finder.findFormat(nodes[i]);
                    if (formatNode) {
                        RangeUtils.split(range, formatNode, true);
                    }
                }
            }
        },
        consolidate: function(nodes) {
            var node, last;
            while (nodes.length > 1) {
                node = nodes.pop();
                last = nodes[nodes.length - 1];
                if (node.previousSibling && node.previousSibling.className == KMARKER) {
                    last.appendChild(node.previousSibling);
                }
                if (node.tagName == last.tagName && node.previousSibling == last && node.style.cssText == last.style.cssText) {
                    while (node.firstChild) {
                        last.appendChild(node.firstChild);
                    }
                    dom.remove(node);
                }
            }
        }
    });
    var GreedyInlineFormatFinder = InlineFormatFinder.extend({
        init: function(format, greedyProperty) {
            var that = this;
            that.format = format;
            that.greedyProperty = greedyProperty;
            InlineFormatFinder.fn.init.call(that, format);
        },
        getInlineCssValue: function(node) {
            var attributes = node.attributes, trim = $.trim, i, l, attribute, name, attributeValue, css, pair, cssIndex, len, propertyAndValue, property, value;
            if (!attributes) {
                return;
            }
            for (i = 0, l = attributes.length; i < l; i++) {
                attribute = attributes[i];
                name = attribute.nodeName;
                attributeValue = attribute.nodeValue;
                if (attribute.specified && name == "style") {
                    css = trim(attributeValue || node.style.cssText).split(";");
                    for (cssIndex = 0, len = css.length; cssIndex < len; cssIndex++) {
                        pair = css[cssIndex];
                        if (pair.length) {
                            propertyAndValue = pair.split(":");
                            property = trim(propertyAndValue[0].toLowerCase());
                            value = trim(propertyAndValue[1]);
                            if (property != this.greedyProperty) {
                                continue;
                            }
                            return property.indexOf("color") >= 0 ? dom.toHex(value) : value;
                        }
                    }
                }
            }
        },
        getFormatInner: function(node) {
            var $node = $(dom.isDataNode(node) ? node.parentNode : node), parents = $node.parents().andSelf(), i, len, value;
            for (i = 0, len = parents.length; i < len; i++) {
                value = this.greedyProperty == "className" ? parents[i].className : this.getInlineCssValue(parents[i]);
                if (value) {
                    return value;
                }
            }
            return "inherit";
        },
        getFormat: function(nodes) {
            var result = this.getFormatInner(nodes[0]), i, len;
            for (i = 1, len = nodes.length; i < len; i++) {
                if (result != this.getFormatInner(nodes[i])) {
                    return "";
                }
            }
            return result;
        },
        isFormatted: function(nodes) {
            return this.getFormat(nodes) !== "";
        }
    });
    var GreedyInlineFormatter = InlineFormatter.extend({
        init: function(format, values, greedyProperty) {
            var that = this;
            InlineFormatter.fn.init.call(that, format, values);
            that.greedyProperty = greedyProperty;
            that.values = values;
            that.finder = new GreedyInlineFormatFinder(format, greedyProperty);
        },
        activate: function(range, nodes) {
            var that = this, camelCase, greedyProperty = that.greedyProperty, action = "apply";
            that.split(range);
            if (greedyProperty) {
                camelCase = greedyProperty.replace(/-([a-z])/, function(all, letter) {
                    return letter.toUpperCase();
                });
                if (that.values.style[camelCase] == "inherit") {
                    action = "remove";
                }
            }
            that[action](nodes);
        }
    });
    function inlineFormatWillDelayExecution(range) {
        return range.collapsed && !RangeUtils.isExpandable(range);
    }
    var InlineFormatTool = FormatTool.extend({
        init: function(options) {
            FormatTool.fn.init.call(this, extend(options, {
                finder: new InlineFormatFinder(options.format),
                formatter: function() {
                    return new InlineFormatter(options.format);
                }
            }));
            this.willDelayExecution = inlineFormatWillDelayExecution;
        }
    });
    var DelayedExecutionTool = Tool.extend({
        willDelayExecution: inlineFormatWillDelayExecution,
        update: function(ui, nodes, pendingFormats) {
            var list = ui.data(this.type), pendingFormat = pendingFormats.getPending(this.name), format;
            if (pendingFormat && pendingFormat.options.params) {
                format = pendingFormat.options.params.value;
            } else {
                format = this.finder.getFormat(nodes);
            }
            list.close();
            list.value(format);
        }
    });
    var FontTool = DelayedExecutionTool.extend({
        init: function(options) {
            var that = this;
            Tool.fn.init.call(that, options);
            // IE has single selection hence we are using select box instead of combobox
            that.type = kendo.support.browser.msie || kendo.support.touch ? "kendoDropDownList" : "kendoComboBox";
            that.format = [ {
                tags: [ "span" ]
            } ];
            that.finder = new GreedyInlineFormatFinder(that.format, options.cssAttr);
        },
        command: function(commandArguments) {
            var options = this.options, format = this.format, style = {};
            return new Editor.FormatCommand(extend(commandArguments, {
                formatter: function() {
                    style[options.domAttr] = commandArguments.value;
                    return new GreedyInlineFormatter(format, {
                        style: style
                    }, options.cssAttr);
                }
            }));
        },
        initialize: function(ui, initOptions) {
            var editor = initOptions.editor, options = this.options, toolName = options.name, dataSource, defaultValue = [];
            if (options.defaultValue) {
                defaultValue = [ {
                    text: editor.options.messages[options.defaultValue[0].text],
                    value: options.defaultValue[0].value
                } ];
            }
            dataSource = defaultValue.concat(options.items ? options.items : editor.options[toolName]);
            ui[this.type]({
                dataTextField: "text",
                dataValueField: "value",
                dataSource: dataSource,
                change: function() {
                    Tool.exec(editor, toolName, this.value());
                },
                highlightFirst: false
            });
            ui.closest(".k-widget").removeClass("k-" + toolName).find("*").andSelf().attr("unselectable", "on");
            ui.data(this.type).value("inherit");
        }
    });
    var ColorTool = Tool.extend({
        init: function(options) {
            Tool.fn.init.call(this, options);
            this.options = options;
            this.format = [ {
                tags: [ "span" ]
            } ];
        },
        update: function() {
            this._widget.close();
        },
        command: function(commandArguments) {
            var options = this.options, format = this.format, style = {};
            return new Editor.FormatCommand(extend(commandArguments, {
                formatter: function() {
                    style[options.domAttr] = commandArguments.value;
                    return new GreedyInlineFormatter(format, {
                        style: style
                    }, options.cssAttr);
                }
            }));
        },
        willDelayExecution: inlineFormatWillDelayExecution,
        initialize: function(ui, initOptions) {
            var editor = initOptions.editor, toolName = this.name;
            ui = this._widget = new kendo.ui.ColorPicker(ui, {
                value: "#000",
                toolIcon: "k-" + this.options.name,
                palette: "websafe",
                change: function() {
                    var color = ui.value();
                    if (color) {
                        Tool.exec(editor, toolName, color);
                    }
                }
            });
            ui.bind("activate", function(ev) {
                ev.preventDefault();
                ui.trigger("change");
                editor.focus();
            });
            ui.element.attr("title", initOptions.title);
        }
    });
    var StyleTool = DelayedExecutionTool.extend({
        init: function(options) {
            var that = this;
            Tool.fn.init.call(that, options);
            that.type = "kendoSelectBox";
            that.format = [ {
                tags: [ "span" ]
            } ];
            that.finder = new GreedyInlineFormatFinder(that.format, "className");
        },
        command: function(commandArguments) {
            var format = this.format;
            return new Editor.FormatCommand(extend(commandArguments, {
                formatter: function() {
                    return new GreedyInlineFormatter(format, {
                        className: commandArguments.value
                    });
                }
            }));
        },
        initialize: function(ui, initOptions) {
            var editor = initOptions.editor, options = this.options;
            new Editor.SelectBox(ui, {
                dataTextField: "text",
                dataValueField: "value",
                dataSource: options.items || editor.options.style,
                title: editor.options.messages.style,
                change: function() {
                    Tool.exec(editor, "style", this.value());
                },
                highlightFirst: false
            });
            ui.closest(".k-widget").removeClass("k-" + this.name).find("*").andSelf().attr("unselectable", "on");
        }
    });
    extend(Editor, {
        InlineFormatFinder: InlineFormatFinder,
        InlineFormatter: InlineFormatter,
        GreedyInlineFormatFinder: GreedyInlineFormatFinder,
        GreedyInlineFormatter: GreedyInlineFormatter,
        InlineFormatTool: InlineFormatTool,
        FontTool: FontTool,
        ColorTool: ColorTool,
        StyleTool: StyleTool
    });
    registerTool("style", new Editor.StyleTool({
        template: new ToolTemplate({
            template: EditorUtils.dropDownListTemplate,
            title: "Styles"
        })
    }));
    registerFormat("bold", [ {
        tags: [ "strong" ]
    }, {
        tags: [ "span" ],
        attr: {
            style: {
                fontWeight: "bold"
            }
        }
    } ]);
    registerTool("bold", new InlineFormatTool({
        key: "B",
        ctrl: true,
        format: formats.bold,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Bold"
        })
    }));
    registerFormat("italic", [ {
        tags: [ "em" ]
    }, {
        tags: [ "span" ],
        attr: {
            style: {
                fontStyle: "italic"
            }
        }
    } ]);
    registerTool("italic", new InlineFormatTool({
        key: "I",
        ctrl: true,
        format: formats.italic,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Italic"
        })
    }));
    registerFormat("underline", [ {
        tags: [ "span" ],
        attr: {
            style: {
                textDecoration: "underline"
            }
        }
    } ]);
    registerTool("underline", new InlineFormatTool({
        key: "U",
        ctrl: true,
        format: formats.underline,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Underline"
        })
    }));
    registerFormat("strikethrough", [ {
        tags: [ "del" ]
    }, {
        tags: [ "span" ],
        attr: {
            style: {
                textDecoration: "line-through"
            }
        }
    } ]);
    registerTool("strikethrough", new InlineFormatTool({
        format: formats.strikethrough,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Strikethrough"
        })
    }));
    registerFormat("superscript", [ {
        tags: [ "sup" ]
    } ]);
    registerTool("superscript", new InlineFormatTool({
        format: formats.superscript,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Superscript"
        })
    }));
    registerFormat("subscript", [ {
        tags: [ "sub" ]
    } ]);
    registerTool("subscript", new InlineFormatTool({
        format: formats.subscript,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Subscript"
        })
    }));
    registerTool("foreColor", new ColorTool({
        cssAttr: "color",
        domAttr: "color",
        name: "foreColor",
        template: new ToolTemplate({
            template: EditorUtils.colorPickerTemplate,
            title: "Color"
        })
    }));
    registerTool("backColor", new ColorTool({
        cssAttr: "background-color",
        domAttr: "backgroundColor",
        name: "backColor",
        template: new ToolTemplate({
            template: EditorUtils.colorPickerTemplate,
            title: "Background Color"
        })
    }));
    registerTool("fontName", new FontTool({
        cssAttr: "font-family",
        domAttr: "fontFamily",
        name: "fontName",
        defaultValue: [ {
            text: "fontNameInherit",
            value: "inherit"
        } ],
        template: new ToolTemplate({
            template: EditorUtils.comboBoxTemplate,
            title: "Font Name"
        })
    }));
    registerTool("fontSize", new FontTool({
        cssAttr: "font-size",
        domAttr: "fontSize",
        name: "fontSize",
        defaultValue: [ {
            text: "fontSizeInherit",
            value: "inherit"
        } ],
        template: new ToolTemplate({
            template: EditorUtils.comboBoxTemplate,
            title: "Font Size"
        })
    }));
})(window.kendo.jQuery);

(function($) {
    var kendo = window.kendo, Class = kendo.Class, extend = $.extend, Editor = kendo.ui.editor, formats = kendo.ui.Editor.fn.options.formats, dom = Editor.Dom, Command = Editor.Command, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate, FormatTool = Editor.FormatTool, EditorUtils = Editor.EditorUtils, registerTool = EditorUtils.registerTool, registerFormat = EditorUtils.registerFormat, RangeUtils = Editor.RangeUtils;
    var BlockFormatFinder = Class.extend({
        init: function(format) {
            this.format = format;
        },
        contains: function(node, children) {
            var i, len, child;
            for (i = 0, len = children.length; i < len; i++) {
                child = children[i];
                if (!child || !dom.isAncestorOrSelf(node, child)) {
                    return false;
                }
            }
            return true;
        },
        findSuitable: function(nodes) {
            var format = this.format, suitable = [], i, len, candidate;
            for (i = 0, len = nodes.length; i < len; i++) {
                candidate = dom.ofType(nodes[i], format[0].tags) ? nodes[i] : dom.parentOfType(nodes[i], format[0].tags);
                if (!candidate) {
                    return [];
                }
                if ($.inArray(candidate, suitable) < 0) {
                    suitable.push(candidate);
                }
            }
            for (i = 0, len = suitable.length; i < len; i++) {
                if (this.contains(suitable[i], suitable)) {
                    return [ suitable[i] ];
                }
            }
            return suitable;
        },
        findFormat: function(sourceNode) {
            var format = this.format, i, len, node, tags, attributes;
            for (i = 0, len = format.length; i < len; i++) {
                node = sourceNode;
                tags = format[i].tags;
                attributes = format[i].attr;
                while (node) {
                    if (dom.ofType(node, tags) && dom.attrEquals(node, attributes)) {
                        return node;
                    }
                    node = node.parentNode;
                }
            }
            return null;
        },
        getFormat: function(nodes) {
            var that = this, findFormat = function(node) {
                return that.findFormat(dom.isDataNode(node) ? node.parentNode : node);
            }, result = findFormat(nodes[0]), i, len;
            if (!result) {
                return "";
            }
            for (i = 1, len = nodes.length; i < len; i++) {
                if (result != findFormat(nodes[i])) {
                    return "";
                }
            }
            return result.nodeName.toLowerCase();
        },
        isFormatted: function(nodes) {
            for (var i = 0, len = nodes.length; i < len; i++) {
                if (!this.findFormat(nodes[i])) {
                    return false;
                }
            }
            return true;
        }
    });
    var BlockFormatter = Class.extend({
        init: function(format, values) {
            this.format = format;
            this.values = values;
            this.finder = new BlockFormatFinder(format);
        },
        wrap: function(tag, attributes, nodes) {
            var commonAncestor = nodes.length == 1 ? dom.blockParentOrBody(nodes[0]) : dom.commonAncestor.apply(null, nodes);
            if (dom.isInline(commonAncestor)) {
                commonAncestor = dom.blockParentOrBody(commonAncestor);
            }
            var ancestors = dom.significantChildNodes(commonAncestor), position = dom.findNodeIndex(ancestors[0]), wrapper = dom.create(commonAncestor.ownerDocument, tag, attributes), i, ancestor;
            for (i = 0; i < ancestors.length; i++) {
                ancestor = ancestors[i];
                if (dom.isBlock(ancestor)) {
                    dom.attr(ancestor, attributes);
                    if (wrapper.childNodes.length) {
                        dom.insertBefore(wrapper, ancestor);
                        wrapper = wrapper.cloneNode(false);
                    }
                    position = dom.findNodeIndex(ancestor) + 1;
                    continue;
                }
                wrapper.appendChild(ancestor);
            }
            if (wrapper.firstChild) {
                dom.insertAt(commonAncestor, wrapper, position);
            }
        },
        apply: function(nodes) {
            var that = this, formatNodes = dom.is(nodes[0], "img") ? [ nodes[0] ] : that.finder.findSuitable(nodes), formatToApply = formatNodes.length ? EditorUtils.formatByName(dom.name(formatNodes[0]), that.format) : that.format[0], tag = formatToApply.tags[0], attributes = extend({}, formatToApply.attr, that.values), i, len;
            if (formatNodes.length) {
                for (i = 0, len = formatNodes.length; i < len; i++) {
                    dom.attr(formatNodes[i], attributes);
                }
            } else {
                that.wrap(tag, attributes, nodes);
            }
        },
        remove: function(nodes) {
            var i, l, formatNode, namedFormat;
            for (i = 0, l = nodes.length; i < l; i++) {
                formatNode = this.finder.findFormat(nodes[i]);
                if (formatNode) {
                    if (dom.ofType(formatNode, [ "p", "img", "li" ])) {
                        namedFormat = EditorUtils.formatByName(dom.name(formatNode), this.format);
                        if (namedFormat.attr.style) {
                            dom.unstyle(formatNode, namedFormat.attr.style);
                        }
                        if (namedFormat.attr.className) {
                            dom.removeClass(formatNode, namedFormat.attr.className);
                        }
                    } else {
                        dom.unwrap(formatNode);
                    }
                }
            }
        },
        toggle: function(range) {
            var that = this, nodes = RangeUtils.nodes(range);
            if (that.finder.isFormatted(nodes)) {
                that.remove(nodes);
            } else {
                that.apply(nodes);
            }
        }
    });
    var GreedyBlockFormatter = Class.extend({
        init: function(format, values) {
            var that = this;
            that.format = format;
            that.values = values;
            that.finder = new BlockFormatFinder(format);
        },
        apply: function(nodes) {
            var format = this.format, blocks = dom.blockParents(nodes), formatTag = format[0].tags[0], i, len, list, formatter, range;
            if (blocks.length) {
                for (i = 0, len = blocks.length; i < len; i++) {
                    if (dom.is(blocks[i], "li")) {
                        list = blocks[i].parentNode;
                        formatter = new Editor.ListFormatter(list.nodeName.toLowerCase(), formatTag);
                        range = this.editor.createRange();
                        range.selectNode(blocks[i]);
                        formatter.toggle(range);
                    } else {
                        dom.changeTag(blocks[i], formatTag);
                    }
                }
            } else {
                new BlockFormatter(format, this.values).apply(nodes);
            }
        },
        toggle: function(range) {
            var nodes = RangeUtils.textNodes(range);
            if (!nodes.length) {
                range.selectNodeContents(range.commonAncestorContainer);
                nodes = RangeUtils.textNodes(range);
                if (!nodes.length) {
                    nodes = dom.significantChildNodes(range.commonAncestorContainer);
                }
            }
            this.apply(nodes);
        }
    });
    var FormatCommand = Command.extend({
        init: function(options) {
            options.formatter = options.formatter();
            Command.fn.init.call(this, options);
        }
    });
    var BlockFormatTool = FormatTool.extend({
        init: function(options) {
            FormatTool.fn.init.call(this, extend(options, {
                finder: new BlockFormatFinder(options.format),
                formatter: function() {
                    return new BlockFormatter(options.format);
                }
            }));
        }
    });
    var FormatBlockTool = Tool.extend({
        init: function(options) {
            Tool.fn.init.call(this, options);
            this.finder = new BlockFormatFinder([ {
                tags: dom.blockElements
            } ]);
        },
        command: function(commandArguments) {
            return new FormatCommand(extend(commandArguments, {
                formatter: function() {
                    return new GreedyBlockFormatter([ {
                        tags: [ commandArguments.value ]
                    } ], {});
                }
            }));
        },
        update: function(ui, nodes) {
            var list;
            if (ui.is("select")) {
                list = ui.data("kendoSelectBox");
            } else {
                list = ui.find("select").data("kendoSelectBox");
            }
            list.close();
            list.value(this.finder.getFormat(nodes));
        },
        initialize: function(ui, initOptions) {
            var editor = initOptions.editor, toolName = "formatBlock";
            new Editor.SelectBox(ui, {
                dataTextField: "text",
                dataValueField: "value",
                dataSource: this.options.items ? this.options.items : editor.options.formatBlock,
                title: editor.options.messages.formatBlock,
                change: function() {
                    Tool.exec(editor, toolName, this.value());
                },
                highlightFirst: false
            });
            ui.closest(".k-widget").removeClass("k-" + toolName).find("*").andSelf().attr("unselectable", "on");
        }
    });
    extend(Editor, {
        BlockFormatFinder: BlockFormatFinder,
        BlockFormatter: BlockFormatter,
        GreedyBlockFormatter: GreedyBlockFormatter,
        FormatCommand: FormatCommand,
        BlockFormatTool: BlockFormatTool,
        FormatBlockTool: FormatBlockTool
    });
    registerTool("formatBlock", new FormatBlockTool({
        template: new ToolTemplate({
            template: EditorUtils.dropDownListTemplate
        })
    }));
    registerFormat("justifyLeft", [ {
        tags: dom.blockElements,
        attr: {
            style: {
                textAlign: "left"
            }
        }
    }, {
        tags: [ "img" ],
        attr: {
            style: {
                "float": "left"
            }
        }
    } ]);
    registerTool("justifyLeft", new BlockFormatTool({
        format: formats.justifyLeft,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Justify Left"
        })
    }));
    registerFormat("justifyCenter", [ {
        tags: dom.blockElements,
        attr: {
            style: {
                textAlign: "center"
            }
        }
    }, {
        tags: [ "img" ],
        attr: {
            style: {
                display: "block",
                marginLeft: "auto",
                marginRight: "auto"
            }
        }
    } ]);
    registerTool("justifyCenter", new BlockFormatTool({
        format: formats.justifyCenter,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Justify Center"
        })
    }));
    registerFormat("justifyRight", [ {
        tags: dom.blockElements,
        attr: {
            style: {
                textAlign: "right"
            }
        }
    }, {
        tags: [ "img" ],
        attr: {
            style: {
                "float": "right"
            }
        }
    } ]);
    registerTool("justifyRight", new BlockFormatTool({
        format: formats.justifyRight,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Justify Right"
        })
    }));
    registerFormat("justifyFull", [ {
        tags: dom.blockElements,
        attr: {
            style: {
                textAlign: "justify"
            }
        }
    } ]);
    registerTool("justifyFull", new BlockFormatTool({
        format: formats.justifyFull,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Justify Full"
        })
    }));
})(window.kendo.jQuery);

(function($) {
    // Imports ================================================================
    var kendo = window.kendo, extend = $.extend, Editor = kendo.ui.editor, dom = Editor.Dom, Command = Editor.Command, Tool = Editor.Tool, BlockFormatter = Editor.BlockFormatter, normalize = dom.normalize, RangeUtils = Editor.RangeUtils, registerTool = Editor.EditorUtils.registerTool;
    var ParagraphCommand = Command.extend({
        init: function(options) {
            this.options = options;
            Command.fn.init.call(this, options);
        },
        exec: function() {
            var range = this.getRange(), doc = RangeUtils.documentFromRange(range), parent, previous, next, container, emptyParagraphContent = kendo.support.browser.msie ? "" : '<br _moz_dirty="" />', paragraph, marker, li, heading, rng, // necessary while the emptyParagraphContent is empty under IE
            blocks = "p,h1,h2,h3,h4,h5,h6".split(","), startInBlock = dom.parentOfType(range.startContainer, blocks), endInBlock = dom.parentOfType(range.endContainer, blocks), shouldTrim = startInBlock && !endInBlock || !startInBlock && endInBlock;
            function clean(node) {
                if (node.firstChild && dom.is(node.firstChild, "br")) {
                    dom.remove(node.firstChild);
                }
                if (dom.isDataNode(node) && !node.nodeValue) {
                    node = node.parentNode;
                }
                if (node && !dom.is(node, "img")) {
                    while (node.firstChild && node.firstChild.nodeType == 1) {
                        node = node.firstChild;
                    }
                    if (node.innerHTML === "") {
                        node.innerHTML = emptyParagraphContent;
                    }
                }
            }
            range.deleteContents();
            marker = dom.create(doc, "a");
            range.insertNode(marker);
            if (!marker.parentNode) {
                // inserting paragraph in Firefox full body range
                container = range.commonAncestorContainer;
                container.innerHTML = "";
                container.appendChild(marker);
            }
            normalize(marker.parentNode);
            li = dom.parentOfType(marker, [ "li" ]);
            heading = dom.parentOfType(marker, "h1,h2,h3,h4,h5,h6".split(","));
            if (li) {
                rng = range.cloneRange();
                rng.selectNode(li);
                // hitting 'enter' in empty li
                if (!RangeUtils.textNodes(rng).length) {
                    paragraph = dom.create(doc, "p");
                    if (li.nextSibling) {
                        RangeUtils.split(rng, li.parentNode);
                    }
                    dom.insertAfter(paragraph, li.parentNode);
                    dom.remove(li.parentNode.childNodes.length == 1 ? li.parentNode : li);
                    paragraph.innerHTML = emptyParagraphContent;
                    next = paragraph;
                }
            } else if (heading && !marker.nextSibling) {
                paragraph = dom.create(doc, "p");
                dom.insertAfter(paragraph, heading);
                paragraph.innerHTML = emptyParagraphContent;
                dom.remove(marker);
                next = paragraph;
            }
            if (!next) {
                if (!(li || heading)) {
                    new BlockFormatter([ {
                        tags: [ "p" ]
                    } ]).apply([ marker ]);
                }
                range.selectNode(marker);
                parent = dom.parentOfType(marker, [ li ? "li" : heading ? dom.name(heading) : "p" ]);
                RangeUtils.split(range, parent, shouldTrim);
                previous = parent.previousSibling;
                if (dom.is(previous, "li") && previous.firstChild && !dom.is(previous.firstChild, "br")) {
                    previous = previous.firstChild;
                }
                next = parent.nextSibling;
                if (dom.is(next, "li") && next.firstChild && !dom.is(next.firstChild, "br")) {
                    next = next.firstChild;
                }
                dom.remove(parent);
                clean(previous);
                clean(next);
                // normalize updates the caret display in Gecko
                normalize(previous);
            }
            normalize(next);
            if (dom.is(next, "img")) {
                range.setStartBefore(next);
            } else {
                range.selectNodeContents(next);
                var textNode = RangeUtils.textNodes(range)[0];
                if (textNode) {
                    range.selectNodeContents(textNode);
                }
            }
            range.collapse(true);
            dom.scrollTo(next);
            RangeUtils.selectRange(range);
        }
    });
    var NewLineCommand = Command.extend({
        init: function(options) {
            this.options = options;
            Command.fn.init.call(this, options);
        },
        exec: function() {
            var range = this.getRange();
            range.deleteContents();
            var br = dom.create(RangeUtils.documentFromRange(range), "br");
            range.insertNode(br);
            normalize(br.parentNode);
            if (!kendo.support.browser.msie && (!br.nextSibling || dom.isWhitespace(br.nextSibling))) {
                //Gecko and WebKit cannot put the caret after only one br.
                var filler = br.cloneNode(true);
                filler.setAttribute("_moz_dirty", "");
                dom.insertAfter(filler, br);
            }
            range.setStartAfter(br);
            range.collapse(true);
            dom.scrollTo(br.nextSibling);
            RangeUtils.selectRange(range);
        }
    });
    extend(Editor, {
        ParagraphCommand: ParagraphCommand,
        NewLineCommand: NewLineCommand
    });
    registerTool("insertLineBreak", new Tool({
        key: 13,
        shift: true,
        command: NewLineCommand
    }));
    registerTool("insertParagraph", new Tool({
        key: 13,
        command: ParagraphCommand
    }));
})(window.kendo.jQuery);

(function($) {
    // Imports ================================================================
    var kendo = window.kendo, Class = kendo.Class, extend = $.extend, Editor = kendo.ui.editor, dom = Editor.Dom, RangeUtils = Editor.RangeUtils, EditorUtils = Editor.EditorUtils, Command = Editor.Command, ToolTemplate = Editor.ToolTemplate, FormatTool = Editor.FormatTool, BlockFormatFinder = Editor.BlockFormatFinder, textNodes = RangeUtils.textNodes, registerTool = Editor.EditorUtils.registerTool;
    var ListFormatFinder = BlockFormatFinder.extend({
        init: function(tag) {
            this.tag = tag;
            var tags = this.tags = [ tag == "ul" ? "ol" : "ul", tag ];
            BlockFormatFinder.fn.init.call(this, [ {
                tags: tags
            } ]);
        },
        isFormatted: function(nodes) {
            var formatNodes = [], formatNode;
            for (var i = 0; i < nodes.length; i++) {
                if ((formatNode = this.findFormat(nodes[i])) && dom.name(formatNode) == this.tag) {
                    formatNodes.push(formatNode);
                }
            }
            if (formatNodes.length < 1) {
                return false;
            }
            if (formatNodes.length != nodes.length) {
                return false;
            }
            // check if sequential lists are selected
            for (i = 0; i < formatNodes.length; i++) {
                if (formatNodes[i].parentNode != formatNode.parentNode) {
                    break;
                }
                if (formatNodes[i] != formatNode) {
                    return false;
                }
            }
            return true;
        },
        findSuitable: function(nodes) {
            var candidate = dom.parentOfType(nodes[0], this.tags);
            if (candidate && dom.name(candidate) == this.tag) {
                return candidate;
            }
            return null;
        }
    });
    var ListFormatter = Class.extend({
        init: function(tag, unwrapTag) {
            var that = this;
            that.finder = new ListFormatFinder(tag);
            that.tag = tag;
            that.unwrapTag = unwrapTag;
        },
        wrap: function(list, nodes) {
            var li = dom.create(list.ownerDocument, "li"), i, node;
            for (i = 0; i < nodes.length; i++) {
                node = nodes[i];
                if (dom.is(node, "li")) {
                    list.appendChild(node);
                    continue;
                }
                if (dom.is(node, "ul") || dom.is(node, "ol")) {
                    while (node.firstChild) {
                        list.appendChild(node.firstChild);
                    }
                    continue;
                }
                if (dom.is(node, "td")) {
                    while (node.firstChild) {
                        li.appendChild(node.firstChild);
                    }
                    list.appendChild(li);
                    node.appendChild(list);
                    list = list.cloneNode(false);
                    li = li.cloneNode(false);
                    continue;
                }
                li.appendChild(node);
                if (dom.isBlock(node)) {
                    list.appendChild(li);
                    dom.unwrap(node);
                    li = li.cloneNode(false);
                }
            }
            if (li.firstChild) {
                list.appendChild(li);
            }
        },
        containsAny: function(parent, nodes) {
            for (var i = 0; i < nodes.length; i++) {
                if (dom.isAncestorOrSelf(parent, nodes[i])) {
                    return true;
                }
            }
            return false;
        },
        suitable: function(candidate, nodes) {
            if (candidate.className == "k-marker") {
                var sibling = candidate.nextSibling;
                if (sibling && dom.isBlock(sibling)) {
                    return false;
                }
                sibling = candidate.previousSibling;
                if (sibling && dom.isBlock(sibling)) {
                    return false;
                }
            }
            return this.containsAny(candidate, nodes) || dom.isInline(candidate) || candidate.nodeType == 3;
        },
        split: function(range) {
            var nodes = textNodes(range), start, end;
            if (nodes.length) {
                start = dom.parentOfType(nodes[0], [ "li" ]);
                end = dom.parentOfType(nodes[nodes.length - 1], [ "li" ]);
                range.setStartBefore(start);
                range.setEndAfter(end);
                for (var i = 0, l = nodes.length; i < l; i++) {
                    var formatNode = this.finder.findFormat(nodes[i]);
                    if (formatNode) {
                        var parents = $(formatNode).parents("ul,ol");
                        if (parents[0]) {
                            RangeUtils.split(range, parents.last()[0], true);
                        } else {
                            RangeUtils.split(range, formatNode, true);
                        }
                    }
                }
            }
        },
        merge: function(tag, formatNode) {
            var prev = formatNode.previousSibling, next;
            while (prev && (prev.className == "k-marker" || prev.nodeType == 3 && dom.isWhitespace(prev))) {
                prev = prev.previousSibling;
            }
            // merge with previous list
            if (prev && dom.name(prev) == tag) {
                while (formatNode.firstChild) {
                    prev.appendChild(formatNode.firstChild);
                }
                dom.remove(formatNode);
                formatNode = prev;
            }
            next = formatNode.nextSibling;
            while (next && (next.className == "k-marker" || next.nodeType == 3 && dom.isWhitespace(next))) {
                next = next.nextSibling;
            }
            // merge with next list
            if (next && dom.name(next) == tag) {
                while (formatNode.lastChild) {
                    next.insertBefore(formatNode.lastChild, next.firstChild);
                }
                dom.remove(formatNode);
            }
        },
        applyOnSection: function(section, nodes) {
            var tag = this.tag, commonAncestor;
            if (nodes.length == 1) {
                commonAncestor = dom.parentOfType(nodes[0], [ "ul", "ol" ]);
            } else {
                commonAncestor = dom.commonAncestor.apply(null, nodes);
            }
            if (!commonAncestor) {
                commonAncestor = dom.parentOfType(nodes[0], [ "p", "td" ]) || nodes[0].ownerDocument.body;
            }
            if (dom.isInline(commonAncestor)) {
                commonAncestor = dom.blockParentOrBody(commonAncestor);
            }
            var ancestors = [];
            var formatNode = this.finder.findSuitable(nodes);
            if (!formatNode) {
                formatNode = new ListFormatFinder(tag == "ul" ? "ol" : "ul").findSuitable(nodes);
            }
            var childNodes = dom.significantChildNodes(commonAncestor);
            if (!childNodes.length) {
                childNodes = nodes;
            }
            if (/table|tbody/.test(dom.name(commonAncestor))) {
                childNodes = $.map(nodes, function(node) {
                    return dom.parentOfType(node, [ "td" ]);
                });
            }
            function pushAncestor() {
                ancestors.push(this);
            }
            for (var i = 0; i < childNodes.length; i++) {
                var child = childNodes[i];
                var nodeName = dom.name(child);
                if (this.suitable(child, nodes) && (!formatNode || !dom.isAncestorOrSelf(formatNode, child))) {
                    if (formatNode && (nodeName == "ul" || nodeName == "ol")) {
                        // merging lists
                        //Array.prototype.push.apply(ancestors, $.toArray(child.childNodes));
                        $.each(child.childNodes, pushAncestor);
                        dom.remove(child);
                    } else {
                        ancestors.push(child);
                    }
                }
            }
            if (ancestors.length == childNodes.length && commonAncestor != nodes[0].ownerDocument.body && !/table|tbody|tr|td/.test(dom.name(commonAncestor))) {
                ancestors = [ commonAncestor ];
            }
            if (!formatNode) {
                formatNode = dom.create(commonAncestor.ownerDocument, tag);
                dom.insertBefore(formatNode, ancestors[0]);
            }
            this.wrap(formatNode, ancestors);
            if (!dom.is(formatNode, tag)) {
                dom.changeTag(formatNode, tag);
            }
            this.merge(tag, formatNode);
        },
        apply: function(nodes) {
            var i = 0, sections = [], lastSection, lastNodes, section;
            // split nodes into sections that need to be different lists
            do {
                section = dom.parentOfType(nodes[i], [ "td", "body" ]);
                if (!lastSection || section != lastSection) {
                    if (lastSection) {
                        sections.push({
                            section: lastSection,
                            nodes: lastNodes
                        });
                    }
                    lastNodes = [ nodes[i] ];
                    lastSection = section;
                } else {
                    lastNodes.push(nodes[i]);
                }
                i++;
            } while (i < nodes.length);
            sections.push({
                section: lastSection,
                nodes: lastNodes
            });
            for (i = 0; i < sections.length; i++) {
                this.applyOnSection(sections[i].section, sections[i].nodes);
            }
        },
        unwrap: function(ul) {
            var fragment = ul.ownerDocument.createDocumentFragment(), unwrapTag = this.unwrapTag, parents, li, p, child;
            for (li = ul.firstChild; li; li = li.nextSibling) {
                p = dom.create(ul.ownerDocument, unwrapTag || "p");
                while (li.firstChild) {
                    child = li.firstChild;
                    if (dom.isBlock(child)) {
                        if (p.firstChild) {
                            fragment.appendChild(p);
                            p = dom.create(ul.ownerDocument, unwrapTag || "p");
                        }
                        fragment.appendChild(child);
                    } else {
                        p.appendChild(child);
                    }
                }
                if (p.firstChild) {
                    fragment.appendChild(p);
                }
            }
            parents = $(ul).parents("ul,ol");
            if (parents[0]) {
                dom.insertAfter(fragment, parents.last()[0]);
                parents.last().remove();
            } else {
                dom.insertAfter(fragment, ul);
            }
            dom.remove(ul);
        },
        remove: function(nodes) {
            var formatNode;
            for (var i = 0, l = nodes.length; i < l; i++) {
                formatNode = this.finder.findFormat(nodes[i]);
                if (formatNode) {
                    this.unwrap(formatNode);
                }
            }
        },
        toggle: function(range) {
            var that = this, nodes = textNodes(range), ancestor = range.commonAncestorContainer;
            if (!nodes.length) {
                range.selectNodeContents(ancestor);
                nodes = textNodes(range);
                if (!nodes.length) {
                    var text = ancestor.ownerDocument.createTextNode("");
                    range.startContainer.appendChild(text);
                    nodes = [ text ];
                    range.selectNode(text.parentNode);
                }
            }
            if (that.finder.isFormatted(nodes)) {
                that.split(range);
                that.remove(nodes);
            } else {
                that.apply(nodes);
            }
        }
    });
    var ListCommand = Command.extend({
        init: function(options) {
            options.formatter = new ListFormatter(options.tag);
            Command.fn.init.call(this, options);
        }
    });
    var ListTool = FormatTool.extend({
        init: function(options) {
            this.options = options;
            FormatTool.fn.init.call(this, extend(options, {
                finder: new ListFormatFinder(options.tag)
            }));
        },
        command: function(commandArguments) {
            return new ListCommand(extend(commandArguments, {
                tag: this.options.tag
            }));
        }
    });
    extend(Editor, {
        ListFormatFinder: ListFormatFinder,
        ListFormatter: ListFormatter,
        ListCommand: ListCommand,
        ListTool: ListTool
    });
    registerTool("insertUnorderedList", new ListTool({
        tag: "ul",
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Remove Link"
        })
    }));
    registerTool("insertOrderedList", new ListTool({
        tag: "ol",
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Remove Link"
        })
    }));
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, Class = kendo.Class, extend = $.extend, Editor = kendo.ui.editor, dom = Editor.Dom, RangeUtils = Editor.RangeUtils, EditorUtils = Editor.EditorUtils, Command = Editor.Command, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate, InlineFormatter = Editor.InlineFormatter, InlineFormatFinder = Editor.InlineFormatFinder, textNodes = RangeUtils.textNodes, registerTool = Editor.EditorUtils.registerTool;
    var LinkFormatFinder = Class.extend({
        findSuitable: function(sourceNode) {
            return dom.parentOfType(sourceNode, [ "a" ]);
        }
    });
    var LinkFormatter = Class.extend({
        init: function() {
            this.finder = new LinkFormatFinder();
        },
        apply: function(range, attributes) {
            var nodes = textNodes(range), markers, doc, formatter, a;
            if (attributes.innerHTML) {
                markers = RangeUtils.getMarkers(range);
                doc = RangeUtils.documentFromRange(range);
                range.deleteContents();
                a = dom.create(doc, "a", attributes);
                range.insertNode(a);
                if (dom.name(a.parentNode) == "a") {
                    dom.insertAfter(a, a.parentNode);
                }
                if (markers.length > 1) {
                    dom.insertAfter(markers[markers.length - 1], a);
                    dom.insertAfter(markers[1], a);
                    dom[nodes.length > 0 ? "insertBefore" : "insertAfter"](markers[0], a);
                }
            } else {
                formatter = new InlineFormatter([ {
                    tags: [ "a" ]
                } ], attributes);
                formatter.finder = this.finder;
                formatter.apply(nodes);
            }
        }
    });
    var UnlinkCommand = Command.extend({
        init: function(options) {
            options.formatter = /** @ignore */ {
                toggle: function(range) {
                    new InlineFormatter([ {
                        tags: [ "a" ]
                    } ]).remove(textNodes(range));
                }
            };
            this.options = options;
            Command.fn.init.call(this, options);
        }
    });
    var LinkCommand = Command.extend({
        init: function(options) {
            var cmd = this;
            cmd.options = options;
            Command.fn.init.call(cmd, options);
            cmd.formatter = new LinkFormatter();
            if (!options.url) {
                cmd.attributes = null;
                cmd.async = true;
            } else {
                this.exec = function() {
                    this.formatter.apply(options.range, {
                        href: options.url,
                        innerHTML: options.text || options.url,
                        target: options.target
                    });
                };
            }
        },
        _dialogTemplate: function(showText) {
            return kendo.template('<div class="k-editor-dialog">' + "<ol>" + "<li class='k-form-text-row'>" + "<label for='k-editor-link-url'>#: messages.linkWebAddress #</label>" + "<input type='text' class='k-input' id='k-editor-link-url'>" + "</li>" + "# if (showText) { #" + "<li class='k-form-text-row'>" + "<label for='k-editor-link-text'>#: messages.linkText #</label>" + "<input type='text' class='k-input' id='k-editor-link-text'>" + "</li>" + "# } #" + "<li class='k-form-text-row'>" + "<label for='k-editor-link-title'>#: messages.linkToolTip #</label>" + "<input type='text' class='k-input' id='k-editor-link-title'>" + "</li>" + "<li class='k-form-checkbox-row'>" + "<input type='checkbox' id='k-editor-link-target'>" + "<label for='k-editor-link-target'>#: messages.linkOpenInNewWindow #</label>" + "</li>" + "</ol>" + "<div class='k-button-wrapper'>" + '<button class="k-dialog-insert k-button">#: messages.dialogInsert #</button>' + "&nbsp;#: messages.dialogButtonSeparator #&nbsp;" + '<a href="\\#" class="k-dialog-close k-link">#: messages.dialogCancel #</a>' + "</div>" + "</div>")({
                messages: this.editor.options.messages,
                showText: showText
            });
        },
        exec: function() {
            var that = this, range = that.getRange(), collapsed = range.collapsed, nodes, initialText = null, messages = that.editor.options.messages;
            range = that.lockRange(true);
            nodes = textNodes(range);
            function apply(e) {
                var element = dialog.element, href = $("#k-editor-link-url", element).val(), title, text, target;
                if (href && href != "http://") {
                    that.attributes = {
                        href: href
                    };
                    title = $("#k-editor-link-title", element).val();
                    if (title) {
                        that.attributes.title = title;
                    }
                    text = $("#k-editor-link-text", element).val();
                    if (text !== initialText) {
                        that.attributes.innerHTML = text || href;
                    }
                    target = $("#k-editor-link-target", element).is(":checked");
                    that.attributes.target = target ? "_blank" : null;
                    that.formatter.apply(range, that.attributes);
                }
                close(e);
                if (that.change) {
                    that.change();
                }
            }
            function close(e) {
                e.preventDefault();
                dialog.destroy();
                dom.windowFromDocument(RangeUtils.documentFromRange(range)).focus();
                that.releaseRange(range);
            }
            var a = nodes.length ? that.formatter.finder.findSuitable(nodes[0]) : null;
            var showText = nodes.length <= 1 || nodes.length == 2 && collapsed;
            var dialog = EditorUtils.createDialog(that._dialogTemplate(showText), that.editor, extend({}, that.editor.options.dialogOptions, {
                title: messages.createLink,
                close: close,
                visible: false
            })).find(".k-dialog-insert").click(apply).end().find(".k-dialog-close").click(close).end().find(".k-form-text-row input").keydown(function(e) {
                var keys = kendo.keys;
                if (e.keyCode == keys.ENTER) {
                    apply(e);
                } else if (e.keyCode == keys.ESC) {
                    close(e);
                }
            }).end().find("#k-editor-link-url").val(a ? a.getAttribute("href", 2) : "http://").end().find("#k-editor-link-text").val(nodes.length > 0 ? nodes.length == 1 ? nodes[0].nodeValue : nodes[0].nodeValue + nodes[1].nodeValue : "").end().find("#k-editor-link-title").val(a ? a.title : "").end().find("#k-editor-link-target").attr("checked", a ? a.target == "_blank" : false).end().data("kendoWindow").center().open();
            if (showText && nodes.length > 0) {
                initialText = $("#k-editor-link-text", dialog.element).val();
            }
            $("#k-editor-link-url", dialog.element).focus().select();
        },
        redo: function() {
            var that = this, range = that.lockRange(true);
            that.formatter.apply(range, that.attributes);
            that.releaseRange(range);
        }
    });
    var UnlinkTool = Tool.extend({
        init: function(options) {
            this.options = options;
            this.finder = new InlineFormatFinder([ {
                tags: [ "a" ]
            } ]);
            Tool.fn.init.call(this, $.extend(options, {
                command: UnlinkCommand
            }));
        },
        initialize: function(ui, options) {
            Tool.fn.initialize.call(this, ui, options);
            ui.addClass("k-state-disabled");
        },
        update: function(ui, nodes) {
            ui.toggleClass("k-state-disabled", !this.finder.isFormatted(nodes)).removeClass("k-state-hover");
        }
    });
    extend(kendo.ui.editor, {
        LinkFormatFinder: LinkFormatFinder,
        LinkFormatter: LinkFormatter,
        UnlinkCommand: UnlinkCommand,
        LinkCommand: LinkCommand,
        UnlinkTool: UnlinkTool
    });
    registerTool("createLink", new Tool({
        key: "K",
        ctrl: true,
        command: LinkCommand,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Create Link"
        })
    }));
    registerTool("unlink", new UnlinkTool({
        key: "K",
        ctrl: true,
        shift: true,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Remove Link"
        })
    }));
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, extend = $.extend, Editor = kendo.ui.editor, EditorUtils = Editor.EditorUtils, dom = Editor.Dom, registerTool = EditorUtils.registerTool, ToolTemplate = Editor.ToolTemplate, RangeUtils = Editor.RangeUtils, Command = Editor.Command, keys = kendo.keys, KEDITORIMAGEURL = "#k-editor-image-url", KEDITORIMAGETITLE = "#k-editor-image-title";
    var ImageCommand = Command.extend({
        init: function(options) {
            var that = this;
            Command.fn.init.call(that, options);
            that.async = true;
            that.attributes = {};
        },
        insertImage: function(img, range) {
            var attributes = this.attributes;
            if (attributes.src && attributes.src != "http://") {
                if (!img) {
                    img = dom.create(RangeUtils.documentFromRange(range), "img", attributes);
                    img.onload = img.onerror = function() {
                        img.removeAttribute("complete");
                        img.removeAttribute("width");
                        img.removeAttribute("height");
                    };
                    range.deleteContents();
                    range.insertNode(img);
                    range.setStartAfter(img);
                    range.setEndAfter(img);
                    RangeUtils.selectRange(range);
                    return true;
                } else {
                    dom.attr(img, attributes);
                }
            }
            return false;
        },
        _dialogTemplate: function(showBrowser) {
            return kendo.template('<div class="k-editor-dialog">' + "# if (showBrowser) { #" + '<div class="k-imagebrowser"></div>' + "# } #" + "<ol>" + '<li class="k-form-text-row">' + '<label for="k-editor-image-url">#: messages.imageWebAddress #</label>' + '<input type="text" class="k-input" id="k-editor-image-url">' + "</li>" + '<li class="k-form-text-row">' + '<label for="k-editor-image-title">#: messages.imageAltText #</label>' + '<input type="text" class="k-input" id="k-editor-image-title">' + "</li>" + "</ol>" + '<div class="k-button-wrapper">' + '<button class="k-dialog-insert k-button">#: messages.dialogInsert #</button>' + "&nbsp;#: messages.dialogButtonSeparator #&nbsp;" + '<a href="\\#" class="k-dialog-close k-link">#: messages.dialogCancel #</a>' + "</div>" + "</div>")({
                messages: this.editor.options.messages,
                showBrowser: showBrowser
            });
        },
        redo: function() {
            var that = this, range = that.lockRange();
            if (!that.insertImage(RangeUtils.image(range), range)) {
                that.releaseRange(range);
            }
        },
        exec: function() {
            var that = this, range = that.lockRange(), applied = false, img = RangeUtils.image(range), dialog, dialogWidth, options = that.editor.options, messages = options.messages, imageBrowser = options.imageBrowser, showBrowser = !!(kendo.ui.ImageBrowser && imageBrowser && imageBrowser.transport && imageBrowser.transport.read !== undefined);
            function apply(e) {
                var element = dialog.element;
                that.attributes = {
                    src: element.find(KEDITORIMAGEURL).val().replace(/ /g, "%20"),
                    alt: element.find(KEDITORIMAGETITLE).val()
                };
                applied = that.insertImage(img, range);
                close(e);
                if (that.change) {
                    that.change();
                }
            }
            function close(e) {
                e.preventDefault();
                dialog.destroy();
                dom.windowFromDocument(RangeUtils.documentFromRange(range)).focus();
                if (!applied) {
                    that.releaseRange(range);
                }
            }
            function keyDown(e) {
                if (e.keyCode == keys.ENTER) {
                    apply(e);
                } else if (e.keyCode == keys.ESC) {
                    close(e);
                }
            }
            dialogWidth = showBrowser ? {
                width: "960px",
                height: "650px"
            } : {};
            dialog = EditorUtils.createDialog(that._dialogTemplate(showBrowser), that.editor, extend(dialogWidth, options.dialogOptions, {
                title: messages.insertImage,
                close: close,
                visible: false,
                resizable: showBrowser,
                activate: function() {
                    if (showBrowser) {
                        var that = this;
                        new kendo.ui.ImageBrowser(that.element.find(".k-imagebrowser"), extend({}, imageBrowser, {
                            change: function() {
                                that.element.find(KEDITORIMAGEURL).val(this.value());
                            },
                            apply: apply
                        }));
                    }
                }
            })).find(".k-dialog-insert").click(apply).end().find(".k-dialog-close").click(close).end().find(".k-form-text-row input").keydown(keyDown).end().find(KEDITORIMAGEURL).val(img ? img.getAttribute("src", 2) : "http://").end().find(KEDITORIMAGETITLE).val(img ? img.alt : "").end().data("kendoWindow").center().open();
            $(KEDITORIMAGEURL, dialog.element).focus().select();
        }
    });
    kendo.ui.editor.ImageCommand = ImageCommand;
    registerTool("insertImage", new Editor.Tool({
        command: ImageCommand,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Insert Image"
        })
    }));
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, DropDownList = kendo.ui.DropDownList;
    var SelectBox = DropDownList.extend({
        init: function(element, options) {
            var that = this;
            DropDownList.fn.init.call(that, element, options);
            that.value(that.options.title);
        },
        options: {
            name: "SelectBox"
        },
        value: function(value) {
            var that = this, result = DropDownList.fn.value.call(that, value);
            if (value === undefined) {
                return result;
            }
            if (value !== DropDownList.fn.value.call(that)) {
                that.text(that.options.title);
                that._current.removeClass("k-state-selected");
                that.current(null);
                that._oldIndex = that.selectedIndex = -1;
            }
        }
    });
    kendo.ui.plugin(SelectBox);
    kendo.ui.editor.SelectBox = SelectBox;
})(window.kendo.jQuery);

(function($, undefined) {
    // Imports ================================================================
    var kendo = window.kendo, Class = kendo.Class, extend = $.extend, Editor = kendo.ui.editor, dom = Editor.Dom, EditorUtils = Editor.EditorUtils, registerTool = EditorUtils.registerTool, Command = Editor.Command, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate, RangeUtils = Editor.RangeUtils, blockElements = dom.blockElements, BlockFormatFinder = Editor.BlockFormatFinder, BlockFormatter = Editor.BlockFormatter;
    function indent(node, value) {
        var isRtl = $(node).css("direction") == "rtl", indentDirection = isRtl ? "Right" : "Left", property = dom.name(node) != "td" ? "margin" + indentDirection : "padding" + indentDirection;
        if (value === undefined) {
            return node.style[property] || 0;
        } else {
            if (value > 0) {
                node.style[property] = value + "px";
            } else {
                node.style[property] = "";
                if (!node.style.cssText) {
                    node.removeAttribute("style");
                }
            }
        }
    }
    var IndentFormatter = Class.extend({
        init: function() {
            this.finder = new BlockFormatFinder([ {
                tags: dom.blockElements
            } ]);
        },
        apply: function(nodes) {
            var formatNodes = this.finder.findSuitable(nodes), targets = [], i, len, formatNode, parentList, sibling;
            if (formatNodes.length) {
                for (i = 0, len = formatNodes.length; i < len; i++) {
                    if (dom.is(formatNodes[i], "li")) {
                        if (!$(formatNodes[i]).index()) {
                            targets.push(formatNodes[i].parentNode);
                        } else if ($.inArray(formatNodes[i].parentNode, targets) < 0) {
                            targets.push(formatNodes[i]);
                        }
                    } else {
                        targets.push(formatNodes[i]);
                    }
                }
                while (targets.length) {
                    formatNode = targets.shift();
                    if (dom.is(formatNode, "li")) {
                        parentList = formatNode.parentNode;
                        sibling = $(formatNode).prev("li");
                        var siblingList = sibling.find("ul,ol").last();
                        var nestedList = $(formatNode).children("ul,ol")[0];
                        if (nestedList && sibling[0]) {
                            if (siblingList[0]) {
                                siblingList.append(formatNode);
                                siblingList.append($(nestedList).children());
                                dom.remove(nestedList);
                            } else {
                                sibling.append(nestedList);
                                nestedList.insertBefore(formatNode, nestedList.firstChild);
                            }
                        } else {
                            nestedList = sibling.children("ul,ol")[0];
                            if (!nestedList) {
                                nestedList = dom.create(formatNode.ownerDocument, dom.name(parentList));
                                sibling.append(nestedList);
                            }
                            while (formatNode && formatNode.parentNode == parentList) {
                                nestedList.appendChild(formatNode);
                                formatNode = targets.shift();
                            }
                        }
                    } else {
                        var marginLeft = parseInt(indent(formatNode), 10) + 30;
                        indent(formatNode, marginLeft);
                        for (var targetIndex = 0; targetIndex < targets.length; targetIndex++) {
                            if ($.contains(formatNode, targets[targetIndex])) {
                                targets.splice(targetIndex, 1);
                            }
                        }
                    }
                }
            } else {
                var formatter = new BlockFormatter([ {
                    tags: "p"
                } ], {
                    style: {
                        marginLeft: 30
                    }
                });
                formatter.apply(nodes);
            }
        },
        remove: function(nodes) {
            var formatNodes = this.finder.findSuitable(nodes), targetNode, i, len, list, listParent, siblings, formatNode, marginLeft;
            for (i = 0, len = formatNodes.length; i < len; i++) {
                formatNode = $(formatNodes[i]);
                if (formatNode.is("li")) {
                    list = formatNode.parent();
                    listParent = list.parent();
                    // listParent will be ul or ol in case of invalid dom - <ul><li></li><ul><li></li></ul></ul>
                    if (listParent.is("li,ul,ol") && !indent(list[0])) {
                        // skip already processed nodes
                        if (targetNode && $.contains(targetNode, listParent[0])) {
                            continue;
                        }
                        siblings = formatNode.nextAll("li");
                        if (siblings.length) {
                            $(list[0].cloneNode(false)).appendTo(formatNode).append(siblings);
                        }
                        if (listParent.is("li")) {
                            formatNode.insertAfter(listParent);
                        } else {
                            formatNode.appendTo(listParent);
                        }
                        if (!list.children("li").length) {
                            list.remove();
                        }
                        continue;
                    } else {
                        if (targetNode == list[0]) {
                            // removing format on sibling LI elements
                            continue;
                        }
                        targetNode = list[0];
                    }
                } else {
                    targetNode = formatNodes[i];
                }
                marginLeft = parseInt(indent(targetNode), 10) - 30;
                indent(targetNode, marginLeft);
            }
        }
    });
    var IndentCommand = Command.extend({
        init: function(options) {
            options.formatter = /** @ignore */ {
                toggle: function(range) {
                    new IndentFormatter().apply(RangeUtils.nodes(range));
                }
            };
            Command.fn.init.call(this, options);
        }
    });
    var OutdentCommand = Command.extend({
        init: function(options) {
            options.formatter = {
                toggle: function(range) {
                    new IndentFormatter().remove(RangeUtils.nodes(range));
                }
            };
            Command.fn.init.call(this, options);
        }
    });
    var OutdentTool = Tool.extend({
        init: function(options) {
            Tool.fn.init.call(this, options);
            this.finder = new BlockFormatFinder([ {
                tags: blockElements
            } ]);
        },
        update: function(ui, nodes) {
            var suitable = this.finder.findSuitable(nodes), isOutdentable, listParentsCount, i, len;
            for (i = 0, len = suitable.length; i < len; i++) {
                isOutdentable = indent(suitable[i]);
                if (!isOutdentable) {
                    listParentsCount = $(suitable[i]).parents("ul,ol").length;
                    isOutdentable = dom.is(suitable[i], "li") && (listParentsCount > 1 || indent(suitable[i].parentNode)) || dom.ofType(suitable[i], [ "ul", "ol" ]) && listParentsCount > 0;
                }
                if (isOutdentable) {
                    ui.removeClass("k-state-disabled");
                    return;
                }
            }
            ui.addClass("k-state-disabled").removeClass("k-state-hover");
        }
    });
    extend(Editor, {
        IndentFormatter: IndentFormatter,
        IndentCommand: IndentCommand,
        OutdentCommand: OutdentCommand,
        OutdentTool: OutdentTool
    });
    registerTool("indent", new Tool({
        command: IndentCommand,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Indent"
        })
    }));
    registerTool("outdent", new OutdentTool({
        command: OutdentCommand,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "Outdent"
        })
    }));
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, extend = $.extend, Editor = kendo.ui.editor, dom = Editor.Dom, RangeUtils = Editor.RangeUtils, EditorUtils = Editor.EditorUtils, Command = Editor.Command, Tool = Editor.Tool, ToolTemplate = Editor.ToolTemplate;
    var ViewHtmlCommand = Command.extend({
        init: function(options) {
            var cmd = this;
            cmd.options = options;
            Command.fn.init.call(cmd, options);
            cmd.attributes = null;
            cmd.async = true;
        },
        exec: function() {
            var that = this, editor = that.editor, range = editor.getRange(), dialog = $(ViewHtmlCommand.template).appendTo(document.body), content = ViewHtmlCommand.indent(editor.value()), textarea = ".k-editor-textarea", messages = editor.options.messages;
            function apply(e) {
                editor.value(dialog.find(textarea).val());
                close(e);
                if (that.change) {
                    that.change();
                }
                editor.trigger("change");
            }
            function close(e) {
                e.preventDefault();
                dialog.data("kendoWindow").destroy();
                dom.windowFromDocument(RangeUtils.documentFromRange(range)).focus();
            }
            dialog.kendoWindow(extend({}, editor.options.dialogOptions, {
                title: messages.viewHtml,
                close: close
            })).hide().find(textarea).val(content).end().find(".k-dialog-update").click(apply).end().find(".k-dialog-close").click(close).end().show().data("kendoWindow").center();
            dialog.find(textarea).focus();
        }
    });
    extend(ViewHtmlCommand, {
        template: "<div class='k-editor-dialog'>" + "<textarea class='k-editor-textarea k-input'></textarea>" + "<div class='k-button-wrapper'>" + "<button class='k-dialog-update k-button'>Update</button>" + "&nbsp;or&nbsp;" + "<a href='#' class='k-dialog-close k-link'>Close</a>" + "</div>" + "</div>",
        indent: function(content) {
            return content.replace(/<\/(p|li|ul|ol|h[1-6]|table|tr|td|th)>/gi, "</$1>\n").replace(/<(ul|ol)([^>]*)><li/gi, "<$1$2>\n<li").replace(/<br \/>/gi, "<br />\n").replace(/\n$/, "");
        }
    });
    kendo.ui.editor.ViewHtmlCommand = ViewHtmlCommand;
    Editor.EditorUtils.registerTool("viewHtml", new Tool({
        command: ViewHtmlCommand,
        template: new ToolTemplate({
            template: EditorUtils.buttonTemplate,
            title: "View HTML"
        })
    }));
})(window.kendo.jQuery);

(function($) {
    // Imports ================================================================
    var kendo = window.kendo, Class = kendo.Class, extend = $.extend, Editor = kendo.ui.editor, RangeUtils = Editor.RangeUtils, Marker = Editor.Marker;
    var PendingFormats = Class.extend({
        init: function(editor) {
            this.editor = editor;
            this.formats = [];
        },
        apply: function(range) {
            if (!this.hasPending()) {
                return;
            }
            var marker = new Marker();
            marker.addCaret(range);
            var caret = range.startContainer.childNodes[range.startOffset];
            var target = caret.previousSibling;
            if (target) {
                /* under IE, target may be a zero-length text node */
                if (!target.nodeValue) {
                    target = target.previousSibling;
                }
                range.setStart(target, target.nodeValue.length - 1);
            } else {
                range.setStart(range.startContainer, range.startOffset);
            }
            marker.add(range);
            if (!RangeUtils.textNodes(range).length) {
                marker.remove(range);
                range.collapse(true);
                this.editor.selectRange(range);
                return;
            }
            var textNode = marker.end.previousSibling.previousSibling;
            var pendingFormat, formats = this.formats;
            for (var i = 0; i < formats.length; i++) {
                pendingFormat = formats[i];
                var command = pendingFormat.command(extend({
                    range: range
                }, pendingFormat.options.params));
                command.editor = this.editor;
                command.exec();
                range.selectNode(textNode);
            }
            marker.remove(range);
            if (textNode.parentNode) {
                range.setStart(textNode, 1);
                range.collapse(true);
            }
            this.clear();
            this.editor.selectRange(range);
        },
        hasPending: function() {
            return this.formats.length > 0;
        },
        isPending: function(format) {
            return !!this.getPending(format);
        },
        getPending: function(format) {
            var formats = this.formats;
            for (var i = 0; i < formats.length; i++) {
                if (formats[i].name == format) {
                    return formats[i];
                }
            }
            return;
        },
        toggle: function(format) {
            var formats = this.formats;
            for (var i = 0; i < formats.length; i++) {
                if (formats[i].name == format.name) {
                    if (formats[i].params && formats[i].params.value != format.params.value) {
                        formats[i].params.value = format.params.value;
                    } else {
                        formats.splice(i, 1);
                    }
                    return;
                }
            }
            formats.push(format);
        },
        clear: function() {
            this.formats = [];
        }
    });
    extend(Editor, {
        PendingFormats: PendingFormats
    });
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, keys = kendo.keys, ui = kendo.ui, Widget = ui.Widget, activeElement = kendo._activeElement, extractFormat = kendo._extractFormat, parse = kendo.parseFloat, placeholderSupported = kendo.support.placeholder, getCulture = kendo.getCulture, CHANGE = "change", DISABLED = "disabled", READONLY = "readonly", INPUT = "k-input", SPIN = "spin", ns = ".kendoNumericTextBox", TOUCHEND = "touchend", MOUSELEAVE = "mouseleave" + ns, MOUSEUP = "touchcancel" + ns + " " + "touchend" + ns + " mouseup" + ns + " " + MOUSELEAVE, HOVEREVENTS = "mouseenter" + ns + " " + MOUSELEAVE, DEFAULT = "k-state-default", FOCUSED = "k-state-focused", HOVER = "k-state-hover", FOCUS = "focus", POINT = ".", SELECTED = "k-state-selected", STATEDISABLED = "k-state-disabled", ARIA_DISABLED = "aria-disabled", ARIA_READONLY = "aria-readonly", NULL = null, proxy = $.proxy, decimals = {
        190: ".",
        188: ","
    };
    var NumericTextBox = Widget.extend({
        init: function(element, options) {
            var that = this, isStep = options && options.step !== undefined, min, max, step, value, disabled;
            Widget.fn.init.call(that, element, options);
            options = that.options;
            element = that.element.on("blur" + ns, proxy(that._focusout, that)).attr("role", "spinbutton");
            options.placeholder = options.placeholder || element.attr("placeholder");
            that._reset();
            that._wrapper();
            that._arrows();
            that._input();
            if (!kendo.support.mobileOS) {
                that._text.on(FOCUS + ns, proxy(that._click, that));
            } else {
                that._text.on(TOUCHEND + ns + " " + FOCUS + ns, function(e) {
                    that._toggleText(false);
                    if (e.type === FOCUS) {
                        element.focus();
                    }
                });
            }
            min = that.min(element.attr("min"));
            max = that.max(element.attr("max"));
            step = that._parse(element.attr("step"));
            if (options.min === NULL && min !== NULL) {
                options.min = min;
            }
            if (options.max === NULL && max !== NULL) {
                options.max = max;
            }
            if (!isStep && step !== NULL) {
                options.step = step;
            }
            element.attr("aria-valuemin", options.min).attr("aria-valuemax", options.max);
            options.format = extractFormat(options.format);
            value = options.value;
            that.value(value !== NULL ? value : element.val());
            disabled = element.is("[disabled]");
            if (disabled) {
                that.enable(false);
            } else {
                that.readonly(element.is("[readonly]"));
            }
            kendo.notify(that);
        },
        options: {
            name: "NumericTextBox",
            decimals: NULL,
            min: NULL,
            max: NULL,
            value: NULL,
            step: 1,
            culture: "",
            format: "n",
            spinners: true,
            placeholder: "",
            upArrowText: "Increase value",
            downArrowText: "Decrease value"
        },
        events: [ CHANGE, SPIN ],
        _editable: function(options) {
            var that = this, element = that.element, disable = options.disable, readonly = options.readonly, text = that._text.add(element), wrapper = that._inputWrapper.off(HOVEREVENTS);
            that._toggleText(true);
            that._upArrowEventHandler.unbind("press");
            that._downArrowEventHandler.unbind("press");
            element.off("keydown" + ns).off("paste" + ns);
            if (!readonly && !disable) {
                wrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
                text.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false);
                that._upArrowEventHandler.bind("press", function(e) {
                    e.preventDefault();
                    that._spin(1);
                    that._upArrow.addClass(SELECTED);
                });
                that._downArrowEventHandler.bind("press", function(e) {
                    e.preventDefault();
                    that._spin(-1);
                    that._downArrow.addClass(SELECTED);
                });
                that.element.on("keydown" + ns, proxy(that._keydown, that)).on("paste" + ns, proxy(that._paste, that));
            } else {
                wrapper.addClass(disable ? STATEDISABLED : DEFAULT).removeClass(disable ? DEFAULT : STATEDISABLED);
                text.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;
            that.element.add(that._text).add(that._upArrow).add(that._downArrow).add(that._inputWrapper).off(ns);
            if (that._form) {
                that._form.off("reset", that._resetHandler);
            }
            Widget.fn.destroy.call(that);
        },
        min: function(value) {
            return this._option("min", value);
        },
        max: function(value) {
            return this._option("max", value);
        },
        step: function(value) {
            return this._option("step", value);
        },
        value: function(value) {
            var that = this, adjusted;
            if (value === undefined) {
                return that._value;
            }
            value = that._parse(value);
            adjusted = that._adjust(value);
            if (value !== adjusted) {
                return;
            }
            that._update(value);
            that._old = that._value;
        },
        focus: function() {
            this._focusin();
        },
        _adjust: function(value) {
            var that = this, options = that.options, min = options.min, max = options.max;
            if (value === NULL) {
                return value;
            }
            if (min !== NULL && value < min) {
                value = min;
            } else if (max !== NULL && value > max) {
                value = max;
            }
            return value;
        },
        _arrows: function() {
            var that = this, arrows, options = that.options, spinners = options.spinners, element = that.element;
            arrows = element.siblings(".k-icon");
            if (!arrows[0]) {
                arrows = $(buttonHtml("n", options.upArrowText) + buttonHtml("s", options.downArrowText)).insertAfter(element);
                arrows.wrapAll('<span class="k-select"/>');
            }
            arrows.on(MOUSEUP, function() {
                clearTimeout(that._spinning);
                arrows.removeClass(SELECTED);
            });
            if (!spinners) {
                arrows.parent().toggle(spinners);
                that._inputWrapper.addClass("k-expand-padding");
            }
            that._upArrow = arrows.eq(0);
            that._upArrowEventHandler = new kendo.UserEvents(that._upArrow);
            that._downArrow = arrows.eq(1);
            that._downArrowEventHandler = new kendo.UserEvents(that._downArrow);
        },
        _blur: function() {
            var that = this;
            that._toggleText(true);
            that._change(that.element.val());
        },
        _click: function(e) {
            var that = this;
            clearTimeout(that._focusing);
            that._focusing = setTimeout(function() {
                var input = e.target, idx = caret(input)[0], value = input.value.substring(0, idx), format = that._format(that.options.format), group = format[","], groupRegExp = new RegExp("\\" + group, "g"), extractRegExp = new RegExp("([\\d\\" + group + "]+)(\\" + format[POINT] + ")?(\\d+)?"), result = extractRegExp.exec(value), caretPosition = 0;
                if (result) {
                    caretPosition = result[0].replace(groupRegExp, "").length;
                    if (value.indexOf("(") != -1 && that._value < 0) {
                        caretPosition++;
                    }
                }
                that._focusin();
                caret(that.element[0], caretPosition);
            });
        },
        _change: function(value) {
            var that = this;
            that._update(value);
            value = that._value;
            if (that._old != value) {
                that._old = value;
                that.trigger(CHANGE);
                // trigger the DOM change event so any subscriber gets notified
                that.element.trigger(CHANGE);
            }
        },
        _culture: function(culture) {
            return culture || getCulture(this.options.culture);
        },
        _focusin: function() {
            var that = this;
            that._inputWrapper.addClass(FOCUSED);
            that._toggleText(false);
            that.element[0].focus();
        },
        _focusout: function() {
            var that = this;
            clearTimeout(that._focusing);
            that._inputWrapper.removeClass(FOCUSED).removeClass(HOVER);
            that._blur();
        },
        _format: function(format, culture) {
            var numberFormat = this._culture(culture).numberFormat;
            format = format.toLowerCase();
            if (format.indexOf("c") > -1) {
                numberFormat = numberFormat.currency;
            } else if (format.indexOf("p") > -1) {
                numberFormat = numberFormat.percent;
            }
            return numberFormat;
        },
        _input: function() {
            var that = this, CLASSNAME = "k-formatted-value", element = that.element.addClass(INPUT).show()[0], accessKey = element.accessKey, wrapper = that.wrapper, text;
            text = wrapper.find(POINT + CLASSNAME);
            if (!text[0]) {
                text = $('<input type="text"/>').insertBefore(element).addClass(CLASSNAME);
            }
            element.type = "text";
            text[0].tabIndex = element.tabIndex;
            text[0].style.cssText = element.style.cssText;
            text.prop("placeholder", that.options.placeholder);
            if (accessKey) {
                text.attr("accesskey", accessKey);
                element.accessKey = "";
            }
            that._text = text.addClass(element.className);
        },
        _keydown: function(e) {
            var that = this, key = e.keyCode;
            if (key == keys.DOWN) {
                that._step(-1);
            } else if (key == keys.UP) {
                that._step(1);
            } else if (key == keys.ENTER) {
                that._change(that.element.val());
            }
            if (that._prevent(key, e.shiftKey) && !e.ctrlKey) {
                e.preventDefault();
            }
        },
        _paste: function(e) {
            var that = this, element = e.target, value = element.value;
            setTimeout(function() {
                if (that._parse(element.value) === NULL) {
                    that._update(value);
                }
            });
        },
        _prevent: function(key, shiftKey) {
            var that = this, element = that.element[0], value = element.value, options = that.options, min = options.min, numberFormat = that._format(options.format), separator = numberFormat[POINT], precision = options.decimals, selection = caret(element), selectionStart = selection[0], selectionEnd = selection[1], textSelected = selectionStart === 0 && selectionEnd === value.length, prevent = true, number;
            if (precision === NULL) {
                precision = numberFormat.decimals;
            }
            if (key > 16 && key < 21 || key > 32 && key < 37 || key > 47 && key < 58 || key > 95 && key < 106 || key == keys.INSERT || key == keys.DELETE || key == keys.LEFT || key == keys.RIGHT || key == keys.TAB || key == keys.BACKSPACE || key == keys.ENTER) {
                prevent = false;
                if (shiftKey) {
                    number = parseInt(String.fromCharCode(key), 10);
                    if (!isNaN(number)) {
                        number = number + "";
                        value = value.substring(0, selectionStart) + number + value.substring(selectionEnd);
                        if (element.maxLength === -1 || element.maxLength >= value.length) {
                            element.value = value;
                            caret(element, selectionStart + number.length);
                        }
                        prevent = true;
                    }
                }
            } else if ((decimals[key] === separator || key == 110) && precision > 0 && (value.indexOf(separator) == -1 || textSelected)) {
                if (key == 110) {
                    element.value = value.substring(0, selectionStart) + separator + value.substring(selectionEnd);
                    caret(element, selectionStart + separator.length);
                } else if (!shiftKey) {
                    prevent = false;
                }
            } else if ((min === NULL || min < 0) && value.indexOf("-") == -1 && (key == 189 || key == 109 || key == 173) && selectionStart === 0) {
                //sign
                prevent = false;
            }
            return prevent;
        },
        _option: function(option, value) {
            var that = this, options = that.options;
            if (value === undefined) {
                return options[option];
            }
            value = that._parse(value);
            if (!value && option === "step") {
                return;
            }
            options[option] = that._parse(value);
            that.element.attr("aria-value" + option, options[option]);
        },
        _spin: function(step, timeout) {
            var that = this;
            timeout = timeout || 500;
            clearTimeout(that._spinning);
            that._spinning = setTimeout(function() {
                that._spin(step, 50);
            }, timeout);
            that._step(step);
        },
        _step: function(step) {
            var that = this, element = that.element, value = that._parse(element.val()) || 0;
            if (activeElement() != element[0]) {
                that._focusin();
            }
            value += that.options.step * step;
            that._update(that._adjust(value));
            that.trigger(SPIN);
        },
        _toggleHover: function(e) {
            $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter");
        },
        _toggleText: function(toggle) {
            var that = this;
            that._text.toggle(toggle);
            that.element.toggle(!toggle);
        },
        _parse: function(value, culture) {
            return parse(value, this._culture(culture), this.options.format);
        },
        _update: function(value) {
            var that = this, options = that.options, format = options.format, decimals = options.decimals, culture = that._culture(), numberFormat = that._format(format, culture), isNotNull;
            if (decimals === NULL) {
                decimals = numberFormat.decimals;
            }
            value = that._parse(value, culture);
            isNotNull = value !== NULL;
            if (isNotNull) {
                value = parseFloat(value.toFixed(decimals));
            }
            that._value = value = that._adjust(value);
            that._placeholder(kendo.toString(value, format, culture));
            that.element.val(isNotNull ? value.toString().replace(POINT, numberFormat[POINT]) : "").attr("aria-valuenow", value);
        },
        _placeholder: function(value) {
            this._text.val(value);
            if (!placeholderSupported && !value) {
                this._text.val(this.options.placeholder);
            }
        },
        _wrapper: function() {
            var that = this, element = that.element, DOMElement = element[0], wrapper;
            wrapper = element.parents(".k-numerictextbox");
            if (!wrapper.is("span.k-numerictextbox")) {
                wrapper = element.hide().wrap('<span class="k-numeric-wrap k-state-default" />').parent();
                wrapper = wrapper.wrap("<span/>").parent();
            }
            wrapper[0].style.cssText = DOMElement.style.cssText;
            DOMElement.style.width = "";
            that.wrapper = wrapper.addClass("k-widget k-numerictextbox").addClass(DOMElement.className).css("display", "");
            that._inputWrapper = $(wrapper[0].firstChild);
        },
        _reset: function() {
            var that = this, element = that.element, form = element.closest("form");
            if (form[0]) {
                that._resetHandler = function() {
                    setTimeout(function() {
                        that.value(element[0].value);
                    });
                };
                that._form = form.on("reset", that._resetHandler);
            }
        }
    });
    function buttonHtml(className, text) {
        return '<span unselectable="on" class="k-link"><span unselectable="on" class="k-icon k-i-arrow-' + className + '" title="' + text + '">' + text + "</span></span>";
    }
    function caret(element, position) {
        var range, isPosition = position !== undefined;
        if (element.selectionStart !== undefined) {
            if (isPosition) {
                element.focus();
                element.setSelectionRange(position, position);
            } else {
                position = [ element.selectionStart, element.selectionEnd ];
            }
        } else if (document.selection) {
            if ($(element).is(":visible")) {
                element.focus();
            }
            range = document.selection.createRange();
            if (isPosition) {
                range.move("character", position);
                range.select();
            } else {
                var rangeElement = element.createTextRange(), rangeDuplicated = rangeElement.duplicate(), selectionStart, selectionEnd;
                rangeElement.moveToBookmark(range.getBookmark());
                rangeDuplicated.setEndPoint("EndToStart", rangeElement);
                selectionStart = rangeDuplicated.text.length;
                selectionEnd = selectionStart + rangeElement.text.length;
                position = [ selectionStart, selectionEnd ];
            }
        }
        return position;
    }
    ui.plugin(NumericTextBox);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, activeElement = kendo._activeElement, touch = kendo.support.touch && kendo.support.mobileOS, mobile = touch || kendo.support.pointers, MOUSEDOWN = "mousedown", CLICK = "click", extend = $.extend, proxy = $.proxy, each = $.each, template = kendo.template, keys = kendo.keys, Widget = ui.Widget, excludedNodesRegExp = /^(ul|a|div)$/i, NS = ".kendoMenu", IMG = "img", OPEN = "open", MENU = "k-menu", LINK = "k-link", LAST = "k-last", CLOSE = "close", TIMER = "timer", FIRST = "k-first", IMAGE = "k-image", SELECT = "select", ZINDEX = "zIndex", ACTIVATE = "activate", DEACTIVATE = "deactivate", MOUSEENTER = kendo.support.pointers ? "MSPointerOver" : "mouseenter", MOUSELEAVE = kendo.support.pointers ? "MSPointerOut" : "mouseleave", KENDOPOPUP = "kendoPopup", DEFAULTSTATE = "k-state-default", HOVERSTATE = "k-state-hover", FOCUSEDSTATE = "k-state-focused", DISABLEDSTATE = "k-state-disabled", groupSelector = ".k-group", allItemsSelector = ":not(.k-list) > .k-item", disabledSelector = ".k-item.k-state-disabled", itemSelector = ".k-item:not(.k-state-disabled)", linkSelector = ".k-item:not(.k-state-disabled) > .k-link", exclusionSelector = ":not(.k-item.k-separator)", nextSelector = exclusionSelector + ":eq(0)", lastSelector = exclusionSelector + ":last", templateSelector = "div:not(.k-animation-container,.k-list-container)", templates = {
        content: template("<div class='k-content k-group' tabindex='-1'>#= content(item) #</div>"),
        group: template("<ul class='#= groupCssClass(group) #'#= groupAttributes(group) # role='menu' aria-hidden='true'>" + "#= renderItems(data) #" + "</ul>"),
        itemWrapper: template("<#= tag(item) # class='#= textClass(item) #'#= textAttributes(item) #>" + "#= image(item) ##= sprite(item) ##= text(item) #" + "#= arrow(data) #" + "</#= tag(item) #>"),
        item: template("<li class='#= wrapperCssClass(group, item) #' role='menuitem' #=item.items ? \"aria-haspopup='true'\": \"\"#" + "#=item.enabled === false ? \"aria-disabled='true'\" : ''#>" + "#= itemWrapper(data) #" + "# if (item.items) { #" + "#= subGroup({ items: item.items, menu: menu, group: { expanded: item.expanded } }) #" + "# } else if (item.content || item.contentUrl) { #" + "#= renderContent(data) #" + "# } #" + "</li>"),
        image: template("<img class='k-image' alt='' src='#= imageUrl #' />"),
        arrow: template("<span class='#= arrowClass(item, group) #'></span>"),
        sprite: template("<span class='k-sprite #= spriteCssClass #'></span>"),
        empty: template("")
    }, rendering = {
        wrapperCssClass: function(group, item) {
            var result = "k-item", index = item.index;
            if (item.enabled === false) {
                result += " k-state-disabled";
            } else {
                result += " k-state-default";
            }
            if (group.firstLevel && index === 0) {
                result += " k-first";
            }
            if (index == group.length - 1) {
                result += " k-last";
            }
            if (item.cssClass) {
                result += " " + item.cssClass;
            }
            return result;
        },
        textClass: function() {
            return LINK;
        },
        textAttributes: function(item) {
            return item.url ? " href='" + item.url + "'" : "";
        },
        arrowClass: function(item, group) {
            var result = "k-icon";
            if (group.horizontal) {
                result += " k-i-arrow-s";
            } else {
                result += " k-i-arrow-e";
            }
            return result;
        },
        text: function(item) {
            return item.encoded === false ? item.text : kendo.htmlEncode(item.text);
        },
        tag: function(item) {
            return item.url ? "a" : "span";
        },
        groupAttributes: function(group) {
            return group.expanded !== true ? " style='display:none'" : "";
        },
        groupCssClass: function() {
            return "k-group";
        },
        content: function(item) {
            return item.content ? item.content : "&nbsp;";
        }
    };
    function getEffectDirection(direction, root) {
        direction = direction.split(" ")[!root + 0] || direction;
        return direction.replace("top", "up").replace("bottom", "down");
    }
    function parseDirection(direction, root, isRtl) {
        direction = direction.split(" ")[!root + 0] || direction;
        var output = {
            origin: [ "bottom", isRtl ? "right" : "left" ],
            position: [ "top", isRtl ? "right" : "left" ]
        }, horizontal = /left|right/.test(direction);
        if (horizontal) {
            output.origin = [ "top", direction ];
            output.position[1] = kendo.directions[direction].reverse;
        } else {
            output.origin[0] = direction;
            output.position[0] = kendo.directions[direction].reverse;
        }
        output.origin = output.origin.join(" ");
        output.position = output.position.join(" ");
        return output;
    }
    function contains(parent, child) {
        try {
            return $.contains(parent, child);
        } catch (e) {
            return false;
        }
    }
    function updateItemClasses(item) {
        item = $(item);
        item.addClass("k-item").children(IMG).addClass(IMAGE);
        item.children("a").addClass(LINK).children(IMG).addClass(IMAGE);
        item.filter(":not([disabled])").addClass(DEFAULTSTATE);
        item.filter(".k-separator:empty").append("&nbsp;");
        item.filter("li[disabled]").addClass(DISABLEDSTATE).removeAttr("disabled").attr("aria-disabled", true);
        if (!item.filter("[role]").length) {
            item.attr("role", "menuitem");
        }
        if (!item.children("." + LINK).length) {
            item.contents().filter(function() {
                return !this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !$.trim(this.nodeValue));
            }).wrapAll("<span class='" + LINK + "'/>");
        }
        updateArrow(item);
        updateFirstLast(item);
    }
    function updateArrow(item) {
        item = $(item);
        item.find("> .k-link > [class*=k-i-arrow]").remove();
        item.filter(":has(.k-group)").children(".k-link:not(:has([class*=k-i-arrow]))").each(function() {
            var item = $(this), parent = item.parent().parent();
            item.append("<span class='k-icon " + (parent.hasClass(MENU + "-horizontal") ? "k-i-arrow-s" : "k-i-arrow-e") + "'/>");
        });
    }
    function updateFirstLast(item) {
        item = $(item);
        item.filter(".k-first:not(:first-child)").removeClass(FIRST);
        item.filter(".k-last:not(:last-child)").removeClass(LAST);
        item.filter(":first-child").addClass(FIRST);
        item.filter(":last-child").addClass(LAST);
    }
    var Menu = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            element = that.wrapper = that.element;
            options = that.options;
            that._initData(options);
            that._updateClasses();
            that._animations(options);
            that.nextItemZIndex = 100;
            that._tabindex();
            that._focusProxy = proxy(that._focusHandler, that);
            element.on("touchstart MSPointerDown", that._focusProxy).on(CLICK + NS, disabledSelector, false).on(CLICK + NS, itemSelector, proxy(that._click, that)).on("keydown" + NS, proxy(that._keydown, that)).on("focus" + NS, proxy(that._focus, that)).on("focus" + NS, ".k-content", proxy(that._focus, that)).on("blur" + NS, proxy(that._removeHoverItem, that)).on(MOUSEENTER + NS, itemSelector, proxy(that._mouseenter, that)).on(MOUSELEAVE + NS, itemSelector, proxy(that._mouseleave, that)).on(MOUSEENTER + NS + " " + MOUSELEAVE + NS + " " + MOUSEDOWN + NS + " " + CLICK + NS, linkSelector, proxy(that._toggleHover, that));
            if (options.openOnClick) {
                that.clicked = false;
                that._documentClickHandler = proxy(that._documentClick, that);
                $(document).click(that._documentClickHandler);
            }
            element.attr("role", "menubar");
            if (element[0].id) {
                that._ariaId = kendo.format("{0}_mn_active", element[0].id);
            }
            kendo.notify(that);
        },
        events: [ OPEN, CLOSE, ACTIVATE, DEACTIVATE, SELECT ],
        options: {
            name: "Menu",
            animation: {
                open: {
                    duration: 200
                },
                close: {
                    // if close animation effects are defined, they will be used instead of open.reverse
                    duration: 100
                }
            },
            orientation: "horizontal",
            direction: "default",
            openOnClick: false,
            closeOnClick: true,
            hoverDelay: 100
        },
        _initData: function(options) {
            var that = this;
            if (options.dataSource) {
                that.element.empty();
                that.append(options.dataSource, that.element);
            }
        },
        setOptions: function(options) {
            var animation = this.options.animation;
            this._animations(options);
            options.animation = extend(true, animation, options.animation);
            if ("dataSource" in options) {
                this._initData(options);
            }
            Widget.fn.setOptions.call(this, options);
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that.element.off(NS);
            if (that._documentClickHandler) {
                $(document).unbind("click", that._documentClickHandler);
            }
        },
        enable: function(element, enable) {
            this._toggleDisabled(element, enable !== false);
            return this;
        },
        disable: function(element) {
            this._toggleDisabled(element, false);
            return this;
        },
        append: function(item, referenceItem) {
            referenceItem = this.element.find(referenceItem);
            var inserted = this._insert(item, referenceItem, referenceItem.length ? referenceItem.find("> .k-group, > .k-animation-container > .k-group") : null);
            each(inserted.items, function() {
                inserted.group.append(this);
                updateArrow(this);
            });
            updateArrow(referenceItem);
            updateFirstLast(inserted.group.find(".k-first, .k-last").add(inserted.items));
            return this;
        },
        insertBefore: function(item, referenceItem) {
            referenceItem = this.element.find(referenceItem);
            var inserted = this._insert(item, referenceItem, referenceItem.parent());
            each(inserted.items, function() {
                referenceItem.before(this);
                updateArrow(this);
                updateFirstLast(this);
            });
            updateFirstLast(referenceItem);
            return this;
        },
        insertAfter: function(item, referenceItem) {
            referenceItem = this.element.find(referenceItem);
            var inserted = this._insert(item, referenceItem, referenceItem.parent());
            each(inserted.items, function() {
                referenceItem.after(this);
                updateArrow(this);
                updateFirstLast(this);
            });
            updateFirstLast(referenceItem);
            return this;
        },
        _insert: function(item, referenceItem, parent) {
            var that = this, items, groups;
            if (!referenceItem || !referenceItem.length) {
                parent = that.element;
            }
            var plain = $.isPlainObject(item), groupData = {
                firstLevel: parent.hasClass(MENU),
                horizontal: parent.hasClass(MENU + "-horizontal"),
                expanded: true,
                length: parent.children().length
            };
            if (referenceItem && !parent.length) {
                parent = $(Menu.renderGroup({
                    group: groupData
                })).appendTo(referenceItem);
            }
            if (plain || $.isArray(item)) {
                // is JSON
                items = $.map(plain ? [ item ] : item, function(value, idx) {
                    if (typeof value === "string") {
                        return $(value);
                    } else {
                        return $(Menu.renderItem({
                            group: groupData,
                            item: extend(value, {
                                index: idx
                            })
                        }));
                    }
                });
            } else {
                items = $(item);
                groups = items.find("> ul").addClass("k-group").attr("role", "menu");
                items = items.filter("li");
                items.add(groups.find("> li")).each(function() {
                    updateItemClasses(this);
                });
            }
            return {
                items: items,
                group: parent
            };
        },
        remove: function(element) {
            element = this.element.find(element);
            var that = this, parent = element.parentsUntil(that.element, allItemsSelector), group = element.parent("ul");
            element.remove();
            if (group && !group.children(allItemsSelector).length) {
                var container = group.parent(".k-animation-container");
                if (container.length) {
                    container.remove();
                } else {
                    group.remove();
                }
            }
            if (parent.length) {
                parent = parent.eq(0);
                updateArrow(parent);
                updateFirstLast(parent);
            }
            return that;
        },
        open: function(element) {
            var that = this, options = that.options, horizontal = options.orientation == "horizontal", direction = options.direction, isRtl = kendo.support.isRtl(that.wrapper);
            element = that.element.find(element);
            if (/^(top|bottom|default)$/.test(direction)) {
                if (isRtl) {
                    direction = horizontal ? (direction + " left").replace("default", "bottom") : "left";
                } else {
                    direction = horizontal ? (direction + " right").replace("default", "bottom") : "right";
                }
            }
            element.siblings().find(">.k-popup:visible,>.k-animation-container>.k-popup:visible").each(function() {
                var popup = $(this).data("kendoPopup");
                if (popup) {
                    popup.close();
                }
            });
            element.each(function() {
                var li = $(this);
                clearTimeout(li.data(TIMER));
                li.data(TIMER, setTimeout(function() {
                    var ul = li.find(".k-group:first:hidden"), popup;
                    if (ul[0] && that.trigger(OPEN, {
                        item: li[0]
                    }) === false) {
                        li.data(ZINDEX, li.css(ZINDEX));
                        li.css(ZINDEX, that.nextItemZIndex++);
                        popup = ul.data(KENDOPOPUP);
                        var root = li.parent().hasClass(MENU), parentHorizontal = root && horizontal, directions = parseDirection(direction, root, isRtl), effects = options.animation.open.effects, openEffects = effects !== undefined ? effects : "slideIn:" + getEffectDirection(direction, root);
                        if (!popup) {
                            popup = ul.kendoPopup({
                                activate: function() {
                                    that.trigger(ACTIVATE, {
                                        item: this.wrapper.parent()
                                    });
                                },
                                deactivate: function() {
                                    that.trigger(DEACTIVATE, {
                                        item: this.wrapper.parent()
                                    });
                                },
                                origin: directions.origin,
                                position: directions.position,
                                collision: options.popupCollision !== undefined ? options.popupCollision : parentHorizontal ? "fit" : "fit flip",
                                anchor: li,
                                appendTo: li,
                                animation: {
                                    open: extend(true, {
                                        effects: openEffects
                                    }, options.animation.open),
                                    close: options.animation.close
                                },
                                close: function(e) {
                                    var li = e.sender.wrapper.parent();
                                    if (!that.trigger(CLOSE, {
                                        item: li[0]
                                    })) {
                                        li.css(ZINDEX, li.data(ZINDEX));
                                        li.removeData(ZINDEX);
                                    } else {
                                        e.preventDefault();
                                    }
                                }
                            }).data(KENDOPOPUP);
                        } else {
                            popup = ul.data(KENDOPOPUP);
                            popup.options.origin = directions.origin;
                            popup.options.position = directions.position;
                            popup.options.animation.open.effects = openEffects;
                        }
                        ul.removeAttr("aria-hidden");
                        popup.open();
                    }
                }, that.options.hoverDelay));
            });
            return that;
        },
        close: function(items) {
            var that = this, element = that.element;
            items = element.find(items);
            if (!items.length) {
                items = element.find(">.k-item");
            }
            items.each(function() {
                var li = $(this);
                clearTimeout(li.data(TIMER));
                li.data(TIMER, setTimeout(function() {
                    var popup = li.find(".k-group:first:visible").data(KENDOPOPUP);
                    if (popup) {
                        popup.close();
                        popup.element.attr("aria-hidden", true);
                    }
                }, that.options.hoverDelay));
            });
            return that;
        },
        _toggleDisabled: function(items, enable) {
            this.element.find(items).each(function() {
                $(this).toggleClass(DEFAULTSTATE, enable).toggleClass(DISABLEDSTATE, !enable).attr("aria-disabled", !enable);
            });
        },
        _toggleHover: function(e) {
            var target = $(kendo.eventTarget(e) || e.target).closest(allItemsSelector), isEnter = e.type == MOUSEENTER || MOUSEDOWN.indexOf(e.type) !== -1;
            if (!target.parents("li." + DISABLEDSTATE).length) {
                target.toggleClass(HOVERSTATE, isEnter || e.type == "mousedown" || e.type == "click");
            }
            this._removeHoverItem();
        },
        _removeHoverItem: function() {
            var oldHoverItem = this._hoverItem();
            if (oldHoverItem && oldHoverItem.hasClass(FOCUSEDSTATE)) {
                oldHoverItem.removeClass(FOCUSEDSTATE);
                this._oldHoverItem = null;
            }
        },
        _updateClasses: function() {
            var element = this.element, items;
            element.addClass("k-widget k-reset k-header " + MENU).addClass(MENU + "-" + this.options.orientation);
            element.find("li > ul").addClass("k-group").attr("role", "menu").attr("aria-hidden", element.is(":visible")).end().find("li > div").addClass("k-content").attr("tabindex", "-1");
            // Capture the focus before the Menu
            items = element.find("> li,.k-group > li");
            items.each(function() {
                updateItemClasses(this);
            });
        },
        _mouseenter: function(e) {
            var that = this, element = $(e.currentTarget), hasChildren = element.children(".k-animation-container").length || element.children(groupSelector).length;
            if (e.delegateTarget != element.parents(".k-menu")[0]) {
                return;
            }
            if (!that.options.openOnClick || that.clicked) {
                if (!contains(e.currentTarget, e.relatedTarget) && hasChildren) {
                    that.open(element);
                }
            }
            if (that.options.openOnClick && that.clicked || mobile) {
                element.siblings().each(proxy(function(_, sibling) {
                    that.close(sibling);
                }, that));
            }
        },
        _mouseleave: function(e) {
            var that = this, element = $(e.currentTarget), hasChildren = element.children(".k-animation-container").length || element.children(groupSelector).length;
            if (element.parentsUntil(".k-animation-container", ".k-list-container,.k-calendar-container")[0]) {
                e.stopImmediatePropagation();
                return;
            }
            if (!that.options.openOnClick && !touch && !(kendo.support.pointers && e.originalEvent.pointerType == 2) && !contains(e.currentTarget, e.relatedTarget) && hasChildren) {
                that.close(element);
            }
        },
        _click: function(e) {
            var that = this, openHandle, options = that.options, target = $(kendo.eventTarget(e)), nodeName = target[0] ? target[0].nodeName.toUpperCase() : "", formNode = nodeName == "INPUT" || nodeName == "SELECT" || nodeName == "BUTTON", link = target.closest("." + LINK), element = target.closest(allItemsSelector), href = link.attr("href"), childGroup, childGroupVisible, isLink = !!href && href.charAt(href.length - 1) != "#";
            if (element.children(templateSelector)[0]) {
                return;
            }
            if (element.hasClass(DISABLEDSTATE)) {
                e.preventDefault();
                return;
            }
            if (!e.handled && that.trigger(SELECT, {
                item: element[0]
            }) && !formNode) {
                // We shouldn't stop propagation and shoudn't prevent form elements.
                e.preventDefault();
            }
            e.handled = true;
            childGroup = element.children(groupSelector + ",.k-animation-container");
            childGroupVisible = childGroup.is(":visible");
            if (options.closeOnClick && !isLink && (!childGroup.length || options.openOnClick && childGroupVisible)) {
                element.removeClass(HOVERSTATE).css("height");
                // Force refresh for Chrome
                that._oldHoverItem = that._findRootParent(element);
                that.close(link.parentsUntil(that.element, allItemsSelector));
                that.clicked = false;
                if ("MSPointerUp".indexOf(e.type) != -1) {
                    e.preventDefault();
                }
                return;
            }
            if ((!element.parent().hasClass(MENU) || !options.openOnClick) && mobile) {
                return;
            }
            if (!isLink && !formNode) {
                e.preventDefault();
            }
            that.clicked = true;
            openHandle = childGroup.is(":visible") ? CLOSE : OPEN;
            if (!options.closeOnClick && openHandle == CLOSE) {
                return;
            }
            that[openHandle](element);
        },
        _documentClick: function(e) {
            if (contains(this.element[0], e.target)) {
                return;
            }
            this.clicked = false;
        },
        _focus: function(e) {
            var that = this, target = e.target, hoverItem = that._hoverItem(), active = activeElement();
            if (target != that.wrapper[0] && !$(target).is(":focusable")) {
                e.stopPropagation();
                $(target).closest(".k-content").closest(".k-group").closest(".k-item").addClass(FOCUSEDSTATE);
                that.wrapper.focus();
                return;
            }
            if (active === e.currentTarget) {
                if (hoverItem.length) {
                    that._moveHover([], hoverItem);
                } else if (!that._oldHoverItem) {
                    that._moveHover([], that.wrapper.children().first());
                }
            }
        },
        _keydown: function(e) {
            var that = this, key = e.keyCode, hoverItem = that._oldHoverItem, target, belongsToVertical, hasChildren, isRtl = kendo.support.isRtl(that.wrapper);
            if (e.target != e.currentTarget && key != keys.ESC) {
                return;
            }
            if (!hoverItem) {
                hoverItem = that._oldHoverItem = that._hoverItem();
            }
            belongsToVertical = that._itemBelongsToVertival(hoverItem);
            hasChildren = that._itemHasChildren(hoverItem);
            if (key == keys.RIGHT) {
                target = that[isRtl ? "_itemLeft" : "_itemRight"](hoverItem, belongsToVertical, hasChildren);
            } else if (key == keys.LEFT) {
                target = that[isRtl ? "_itemRight" : "_itemLeft"](hoverItem, belongsToVertical, hasChildren);
            } else if (key == keys.DOWN) {
                target = that._itemDown(hoverItem, belongsToVertical, hasChildren);
            } else if (key == keys.UP) {
                target = that._itemUp(hoverItem, belongsToVertical, hasChildren);
            } else if (key == keys.ESC) {
                target = that._itemEsc(hoverItem, belongsToVertical);
            } else if (key == keys.ENTER || key == keys.SPACEBAR) {
                target = hoverItem.children(".k-link");
                if (target.length > 0) {
                    that._click({
                        target: target[0],
                        preventDefault: function() {}
                    });
                    that._moveHover(hoverItem, that._findRootParent(hoverItem));
                }
            } else if (key == keys.TAB) {
                target = that._findRootParent(hoverItem);
                that.close(target);
                that._moveHover(hoverItem, target);
                return;
            }
            if (target && target[0]) {
                e.preventDefault();
                e.stopPropagation();
            }
        },
        _hoverItem: function() {
            return this.wrapper.find(".k-item.k-state-hover,.k-item.k-state-focused").filter(":visible");
        },
        _itemBelongsToVertival: function(item) {
            var menuIsVertical = this.wrapper.hasClass("k-menu-vertical");
            if (!item.length) {
                return menuIsVertical;
            }
            return item.parent().hasClass("k-group") || menuIsVertical;
        },
        _itemHasChildren: function(item) {
            if (!item.length) {
                return false;
            }
            return item.children("ul.k-group, div.k-animation-container").length > 0;
        },
        _moveHover: function(item, nextItem) {
            var that = this, id = that._ariaId;
            if (item.length && nextItem.length) {
                item.removeClass(FOCUSEDSTATE);
                if (item[0].id === id) {
                    item.removeAttr("id");
                }
            }
            if (nextItem.length) {
                if (nextItem[0].id) {
                    id = nextItem[0].id;
                }
                nextItem.addClass(FOCUSEDSTATE);
                that._oldHoverItem = nextItem;
                if (id) {
                    that.element.removeAttr("aria-activedescendant");
                    nextItem.attr("id", id);
                    that.element.attr("aria-activedescendant", id);
                }
            }
        },
        _findRootParent: function(item) {
            if (item.parent().hasClass("k-menu")) {
                return item;
            } else {
                return item.parentsUntil(".k-menu", "li.k-item").last();
            }
        },
        _isRootItem: function(item) {
            return item.parent().hasClass("k-menu");
        },
        _itemRight: function(item, belongsToVertical, hasChildren) {
            var that = this, nextItem, parentItem;
            if (!belongsToVertical) {
                nextItem = item.nextAll(nextSelector);
                if (!nextItem.length) {
                    nextItem = item.prevAll(lastSelector);
                }
            } else if (hasChildren) {
                that.open(item);
                nextItem = item.find(".k-group").children().first();
            } else if (that.options.orientation == "horizontal") {
                parentItem = that._findRootParent(item);
                that.close(parentItem);
                nextItem = parentItem.nextAll(nextSelector);
            }
            if (nextItem && !nextItem.length) {
                nextItem = that.wrapper.children(".k-item").first();
            } else if (!nextItem) {
                nextItem = [];
            }
            that._moveHover(item, nextItem);
            return nextItem;
        },
        _itemLeft: function(item, belongsToVertical) {
            var that = this, nextItem;
            if (!belongsToVertical) {
                nextItem = item.prevAll(nextSelector);
                if (!nextItem.length) {
                    nextItem = item.nextAll(lastSelector);
                }
            } else {
                nextItem = item.parent().closest(".k-item");
                that.close(nextItem);
                if (that._isRootItem(nextItem) && that.options.orientation == "horizontal") {
                    nextItem = nextItem.prevAll(nextSelector);
                }
            }
            if (!nextItem.length) {
                nextItem = that.wrapper.children(".k-item").last();
            }
            that._moveHover(item, nextItem);
            return nextItem;
        },
        _itemDown: function(item, belongsToVertical, hasChildren) {
            var that = this, nextItem;
            if (!belongsToVertical) {
                if (!hasChildren || item.hasClass(DISABLEDSTATE)) {
                    return;
                } else {
                    that.open(item);
                    nextItem = item.find(".k-group").children().first();
                }
            } else {
                nextItem = item.nextAll(nextSelector);
            }
            if (!nextItem.length && item.length) {
                nextItem = item.parent().children().first();
            } else if (!item.length) {
                nextItem = that.wrapper.children(".k-item").first();
            }
            that._moveHover(item, nextItem);
            return nextItem;
        },
        _itemUp: function(item, belongsToVertical) {
            var that = this, nextItem;
            if (!belongsToVertical) {
                return;
            } else {
                nextItem = item.prevAll(nextSelector);
            }
            if (!nextItem.length && item.length) {
                nextItem = item.parent().children().last();
            } else if (!item.length) {
                nextItem = that.wrapper.children(".k-item").last();
            }
            that._moveHover(item, nextItem);
            return nextItem;
        },
        _itemEsc: function(item, belongsToVertical) {
            var that = this, nextItem;
            if (!belongsToVertical) {
                return item;
            } else {
                nextItem = item.parent().closest(".k-item");
                that.close(nextItem);
                that._moveHover(item, nextItem);
            }
            return nextItem;
        },
        _focusHandler: function(e) {
            var that = this, item = $(kendo.eventTarget(e)).closest(allItemsSelector);
            setTimeout(function() {
                that._moveHover([], item);
                if (item.children(".k-content")[0]) {
                    item.parent().closest(".k-item").removeClass(FOCUSEDSTATE);
                }
            }, 200);
        },
        _animations: function(options) {
            if (options && "animation" in options && !options.animation) {
                options.animation = {
                    open: {
                        effects: {}
                    },
                    close: {
                        hide: true,
                        effects: {}
                    }
                };
            }
        }
    });
    // client-side rendering
    extend(Menu, {
        renderItem: function(options) {
            options = extend({
                menu: {},
                group: {}
            }, options);
            var empty = templates.empty, item = options.item;
            return templates.item(extend(options, {
                image: item.imageUrl ? templates.image : empty,
                sprite: item.spriteCssClass ? templates.sprite : empty,
                itemWrapper: templates.itemWrapper,
                renderContent: Menu.renderContent,
                arrow: item.items || item.content ? templates.arrow : empty,
                subGroup: Menu.renderGroup
            }, rendering));
        },
        renderGroup: function(options) {
            return templates.group(extend({
                renderItems: function(options) {
                    var html = "", i = 0, items = options.items, len = items ? items.length : 0, group = extend({
                        length: len
                    }, options.group);
                    for (;i < len; i++) {
                        html += Menu.renderItem(extend(options, {
                            group: group,
                            item: extend({
                                index: i
                            }, items[i])
                        }));
                    }
                    return html;
                }
            }, options, rendering));
        },
        renderContent: function(options) {
            return templates.content(extend(options, rendering));
        }
    });
    kendo.ui.plugin(Menu);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, extend = $.extend, isFunction = $.isFunction, isPlainObject = $.isPlainObject, inArray = $.inArray, nameSpecialCharRegExp = /("|'|\[|\]|\$|\.|\:|\+)/g, ERRORTEMPLATE = '<div class="k-widget k-tooltip k-tooltip-validation" style="margin:0.5em"><span class="k-icon k-warning"> </span>' + '#=message#<div class="k-callout k-callout-n"></div></div>', CHANGE = "change";
    var specialRules = [ "url", "email", "number", "date", "boolean" ];
    function fieldType(field) {
        field = field != null ? field : "";
        return field.type || $.type(field) || "string";
    }
    function convertToValueBinding(container) {
        container.find(":input:not(:button, [" + kendo.attr("role") + "=upload], [" + kendo.attr("skip") + "], [type=file]), select").each(function() {
            var bindAttr = kendo.attr("bind"), binding = this.getAttribute(bindAttr) || "", bindingName = this.type === "checkbox" || this.type === "radio" ? "checked:" : "value:", fieldName = this.name;
            if (binding.indexOf(bindingName) === -1 && fieldName) {
                binding += (binding.length ? "," : "") + bindingName + fieldName;
                $(this).attr(bindAttr, binding);
            }
        });
    }
    function createAttributes(options) {
        var field = (options.model.fields || options.model)[options.field], type = fieldType(field), validation = field ? field.validation : {}, ruleName, DATATYPE = kendo.attr("type"), BINDING = kendo.attr("bind"), rule, attr = {
            name: options.field
        };
        for (ruleName in validation) {
            rule = validation[ruleName];
            if (inArray(ruleName, specialRules) >= 0) {
                attr[DATATYPE] = ruleName;
            } else if (!isFunction(rule)) {
                attr[ruleName] = isPlainObject(rule) ? rule.value || ruleName : rule;
            }
            attr[kendo.attr(ruleName + "-msg")] = rule.message;
        }
        if (inArray(type, specialRules) >= 0) {
            attr[DATATYPE] = type;
        }
        attr[BINDING] = (type === "boolean" ? "checked:" : "value:") + options.field;
        return attr;
    }
    function convertItems(items) {
        var idx, length, item, value, text, result;
        if (items && items.length) {
            result = [];
            for (idx = 0, length = items.length; idx < length; idx++) {
                item = items[idx];
                text = item.text || item.value || item;
                value = item.value == null ? item.text || item : item.value;
                result[idx] = {
                    text: text,
                    value: value
                };
            }
        }
        return result;
    }
    var editors = {
        number: function(container, options) {
            var attr = createAttributes(options);
            $('<input type="text"/>').attr(attr).appendTo(container).kendoNumericTextBox({
                format: options.format
            });
            $("<span " + kendo.attr("for") + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container);
        },
        date: function(container, options) {
            var attr = createAttributes(options), format = options.format;
            if (format) {
                format = kendo._extractFormat(format);
            }
            attr[kendo.attr("format")] = format;
            $('<input type="text"/>').attr(attr).appendTo(container).kendoDatePicker({
                format: options.format
            });
            $("<span " + kendo.attr("for") + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container);
        },
        string: function(container, options) {
            var attr = createAttributes(options);
            $('<input type="text" class="k-input k-textbox"/>').attr(attr).appendTo(container);
        },
        "boolean": function(container, options) {
            var attr = createAttributes(options);
            $('<input type="checkbox" />').attr(attr).appendTo(container);
        },
        values: function(container, options) {
            var attr = createAttributes(options);
            $("<select " + kendo.attr("text-field") + '="text"' + kendo.attr("value-field") + '="value"' + kendo.attr("source") + "='" + kendo.stringify(convertItems(options.values)).replace(/\'/g, "&apos;") + "'" + kendo.attr("role") + '="dropdownlist"/>').attr(attr).appendTo(container);
            $("<span " + kendo.attr("for") + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container);
        }
    };
    function addValidationRules(modelField, rules) {
        var validation = modelField ? modelField.validation || {} : {}, rule;
        for (rule in validation) {
            if (isFunction(validation[rule])) {
                rules[rule] = validation[rule];
            }
        }
    }
    var Editable = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            that._validateProxy = $.proxy(that._validate, that);
            that.refresh();
        },
        events: [ CHANGE ],
        options: {
            name: "Editable",
            editors: editors,
            clearContainer: true,
            errorTemplate: ERRORTEMPLATE
        },
        editor: function(field, modelField) {
            var that = this, editors = that.options.editors, isObject = isPlainObject(field), fieldName = isObject ? field.field : field, model = that.options.model || {}, isValuesEditor = isObject && field.values, type = isValuesEditor ? "values" : fieldType(modelField), isCustomEditor = isObject && field.editor, editor = isCustomEditor ? field.editor : editors[type], container = that.element.find("[" + kendo.attr("container-for") + "=" + fieldName.replace(nameSpecialCharRegExp, "\\$1") + "]");
            editor = editor ? editor : editors["string"];
            if (isCustomEditor && typeof field.editor === "string") {
                editor = function(container) {
                    container.append(field.editor);
                };
            }
            container = container.length ? container : that.element;
            editor(container, extend(true, {}, isObject ? field : {
                field: fieldName
            }, {
                model: model
            }));
        },
        _validate: function(e) {
            var that = this, isBoolean = typeof e.value === "boolean", input, preventChangeTrigger = that._validationEventInProgress, values = {};
            values[e.field] = e.value;
            input = $(":input[" + kendo.attr("bind") + '="' + (isBoolean ? "checked:" : "value:") + e.field.replace(nameSpecialCharRegExp, "\\$1") + '"]', that.element);
            try {
                that._validationEventInProgress = true;
                if (!that.validatable.validateInput(input) || !preventChangeTrigger && that.trigger(CHANGE, {
                    values: values
                })) {
                    e.preventDefault();
                }
            } finally {
                that._validationEventInProgress = false;
            }
        },
        end: function() {
            return this.validatable.validate();
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that.options.model.unbind("set", that._validateProxy);
            kendo.unbind(that.element);
            kendo.destroy(that.element);
            that.element.removeData("kendoValidator");
        },
        refresh: function() {
            var that = this, idx, length, fields = that.options.fields || [], container = that.options.clearContainer ? that.element.empty() : that.element, model = that.options.model || {}, rules = {}, field, isObject, fieldName, modelField, modelFields;
            if (!$.isArray(fields)) {
                fields = [ fields ];
            }
            for (idx = 0, length = fields.length; idx < length; idx++) {
                field = fields[idx];
                isObject = isPlainObject(field);
                fieldName = isObject ? field.field : field;
                modelField = (model.fields || model)[fieldName];
                addValidationRules(modelField, rules);
                that.editor(field, modelField);
            }
            if (!length) {
                modelFields = model.fields || model;
                for (fieldName in modelFields) {
                    addValidationRules(modelFields[fieldName], rules);
                }
            }
            convertToValueBinding(container);
            kendo.bind(container, that.options.model);
            that.options.model.bind("set", that._validateProxy);
            that.validatable = container.kendoValidator({
                validateOnBlur: false,
                errorTemplate: that.options.errorTemplate || undefined,
                rules: rules
            }).data("kendoValidator");
            container.find(":focusable:first").focus();
        }
    });
    ui.plugin(Editable);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, proxy = $.proxy, POPUP = "kendoPopup", INIT = "init", NS = ".kendoFilterMenu", EQ = "Is equal to", NEQ = "Is not equal to", roles = {
        number: "numerictextbox",
        date: "datepicker"
    }, isFunction = $.isFunction, Widget = ui.Widget;
    var booleanTemplate = "<div>" + '<div class="k-filter-help-text">#=messages.info#</div>' + "<label>" + '<input type="radio" data-#=ns#bind="checked: filters[0].value" value="true" name="filters[0].value"/>' + "#=messages.isTrue#" + "</label>" + "<label>" + '<input type="radio" data-#=ns#bind="checked: filters[0].value" value="false" name="filters[0].value"/>' + "#=messages.isFalse#" + "</label>" + "<div>" + '<button type="submit" class="k-button">#=messages.filter#</button>' + '<button type="reset" class="k-button">#=messages.clear#</button>' + "</div>" + "</div>";
    var defaultTemplate = "<div>" + '<div class="k-filter-help-text">#=messages.info#</div>' + '<select data-#=ns#bind="value: filters[0].operator" data-#=ns#role="dropdownlist">' + "#for(var op in operators){#" + '<option value="#=op#">#=operators[op]#</option>' + "#}#" + "</select>" + "#if(values){#" + '<select data-#=ns#bind="value:filters[0].value" data-#=ns#text-field="text" data-#=ns#value-field="value" data-#=ns#source=\'#=kendo.stringify(values).replace(/\'/g,"&\\#39;")#\' data-#=ns#role="dropdownlist" data-#=ns#option-label="#=messages.selectValue#">' + "</select>" + "#}else{#" + '<input data-#=ns#bind="value:filters[0].value" class="k-textbox" type="text" #=role ? "data-" + ns + "role=\'" + role + "\'" : ""# />' + "#}#" + "#if(extra){#" + '<select class="k-filter-and" data-#=ns#bind="value: logic" data-#=ns#role="dropdownlist">' + '<option value="and">#=messages.and#</option>' + '<option value="or">#=messages.or#</option>' + "</select>" + '<select data-#=ns#bind="value: filters[1].operator" data-#=ns#role="dropdownlist">' + "#for(var op in operators){#" + '<option value="#=op#">#=operators[op]#</option>' + "#}#" + "</select>" + "#if(values){#" + '<select data-#=ns#bind="value:filters[1].value" data-#=ns#text-field="text" data-#=ns#value-field="value" data-#=ns#source=\'#=kendo.stringify(values).replace(/\'/g,"&\\#39;")#\' data-#=ns#role="dropdownlist" data-#=ns#option-label="#=messages.selectValue#">' + "</select>" + "#}else{#" + '<input data-#=ns#bind="value: filters[1].value" class="k-textbox" type="text" #=role ? "data-" + ns + "role=\'" + role + "\'" : ""#/>' + "#}#" + "#}#" + "<div>" + '<button type="submit" class="k-button">#=messages.filter#</button>' + '<button type="reset" class="k-button">#=messages.clear#</button>' + "</div>" + "</div>";
    function removeFiltersForField(expression, field) {
        if (expression.filters) {
            expression.filters = $.grep(expression.filters, function(filter) {
                removeFiltersForField(filter, field);
                if (filter.filters) {
                    return filter.filters.length;
                } else {
                    return filter.field != field;
                }
            });
        }
    }
    function convertItems(items) {
        var idx, length, item, value, text, result;
        if (items && items.length) {
            result = [];
            for (idx = 0, length = items.length; idx < length; idx++) {
                item = items[idx];
                text = item.text || item.value || item;
                value = item.value == null ? item.text || item : item.value;
                result[idx] = {
                    text: text,
                    value: value
                };
            }
        }
        return result;
    }
    var FilterMenu = Widget.extend({
        init: function(element, options) {
            var that = this, type = "string", link, field;
            Widget.fn.init.call(that, element, options);
            that.operators = options.operators || {};
            element = that.element;
            options = that.options;
            if (!options.appendToElement) {
                link = element.addClass("k-filterable").find(".k-grid-filter");
                if (!link[0]) {
                    link = element.prepend('<a class="k-grid-filter" href="#"><span class="k-icon k-filter"/></a>').find(".k-grid-filter");
                }
                link.attr("tabindex", -1).on("click" + NS, proxy(that._click, that));
            }
            that.link = link || $();
            that.dataSource = options.dataSource;
            that.field = options.field || element.attr(kendo.attr("field"));
            that.model = that.dataSource.reader.model;
            that._parse = function(value) {
                return value + "";
            };
            if (that.model && that.model.fields) {
                field = that.model.fields[that.field];
                if (field) {
                    type = field.type || "string";
                    if (field.parse) {
                        that._parse = proxy(field.parse, field);
                    }
                }
            }
            if (options.values) {
                type = "enums";
            }
            that.type = type;
            if (options.appendToElement) {
                // force creation if used in column menu
                that._init();
            }
        },
        _init: function() {
            var that = this, options = that.options, operators = that.operators || {}, initial, ui = options.ui, setUI = isFunction(ui), role, type = that.type;
            that._refreshHandler = proxy(that.refresh, that);
            that.dataSource.bind("change", that._refreshHandler);
            operators = operators[type] || options.operators[type];
            for (initial in operators) {
                // get the first operator
                break;
            }
            that._defaultFilter = function() {
                return {
                    field: that.field,
                    operator: initial || "eq",
                    value: ""
                };
            };
            if (!setUI) {
                role = ui || roles[type];
            }
            that.form = $('<form class="k-filter-menu"/>').html(kendo.template(type === "boolean" ? booleanTemplate : defaultTemplate)({
                field: that.field,
                format: options.format,
                ns: kendo.ns,
                messages: options.messages,
                extra: options.extra,
                operators: operators,
                type: type,
                role: role,
                values: convertItems(options.values)
            })).on("keydown" + NS, proxy(that._keydown, that)).on("submit" + NS, proxy(that._submit, that)).on("reset" + NS, proxy(that._reset, that));
            if (!options.appendToElement) {
                that.popup = that.form[POPUP]({
                    anchor: that.link,
                    open: proxy(that._open, that),
                    activate: proxy(that._activate, that),
                    close: that.options.closeCallback
                }).data(POPUP);
            } else {
                that.element.append(that.form);
                that.popup = that.element.closest(".k-popup").data(POPUP);
            }
            if (setUI) {
                that.form.find(".k-textbox").removeClass("k-textbox").each(function() {
                    ui($(this));
                });
            }
            that.form.find("[" + kendo.attr("role") + "=numerictextbox]").removeClass("k-textbox").end().find("[" + kendo.attr("role") + "=datetimepicker]").removeClass("k-textbox").end().find("[" + kendo.attr("role") + "=timepicker]").removeClass("k-textbox").end().find("[" + kendo.attr("role") + "=datepicker]").removeClass("k-textbox");
            that.refresh();
            that.trigger(INIT, {
                field: that.field,
                container: that.form
            });
        },
        refresh: function() {
            var that = this, expression = that.dataSource.filter() || {
                filters: [],
                logic: "and"
            };
            that.filterModel = kendo.observable({
                logic: "and",
                filters: [ that._defaultFilter(), that._defaultFilter() ]
            });
            //NOTE: binding the form element directly causes weird error in IE when grid is bound through MVVM and column is sorted
            kendo.bind(that.form.children().first(), that.filterModel);
            if (that._bind(expression)) {
                that.link.addClass("k-state-active");
            } else {
                that.link.removeClass("k-state-active");
            }
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            if (that.form) {
                kendo.unbind(that.form);
                kendo.destroy(that.form);
                that.form.unbind(NS);
                that.popup.destroy();
            }
            that.link.unbind(NS);
            that.dataSource.unbind("change", that._refreshHandler);
        },
        _bind: function(expression) {
            var that = this, filters = expression.filters, idx, length, found = false, current = 0, filterModel = that.filterModel, currentFilter, filter;
            for (idx = 0, length = filters.length; idx < length; idx++) {
                filter = filters[idx];
                if (filter.field == that.field) {
                    filterModel.set("logic", expression.logic);
                    currentFilter = filterModel.filters[current];
                    if (!currentFilter) {
                        filterModel.filters.push({
                            field: that.field
                        });
                        currentFilter = filterModel.filters[current];
                    }
                    currentFilter.set("value", that._parse(filter.value));
                    currentFilter.set("operator", filter.operator);
                    current++;
                    found = true;
                } else if (filter.filters) {
                    found = found || that._bind(filter);
                }
            }
            return found;
        },
        _merge: function(expression) {
            var that = this, logic = expression.logic || "and", filters = expression.filters, filter, result = that.dataSource.filter() || {
                filters: [],
                logic: "and"
            }, idx, length;
            removeFiltersForField(result, that.field);
            filters = $.grep(filters, function(filter) {
                return filter.value !== "";
            });
            for (idx = 0, length = filters.length; idx < length; idx++) {
                filter = filters[idx];
                filter.value = that._parse(filter.value);
            }
            if (filters.length) {
                if (result.filters.length) {
                    expression.filters = filters;
                    if (result.logic !== "and") {
                        result.filters = [ {
                            logic: result.logic,
                            filters: result.filters
                        } ];
                        result.logic = "and";
                    }
                    if (filters.length > 1) {
                        result.filters.push(expression);
                    } else {
                        result.filters.push(filters[0]);
                    }
                } else {
                    result.filters = filters;
                    result.logic = logic;
                }
            }
            return result;
        },
        filter: function(expression) {
            expression = this._merge(expression);
            if (expression.filters.length) {
                this.dataSource.filter(expression);
            }
        },
        clear: function() {
            var that = this, expression = that.dataSource.filter() || {
                filters: []
            };
            expression.filters = $.grep(expression.filters, function(filter) {
                if (filter.filters) {
                    filter.filters = $.grep(filter.filters, function(expr) {
                        return expr.field != that.field;
                    });
                    return filter.filters.length;
                }
                return filter.field != that.field;
            });
            if (!expression.filters.length) {
                expression = null;
            }
            that.dataSource.filter(expression);
        },
        _submit: function(e) {
            var that = this;
            e.preventDefault();
            that.filter(that.filterModel.toJSON());
            that.popup.close();
        },
        _reset: function() {
            this.clear();
            this.popup.close();
        },
        _click: function(e) {
            e.preventDefault();
            e.stopPropagation();
            if (!this.popup) {
                this._init();
            }
            this.popup.toggle();
        },
        _open: function() {
            var popup;
            $(".k-filter-menu").not(this.form).each(function() {
                popup = $(this).data(POPUP);
                if (popup) {
                    popup.close();
                }
            });
        },
        _activate: function() {
            this.form.find(":focusable:first").focus();
        },
        _keydown: function(e) {
            if (e.keyCode == kendo.keys.ESC) {
                this.popup.close();
            }
        },
        events: [ INIT ],
        options: {
            name: "FilterMenu",
            extra: true,
            appendToElement: false,
            type: "string",
            operators: {
                string: {
                    eq: EQ,
                    neq: NEQ,
                    startswith: "Starts with",
                    contains: "Contains",
                    doesnotcontain: "Does not contain",
                    endswith: "Ends with"
                },
                number: {
                    eq: EQ,
                    neq: NEQ,
                    gte: "Is greater than or equal to",
                    gt: "Is greater than",
                    lte: "Is less than or equal to",
                    lt: "Is less than"
                },
                date: {
                    eq: EQ,
                    neq: NEQ,
                    gte: "Is after or equal to",
                    gt: "Is after",
                    lte: "Is before or equal to",
                    lt: "Is before"
                },
                enums: {
                    eq: EQ,
                    neq: NEQ
                }
            },
            messages: {
                info: "Show items with value that:",
                isTrue: "is true",
                isFalse: "is false",
                filter: "Filter",
                clear: "Clear",
                and: "And",
                or: "Or",
                selectValue: "-Select value-"
            }
        }
    });
    ui.plugin(FilterMenu);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, keys = kendo.keys, extend = $.extend, each = $.each, template = kendo.template, Widget = ui.Widget, excludedNodesRegExp = /^(ul|a|div)$/i, NS = ".kendoPanelBar", IMG = "img", HREF = "href", LAST = "k-last", LINK = "k-link", LINKSELECTOR = "." + LINK, ERROR = "error", ITEM = ".k-item", GROUP = ".k-group", VISIBLEGROUP = GROUP + ":visible", IMAGE = "k-image", FIRST = "k-first", EXPAND = "expand", SELECT = "select", CONTENT = "k-content", ACTIVATE = "activate", COLLAPSE = "collapse", MOUSEENTER = "mouseenter", MOUSELEAVE = "mouseleave", CONTENTLOAD = "contentLoad", ACTIVECLASS = "k-state-active", GROUPS = "> .k-panel", CONTENTS = "> .k-content", FOCUSEDCLASS = "k-state-focused", DISABLEDCLASS = "k-state-disabled", SELECTEDCLASS = "k-state-selected", SELECTEDSELECTOR = "." + SELECTEDCLASS, HIGHLIGHTEDCLASS = "k-state-highlighted", ACTIVEITEMSELECTOR = ITEM + ":not(.k-state-disabled)", clickableItems = ACTIVEITEMSELECTOR + " > .k-link", disabledItems = ITEM + ".k-state-disabled > .k-link", selectableItems = "> li > " + SELECTEDSELECTOR + ", .k-panel > li > " + SELECTEDSELECTOR, defaultState = "k-state-default", ARIA_DISABLED = "aria-disabled", ARIA_EXPANDED = "aria-expanded", ARIA_HIDDEN = "aria-hidden", ARIA_SELECTED = "aria-selected", VISIBLE = ":visible", EMPTY = ":empty", SINGLE = "single", templates = {
        content: template("<div role='region' class='k-content'#= contentAttributes(data) #>#= content(item) #</div>"),
        group: template("<ul role='group' aria-hidden='true' class='#= groupCssClass(group) #'#= groupAttributes(group) #>" + "#= renderItems(data) #" + "</ul>"),
        itemWrapper: template("<#= tag(item) # class='#= textClass(item, group) #' #= contentUrl(item) ##= textAttributes(item) #>" + "#= image(item) ##= sprite(item) ##= text(item) #" + "#= arrow(data) #" + "</#= tag(item) #>"),
        item: template("<li role='menuitem' #=aria(item)#class='#= wrapperCssClass(group, item) #'>" + "#= itemWrapper(data) #" + "# if (item.items) { #" + "#= subGroup({ items: item.items, panelBar: panelBar, group: { expanded: item.expanded } }) #" + "# } else if (item.content || item.contentUrl) { #" + "#= renderContent(data) #" + "# } #" + "</li>"),
        image: template("<img class='k-image' alt='' src='#= imageUrl #' />"),
        arrow: template("<span class='#= arrowClass(item) #'></span>"),
        sprite: template("<span class='k-sprite #= spriteCssClass #'></span>"),
        empty: template("")
    }, rendering = {
        aria: function(item) {
            var attr = "";
            if (item.items || item.content || item.contentUrl) {
                attr += ARIA_EXPANDED + "='" + (item.expanded ? "true" : "false") + "' ";
            }
            if (item.enabled === false) {
                attr += ARIA_DISABLED + "='true'";
            }
            return attr;
        },
        wrapperCssClass: function(group, item) {
            var result = "k-item", index = item.index;
            if (item.enabled === false) {
                result += " " + DISABLEDCLASS;
            } else if (item.expanded === true) {
                result += " " + ACTIVECLASS;
            } else {
                result += " k-state-default";
            }
            if (index === 0) {
                result += " k-first";
            }
            if (index == group.length - 1) {
                result += " k-last";
            }
            if (item.cssClass) {
                result += " " + item.cssClass;
            }
            return result;
        },
        textClass: function(item, group) {
            var result = LINK;
            if (group.firstLevel) {
                result += " k-header";
            }
            return result;
        },
        textAttributes: function(item) {
            return item.url ? " href='" + item.url + "'" : "";
        },
        arrowClass: function(item) {
            var result = "k-icon";
            result += item.expanded ? " k-i-arrow-n k-panelbar-collapse" : " k-i-arrow-s k-panelbar-expand";
            return result;
        },
        text: function(item) {
            return item.encoded === false ? item.text : kendo.htmlEncode(item.text);
        },
        tag: function(item) {
            return item.url ? "a" : "span";
        },
        groupAttributes: function(group) {
            return group.expanded !== true ? " style='display:none'" : "";
        },
        groupCssClass: function() {
            return "k-group k-panel";
        },
        contentAttributes: function(content) {
            return content.item.expanded !== true ? " style='display:none'" : "";
        },
        content: function(item) {
            return item.content ? item.content : item.contentUrl ? "" : "&nbsp;";
        },
        contentUrl: function(item) {
            return item.contentUrl ? kendo.attr("content-url") + '="' + item.contentUrl + '"' : "";
        }
    };
    function updateArrow(items) {
        items = $(items);
        items.children(LINKSELECTOR).children(".k-icon").remove();
        items.filter(":has(.k-panel),:has(.k-content)").children(".k-link:not(:has([class*=k-i-arrow]))").each(function() {
            var item = $(this), parent = item.parent();
            item.append("<span class='k-icon " + (parent.hasClass(ACTIVECLASS) ? "k-i-arrow-n k-panelbar-collapse" : "k-i-arrow-s k-panelbar-expand") + "'/>");
        });
    }
    function updateFirstLast(items) {
        items = $(items);
        items.filter(".k-first:not(:first-child)").removeClass(FIRST);
        items.filter(".k-last:not(:last-child)").removeClass(LAST);
        items.filter(":first-child").addClass(FIRST);
        items.filter(":last-child").addClass(LAST);
    }
    var PanelBar = Widget.extend({
        init: function(element, options) {
            var that = this, content;
            Widget.fn.init.call(that, element, options);
            element = that.wrapper = that.element.addClass("k-widget k-reset k-header k-panelbar");
            options = that.options;
            if (element[0].id) {
                that._itemId = element[0].id + "_pb_active";
            }
            that._tabindex();
            that._initData(options);
            that._updateClasses();
            that._animations(options);
            element.on("click" + NS, clickableItems, function(e) {
                if (that._click($(e.currentTarget))) {
                    e.preventDefault();
                }
            }).on(MOUSEENTER + NS + " " + MOUSELEAVE + NS, clickableItems, that._toggleHover).on("click" + NS, disabledItems, false).on("keydown" + NS, $.proxy(that._keydown, that)).on("focus" + NS, function() {
                var item = that.select();
                that._current(item[0] ? item : that._first());
            }).on("blur" + NS, function() {
                that._current(null);
            }).attr("role", "menu");
            /*if (options.contentUrls) {
                element.find("> .k-item")
                    .each(function(index, item) {
                        var span = $(item).find(LINKSELECTOR);
                        span.replaceWith('<a class="k-link k-header" href="' + options.contentUrls[index] + '">' + span.html() + '</a>');
                    });
            }*/
            content = element.find("li." + ACTIVECLASS + " > ." + CONTENT);
            if (content[0]) {
                that.expand(content.parent(), false);
            }
            kendo.notify(that);
        },
        events: [ EXPAND, COLLAPSE, SELECT, ACTIVATE, ERROR, CONTENTLOAD ],
        options: {
            name: "PanelBar",
            animation: {
                expand: {
                    effects: "expand:vertical",
                    duration: 200
                },
                collapse: {
                    // if collapse animation effects are defined, they will be used instead of expand.reverse
                    duration: 200
                }
            },
            expandMode: "multiple"
        },
        destroy: function() {
            Widget.fn.destroy.call(this);
            this.element.off(NS);
            kendo.destroy(this.element);
        },
        _initData: function(options) {
            var that = this;
            if (options.dataSource) {
                that.element.empty();
                that.append(options.dataSource, that.element);
            }
        },
        setOptions: function(options) {
            var animation = this.options.animation;
            this._animations(options);
            options.animation = extend(true, animation, options.animation);
            if ("dataSource" in options) {
                this._initData(options);
            }
            Widget.fn.setOptions.call(this, options);
        },
        expand: function(element, useAnimation) {
            var that = this, animBackup = {};
            useAnimation = useAnimation !== false;
            element = this.element.find(element);
            element.each(function(index, item) {
                item = $(item);
                var groups = item.find(GROUPS).add(item.find(CONTENTS));
                if (!item.hasClass(DISABLEDCLASS) && groups.length > 0) {
                    if (that.options.expandMode == SINGLE && that._collapseAllExpanded(item)) {
                        return that;
                    }
                    element.find("." + HIGHLIGHTEDCLASS).removeClass(HIGHLIGHTEDCLASS);
                    item.addClass(HIGHLIGHTEDCLASS);
                    if (!useAnimation) {
                        animBackup = that.options.animation;
                        that.options.animation = {
                            expand: {
                                effects: {}
                            },
                            collapse: {
                                hide: true,
                                effects: {}
                            }
                        };
                    }
                    if (!that._triggerEvent(EXPAND, item)) {
                        that._toggleItem(item, false);
                    }
                    if (!useAnimation) {
                        that.options.animation = animBackup;
                    }
                }
            });
            return that;
        },
        collapse: function(element, useAnimation) {
            var that = this, animBackup = {};
            useAnimation = useAnimation !== false;
            element = that.element.find(element);
            element.each(function(index, item) {
                item = $(item);
                var groups = item.find(GROUPS).add(item.find(CONTENTS));
                if (!item.hasClass(DISABLEDCLASS) && groups.is(VISIBLE)) {
                    item.removeClass(HIGHLIGHTEDCLASS);
                    if (!useAnimation) {
                        animBackup = that.options.animation;
                        that.options.animation = {
                            expand: {
                                effects: {}
                            },
                            collapse: {
                                hide: true,
                                effects: {}
                            }
                        };
                    }
                    if (!that._triggerEvent(COLLAPSE, item)) {
                        that._toggleItem(item, true);
                    }
                    if (!useAnimation) {
                        that.options.animation = animBackup;
                    }
                }
            });
            return that;
        },
        _toggleDisabled: function(element, enable) {
            element = this.element.find(element);
            element.toggleClass(defaultState, enable).toggleClass(DISABLEDCLASS, !enable).attr(ARIA_DISABLED, !enable);
        },
        select: function(element) {
            var that = this;
            if (element === undefined) {
                return that.element.find(selectableItems).parent();
            }
            that.element.find(element).each(function() {
                var item = $(this), link = item.children(LINKSELECTOR);
                if (item.hasClass(DISABLEDCLASS)) {
                    return that;
                }
                that._updateSelected(link);
            });
            return that;
        },
        enable: function(element, state) {
            this._toggleDisabled(element, state !== false);
            return this;
        },
        disable: function(element) {
            this._toggleDisabled(element, false);
            return this;
        },
        append: function(item, referenceItem) {
            referenceItem = this.element.find(referenceItem);
            var inserted = this._insert(item, referenceItem, referenceItem.length ? referenceItem.find(GROUPS) : null);
            each(inserted.items, function() {
                inserted.group.append(this);
                updateFirstLast(this);
            });
            updateArrow(referenceItem);
            updateFirstLast(inserted.group.find(".k-first, .k-last"));
            inserted.group.height("auto");
            return this;
        },
        insertBefore: function(item, referenceItem) {
            referenceItem = this.element.find(referenceItem);
            var inserted = this._insert(item, referenceItem, referenceItem.parent());
            each(inserted.items, function() {
                referenceItem.before(this);
                updateFirstLast(this);
            });
            updateFirstLast(referenceItem);
            inserted.group.height("auto");
            return this;
        },
        insertAfter: function(item, referenceItem) {
            referenceItem = this.element.find(referenceItem);
            var inserted = this._insert(item, referenceItem, referenceItem.parent());
            each(inserted.items, function() {
                referenceItem.after(this);
                updateFirstLast(this);
            });
            updateFirstLast(referenceItem);
            inserted.group.height("auto");
            return this;
        },
        remove: function(element) {
            element = this.element.find(element);
            var that = this, parent = element.parentsUntil(that.element, ITEM), group = element.parent("ul");
            element.remove();
            if (group && !group.hasClass("k-panelbar") && !group.children(ITEM).length) {
                group.remove();
            }
            if (parent.length) {
                parent = parent.eq(0);
                updateArrow(parent);
                updateFirstLast(parent);
            }
            return that;
        },
        reload: function(element) {
            var that = this;
            element = that.element.find(element);
            element.each(function() {
                var item = $(this);
                that._ajaxRequest(item, item.children("." + CONTENT), !item.is(VISIBLE));
            });
        },
        _first: function() {
            return this.element.children(ACTIVEITEMSELECTOR).first();
        },
        _last: function() {
            var item = this.element.children(ACTIVEITEMSELECTOR).last(), group = item.children(VISIBLEGROUP);
            if (group[0]) {
                return group.children(ACTIVEITEMSELECTOR).last();
            }
            return item;
        },
        _current: function(candidate) {
            var that = this, focused = that._focused, id = that._itemId;
            if (candidate === undefined) {
                return focused;
            }
            that.element.removeAttr("aria-activedescendant");
            if (focused) {
                if (focused[0].id === id) {
                    focused.removeAttr("id");
                }
                focused.children(LINKSELECTOR).removeClass(FOCUSEDCLASS);
            }
            if (candidate) {
                id = candidate[0].id || id;
                candidate.attr("id", id).children(LINKSELECTOR).addClass(FOCUSEDCLASS);
                that.element.attr("aria-activedescendant", id);
            }
            that._focused = candidate;
        },
        _keydown: function(e) {
            var that = this, key = e.keyCode, current = that._current();
            if (e.target != e.currentTarget) {
                return;
            }
            if (key == keys.DOWN || key == keys.RIGHT) {
                that._current(that._nextItem(current));
                e.preventDefault();
            } else if (key == keys.UP || key == keys.LEFT) {
                that._current(that._prevItem(current));
                e.preventDefault();
            } else if (key == keys.ENTER || key == keys.SPACEBAR) {
                that._click(current.children(LINKSELECTOR));
                e.preventDefault();
            } else if (key == keys.HOME) {
                that._current(that._first());
                e.preventDefault();
            } else if (key == keys.END) {
                that._current(that._last());
                e.preventDefault();
            }
        },
        _nextItem: function(item) {
            if (!item) {
                return this._first();
            }
            var group = item.children(VISIBLEGROUP), next = item.next();
            if (group[0]) {
                next = group.children("." + FIRST);
            }
            if (!next[0]) {
                next = item.parent(VISIBLEGROUP).parent(ITEM).next();
            }
            if (!next[0] || !next.is(":visible")) {
                next = this._first();
            }
            if (next.hasClass(DISABLEDCLASS)) {
                next = this._nextItem(next);
            }
            return next;
        },
        _prevItem: function(item) {
            if (!item) {
                return this._last();
            }
            var prev = item.prev(), result;
            if (!prev[0]) {
                prev = item.parent(VISIBLEGROUP).parent(ITEM);
                if (!prev[0]) {
                    prev = this._last();
                }
            } else {
                result = prev;
                while (result[0]) {
                    result = result.children(VISIBLEGROUP).children("." + LAST);
                    if (result[0]) {
                        prev = result;
                    }
                }
            }
            if (prev.hasClass(DISABLEDCLASS)) {
                prev = this._prevItem(prev);
            }
            return prev;
        },
        _insert: function(item, referenceItem, parent) {
            var that = this, items, plain = $.isPlainObject(item), isReferenceItem = referenceItem && referenceItem[0], groupData;
            if (!isReferenceItem) {
                parent = that.element;
            }
            groupData = {
                firstLevel: parent.hasClass("k-panelbar"),
                expanded: parent.parent().hasClass(ACTIVECLASS),
                length: parent.children().length
            };
            if (isReferenceItem && !parent.length) {
                parent = $(PanelBar.renderGroup({
                    group: groupData
                })).appendTo(referenceItem);
            }
            if (plain || $.isArray(item)) {
                // is JSON
                items = $.map(plain ? [ item ] : item, function(value, idx) {
                    if (typeof value === "string") {
                        return $(value);
                    } else {
                        return $(PanelBar.renderItem({
                            group: groupData,
                            item: extend(value, {
                                index: idx
                            })
                        }));
                    }
                });
                if (isReferenceItem) {
                    referenceItem.attr(ARIA_EXPANDED, false);
                }
            } else {
                items = $(item);
                that._updateItemsClasses(items);
            }
            return {
                items: items,
                group: parent
            };
        },
        _toggleHover: function(e) {
            var target = $(e.currentTarget);
            if (!target.parents("li." + DISABLEDCLASS).length) {
                target.toggleClass("k-state-hover", e.type == MOUSEENTER);
            }
        },
        _updateClasses: function() {
            var that = this, panels, items;
            panels = that.element.find("li > ul").not(function() {
                return $(this).parentsUntil(".k-panelbar", "div").length;
            }).addClass("k-group k-panel").attr("role", "group");
            panels.parent().attr(ARIA_EXPANDED, false).not("." + ACTIVECLASS).children("ul").attr(ARIA_HIDDEN, true).hide();
            items = that.element.add(panels).children();
            that._updateItemsClasses(items);
            updateArrow(items);
            updateFirstLast(items);
        },
        _updateItemsClasses: function(items) {
            var length = items.length, idx = 0;
            for (;idx < length; idx++) {
                this._updateItemClasses(items[idx], idx);
            }
        },
        _updateItemClasses: function(item, index) {
            var selected = this._selected, contentUrls = this.options.contentUrls, url = contentUrls && contentUrls[index], root = this.element[0], wrapElement, link;
            item = $(item).addClass("k-item").attr("role", "menuitem");
            item.children(IMG).addClass(IMAGE);
            link = item.children("a").addClass(LINK);
            if (link[0]) {
                link.attr("href", url);
                //url can be undefined
                link.children(IMG).addClass(IMAGE);
            }
            item.filter(":not([disabled]):not([class*=k-state])").addClass("k-state-default");
            item.filter("li[disabled]").addClass("k-state-disabled").attr(ARIA_DISABLED, true).removeAttr("disabled");
            item.children("div").addClass(CONTENT).attr("role", "region").attr(ARIA_HIDDEN, true).hide().parent().attr(ARIA_EXPANDED, false);
            link = item.children(SELECTEDSELECTOR);
            if (link[0]) {
                if (selected) {
                    selected.removeAttr(ARIA_SELECTED).children(SELECTEDSELECTOR).removeClass(SELECTEDCLASS);
                }
                link.addClass(SELECTEDCLASS);
                this._selected = item.attr(ARIA_SELECTED, true);
            }
            if (!item.children(LINKSELECTOR)[0]) {
                wrapElement = "<span class='" + LINK + "'/>";
                if (contentUrls && contentUrls[index] && item[0].parentNode == root) {
                    wrapElement = '<a class="k-link k-header" href="' + contentUrls[index] + '"/>';
                }
                item.contents().filter(function() {
                    return !this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !$.trim(this.nodeValue));
                }).wrapAll(wrapElement);
            }
            if (item.parent(".k-panelbar")[0]) {
                item.children(LINKSELECTOR).addClass("k-header");
            }
        },
        _click: function(target) {
            var that = this, element = that.element, prevent, contents, href, isAnchor;
            if (target.parents("li." + DISABLEDCLASS).length) {
                return;
            }
            if (target.closest(".k-widget")[0] != element[0]) {
                return;
            }
            var link = target.closest(LINKSELECTOR), item = link.closest(ITEM);
            that._updateSelected(link);
            contents = item.find(GROUPS).add(item.find(CONTENTS));
            href = link.attr(HREF);
            isAnchor = href && (href.charAt(href.length - 1) == "#" || href.indexOf("#" + that.element[0].id + "-") != -1);
            prevent = !!(isAnchor || contents.length);
            if (contents.data("animating")) {
                return prevent;
            }
            if (that._triggerEvent(SELECT, item)) {
                prevent = true;
            }
            if (prevent === false) {
                return;
            }
            if (that.options.expandMode == SINGLE) {
                if (that._collapseAllExpanded(item)) {
                    return prevent;
                }
            }
            if (contents.length) {
                var visibility = contents.is(VISIBLE);
                if (!that._triggerEvent(!visibility ? EXPAND : COLLAPSE, item)) {
                    prevent = that._toggleItem(item, visibility);
                }
            }
            return prevent;
        },
        _toggleItem: function(element, isVisible) {
            var that = this, childGroup = element.find(GROUPS), prevent, content;
            if (childGroup.length) {
                this._toggleGroup(childGroup, isVisible);
                prevent = true;
            } else {
                content = element.children("." + CONTENT);
                if (content.length) {
                    prevent = true;
                    if (!content.is(EMPTY)) {
                        that._toggleGroup(content, isVisible);
                    } else {
                        that._ajaxRequest(element, content, isVisible);
                    }
                }
            }
            return prevent;
        },
        _toggleGroup: function(element, visibility) {
            var that = this, animationSettings = that.options.animation, animation = animationSettings.expand, collapse = extend({}, animationSettings.collapse), hasCollapseAnimation = collapse && "effects" in collapse;
            if (element.is(VISIBLE) != visibility) {
                return;
            }
            element.parent().attr(ARIA_EXPANDED, !visibility).attr(ARIA_HIDDEN, visibility).toggleClass(defaultState, visibility).toggleClass(ACTIVECLASS, !visibility).find("> .k-link > .k-icon").toggleClass("k-i-arrow-n", !visibility).toggleClass("k-panelbar-collapse", !visibility).toggleClass("k-i-arrow-s", visibility).toggleClass("k-panelbar-expand", visibility);
            if (visibility) {
                animation = extend(hasCollapseAnimation ? collapse : extend({
                    reverse: true
                }, animation), {
                    hide: true
                });
            } else {
                animation = extend({
                    complete: function(element) {
                        that._triggerEvent(ACTIVATE, element.closest(ITEM));
                    }
                }, animation);
            }
            element.kendoStop(true, true).kendoAnimate(animation);
        },
        _collapseAllExpanded: function(item) {
            var that = this, children, stopExpand = false;
            if (item.children(LINKSELECTOR).hasClass("k-header")) {
                var groups = item.find(GROUPS).add(item.find(CONTENTS));
                if (groups.is(VISIBLE)) {
                    stopExpand = true;
                }
                if (!(groups.is(VISIBLE) || groups.length === 0)) {
                    children = $(that.element).children();
                    children.find(GROUPS).add(children.find(CONTENTS)).filter(function() {
                        return $(this).is(VISIBLE);
                    }).each(function(index, content) {
                        content = $(content);
                        stopExpand = that._triggerEvent(COLLAPSE, content.closest(ITEM));
                        if (!stopExpand) {
                            that._toggleGroup(content, true);
                        }
                    });
                }
                return stopExpand;
            }
        },
        _ajaxRequest: function(element, contentElement, isVisible) {
            var that = this, statusIcon = element.find(".k-panelbar-collapse, .k-panelbar-expand"), link = element.find(LINKSELECTOR), loadingIconTimeout = setTimeout(function() {
                statusIcon.addClass("k-loading");
            }, 100), data = {}, url = link.attr(HREF);
            $.ajax({
                type: "GET",
                cache: false,
                url: url,
                dataType: "html",
                data: data,
                error: function(xhr, status) {
                    statusIcon.removeClass("k-loading");
                    if (that.trigger(ERROR, {
                        xhr: xhr,
                        status: status
                    })) {
                        this.complete();
                    }
                },
                complete: function() {
                    clearTimeout(loadingIconTimeout);
                    statusIcon.removeClass("k-loading");
                },
                success: function(data) {
                    try {
                        contentElement.html(data);
                    } catch (e) {
                        var console = window.console;
                        if (console && console.error) {
                            console.error(e.name + ": " + e.message + " in " + url);
                        }
                        this.error(this.xhr, "error");
                    }
                    that._toggleGroup(contentElement, isVisible);
                    that.trigger(CONTENTLOAD, {
                        item: element[0],
                        contentElement: contentElement[0]
                    });
                }
            });
        },
        _triggerEvent: function(eventName, element) {
            var that = this;
            return that.trigger(eventName, {
                item: element[0]
            });
        },
        _updateSelected: function(link) {
            var that = this, element = that.element, item = link.parent(ITEM), selected = that._selected;
            if (selected) {
                selected.removeAttr(ARIA_SELECTED);
            }
            that._selected = item.attr(ARIA_SELECTED, true);
            element.find(selectableItems).removeClass(SELECTEDCLASS);
            element.find("> .k-state-highlighted, .k-panel > .k-state-highlighted").removeClass(HIGHLIGHTEDCLASS);
            link.addClass(SELECTEDCLASS);
            link.parentsUntil(element, ITEM).filter(":has(.k-header)").addClass(HIGHLIGHTEDCLASS);
            that._current(item);
        },
        _animations: function(options) {
            if (options && "animation" in options && !options.animation) {
                options.animation = {
                    expand: {
                        effects: {}
                    },
                    collapse: {
                        hide: true,
                        effects: {}
                    }
                };
            }
        }
    });
    // client-side rendering
    extend(PanelBar, {
        renderItem: function(options) {
            options = extend({
                panelBar: {},
                group: {}
            }, options);
            var empty = templates.empty, item = options.item;
            return templates.item(extend(options, {
                image: item.imageUrl ? templates.image : empty,
                sprite: item.spriteCssClass ? templates.sprite : empty,
                itemWrapper: templates.itemWrapper,
                renderContent: PanelBar.renderContent,
                arrow: item.items || item.content || item.contentUrl ? templates.arrow : empty,
                subGroup: PanelBar.renderGroup
            }, rendering));
        },
        renderGroup: function(options) {
            return templates.group(extend({
                renderItems: function(options) {
                    var html = "", i = 0, items = options.items, len = items ? items.length : 0, group = extend({
                        length: len
                    }, options.group);
                    for (;i < len; i++) {
                        html += PanelBar.renderItem(extend(options, {
                            group: group,
                            item: extend({
                                index: i
                            }, items[i])
                        }));
                    }
                    return html;
                }
            }, options, rendering));
        },
        renderContent: function(options) {
            return templates.content(extend(options, rendering));
        }
    });
    kendo.ui.plugin(PanelBar);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, keys = kendo.keys, map = $.map, each = $.each, trim = $.trim, extend = $.extend, template = kendo.template, Widget = ui.Widget, excludedNodesRegExp = /^(a|div)$/i, NS = ".kendoTabStrip", IMG = "img", HREF = "href", PREV = "prev", LINK = "k-link", LAST = "k-last", CLICK = "click", ERROR = "error", EMPTY = ":empty", IMAGE = "k-image", FIRST = "k-first", SELECT = "select", ACTIVATE = "activate", CONTENT = "k-content", CONTENTURL = "contentUrl", MOUSEENTER = "mouseenter", MOUSELEAVE = "mouseleave", CONTENTLOAD = "contentLoad", DISABLEDSTATE = "k-state-disabled", DEFAULTSTATE = "k-state-default", ACTIVESTATE = "k-state-active", FOCUSEDSTATE = "k-state-focused", HOVERSTATE = "k-state-hover", TABONTOP = "k-tab-on-top", NAVIGATABLEITEMS = ".k-item:not(." + DISABLEDSTATE + ")", HOVERABLEITEMS = ".k-tabstrip-items > " + NAVIGATABLEITEMS + ":not(." + ACTIVESTATE + ")", templates = {
        content: template("<div class='k-content'#= contentAttributes(data) # role='tabpanel'>#= content(item) #</div>"),
        itemWrapper: template("<#= tag(item) # class='k-link'#= contentUrl(item) ##= textAttributes(item) #>" + "#= image(item) ##= sprite(item) ##= text(item) #" + "</#= tag(item) #>"),
        item: template("<li class='#= wrapperCssClass(group, item) #' role='tab' #=item.active ? \"aria-selected='true'\" : ''#>" + "#= itemWrapper(data) #" + "</li>"),
        image: template("<img class='k-image' alt='' src='#= imageUrl #' />"),
        sprite: template("<span class='k-sprite #= spriteCssClass #'></span>"),
        empty: template("")
    }, rendering = {
        wrapperCssClass: function(group, item) {
            var result = "k-item", index = item.index;
            if (item.enabled === false) {
                result += " k-state-disabled";
            } else {
                result += " k-state-default";
            }
            if (index === 0) {
                result += " k-first";
            }
            if (index == group.length - 1) {
                result += " k-last";
            }
            return result;
        },
        textAttributes: function(item) {
            return item.url ? " href='" + item.url + "'" : "";
        },
        text: function(item) {
            return item.encoded === false ? item.text : kendo.htmlEncode(item.text);
        },
        tag: function(item) {
            return item.url ? "a" : "span";
        },
        contentAttributes: function(content) {
            return content.active !== true ? " style='display:none' aria-hidden='true' aria-expanded='false'" : "";
        },
        content: function(item) {
            return item.content ? item.content : item.contentUrl ? "" : "&nbsp;";
        },
        contentUrl: function(item) {
            return item.contentUrl ? kendo.attr("content-url") + '="' + item.contentUrl + '"' : "";
        }
    };
    function updateTabClasses(tabs) {
        tabs.children(IMG).addClass(IMAGE);
        tabs.children("a").addClass(LINK).children(IMG).addClass(IMAGE);
        tabs.filter(":not([disabled]):not([class*=k-state-disabled])").addClass(DEFAULTSTATE);
        tabs.filter("li[disabled]").addClass(DISABLEDSTATE).removeAttr("disabled");
        tabs.filter(":not([class*=k-state])").children("a").filter(":focus").parent().addClass(ACTIVESTATE + " " + TABONTOP);
        tabs.attr("role", "tab");
        tabs.filter("." + ACTIVESTATE).attr("aria-selected", true);
        tabs.each(function() {
            var item = $(this);
            if (!item.children("." + LINK).length) {
                item.contents().filter(function() {
                    return !this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !trim(this.nodeValue));
                }).wrapAll("<a class='" + LINK + "'/>");
            }
        });
    }
    function updateFirstLast(tabGroup) {
        var tabs = tabGroup.children(".k-item");
        tabs.filter(".k-first:not(:first-child)").removeClass(FIRST);
        tabs.filter(".k-last:not(:last-child)").removeClass(LAST);
        tabs.filter(":first-child").addClass(FIRST);
        tabs.filter(":last-child").addClass(LAST);
    }
    var TabStrip = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            that._animations(that.options);
            if (that.element.is("ul")) {
                that.wrapper = that.element.wrapAll("<div />").parent();
            } else {
                that.wrapper = that.element;
            }
            options = that.options;
            that._isRtl = kendo.support.isRtl(that.wrapper);
            that._tabindex();
            that._updateClasses();
            that._dataSource();
            if (options.dataSource) {
                that.dataSource.fetch();
            }
            if (that.options.contentUrls) {
                that.wrapper.find(".k-tabstrip-items > .k-item").each(function(index, item) {
                    $(item).find(">." + LINK).data(CONTENTURL, that.options.contentUrls[index]);
                });
            }
            that.wrapper.on(MOUSEENTER + NS + " " + MOUSELEAVE + NS, HOVERABLEITEMS, that._toggleHover).on("keydown" + NS, $.proxy(that._keydown, that)).on("focus" + NS, $.proxy(that._active, that)).on("blur" + NS, function() {
                that._current(null);
            });
            that.wrapper.children(".k-tabstrip-items").on(CLICK + NS, ".k-state-disabled .k-link", false).on(CLICK + NS, " > " + NAVIGATABLEITEMS, function(e) {
                if (that._click($(e.currentTarget))) {
                    e.preventDefault();
                }
            });
            var selectedItems = that.tabGroup.children("li." + ACTIVESTATE), content = that.contentHolder(selectedItems.index());
            if (content.length > 0 && content[0].childNodes.length === 0) {
                that.activateTab(selectedItems.eq(0));
            }
            that.element.attr("role", "tablist");
            if (that.element[0].id) {
                that._ariaId = that.element[0].id + "_ts_active";
            }
            kendo.notify(that);
        },
        _active: function() {
            var item = this.tabGroup.children().filter("." + ACTIVESTATE);
            item = item[0] ? item : this._endItem("first");
            if (item[0]) {
                this._current(item);
            }
        },
        _endItem: function(action) {
            return this.tabGroup.children(NAVIGATABLEITEMS)[action]();
        },
        _item: function(item, action) {
            var endItem;
            if (action === PREV) {
                endItem = "last";
            } else {
                endItem = "first";
            }
            if (!item) {
                return this._endItem(endItem);
            }
            item = item[action]();
            if (!item[0]) {
                item = this._endItem(endItem);
            }
            if (item.hasClass(DISABLEDSTATE)) {
                item = this._item(item, action);
            }
            return item;
        },
        _current: function(candidate) {
            var that = this, focused = that._focused, id = that._ariaId;
            if (candidate === undefined) {
                return focused;
            }
            if (focused) {
                if (focused[0].id === id) {
                    focused.removeAttr("id");
                }
                focused.removeClass(FOCUSEDSTATE);
            }
            if (candidate) {
                if (!candidate.hasClass(ACTIVESTATE)) {
                    candidate.addClass(FOCUSEDSTATE);
                }
                that.element.removeAttr("aria-activedescendant");
                id = candidate[0].id || id;
                if (id) {
                    candidate.attr("id", id);
                    that.element.attr("aria-activedescendant", id);
                }
            }
            that._focused = candidate;
        },
        _keydown: function(e) {
            var that = this, key = e.keyCode, current = that._current(), rtl = that._isRtl, action;
            if (e.target != e.currentTarget) {
                return;
            }
            if (key == keys.DOWN || key == keys.RIGHT) {
                action = rtl ? PREV : "next";
            } else if (key == keys.UP || key == keys.LEFT) {
                action = rtl ? "next" : PREV;
            } else if (key == keys.ENTER || key == keys.SPACEBAR) {
                that._click(current);
                e.preventDefault();
            } else if (key == keys.HOME) {
                that._click(that._endItem("first"));
                e.preventDefault();
                return;
            } else if (key == keys.END) {
                that._click(that._endItem("last"));
                e.preventDefault();
                return;
            }
            if (action) {
                that._click(that._item(current, action));
                e.preventDefault();
            }
        },
        _dataSource: function() {
            var that = this;
            if (that.dataSource && that._refreshHandler) {
                that.dataSource.unbind("change", that._refreshHandler);
            } else {
                that._refreshHandler = $.proxy(that.refresh, that);
            }
            that.dataSource = kendo.data.DataSource.create(that.options.dataSource).bind("change", that._refreshHandler);
        },
        setDataSource: function(dataSource) {
            this.options.dataSource = dataSource;
            this._dataSource();
            dataSource.fetch();
        },
        _animations: function(options) {
            if (options && "animation" in options && !options.animation) {
                options.animation = {
                    open: {
                        effects: {}
                    },
                    close: {
                        effects: {}
                    }
                };
            }
        },
        refresh: function(e) {
            var that = this, options = that.options, text = kendo.getter(options.dataTextField), content = kendo.getter(options.dataContentField), contentUrl = kendo.getter(options.dataContentUrlField), image = kendo.getter(options.dataImageUrlField), url = kendo.getter(options.dataUrlField), sprite = kendo.getter(options.dataSpriteCssClass), idx, tabs = [], tab, action, view = that.dataSource.view(), length;
            e = e || {};
            action = e.action;
            if (action) {
                view = e.items;
            }
            for (idx = 0, length = view.length; idx < length; idx++) {
                tab = {
                    text: text(view[idx])
                };
                if (options.dataContentField) {
                    tab.content = content(view[idx]);
                }
                if (options.dataContentUrlField) {
                    tab.contentUrl = contentUrl(view[idx]);
                }
                if (options.dataUrlField) {
                    tab.url = url(view[idx]);
                }
                if (options.dataImageUrlField) {
                    tab.imageUrl = image(view[idx]);
                }
                if (options.dataSpriteCssClass) {
                    tab.spriteCssClass = sprite(view[idx]);
                }
                tabs[idx] = tab;
            }
            if (e.action == "add") {
                if (e.index < that.tabGroup.children().length) {
                    that.insertBefore(tabs, that.tabGroup.children().eq(e.index));
                } else {
                    that.append(tabs);
                }
            } else if (e.action == "remove") {
                for (idx = 0; idx < view.length; idx++) {
                    that.remove(e.index);
                }
            } else if (e.action == "itemchange") {
                idx = that.dataSource.view().indexOf(view[0]);
                if (e.field === options.dataTextField) {
                    that.tabGroup.children().eq(idx).find(".k-link").text(view[0].get(e.field));
                }
            } else {
                that.trigger("dataBinding");
                that.remove("li");
                that.append(tabs);
                that.trigger("dataBound");
            }
        },
        value: function(value) {
            var that = this;
            if (value !== undefined) {
                if (value != that.value()) {
                    that.tabGroup.children().each(function() {
                        if ($.trim($(this).text()) == value) {
                            that.select(this);
                        }
                    });
                }
            } else {
                return that.select().text();
            }
        },
        items: function() {
            return this.tabGroup[0].children;
        },
        setOptions: function(options) {
            var animation = this.options.animation;
            this._animations(options);
            options.animation = extend(true, animation, options.animation);
            Widget.fn.setOptions.call(this, options);
        },
        events: [ SELECT, ACTIVATE, ERROR, CONTENTLOAD, "change", "dataBinding", "dataBound" ],
        options: {
            name: "TabStrip",
            dataTextField: "",
            dataContentField: "",
            dataImageUrlField: "",
            dataUrlField: "",
            dataSpriteCssClass: "",
            dataContentUrlField: "",
            animation: {
                open: {
                    effects: "expand:vertical fadeIn",
                    duration: 200
                },
                close: {
                    // if close animation effects are defined, they will be used instead of open.reverse
                    duration: 200
                }
            },
            collapsible: false
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            if (that._refreshHandler) {
                that.dataSource.unbind("change", that._refreshHandler);
            }
            that.wrapper.off(NS);
            kendo.destroy(that.wrapper);
        },
        select: function(element) {
            var that = this;
            if (arguments.length === 0) {
                return that.tabGroup.children("li." + ACTIVESTATE);
            }
            if (!isNaN(element)) {
                element = that.tabGroup.children().get(element);
            }
            element = that.tabGroup.find(element);
            $(element).each(function(index, item) {
                item = $(item);
                if (!item.hasClass(ACTIVESTATE) && !that.trigger(SELECT, {
                    item: item[0],
                    contentElement: that.contentHolder(item.index())[0]
                })) {
                    that.activateTab(item);
                }
            });
            return that;
        },
        enable: function(element, state) {
            this._toggleDisabled(element, state !== false);
            return this;
        },
        disable: function(element) {
            this._toggleDisabled(element, false);
            return this;
        },
        reload: function(element) {
            element = this.tabGroup.find(element);
            var that = this;
            element.each(function() {
                var item = $(this), contentUrl = item.find("." + LINK).data(CONTENTURL), content = that.contentHolder(item.index());
                if (contentUrl) {
                    that.ajaxRequest(item, content, null, contentUrl);
                }
            });
            return that;
        },
        append: function(tab) {
            var that = this, inserted = that._create(tab);
            each(inserted.tabs, function(idx) {
                that.tabGroup.append(this);
                that.wrapper.append(inserted.contents[idx]);
            });
            updateFirstLast(that.tabGroup);
            that._updateContentElements();
            return that;
        },
        insertBefore: function(tab, referenceTab) {
            var that = this, inserted = that._create(tab), referenceContent = $(that.contentElement(referenceTab.index()));
            each(inserted.tabs, function(idx) {
                referenceTab.before(this);
                referenceContent.before(inserted.contents[idx]);
            });
            updateFirstLast(that.tabGroup);
            that._updateContentElements();
            return that;
        },
        insertAfter: function(tab, referenceTab) {
            var that = this, inserted = that._create(tab), referenceContent = $(that.contentElement(referenceTab.index()));
            each(inserted.tabs, function(idx) {
                referenceTab.after(this);
                referenceContent.after(inserted.contents[idx]);
            });
            updateFirstLast(that.tabGroup);
            that._updateContentElements();
            return that;
        },
        remove: function(elements) {
            var that = this, type = typeof elements, contents = $();
            if (type === "string") {
                elements = that.tabGroup.find(elements);
            } else if (type === "number") {
                elements = that.tabGroup.children().eq(elements);
            }
            elements.each(function() {
                contents.push(that.contentElement($(this).index()));
            });
            elements.remove();
            contents.remove();
            that._updateContentElements();
            return that;
        },
        _create: function(tab) {
            var plain = $.isPlainObject(tab), that = this, tabs, contents;
            if (plain || $.isArray(tab)) {
                tab = $.isArray(tab) ? tab : [ tab ];
                tabs = map(tab, function(value, idx) {
                    return $(TabStrip.renderItem({
                        group: that.tabGroup,
                        item: extend(value, {
                            index: idx
                        })
                    }));
                });
                contents = map(tab, function(value, idx) {
                    if (value.content || value.contentUrl) {
                        return $(TabStrip.renderContent({
                            item: extend(value, {
                                index: idx
                            })
                        }));
                    }
                });
            } else {
                tabs = $(tab);
                contents = $("<div class='" + CONTENT + "'/>");
                updateTabClasses(tabs);
            }
            return {
                tabs: tabs,
                contents: contents
            };
        },
        _toggleDisabled: function(element, enable) {
            element = this.tabGroup.find(element);
            element.each(function() {
                $(this).toggleClass(DEFAULTSTATE, enable).toggleClass(DISABLEDSTATE, !enable);
            });
        },
        _updateClasses: function() {
            var that = this, tabs, activeItem, activeTab;
            that.wrapper.addClass("k-widget k-header k-tabstrip");
            that.tabGroup = that.wrapper.children("ul").addClass("k-tabstrip-items k-reset");
            if (!that.tabGroup[0]) {
                that.tabGroup = $("<ul class='k-tabstrip-items k-reset'/>").appendTo(that.wrapper);
            }
            tabs = that.tabGroup.find("li").addClass("k-item");
            if (tabs.length) {
                activeItem = tabs.filter("." + ACTIVESTATE).index();
                activeTab = activeItem >= 0 ? activeItem : undefined;
                that.tabGroup.contents().filter(function() {
                    return this.nodeType == 3 && !trim(this.nodeValue);
                }).remove();
            }
            if (activeItem >= 0) {
                tabs.eq(activeItem).addClass(TABONTOP);
            }
            that.contentElements = that.wrapper.children("div");
            that.contentElements.addClass(CONTENT).eq(activeTab).addClass(ACTIVESTATE).css({
                display: "block"
            });
            if (tabs.length) {
                updateTabClasses(tabs);
                updateFirstLast(that.tabGroup);
                that._updateContentElements();
            }
        },
        _updateContentElements: function() {
            var that = this, contentUrls = that.options.contentUrls || [], tabStripID = that.element.attr("id"), contentElements = that.wrapper.children("div");
            that.tabGroup.find(".k-item").each(function(idx) {
                var currentContent = contentElements.eq(idx), id = tabStripID + "-" + (idx + 1);
                this.setAttribute("aria-controls", id);
                if (!currentContent.length && contentUrls[idx]) {
                    $("<div id='" + id + "' class='" + CONTENT + "'/>").appendTo(that.wrapper);
                } else {
                    currentContent.attr("id", id);
                }
                currentContent.attr("role", "tabpanel");
                currentContent.filter(":not(." + ACTIVESTATE + ")").attr("aria-hidden", true).attr("aria-expanded", false);
                currentContent.filter("." + ACTIVESTATE).attr("aria-expanded", true);
            });
            that.contentElements = that.contentAnimators = that.wrapper.children("div");
            // refresh the contents
            if (kendo.kineticScrollNeeded && kendo.mobile.ui.Scroller) {
                kendo.touchScroller(that.contentElements);
                that.contentElements = that.contentElements.children(".km-scroll-container");
            }
        },
        _toggleHover: function(e) {
            $(e.currentTarget).toggleClass(HOVERSTATE, e.type == MOUSEENTER);
        },
        _click: function(item) {
            var that = this, link = item.find("." + LINK), href = link.attr(HREF), collapse = that.options.collapsible, contentHolder = that.contentHolder(item.index()), prevent, isAnchor;
            if (item.closest(".k-widget")[0] != that.wrapper[0]) {
                return;
            }
            if (item.is("." + DISABLEDSTATE + (!collapse ? ",." + ACTIVESTATE : ""))) {
                return true;
            }
            isAnchor = link.data(CONTENTURL) || href && (href.charAt(href.length - 1) == "#" || href.indexOf("#" + that.element[0].id + "-") != -1);
            prevent = !href || isAnchor;
            if (that.tabGroup.children("[data-animating]").length) {
                return prevent;
            }
            if (that.trigger(SELECT, {
                item: item[0],
                contentElement: contentHolder[0]
            })) {
                return true;
            }
            if (prevent === false) {
                return;
            }
            if (collapse && item.is("." + ACTIVESTATE)) {
                that.deactivateTab(item);
                return true;
            }
            if (that.activateTab(item)) {
                prevent = true;
            }
            return prevent;
        },
        deactivateTab: function(item) {
            var that = this, animationSettings = that.options.animation, animation = animationSettings.open, close = extend({}, animationSettings.close), hasCloseAnimation = close && "effects" in close;
            item = that.tabGroup.find(item);
            close = extend(hasCloseAnimation ? close : extend({
                reverse: true
            }, animation), {
                hide: true
            });
            if (kendo.size(animation.effects)) {
                item.kendoAddClass(DEFAULTSTATE, {
                    duration: animation.duration
                });
                item.kendoRemoveClass(ACTIVESTATE, {
                    duration: animation.duration
                });
            } else {
                item.addClass(DEFAULTSTATE);
                item.removeClass(ACTIVESTATE);
            }
            item.removeAttr("aria-selected");
            that.contentAnimators.filter("." + ACTIVESTATE).kendoStop(true, true).kendoAnimate(close).removeClass(ACTIVESTATE).attr("aria-hidden", true);
        },
        activateTab: function(item) {
            item = this.tabGroup.find(item);
            var that = this, animationSettings = that.options.animation, animation = animationSettings.open, close = extend({}, animationSettings.close), hasCloseAnimation = close && "effects" in close, neighbours = item.parent().children(), oldTab = neighbours.filter("." + ACTIVESTATE), itemIndex = neighbours.index(item);
            close = extend(hasCloseAnimation ? close : extend({
                reverse: true
            }, animation), {
                hide: true
            });
            // deactivate previously active tab
            if (kendo.size(animation.effects)) {
                oldTab.kendoRemoveClass(ACTIVESTATE, {
                    duration: close.duration
                });
                item.kendoRemoveClass(HOVERSTATE, {
                    duration: close.duration
                });
            } else {
                oldTab.removeClass(ACTIVESTATE);
                item.removeClass(HOVERSTATE);
            }
            // handle content elements
            var contentAnimators = that.contentAnimators;
            if (item.data("in-request")) {
                that.xhr.abort();
                item.removeAttr("data-in-request");
            }
            if (contentAnimators.length === 0) {
                oldTab.removeClass(TABONTOP);
                item.addClass(TABONTOP).css("z-index");
                item.addClass(ACTIVESTATE);
                that._current(item);
                that.trigger("change");
                return false;
            }
            var visibleContents = contentAnimators.filter("." + ACTIVESTATE), contentHolder = that.contentHolder(itemIndex), contentElement = contentHolder.closest(".k-content");
            if (contentHolder.length === 0) {
                visibleContents.removeClass(ACTIVESTATE).attr("aria-hidden", true).kendoStop(true, true).kendoAnimate(close);
                return false;
            }
            item.attr("data-animating", true);
            var isAjaxContent = (item.children("." + LINK).data(CONTENTURL) || false) && contentHolder.is(EMPTY), showContentElement = function() {
                oldTab.removeClass(TABONTOP);
                item.addClass(TABONTOP).css("z-index");
                if (kendo.size(animation.effects)) {
                    oldTab.kendoAddClass(DEFAULTSTATE, {
                        duration: animation.duration
                    });
                    item.kendoAddClass(ACTIVESTATE, {
                        duration: animation.duration
                    });
                } else {
                    oldTab.addClass(DEFAULTSTATE);
                    item.addClass(ACTIVESTATE);
                }
                oldTab.removeAttr("aria-selected");
                item.attr("aria-selected", true);
                that._current(item);
                contentElement.addClass(ACTIVESTATE).removeAttr("aria-hidden").kendoStop(true, true).attr("aria-expanded", true).kendoAnimate(extend({
                    init: function() {
                        that.trigger(ACTIVATE, {
                            item: item[0],
                            contentElement: contentHolder[0]
                        });
                    }
                }, animation, {
                    complete: function() {
                        item.removeAttr("data-animating");
                    }
                }));
            }, showContent = function() {
                if (!isAjaxContent) {
                    showContentElement();
                    that.trigger("change");
                } else {
                    item.removeAttr("data-animating");
                    that.ajaxRequest(item, contentHolder, function() {
                        item.attr("data-animating", true);
                        showContentElement();
                        that.trigger("change");
                    });
                }
            };
            visibleContents.removeClass(ACTIVESTATE);
            visibleContents.attr("aria-hidden", true);
            visibleContents.attr("aria-expanded", false);
            if (visibleContents.length) {
                visibleContents.kendoStop(true, true).kendoAnimate(extend({
                    complete: showContent
                }, close));
            } else {
                showContent();
            }
            return true;
        },
        contentElement: function(itemIndex) {
            if (isNaN(itemIndex - 0)) {
                return undefined;
            }
            var contentElements = this.contentElements && this.contentElements[0] && !kendo.kineticScrollNeeded ? this.contentElements : this.contentAnimators, idTest = new RegExp("-" + (itemIndex + 1) + "$");
            if (contentElements) {
                for (var i = 0, len = contentElements.length; i < len; i++) {
                    if (idTest.test(contentElements.closest(".k-content")[i].id)) {
                        return contentElements[i];
                    }
                }
            }
            return undefined;
        },
        contentHolder: function(itemIndex) {
            var contentElement = $(this.contentElement(itemIndex)), scrollContainer = contentElement.children(".km-scroll-container");
            return kendo.support.touch && scrollContainer[0] ? scrollContainer : contentElement;
        },
        ajaxRequest: function(element, content, complete, url) {
            element = this.tabGroup.find(element);
            if (element.find(".k-loading").length) {
                return;
            }
            var that = this, link = element.find("." + LINK), data = {}, statusIcon = null, loadingIconTimeout = setTimeout(function() {
                statusIcon = $("<span class='k-icon k-loading'/>").prependTo(link);
            }, 100);
            url = url || link.data(CONTENTURL) || link.attr(HREF);
            element.attr("data-in-request", true);
            that.xhr = $.ajax({
                type: "GET",
                cache: false,
                url: url,
                dataType: "html",
                data: data,
                error: function(xhr, status) {
                    if (that.trigger("error", {
                        xhr: xhr,
                        status: status
                    })) {
                        this.complete();
                    }
                },
                complete: function() {
                    element.removeAttr("data-in-request");
                    clearTimeout(loadingIconTimeout);
                    if (statusIcon !== null) {
                        statusIcon.remove();
                    }
                },
                success: function(data) {
                    try {
                        content.html(data);
                    } catch (e) {
                        var console = window.console;
                        if (console && console.error) {
                            console.error(e.name + ": " + e.message + " in " + url);
                        }
                        this.error(this.xhr, "error");
                    }
                    if (complete) {
                        complete.call(that, content);
                    }
                    that.trigger(CONTENTLOAD, {
                        item: element[0],
                        contentElement: content[0]
                    });
                }
            });
        }
    });
    // client-side rendering
    extend(TabStrip, {
        renderItem: function(options) {
            options = extend({
                tabStrip: {},
                group: {}
            }, options);
            var empty = templates.empty, item = options.item;
            return templates.item(extend(options, {
                image: item.imageUrl ? templates.image : empty,
                sprite: item.spriteCssClass ? templates.sprite : empty,
                itemWrapper: templates.itemWrapper
            }, rendering));
        },
        renderContent: function(options) {
            return templates.content(extend(options, rendering));
        }
    });
    kendo.ui.plugin(TabStrip);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, keys = kendo.keys, activeElement = kendo._activeElement, extractFormat = kendo._extractFormat, support = kendo.support, browser = support.browser, ui = kendo.ui, Widget = ui.Widget, OPEN = "open", CLOSE = "close", CHANGE = "change", ns = ".kendoTimePicker", CLICK = "click" + ns, DEFAULT = "k-state-default", DISABLED = "disabled", READONLY = "readonly", LI = "li", SPAN = "<span/>", FOCUSED = "k-state-focused", HOVER = "k-state-hover", HOVEREVENTS = "mouseenter" + ns + " mouseleave" + ns, MOUSEDOWN = "mousedown" + ns, MS_PER_MINUTE = 6e4, MS_PER_DAY = 864e5, SELECTED = "k-state-selected", STATEDISABLED = "k-state-disabled", ARIA_SELECTED = "aria-selected", ARIA_EXPANDED = "aria-expanded", ARIA_HIDDEN = "aria-hidden", ARIA_DISABLED = "aria-disabled", ARIA_READONLY = "aria-readonly", ARIA_ACTIVEDESCENDANT = "aria-activedescendant", ID = "id", isArray = $.isArray, extend = $.extend, proxy = $.proxy, DATE = Date, TODAY = new DATE();
    TODAY = new DATE(TODAY.getFullYear(), TODAY.getMonth(), TODAY.getDate(), 0, 0, 0);
    var TimeView = function(options) {
        var that = this, id = options.id;
        that.options = options;
        that.ul = $('<ul tabindex="-1" role="listbox" aria-hidden="true" unselectable="on" class="k-list k-reset"/>').css({
            overflow: support.kineticScrollNeeded ? "" : "auto"
        }).on(CLICK, LI, proxy(that._click, that)).on("mouseenter" + ns, LI, function() {
            $(this).addClass(HOVER);
        }).on("mouseleave" + ns, LI, function() {
            $(this).removeClass(HOVER);
        });
        that.list = $("<div class='k-list-container'/>").append(that.ul).on(MOUSEDOWN, preventDefault);
        if (id) {
            that._timeViewID = id + "_timeview";
            that._optionID = id + "_option_selected";
            that.ul.attr(ID, that._timeViewID);
        }
        that._popup();
        that.template = kendo.template('<li tabindex="-1" role="option" class="k-item" unselectable="on">#=data#</li>', {
            useWithBlock: false
        });
    };
    TimeView.prototype = {
        current: function(candidate) {
            var that = this, active = that.options.active;
            if (candidate !== undefined) {
                if (that._current) {
                    that._current.removeClass(SELECTED).removeAttr(ARIA_SELECTED).removeAttr(ID);
                }
                if (candidate) {
                    candidate = $(candidate).addClass(SELECTED).attr(ID, that._optionID).attr(ARIA_SELECTED, true);
                    that.scroll(candidate[0]);
                }
                that._current = candidate;
                if (active) {
                    active(candidate);
                }
            } else {
                return that._current;
            }
        },
        close: function() {
            this.popup.close();
        },
        destroy: function() {
            var that = this;
            that.ul.off(ns);
            that.list.off(ns);
            that.popup.destroy();
        },
        open: function() {
            var that = this;
            if (!that.ul[0].firstChild) {
                that.bind();
            }
            that.popup.open();
            if (that._current) {
                that.scroll(that._current[0]);
            }
        },
        dataBind: function(dates) {
            var that = this, options = that.options, format = options.format, toString = kendo.toString, template = that.template, length = dates.length, idx = 0, date, html = "";
            for (;idx < length; idx++) {
                date = dates[idx];
                if (isInRange(date, options.min, options.max)) {
                    html += template(toString(date, format, options.culture));
                }
            }
            that._html(html, length);
        },
        refresh: function() {
            var that = this, options = that.options, format = options.format, offset = dst(), ignoreDST = offset < 0, min = options.min, max = options.max, msMin = getMilliseconds(min), msMax = getMilliseconds(max), msInterval = options.interval * MS_PER_MINUTE, toString = kendo.toString, template = that.template, start = new DATE(+min), startDay = start.getDate(), msStart, lastIdx, idx = 0, length, html = "";
            if (ignoreDST) {
                length = (MS_PER_DAY + offset * MS_PER_MINUTE) / msInterval;
            } else {
                length = MS_PER_DAY / msInterval;
            }
            if (msMin != msMax) {
                if (msMin > msMax) {
                    msMax += MS_PER_DAY;
                }
                length = (msMax - msMin) / msInterval + 1;
            }
            lastIdx = parseInt(length, 10);
            for (;idx < length; idx++) {
                if (idx) {
                    setTime(start, msInterval, ignoreDST);
                }
                if (msMax && lastIdx == idx) {
                    msStart = getMilliseconds(start);
                    if (startDay < start.getDate()) {
                        msStart += MS_PER_DAY;
                    }
                    if (msStart > msMax) {
                        start = new DATE(+max);
                    }
                }
                html += template(toString(start, format, options.culture));
            }
            that._html(html, length);
        },
        bind: function() {
            var that = this, dates = that.options.dates;
            if (dates && dates[0]) {
                that.dataBind(dates);
            } else {
                that.refresh();
            }
        },
        _html: function(html, length) {
            var that = this;
            that.ul[0].innerHTML = html;
            that._height(length);
            that.current(null);
            that.select(that._value);
        },
        scroll: function(item) {
            if (!item) {
                return;
            }
            var ul = this.ul[0], itemOffsetTop = item.offsetTop, itemOffsetHeight = item.offsetHeight, ulScrollTop = ul.scrollTop, ulOffsetHeight = ul.clientHeight, bottomDistance = itemOffsetTop + itemOffsetHeight;
            ul.scrollTop = ulScrollTop > itemOffsetTop ? itemOffsetTop : bottomDistance > ulScrollTop + ulOffsetHeight ? bottomDistance - ulOffsetHeight : ulScrollTop;
        },
        select: function(li) {
            var that = this, options = that.options, current = that._current;
            if (li instanceof Date) {
                li = kendo.toString(li, options.format, options.culture);
            }
            if (typeof li === "string") {
                if (!current || current.text() !== li) {
                    li = $.grep(that.ul[0].childNodes, function(node) {
                        return (node.textContent || node.innerText) == li;
                    });
                    li = li[0] ? li : null;
                } else {
                    li = current;
                }
            }
            that.current(li);
        },
        toggle: function() {
            var that = this;
            if (that.popup.visible()) {
                that.close();
            } else {
                that.open();
            }
        },
        value: function(value) {
            var that = this;
            that._value = value;
            if (that.ul[0].firstChild) {
                that.select(value);
            }
        },
        _click: function(e) {
            var that = this, li = $(e.currentTarget);
            if (!e.isDefaultPrevented()) {
                that.select(li);
                that.options.change(li.text(), true);
                that.close();
            }
        },
        _height: function(length) {
            if (length) {
                var that = this, list = that.list, parent = list.parent(".k-animation-container"), height = that.options.height;
                list.add(parent).show().height(that.ul[0].scrollHeight > height ? height : "auto").hide();
            }
        },
        _parse: function(value) {
            var that = this, options = that.options, current = that._value || TODAY;
            if (value instanceof DATE) {
                return value;
            }
            value = kendo.parseDate(value, options.parseFormats, options.culture);
            if (value) {
                value = new DATE(current.getFullYear(), current.getMonth(), current.getDate(), value.getHours(), value.getMinutes(), value.getSeconds(), value.getMilliseconds());
            }
            return value;
        },
        _adjustListWidth: function() {
            var list = this.list, width = list[0].style.width, wrapper = this.options.anchor, 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);
            }
            width = computedWidth - (list.outerWidth() - list.width());
            list.css({
                fontFamily: wrapper.css("font-family"),
                width: width
            }).data("width", width);
        },
        _popup: function() {
            var that = this, list = that.list, options = that.options, anchor = options.anchor;
            that.popup = new ui.Popup(list, extend(options.popup, {
                anchor: anchor,
                open: options.open,
                close: options.close,
                animation: options.animation,
                isRtl: support.isRtl(options.anchor)
            }));
            kendo.touchScroller(that.popup.element);
        },
        move: function(e) {
            var that = this, key = e.keyCode, ul = that.ul[0], current = that._current, down = key === keys.DOWN;
            if (key === keys.UP || down) {
                if (e.altKey) {
                    that.toggle(down);
                    return;
                } else if (down) {
                    current = current ? current[0].nextSibling : ul.firstChild;
                } else {
                    current = current ? current[0].previousSibling : ul.lastChild;
                }
                if (current) {
                    that.select(current);
                }
                that.options.change(that._current.text());
                e.preventDefault();
            } else if (key === keys.ENTER || key === keys.TAB || key === keys.ESC) {
                e.preventDefault();
                if (current) {
                    that.options.change(current.text(), true);
                }
                that.close();
            }
        }
    };
    function setTime(date, time, ignoreDST) {
        var offset = date.getTimezoneOffset(), offsetDiff;
        date.setTime(date.getTime() + time);
        if (!ignoreDST) {
            offsetDiff = date.getTimezoneOffset() - offset;
            date.setTime(date.getTime() + offsetDiff * MS_PER_MINUTE);
        }
    }
    function dst() {
        var today = new DATE(), midnight = new DATE(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0), noon = new DATE(today.getFullYear(), today.getMonth(), today.getDate(), 12, 0, 0);
        return -1 * (midnight.getTimezoneOffset() - noon.getTimezoneOffset());
    }
    function getMilliseconds(date) {
        return date.getHours() * 60 * MS_PER_MINUTE + date.getMinutes() * MS_PER_MINUTE + date.getSeconds() * 1e3 + date.getMilliseconds();
    }
    function isInRange(value, min, max) {
        var msMin = getMilliseconds(min), msMax = getMilliseconds(max), msValue;
        if (!value || msMin == msMax) {
            return true;
        }
        msValue = getMilliseconds(value);
        if (msMin > msValue) {
            msValue += MS_PER_DAY;
        }
        if (msMax < msMin) {
            msMax += MS_PER_DAY;
        }
        return msValue >= msMin && msValue <= msMax;
    }
    TimeView.getMilliseconds = getMilliseconds;
    kendo.TimeView = TimeView;
    var TimePicker = Widget.extend({
        init: function(element, options) {
            var that = this, ul, timeView, disabled;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            options = that.options;
            normalize(options);
            that._wrapper();
            that.timeView = timeView = new TimeView(extend({}, options, {
                id: element.attr(ID),
                anchor: that.wrapper,
                format: options.format,
                change: function(value, trigger) {
                    if (trigger) {
                        that._change(value);
                    } else {
                        element.val(value);
                    }
                },
                open: function(e) {
                    that.timeView._adjustListWidth();
                    if (that.trigger(OPEN)) {
                        e.preventDefault();
                    } else {
                        element.attr(ARIA_EXPANDED, true);
                        ul.attr(ARIA_HIDDEN, false);
                    }
                },
                close: function(e) {
                    if (that.trigger(CLOSE)) {
                        e.preventDefault();
                    } else {
                        element.attr(ARIA_EXPANDED, false);
                        ul.attr(ARIA_HIDDEN, true);
                    }
                },
                active: function(current) {
                    element.removeAttr(ARIA_ACTIVEDESCENDANT);
                    if (current) {
                        element.attr(ARIA_ACTIVEDESCENDANT, timeView._optionID);
                    }
                }
            }));
            ul = timeView.ul;
            that._icon();
            that._reset();
            element[0].type = "text";
            element.addClass("k-input").attr({
                role: "textbox",
                "aria-haspopup": true,
                "aria-expanded": false,
                "aria-owns": timeView._timeViewID
            });
            disabled = element.is("[disabled]");
            if (disabled) {
                that.enable(false);
            } else {
                that.readonly(element.is("[readonly]"));
            }
            that.value(options.value || element.val());
            kendo.notify(that);
        },
        options: {
            name: "TimePicker",
            min: TODAY,
            max: TODAY,
            format: "",
            dates: [],
            parseFormats: [],
            value: null,
            interval: 30,
            height: 200,
            animation: {}
        },
        events: [ OPEN, CLOSE, CHANGE ],
        setOptions: function(options) {
            var that = this, timeView = that.timeView, timeViewOptions = timeView.options;
            Widget.fn.setOptions.call(that, options);
            normalize(that.options);
            timeView.options = extend(timeViewOptions, that.options, {
                active: timeViewOptions.active,
                change: timeViewOptions.change,
                close: timeViewOptions.close,
                open: timeViewOptions.open
            });
            timeView.ul[0].innerHTML = "";
        },
        dataBind: function(dates) {
            if (isArray(dates)) {
                this.timeView.dataBind(dates);
            }
        },
        _editable: function(options) {
            var that = this, disable = options.disable, readonly = options.readonly, arrow = that._arrow.off(ns), element = that.element.off(ns), wrapper = that._inputWrapper.off(ns);
            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);
                });
                arrow.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.timeView.destroy();
            that.element.off(ns);
            that._arrow.off(ns);
            that._inputWrapper.off(ns);
            if (that._form) {
                that._form.off("reset", that._resetHandler);
            }
        },
        close: function() {
            this.timeView.close();
        },
        open: function() {
            this.timeView.open();
        },
        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();
        },
        _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.timeView.toggle();
            if (!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);
            }
        },
        _icon: function() {
            var that = this, element = that.element, arrow;
            arrow = element.next("span.k-select");
            if (!arrow[0]) {
                arrow = $('<span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-i-clock">select</span></span>').insertAfter(element);
            }
            that._arrow = arrow.attr({
                role: "button",
                "aria-controls": that.timeView._timeViewID
            });
        },
        _keydown: function(e) {
            var that = this, key = e.keyCode, timeView = that.timeView, value = that.element.val();
            if (timeView.popup.visible() || e.altKey) {
                timeView.move(e);
            } else if (key === keys.ENTER && value !== that._oldText) {
                that._change(value);
            }
        },
        _option: function(option, value) {
            var that = this, options = that.options;
            if (value === undefined) {
                return options[option];
            }
            value = that.timeView._parse(value);
            if (!value) {
                return;
            }
            value = new DATE(+value);
            options[option] = value;
            that.timeView.options[option] = value;
            that.timeView.bind();
        },
        _toggleHover: function(e) {
            $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter");
        },
        _update: function(value) {
            var that = this, options = that.options, timeView = that.timeView, date = timeView._parse(value);
            if (!isInRange(date, options.min, options.max)) {
                date = null;
            }
            that._value = date;
            that.element.val(date ? kendo.toString(date, options.format, options.culture) : value);
            timeView.value(date);
            return date;
        },
        _wrapper: function() {
            var that = this, element = that.element, wrapper;
            wrapper = element.parents(".k-timepicker");
            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;
            that.wrapper = wrapper.addClass("k-widget k-timepicker k-header").addClass(element[0].className);
            element.css({
                width: "100%",
                height: element[0].style.height
            });
            that._inputWrapper = $(wrapper[0].firstChild);
        },
        _reset: function() {
            var that = this, element = that.element, form = element.closest("form");
            if (form[0]) {
                that._resetHandler = function() {
                    that.value(element[0].defaultValue);
                };
                that._form = form.on("reset", that._resetHandler);
            }
        }
    });
    function normalize(options) {
        var parseFormats = options.parseFormats;
        options.format = extractFormat(options.format || kendo.getCulture(options.culture).calendars.standard.patterns.t);
        parseFormats = isArray(parseFormats) ? parseFormats : [ parseFormats ];
        parseFormats.splice(0, 0, options.format);
        options.parseFormats = parseFormats;
    }
    function preventDefault(e) {
        e.preventDefault();
    }
    ui.plugin(TimePicker);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, TimeView = kendo.TimeView, parse = kendo.parseDate, activeElement = kendo._activeElement, extractFormat = kendo._extractFormat, calendar = kendo.calendar, isInRange = calendar.isInRange, restrictValue = calendar.restrictValue, isEqualDatePart = calendar.isEqualDatePart, getMilliseconds = TimeView.getMilliseconds, ui = kendo.ui, Widget = ui.Widget, OPEN = "open", CLOSE = "close", CHANGE = "change", ns = ".kendoDateTimePicker", CLICK = "click" + ns, DISABLED = "disabled", READONLY = "readonly", DEFAULT = "k-state-default", FOCUSED = "k-state-focused", HOVER = "k-state-hover", STATEDISABLED = "k-state-disabled", HOVEREVENTS = "mouseenter" + ns + " mouseleave" + ns, MOUSEDOWN = "mousedown" + ns, MONTH = "month", SPAN = "<span/>", ARIA_ACTIVEDESCENDANT = "aria-activedescendant", ARIA_EXPANDED = "aria-expanded", ARIA_HIDDEN = "aria-hidden", ARIA_OWNS = "aria-owns", ARIA_DISABLED = "aria-disabled", ARIA_READONLY = "aria-readonly", DATE = Date, MIN = new DATE(1900, 0, 1), MAX = new DATE(2099, 11, 31), dateViewParams = {
        view: "date"
    }, timeViewParams = {
        view: "time"
    }, extend = $.extend;
    var DateTimePicker = Widget.extend({
        init: function(element, options) {
            var that = this, disabled;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            options = that.options;
            normalize(options);
            that._wrapper();
            that._views();
            that._icons();
            that._reset();
            that._template();
            element[0].type = "text";
            element.addClass("k-input").attr({
                role: "textbox",
                "aria-haspopup": true,
                "aria-expanded": false
            });
            that._midnight = getMilliseconds(options.min) + getMilliseconds(options.max) === 0;
            disabled = element.is("[disabled]");
            if (disabled) {
                that.enable(false);
            } else {
                that.readonly(element.is("[readonly]"));
            }
            that.value(options.value || element.val());
            kendo.notify(that);
        },
        options: {
            name: "DateTimePicker",
            value: null,
            format: "",
            timeFormat: "",
            culture: "",
            parseFormats: [],
            dates: [],
            min: new DATE(MIN),
            max: new DATE(MAX),
            interval: 30,
            height: 200,
            footer: "",
            start: MONTH,
            depth: MONTH,
            animation: {},
            month: {},
            ARIATemplate: 'Current focused date is #=kendo.toString(data.current, "G")#'
        },
        events: [ OPEN, CLOSE, CHANGE ],
        setOptions: function(options) {
            var that = this, dateViewOptions = that.dateView.options, timeViewOptions = that.timeView.options;
            Widget.fn.setOptions.call(that, options);
            normalize(that.options);
            options = that.options;
            extend(dateViewOptions, options, {
                change: dateViewOptions.change,
                close: dateViewOptions.close,
                open: dateViewOptions.open
            });
            extend(timeViewOptions, options, {
                format: options.timeFormat,
                active: timeViewOptions.active,
                change: timeViewOptions.change,
                close: timeViewOptions.close,
                open: timeViewOptions.open
            });
            that.timeView.ul[0].innerHTML = "";
        },
        _editable: function(options) {
            var that = this, element = that.element.off(ns), dateIcon = that._dateIcon.off(ns), timeIcon = that._timeIcon.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("focus" + ns, function() {
                    that._inputWrapper.addClass(FOCUSED);
                }).on("blur" + ns, function() {
                    that._inputWrapper.removeClass(FOCUSED);
                    if (element.val() !== that._oldText) {
                        that._change(element.val());
                    }
                    that.close("date");
                    that.close("time");
                });
                dateIcon.on(MOUSEDOWN, preventDefault).on(CLICK, function() {
                    that.toggle("date");
                    if (!kendo.support.touch && element[0] !== activeElement()) {
                        element.focus();
                    }
                });
                timeIcon.on(MOUSEDOWN, preventDefault).on(CLICK, function() {
                    that.toggle("time");
                    if (!kendo.support.touch && element[0] !== activeElement()) {
                        element.focus();
                    }
                });
            } 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.timeView.destroy();
            that.element.off(ns);
            that._dateIcon.off(ns);
            that._timeIcon.off(ns);
            that._inputWrapper.off(ns);
            if (that._form) {
                that._form.off("reset", that._resetHandler);
            }
        },
        close: function(view) {
            if (view !== "time") {
                view = "date";
            }
            this[view + "View"].close();
        },
        open: function(view) {
            if (view !== "time") {
                view = "date";
            }
            this[view + "View"].open();
        },
        min: function(value) {
            return this._option("min", value);
        },
        max: function(value) {
            return this._option("max", value);
        },
        toggle: function(view) {
            var secondView = "timeView";
            if (view !== "time") {
                view = "date";
            } else {
                secondView = "dateView";
            }
            this[view + "View"].toggle();
            this[secondView].close();
        },
        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();
        },
        _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);
            }
        },
        _option: function(option, value) {
            var that = this, options = that.options, timeView = that.timeView, timeViewOptions = timeView.options, current = that._value || that._old;
            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);
            that._midnight = getMilliseconds(options.min) + getMilliseconds(options.max) === 0;
            if (current && isEqualDatePart(value, current)) {
                if (that._midnight && option == "max") {
                    timeViewOptions[option] = MAX;
                    timeView.dataBind([ MAX ]);
                    return;
                }
                timeViewOptions[option] = value;
            } else {
                timeViewOptions.max = MAX;
                timeViewOptions.min = MIN;
            }
            timeView.bind();
        },
        _toggleHover: function(e) {
            $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter");
        },
        _update: function(value) {
            var that = this, options = that.options, min = options.min, max = options.max, dates = options.dates, timeView = that.timeView, date = parse(value, options.parseFormats, options.culture), rebind, timeViewOptions, old, skip, 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;
            timeView.value(date);
            that.dateView.value(date);
            if (date) {
                old = that._old;
                timeViewOptions = timeView.options;
                if (dates[0]) {
                    dates = $.grep(dates, function(d) {
                        return isEqualDatePart(date, d);
                    });
                    if (dates[0]) {
                        timeView.dataBind(dates);
                        skip = true;
                    }
                }
                if (!skip) {
                    if (isEqualDatePart(date, min)) {
                        timeViewOptions.min = min;
                        timeViewOptions.max = MAX;
                        rebind = true;
                    }
                    if (isEqualDatePart(date, max)) {
                        if (that._midnight) {
                            timeView.dataBind([ MAX ]);
                            skip = true;
                        } else {
                            timeViewOptions.max = max;
                            if (!rebind) {
                                timeViewOptions.min = MIN;
                            }
                            rebind = true;
                        }
                    }
                }
                if (!skip && (!old && rebind || old && !isEqualDatePart(old, date))) {
                    if (!rebind) {
                        timeViewOptions.max = MAX;
                        timeViewOptions.min = MIN;
                    }
                    timeView.bind();
                }
            }
            that.element.val(date ? kendo.toString(date, options.format, options.culture) : value);
            that._updateARIA(date);
            return date;
        },
        _keydown: function(e) {
            var that = this, dateView = that.dateView, timeView = that.timeView, value = that.element.val(), isDateViewVisible = dateView.popup.visible();
            if (e.altKey && e.keyCode === kendo.keys.DOWN) {
                that.toggle(isDateViewVisible ? "time" : "date");
            } else if (isDateViewVisible) {
                dateView.move(e);
                that._updateARIA(dateView._current);
            } else if (timeView.popup.visible()) {
                timeView.move(e);
            } else if (e.keyCode === kendo.keys.ENTER && value !== that._oldText) {
                that._change(value);
            }
        },
        _views: function() {
            var that = this, element = that.element, options = that.options, id = element.attr("id"), dateView, timeView, div, ul, date;
            that.dateView = dateView = new kendo.DateView(extend({}, options, {
                id: id,
                anchor: that.wrapper,
                change: function() {
                    var value = dateView.calendar.value(), msValue = +value, msMin = +options.min, msMax = +options.max, current;
                    if (msValue === msMin || msValue === msMax) {
                        current = new DATE(+that._value);
                        current.setFullYear(value.getFullYear());
                        current.setMonth(value.getMonth());
                        current.setDate(value.getDate());
                        if (isInRange(current, msMin, msMax)) {
                            value = current;
                        }
                    }
                    that._change(value);
                    that.close("date");
                },
                close: function(e) {
                    if (that.trigger(CLOSE, dateViewParams)) {
                        e.preventDefault();
                    } else {
                        element.attr(ARIA_EXPANDED, false);
                        div.attr(ARIA_HIDDEN, true);
                        if (!timeView.popup.visible()) {
                            element.removeAttr(ARIA_OWNS);
                        }
                    }
                },
                open: function(e) {
                    if (that.trigger(OPEN, dateViewParams)) {
                        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);
                            }
                        }
                        div.attr(ARIA_HIDDEN, false);
                        element.attr(ARIA_EXPANDED, true).attr(ARIA_OWNS, dateView._dateViewID);
                    }
                }
            }));
            div = dateView.div;
            that.timeView = timeView = new TimeView({
                id: id,
                value: options.value,
                anchor: that.wrapper,
                animation: options.animation,
                format: options.timeFormat,
                culture: options.culture,
                height: options.height,
                interval: options.interval,
                min: new DATE(MIN),
                max: new DATE(MAX),
                parseFormats: options.parseFormats,
                change: function(value, trigger) {
                    value = timeView._parse(value);
                    if (value < options.min) {
                        value = new DATE(+options.min);
                        timeView.options.min = value;
                    } else if (value > options.max) {
                        value = new DATE(+options.max);
                        timeView.options.max = value;
                    }
                    if (trigger) {
                        that._timeSelected = true;
                        that._change(value);
                    } else {
                        element.val(kendo.toString(value, options.format, options.culture));
                        dateView.value(value);
                        that._updateARIA(value);
                    }
                },
                close: function(e) {
                    if (that.trigger(CLOSE, timeViewParams)) {
                        e.preventDefault();
                    } else {
                        ul.attr(ARIA_HIDDEN, true);
                        element.attr(ARIA_EXPANDED, false);
                        if (!dateView.popup.visible()) {
                            element.removeAttr(ARIA_OWNS);
                        }
                    }
                },
                open: function(e) {
                    timeView._adjustListWidth();
                    if (that.trigger(OPEN, timeViewParams)) {
                        e.preventDefault();
                    } else {
                        ul.attr(ARIA_HIDDEN, false);
                        element.attr(ARIA_EXPANDED, true).attr(ARIA_OWNS, timeView._timeViewID);
                    }
                },
                active: function(current) {
                    element.removeAttr(ARIA_ACTIVEDESCENDANT);
                    if (current) {
                        element.attr(ARIA_ACTIVEDESCENDANT, timeView._optionID);
                    }
                }
            });
            ul = timeView.ul;
        },
        _icons: function() {
            var that = this, element = that.element, icons;
            icons = element.next("span.k-select");
            if (!icons[0]) {
                icons = $('<span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-i-calendar">select</span><span unselectable="on" class="k-icon k-i-clock">select</span></span>').insertAfter(element);
            }
            icons = icons.children();
            that._dateIcon = icons.eq(0).attr({
                role: "button",
                "aria-controls": that.dateView._dateViewID
            });
            that._timeIcon = icons.eq(1).attr({
                role: "button",
                "aria-controls": that.timeView._timeViewID
            });
        },
        _wrapper: function() {
            var that = this, element = that.element, wrapper;
            wrapper = element.parents(".k-datetimepicker");
            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-datetimepicker k-header").addClass(element[0].className);
            that._inputWrapper = $(wrapper[0].firstChild);
        },
        _reset: function() {
            var that = this, element = that.element, form = 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 = kendo.template(this.options.ARIATemplate);
        },
        _updateARIA: function(date) {
            this.element.attr("aria-label", this._ariaTemplate({
                current: date
            }));
        }
    });
    function preventDefault(e) {
        e.preventDefault();
    }
    function normalize(options) {
        var patterns = kendo.getCulture(options.culture).calendars.standard.patterns, timeFormat;
        options.format = extractFormat(options.format || patterns.g);
        options.timeFormat = timeFormat = extractFormat(options.timeFormat || patterns.t);
        kendo.DateView.normalize(options);
        if ($.inArray(timeFormat, options.parseFormats) === -1) {
            options.parseFormats.splice(1, 0, timeFormat);
        }
    }
    ui.plugin(DateTimePicker);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, data = kendo.data, extend = $.extend, template = kendo.template, isArray = $.isArray, Widget = ui.Widget, HierarchicalDataSource = data.HierarchicalDataSource, proxy = $.proxy, keys = kendo.keys, NS = ".kendoTreeView", SELECT = "select", NAVIGATE = "navigate", EXPAND = "expand", CHANGE = "change", CHECKED = "checked", COLLAPSE = "collapse", DRAGSTART = "dragstart", DRAG = "drag", DROP = "drop", DRAGEND = "dragend", DATABOUND = "dataBound", CLICK = "click", VISIBILITY = "visibility", UNDEFINED = "undefined", KSTATEHOVER = "k-state-hover", KTREEVIEW = "k-treeview", VISIBLE = ":visible", NODE = ".k-item", STRING = "string", ARIASELECTED = "aria-selected", ARIADISABLED = "aria-disabled", TreeView, subGroup, nodeContents, nodeIcon, bindings = {
        text: "dataTextField",
        url: "dataUrlField",
        spriteCssClass: "dataSpriteCssClassField",
        imageUrl: "dataImageUrlField"
    }, isDomElement = function(o) {
        return typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
        o && typeof o === "object" && o.nodeType === 1 && typeof o.nodeName === STRING;
    };
    function contentChild(filter) {
        return function(node) {
            var result = node.children(".k-animation-container");
            if (!result.length) {
                result = node;
            }
            return result.children(filter);
        };
    }
    subGroup = contentChild(".k-group");
    nodeContents = contentChild(".k-group,.k-content");
    nodeIcon = function(node) {
        return node.children("div").children(".k-icon");
    };
    function checkboxes(node) {
        return node.children("div").find(".k-checkbox:first :checkbox");
    }
    function updateNodeHtml(node) {
        var wrapper = node.children("div"), group = node.children("ul"), toggleButton = wrapper.children(".k-icon"), checkbox = node.children(":checkbox"), innerWrapper = wrapper.children(".k-in"), currentNode, tmp;
        if (node.hasClass("k-treeview")) {
            return;
        }
        if (!wrapper.length) {
            wrapper = $("<div />").prependTo(node);
        }
        if (!toggleButton.length && group.length) {
            toggleButton = $("<span class='k-icon' />").prependTo(wrapper);
        } else if (!group.length || !group.children().length) {
            toggleButton.remove();
            group.remove();
        }
        if (checkbox.length) {
            $("<span class='k-checkbox' />").appendTo(wrapper).append(checkbox);
        }
        if (!innerWrapper.length) {
            innerWrapper = $("<span class='k-in' />").appendTo(wrapper)[0];
            // move all non-group content in the k-in container
            currentNode = wrapper[0].nextSibling;
            innerWrapper = wrapper.find(".k-in")[0];
            while (currentNode && currentNode.nodeName.toLowerCase() != "ul") {
                tmp = currentNode;
                currentNode = currentNode.nextSibling;
                if (tmp.nodeType == 3) {
                    tmp.nodeValue = $.trim(tmp.nodeValue);
                }
                innerWrapper.appendChild(tmp);
            }
        }
    }
    TreeView = Widget.extend({
        init: function(element, options) {
            var that = this, dataInit, inferred = false;
            if (isArray(options)) {
                dataInit = true;
                options = {
                    dataSource: options
                };
            }
            if (options && typeof options.loadOnDemand == UNDEFINED && isArray(options.dataSource)) {
                options.loadOnDemand = false;
            }
            Widget.prototype.init.call(that, element, options);
            element = that.element;
            options = that.options;
            inferred = element.is("ul") || element.hasClass(KTREEVIEW);
            if (inferred) {
                options.dataSource.list = element.is("ul") ? element : element.children("ul");
            }
            that._animation();
            that._accessors();
            that._templates();
            // render treeview if it's not already rendered
            if (!element.hasClass(KTREEVIEW)) {
                that._wrapper();
                if (inferred) {
                    that.root = element;
                    that._group(that.wrapper);
                }
            } else {
                // otherwise just initialize properties
                that.wrapper = element;
                that.root = element.children("ul").eq(0);
            }
            that._tabindex();
            if (!that.wrapper.filter("[role=tree]").length) {
                that.wrapper.attr("role", "tree");
            }
            that._dataSource(inferred);
            that._attachEvents();
            if (options.dragAndDrop) {
                that.dragging = new TreeViewDragAndDrop(that);
            }
            if (!inferred) {
                if (options.autoBind) {
                    that._progress(true);
                    that.dataSource.fetch();
                }
            } else {
                that._attachUids();
            }
            if (options.checkboxes && options.checkboxes.checkChildren) {
                that._updateIndeterminateInitial(that.wrapper);
            }
            if (that.element[0].id) {
                that._ariaId = kendo.format("{0}_tv_active", that.element[0].id);
            }
        },
        _attachEvents: function() {
            var that = this, clickableItems = ".k-in:not(.k-state-selected,.k-state-disabled)", MOUSEENTER = "mouseenter";
            that.wrapper.on(MOUSEENTER + NS, ".k-in.k-state-selected", function(e) {
                e.preventDefault();
            }).on(MOUSEENTER + NS, clickableItems, function() {
                $(this).addClass(KSTATEHOVER);
            }).on("mouseleave" + NS, clickableItems, function() {
                $(this).removeClass(KSTATEHOVER);
            }).on(CLICK + NS, clickableItems, proxy(that._click, that)).on("dblclick" + NS, ".k-in:not(.k-state-disabled)", proxy(that._toggleButtonClick, that)).on(CLICK + NS, ".k-plus,.k-minus", proxy(that._toggleButtonClick, that)).on("keydown" + NS, proxy(that._keydown, that)).on("focus" + NS, proxy(that._focus, that)).on("blur" + NS, proxy(that._blur, that)).on("mousedown" + NS, ".k-in,.k-checkbox :checkbox,.k-plus,.k-minus", proxy(that._mousedown, that)).on("change" + NS, ".k-checkbox :checkbox", proxy(that._checkboxChange, that)).on("click" + NS, ".k-checkbox :checkbox", proxy(that._checkboxClick, that)).on("click" + NS, function(e) {
                if (!$(e.target).is(":focusable")) {
                    that.focus();
                }
            });
        },
        _checkboxClick: function(e) {
            var checkbox = $(e.target);
            if (checkbox.data("indeterminate")) {
                checkbox.data("indeterminate", false).prop("indeterminate", false).prop(CHECKED, true);
                this._checkboxChange(e);
            }
        },
        _attachUids: function(root, dataSource) {
            var that = this, data, uidAttr = kendo.attr("uid");
            root = root || that.root;
            dataSource = dataSource || that.dataSource;
            data = dataSource.view();
            root.children("li").each(function(index, item) {
                item = $(item).attr(uidAttr, data[index].uid);
                item.attr("role", "treeitem");
                that._attachUids(item.children("ul"), data[index].children);
            });
        },
        _animation: function() {
            var options = this.options, animationOptions = options.animation;
            if (animationOptions === false) {
                animationOptions = {
                    expand: {
                        effects: {}
                    },
                    collapse: {
                        hide: true,
                        effects: {}
                    }
                };
            } else if (!animationOptions.collapse || !("effects" in animationOptions.collapse)) {
                animationOptions.collapse = extend({
                    reverse: true
                }, animationOptions.expand);
            }
            extend(animationOptions.collapse, {
                hide: true
            });
            options.animation = animationOptions;
        },
        _templates: function() {
            var that = this, options = that.options, fieldAccessor = proxy(that._fieldAccessor, that);
            if (options.template && typeof options.template == STRING) {
                options.template = template(options.template);
            } else if (!options.template) {
                options.template = template("# var text = " + fieldAccessor("text") + "(item); #" + "# if (typeof item.encoded != 'undefined' && item.encoded === false) {#" + "#= text #" + "# } else { #" + "#: text #" + "# } #");
            }
            that._checkboxes();
            that.templates = {
                wrapperCssClass: function(group, item) {
                    var result = "k-item", index = item.index;
                    if (group.firstLevel && index === 0) {
                        result += " k-first";
                    }
                    if (index == group.length - 1) {
                        result += " k-last";
                    }
                    return result;
                },
                cssClass: function(group, item) {
                    var result = "", index = item.index, groupLength = group.length - 1;
                    if (group.firstLevel && index === 0) {
                        result += "k-top ";
                    }
                    if (index === 0 && index != groupLength) {
                        result += "k-top";
                    } else if (index == groupLength) {
                        result += "k-bot";
                    } else {
                        result += "k-mid";
                    }
                    return result;
                },
                textClass: function(item) {
                    var result = "k-in";
                    if (item.enabled === false) {
                        result += " k-state-disabled";
                    }
                    if (item.selected === true) {
                        result += " k-state-selected";
                    }
                    return result;
                },
                toggleButtonClass: function(item) {
                    var result = "k-icon";
                    if (item.expanded !== true) {
                        result += " k-plus";
                    } else {
                        result += " k-minus";
                    }
                    if (item.enabled === false) {
                        result += "-disabled";
                    }
                    return result;
                },
                groupAttributes: function(group) {
                    return group.expanded !== true ? " style='display:none'" : "";
                },
                groupCssClass: function(group) {
                    var cssClass = "k-group";
                    if (group.firstLevel) {
                        cssClass += " k-treeview-lines";
                    }
                    return cssClass;
                },
                dragClue: template("<div class='k-header k-drag-clue'>" + "<span class='k-icon k-drag-status'></span>" + "#= treeview.template(data) #" + "</div>"),
                group: template("<ul class='#= r.groupCssClass(group) #'#= r.groupAttributes(group) # role='group'>" + "#= renderItems(data) #" + "</ul>"),
                itemContent: template("# var imageUrl = " + fieldAccessor("imageUrl") + "(item); #" + "# var spriteCssClass = " + fieldAccessor("spriteCssClass") + "(item); #" + "# if (imageUrl) { #" + "<img class='k-image' alt='' src='#= imageUrl #'>" + "# } #" + "# if (spriteCssClass) { #" + "<span class='k-sprite #= spriteCssClass #'></span>" + "# } #" + "#= treeview.template(data) #"),
                itemElement: template("# var url = " + fieldAccessor("url") + "(item); #" + "<div class='#= r.cssClass(group, item) #'>" + "# if (item.hasChildren) { #" + "<span class='#= r.toggleButtonClass(item) #' role='presentation'></span>" + "# } #" + "# if (treeview.checkboxes) { #" + "<span class='k-checkbox' role='presentation'>" + "#= treeview.checkboxes.template(data) #" + "</span>" + "# } #" + "# var tag = url ? 'a' : 'span'; #" + "# var textAttr = url ? ' href=\\'' + url + '\\'' : ''; #" + "<#=tag#  class='#= r.textClass(item) #'#= textAttr #>" + "#= r.itemContent(data) #" + "</#=tag#>" + "</div>"),
                item: template("<li role='treeitem' class='#= r.wrapperCssClass(group, item) #'" + " " + kendo.attr("uid") + "='#= item.uid #'" + "#=item.selected ? \"aria-selected='true'\" : ''#" + "#=item.enabled === false ? \"aria-disabled='true'\" : ''#" + ">" + "#= r.itemElement(data) #" + "</li>"),
                loading: template("<div class='k-icon k-loading' /> Loading...")
            };
        },
        items: function() {
            return this.element.find(".k-item");
        },
        setDataSource: function(dataSource) {
            this.options.dataSource = dataSource;
            this._dataSource();
            this.dataSource.fetch();
        },
        _dataSource: function(silentRead) {
            var that = this, options = that.options, dataSource = options.dataSource;
            function recursiveRead(data) {
                for (var i = 0; i < data.length; i++) {
                    data[i]._initChildren();
                    data[i].children.fetch();
                    recursiveRead(data[i].children.view());
                }
            }
            dataSource = isArray(dataSource) ? {
                data: dataSource
            } : dataSource;
            if (that.dataSource && that._refreshHandler) {
                that.dataSource.unbind(CHANGE, that._refreshHandler);
            } else {
                that._refreshHandler = proxy(that.refresh, that);
            }
            if (!dataSource.fields) {
                dataSource.fields = [ {
                    field: "text"
                }, {
                    field: "url"
                }, {
                    field: "spriteCssClass"
                }, {
                    field: "imageUrl"
                } ];
            }
            that.dataSource = HierarchicalDataSource.create(dataSource);
            if (silentRead) {
                that.dataSource.fetch();
                recursiveRead(that.dataSource.view());
            }
            that.dataSource.bind(CHANGE, that._refreshHandler);
        },
        events: [ DRAGSTART, DRAG, DROP, DRAGEND, DATABOUND, EXPAND, COLLAPSE, SELECT, CHANGE, NAVIGATE ],
        options: {
            name: "TreeView",
            dataSource: {},
            animation: {
                expand: {
                    effects: "expand:vertical",
                    duration: 200
                },
                collapse: {
                    duration: 100
                }
            },
            dragAndDrop: false,
            checkboxes: false,
            autoBind: true,
            loadOnDemand: true,
            template: "",
            dataTextField: null
        },
        _accessors: function() {
            var that = this, options = that.options, i, field, textField, element = that.element;
            for (i in bindings) {
                field = options[bindings[i]];
                textField = element.attr(kendo.attr(i + "-field"));
                if (!field && textField) {
                    field = textField;
                }
                if (!field) {
                    field = i;
                }
                if (!isArray(field)) {
                    field = [ field ];
                }
                options[bindings[i]] = field;
            }
        },
        _fieldAccessor: function(fieldName) {
            var fieldBindings = this.options[bindings[fieldName]], count = fieldBindings.length, result = "(function(item) {";
            if (count === 0) {
                result += "return item['" + fieldName + "'];";
            } else {
                result += "var level = item.level();" + "var levels = [" + $.map(fieldBindings, function(x) {
                    return "function(d){ return " + kendo.expr(x) + "}";
                }).join(",") + "];";
                // generates levels[level < 3 ? level : 2](item);
                result += "return levels[Math.min(level, " + count + "-1)](item)";
            }
            result += "})";
            return result;
        },
        setOptions: function(options) {
            var that = this;
            if ("dragAndDrop" in options && options.dragAndDrop && !that.options.dragAndDrop) {
                that.dragging = new TreeViewDragAndDrop(that);
            }
            Widget.fn.setOptions.call(that, options);
            that._animation();
            that._templates();
        },
        _trigger: function(eventName, node) {
            return this.trigger(eventName, {
                node: node.closest(NODE)[0]
            });
        },
        _setIndeterminate: function(node) {
            var group = subGroup(node), siblings, length, all = true, i;
            if (!group.length) {
                return;
            }
            siblings = checkboxes(group.children());
            length = siblings.length;
            if (length > 1) {
                for (i = 1; i < length; i++) {
                    if (siblings[i].checked != siblings[i - 1].checked || siblings[i].indeterminate || siblings[i - 1].indeterminate) {
                        all = false;
                        break;
                    }
                }
            } else {
                all = !siblings[0].indeterminate;
            }
            checkboxes(node).data("indeterminate", !all).prop("indeterminate", !all).prop(CHECKED, all && siblings[0].checked);
        },
        _updateIndeterminateInitial: function(node) {
            var subnodes = subGroup(node).children(), i;
            if (subnodes.length) {
                for (i = 0; i < subnodes.length; i++) {
                    this._updateIndeterminateInitial(subnodes.eq(i));
                }
                this._setIndeterminate(node);
            }
        },
        _updateIndeterminate: function(node) {
            var parentNode = this.parent(node), checkbox;
            if (parentNode.length) {
                this._setIndeterminate(parentNode);
                checkbox = parentNode.children("div").find(".k-checkbox > :checkbox");
                if (checkbox.prop("indeterminate") === false) {
                    this.dataItem(parentNode).set(CHECKED, checkbox.prop(CHECKED));
                } else {
                    this.dataItem(parentNode).checked = false;
                }
                this._updateIndeterminate(parentNode);
            }
        },
        _checkboxChange: function(e) {
            var checkbox = $(e.target), isChecked = checkbox.prop(CHECKED), node = checkbox.closest(NODE), that = this;
            that.dataItem(node).set(CHECKED, isChecked);
        },
        _toggleButtonClick: function(e) {
            this.toggle($(e.target).closest(NODE));
        },
        _mousedown: function(e) {
            var node = $(e.currentTarget).closest(NODE);
            this._clickTarget = node;
            this.current(node);
        },
        _focusable: function(node) {
            return node && node.length && node.is(":visible") && !node.find(".k-in:first").hasClass("k-state-disabled");
        },
        _focus: function() {
            var current = this.select(), clickTarget = this._clickTarget;
            // suppress initial focus state on touch devices (until keyboard is used)
            if (kendo.support.touch) {
                return;
            }
            if (clickTarget && clickTarget.length) {
                current = clickTarget;
            }
            if (!this._focusable(current)) {
                current = this.current();
            }
            if (!this._focusable(current)) {
                current = this._nextVisible($());
            }
            this.current(current);
        },
        focus: function() {
            var wrapper = this.wrapper, scrollContainer = wrapper[0], scrollTop, body = document.body;
            do {
                scrollContainer = scrollContainer.parentNode;
            } while (scrollContainer.scrollHeight <= scrollContainer.clientHeight && scrollContainer != body);
            scrollTop = scrollContainer.scrollTop;
            wrapper.focus();
            scrollContainer.scrollTop = scrollTop;
        },
        _blur: function() {
            this.current().find(".k-in:first").removeClass("k-state-focused");
        },
        _enabled: function(node) {
            return !node.children("div").children(".k-in").hasClass("k-state-disabled");
        },
        parent: function(node) {
            var wrapperRe = /\bk-treeview\b/, itemRe = /\bk-item\b/, result, skipSelf;
            if (typeof node == STRING) {
                node = this.element.find(node);
            }
            if (!isDomElement(node)) {
                node = node[0];
            }
            skipSelf = itemRe.test(node.className);
            do {
                node = node.parentNode;
                if (itemRe.test(node.className)) {
                    if (skipSelf) {
                        result = node;
                    } else {
                        skipSelf = true;
                    }
                }
            } while (!wrapperRe.test(node.className) && !result);
            return $(result);
        },
        _nextVisible: function(node) {
            var that = this, expanded = that._expanded(node), result;
            if (!node.length || !node.is(":visible")) {
                result = that.root.children().eq(0);
            } else if (expanded) {
                result = subGroup(node).children().first();
            } else {
                while (node.length && !node.next().length) {
                    node = that.parent(node);
                }
                if (node.next().length) {
                    result = node.next();
                } else {
                    result = node;
                }
            }
            if (!that._enabled(result)) {
                result = that._nextVisible(result);
            }
            return result;
        },
        _previousVisible: function(node) {
            var that = this, result;
            if (!node.length || node.prev().length) {
                if (node.length) {
                    result = node.prev();
                } else {
                    result = that.root.children().last();
                }
                while (that._expanded(result)) {
                    result = subGroup(result).children().last();
                }
            } else {
                result = that.parent(node) || node;
            }
            if (!that._enabled(result)) {
                result = that._previousVisible(result);
            }
            return result;
        },
        _keydown: function(e) {
            var that = this, key = e.keyCode, target, focused = that.current(), expanded = that._expanded(focused), checkbox = focused.find(".k-checkbox:first :checkbox"), rtl = kendo.support.isRtl(that.element);
            if (e.target != e.currentTarget) {
                return;
            }
            if (!rtl && key == keys.RIGHT || rtl && key == keys.LEFT) {
                if (expanded) {
                    target = that._nextVisible(focused);
                } else {
                    that.expand(focused);
                }
            } else if (!rtl && key == keys.LEFT || rtl && key == keys.RIGHT) {
                if (expanded) {
                    that.collapse(focused);
                } else {
                    target = that.parent(focused);
                    if (!that._enabled(target)) {
                        target = undefined;
                    }
                }
            } else if (key == keys.DOWN) {
                target = that._nextVisible(focused);
            } else if (key == keys.UP) {
                target = that._previousVisible(focused);
            } else if (key == keys.HOME) {
                target = that._nextVisible($());
            } else if (key == keys.END) {
                target = that._previousVisible($());
            } else if (key == keys.ENTER) {
                if (!focused.find(".k-in:first").hasClass("k-state-selected")) {
                    if (!that._trigger(SELECT, focused)) {
                        that.select(focused);
                    }
                }
            } else if (key == keys.SPACEBAR && checkbox.length) {
                checkbox.prop(CHECKED, !checkbox.prop(CHECKED)).data("indeterminate", false).prop("indeterminate", false);
                that._checkboxChange({
                    target: checkbox
                });
                target = focused;
            }
            if (target) {
                e.preventDefault();
                if (focused[0] != target[0]) {
                    that._trigger(NAVIGATE, target);
                    that.current(target);
                }
            }
        },
        _click: function(e) {
            var that = this, node = $(e.target), contents = nodeContents(node.closest(NODE)), href = node.attr("href"), shouldNavigate;
            if (href) {
                shouldNavigate = href == "#" || href.indexOf("#" + this.element.id + "-") >= 0;
            } else {
                shouldNavigate = contents.length && !contents.children().length;
            }
            if (shouldNavigate) {
                e.preventDefault();
            }
            if (!node.hasClass(".k-state-selected") && !that._trigger(SELECT, node)) {
                that.select(node);
            }
        },
        _wrapper: function() {
            var that = this, element = that.element, wrapper, root, wrapperClasses = "k-widget k-treeview";
            if (element.is("div")) {
                wrapper = element;
                root = wrapper.children("ul").eq(0);
            } else {
                // element is ul
                wrapper = element.wrap("<div />").parent();
                root = element;
            }
            that.wrapper = wrapper.addClass(wrapperClasses);
            that.root = root;
        },
        _group: function(item) {
            var that = this, firstLevel = item.hasClass(KTREEVIEW), group = {
                firstLevel: firstLevel,
                expanded: firstLevel || that._expanded(item)
            }, groupElement = item.children("ul");
            groupElement.addClass(that.templates.groupCssClass(group)).css("display", group.expanded ? "" : "none");
            that._nodes(groupElement, group);
        },
        _nodes: function(groupElement, groupData) {
            var that = this, nodes = groupElement.children("li"), nodeData;
            groupData = extend({
                length: nodes.length
            }, groupData);
            nodes.each(function(i, node) {
                node = $(node);
                nodeData = {
                    index: i,
                    expanded: that._expanded(node)
                };
                updateNodeHtml(node);
                that._updateNodeClasses(node, groupData, nodeData);
                // iterate over child nodes
                that._group(node);
            });
        },
        _checkboxes: function() {
            var options = this.options, checkboxOptions = options.checkboxes, checkboxTemplate;
            if (checkboxOptions || options.checkboxTemplate) {
                if (options.checkboxTemplate) {
                    checkboxTemplate = options.checkboxTemplate;
                } else {
                    checkboxTemplate = "<input type='checkbox' #= (item.enabled === false) ? 'disabled' : '' # #= item.checked ? 'checked' : '' #";
                    if (checkboxOptions.name) {
                        checkboxTemplate += " name='" + checkboxOptions.name + "'";
                    }
                    checkboxTemplate += " />";
                }
                checkboxOptions = extend({
                    template: checkboxTemplate
                }, options.checkboxes);
                if (typeof checkboxOptions.template == STRING) {
                    checkboxOptions.template = template(checkboxOptions.template);
                }
                options.checkboxes = checkboxOptions;
            }
        },
        _updateNodeClasses: function(node, groupData, nodeData) {
            var wrapper = node.children("div"), group = node.children("ul"), templates = this.templates;
            if (node.hasClass("k-treeview")) {
                return;
            }
            nodeData = nodeData || {};
            nodeData.expanded = typeof nodeData.expanded != UNDEFINED ? nodeData.expanded : this._expanded(node);
            nodeData.index = typeof nodeData.index != UNDEFINED ? nodeData.index : node.index();
            nodeData.enabled = typeof nodeData.enabled != UNDEFINED ? nodeData.enabled : !wrapper.children(".k-in").hasClass("k-state-disabled");
            groupData = groupData || {};
            groupData.firstLevel = typeof groupData.firstLevel != UNDEFINED ? groupData.firstLevel : node.parent().parent().hasClass(KTREEVIEW);
            groupData.length = typeof groupData.length != UNDEFINED ? groupData.length : node.parent().children().length;
            // li
            node.removeClass("k-first k-last").addClass(templates.wrapperCssClass(groupData, nodeData));
            // div
            wrapper.removeClass("k-top k-mid k-bot").addClass(templates.cssClass(groupData, nodeData));
            // span
            wrapper.children(".k-in").removeClass("k-in k-state-default k-state-disabled").addClass(templates.textClass(nodeData));
            // toggle button
            if (group.length || node.attr("data-hasChildren") == "true") {
                wrapper.children(".k-icon").removeClass("k-plus k-minus k-plus-disabled k-minus-disabled").addClass(templates.toggleButtonClass(nodeData));
                group.addClass("k-group");
            }
        },
        _processNodes: function(nodes, callback) {
            var that = this;
            that.element.find(nodes).each(function(index, item) {
                callback.call(that, index, $(item).closest(NODE));
            });
        },
        dataItem: function(node) {
            var uid = $(node).closest(NODE).attr(kendo.attr("uid")), dataSource = this.dataSource;
            return dataSource && dataSource.getByUid(uid);
        },
        _insertNode: function(nodeData, index, parentNode, insertCallback, collapsed) {
            var that = this, group = subGroup(parentNode), updatedGroupLength = group.children().length + 1, childrenData, groupData = {
                firstLevel: parentNode.hasClass(KTREEVIEW),
                expanded: !collapsed,
                length: updatedGroupLength
            }, node, i, item, nodeHtml = "", append = function(item, group) {
                item.appendTo(group);
            };
            for (i = 0; i < nodeData.length; i++) {
                item = nodeData[i];
                item.index = index + i;
                nodeHtml += that._renderItem({
                    group: groupData,
                    item: item
                });
            }
            node = $(nodeHtml);
            if (!node.length) {
                return;
            }
            if (!group.length) {
                group = $(that._renderGroup({
                    group: groupData
                })).appendTo(parentNode);
            }
            insertCallback(node, group);
            if (parentNode.hasClass("k-item")) {
                updateNodeHtml(parentNode);
                that._updateNodeClasses(parentNode);
            }
            that._updateNodeClasses(node.prev().first());
            that._updateNodeClasses(node.next().last());
            // render sub-nodes
            for (i = 0; i < nodeData.length; i++) {
                item = nodeData[i];
                if (item.hasChildren) {
                    childrenData = item.children.data();
                    if (childrenData.length) {
                        that._insertNode(childrenData, item.index, node.eq(i), append, !that._expanded(node.eq(i)));
                    }
                }
            }
            return node;
        },
        _updateNode: function(field, items) {
            var that = this, i, node, item, isChecked, isCollapsed, context = {
                treeview: that.options,
                item: item
            };
            function setChecked() {
                that.dataItem(this).set(CHECKED, isChecked);
            }
            if (field == "selected") {
                item = items[0];
                node = that.findByUid(item.uid).find(".k-in:first").removeClass("k-state-hover").toggleClass("k-state-selected", item[field]).end();
                if (item[field]) {
                    that.current(node);
                    node.attr(ARIASELECTED, true);
                } else {
                    node.attr(ARIASELECTED, false);
                }
            } else {
                for (i = 0; i < items.length; i++) {
                    context.item = item = items[i];
                    if (field == "spriteCssClass" || field == "imageUrl" || $.inArray(field, that.options.dataTextField) >= 0) {
                        that.findByUid(item.uid).find(">div>.k-in").html(that.templates.itemContent(context));
                    } else if (field == CHECKED) {
                        node = that.findByUid(item.uid);
                        isChecked = item[field];
                        node.children("div").find(".k-checkbox :checkbox").prop(CHECKED, item[field]).data("indeterminate", false).prop("indeterminate", false);
                        if (that.options.checkboxes.checkChildren) {
                            node.find(".k-checkbox :checkbox").each(setChecked);
                            that._updateIndeterminate(node);
                        }
                    } else if (field == "expanded") {
                        that._toggle(that.findByUid(item.uid), item, item[field]);
                    } else if (field == "enabled") {
                        node = that.findByUid(item.uid);
                        node.find(".k-checkbox :checkbox").prop("disabled", !item[field]);
                        isCollapsed = !nodeContents(node).is(VISIBLE);
                        node.removeAttr(ARIADISABLED);
                        if (!item[field]) {
                            if (item.selected) {
                                item.set("selected", false);
                            }
                            if (item.expanded) {
                                item.set("expanded", false);
                            }
                            isCollapsed = true;
                            node.removeAttr(ARIASELECTED).attr(ARIADISABLED, true);
                        }
                        that._updateNodeClasses(node, {}, {
                            enabled: item[field],
                            expanded: !isCollapsed
                        });
                    }
                }
            }
        },
        refresh: function(e) {
            var that = this, parentNode = that.wrapper, node = e.node, action = e.action, items = e.items, index = e.index, options = that.options, loadOnDemand = options.loadOnDemand, checkChildren = options.checkboxes && options.checkboxes.checkChildren, i;
            function append(items, parentNode) {
                var group = subGroup(parentNode), children = group.children(), collapsed = !that._expanded(parentNode);
                if (typeof index == UNDEFINED) {
                    index = children.length;
                }
                that._insertNode(items, index, parentNode, function(item, group) {
                    // insert node into DOM
                    if (index == children.length) {
                        item.appendTo(group);
                    } else {
                        item.insertBefore(children.eq(index));
                    }
                }, collapsed);
                if (that._expanded(parentNode)) {
                    that._updateNodeClasses(parentNode);
                    subGroup(parentNode).css("display", "block");
                }
            }
            if (e.field) {
                return that._updateNode(e.field, items);
            }
            if (node) {
                parentNode = that.findByUid(node.uid);
                that._progress(parentNode, false);
            }
            if (checkChildren && action != "remove" && node && node.checked) {
                for (i = 0; i < items.length; i++) {
                    items[i].checked = true;
                }
            }
            if (action == "add") {
                append(items, parentNode);
            } else if (action == "remove") {
                that._remove(that.findByUid(items[0].uid), false);
            } else {
                if (node) {
                    subGroup(parentNode).empty();
                    append(items, parentNode);
                } else {
                    that.root = that.wrapper.html(that._renderGroup({
                        items: items,
                        group: {
                            firstLevel: true,
                            expanded: true
                        }
                    })).children("ul");
                }
            }
            for (i = 0; i < items.length; i++) {
                if (!loadOnDemand || items[i].expanded) {
                    items[i].load();
                }
            }
            that.trigger(DATABOUND, {
                node: node ? parentNode : undefined
            });
        },
        expand: function(nodes) {
            this._processNodes(nodes, function(index, item) {
                this.toggle(item, true);
            });
        },
        collapse: function(nodes) {
            this._processNodes(nodes, function(index, item) {
                this.toggle(item, false);
            });
        },
        enable: function(nodes, enable) {
            enable = arguments.length == 2 ? !!enable : true;
            this._processNodes(nodes, function(index, item) {
                this.dataItem(item).set("enabled", enable);
            });
        },
        current: function(node) {
            var that = this, current = that._current, element = that.element, id = that._ariaId;
            if (arguments.length > 0 && node && node.length) {
                if (current) {
                    if (current[0].id === id) {
                        current.removeAttr("id");
                    }
                    current.find(".k-in:first").removeClass("k-state-focused");
                }
                current = that._current = $(node, element).closest(NODE);
                current.find(".k-in:first").addClass("k-state-focused");
                id = current[0].id || id;
                if (id) {
                    that.wrapper.removeAttr("aria-activedescendant");
                    current.attr("id", id);
                    that.wrapper.attr("aria-activedescendant", id);
                }
                return;
            }
            if (!current) {
                current = that._nextVisible($());
            }
            return current;
        },
        select: function(node) {
            var that = this, element = that.element;
            if (!arguments.length) {
                return element.find(".k-state-selected").closest(NODE);
            }
            node = $(node, element).closest(NODE);
            element.find(".k-state-selected").each(function() {
                var dataItem = that.dataItem(this);
                dataItem.set("selected", false);
                delete dataItem.selected;
            });
            if (node.length) {
                that.dataItem(node).set("selected", true);
            }
            that.trigger(CHANGE);
        },
        _toggle: function(node, dataItem, expand) {
            var that = this, options = that.options, contents = nodeContents(node), direction = expand ? "expand" : "collapse", animation = options.animation[direction], loaded;
            if (contents.data("animating")) {
                return;
            }
            if (!that._trigger(direction, node)) {
                that._expanded(node, expand);
                loaded = dataItem && dataItem.loaded();
                if (loaded && contents.children().length > 0) {
                    that._updateNodeClasses(node, {}, {
                        expanded: expand
                    });
                    if (contents.css("display") == (expand ? "block" : "none")) {
                        return;
                    }
                    if (!expand) {
                        contents.css("height", contents.height()).css("height");
                    }
                    contents.kendoStop(true, true).kendoAnimate(extend({
                        reset: true
                    }, animation, {
                        complete: function() {
                            if (expand) {
                                contents.css("height", "");
                            }
                        }
                    }));
                } else if (!loaded || loaded && expand) {
                    if (options.loadOnDemand) {
                        that._progress(node, true);
                    }
                    contents.remove();
                    dataItem.load();
                }
            }
        },
        toggle: function(node, expand) {
            node = $(node);
            if (!nodeIcon(node).is(".k-minus,.k-plus,.k-minus-disabled,.k-plus-disabled")) {
                return;
            }
            if (arguments.length == 1) {
                expand = !this._expanded(node);
            }
            this._expanded(node, expand);
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that.element.off(NS);
            if (that.dragging) {
                that.dragging.destroy();
            }
            kendo.destroy(that.element);
        },
        _expanded: function(node, value) {
            var expandedAttr = kendo.attr("expanded"), dataItem = this.dataItem(node);
            if (arguments.length == 1) {
                return node.attr(expandedAttr) === "true" || dataItem && dataItem.expanded;
            }
            if (nodeContents(node).data("animating")) {
                return;
            }
            if (dataItem) {
                dataItem.set("expanded", value);
            }
            if (value) {
                node.attr(expandedAttr, "true");
                node.attr("aria-expanded", "true");
            } else {
                node.removeAttr(expandedAttr);
                node.attr("aria-expanded", "false");
            }
        },
        _progress: function(node, showProgress) {
            var element = this.element;
            if (arguments.length == 1) {
                showProgress = node;
                if (showProgress) {
                    element.html(this.templates.loading);
                } else {
                    element.empty();
                }
            } else {
                nodeIcon(node).toggleClass("k-loading", showProgress);
            }
        },
        text: function(node, text) {
            var dataItem = this.dataItem(node), fieldBindings = this.options[bindings.text], level = dataItem.level(), length = fieldBindings.length, field = fieldBindings[Math.min(level, length - 1)];
            if (text) {
                dataItem.set(field, text);
            } else {
                return dataItem[field];
            }
        },
        _objectOrSelf: function(node) {
            return $(node).closest("[data-role=treeview]").data("kendoTreeView") || this;
        },
        _dataSourceMove: function(nodeData, group, parentNode, callback) {
            var referenceDataItem, destTreeview = this._objectOrSelf(parentNode || group), destDataSource = destTreeview.dataSource;
            if (parentNode && parentNode[0] != destTreeview.element[0]) {
                referenceDataItem = destTreeview.dataItem(parentNode);
                if (!referenceDataItem.loaded()) {
                    destTreeview._progress(parentNode, true);
                    referenceDataItem.load();
                }
                if (parentNode != this.root) {
                    destDataSource = referenceDataItem.children;
                    if (!destDataSource || !(destDataSource instanceof HierarchicalDataSource)) {
                        referenceDataItem._initChildren();
                        destDataSource = referenceDataItem.children;
                    }
                }
            }
            nodeData = this._toObservableData(nodeData);
            return callback.call(this, destDataSource, nodeData);
        },
        _toObservableData: function(node) {
            var dataItem = node, dataSource, uid;
            if (node instanceof window.jQuery || isDomElement(node)) {
                dataSource = this._objectOrSelf(node).dataSource, uid = $(node).attr(kendo.attr("uid"));
                dataItem = dataSource.getByUid(uid);
                if (dataItem) {
                    dataItem = dataSource.remove(dataItem);
                }
            }
            return dataItem;
        },
        _insert: function(data, model, index) {
            if (!(model instanceof kendo.data.ObservableArray)) {
                if (!isArray(model)) {
                    model = [ model ];
                }
            } else {
                // items will be converted to new Node instances
                model = model.toJSON();
            }
            data.splice.apply(data, [ index, 0 ].concat(model));
            return this.findByUid(data[index].uid);
        },
        insertAfter: function(nodeData, referenceNode) {
            var group = referenceNode.parent(), parentNode;
            if (group.parent().is("li")) {
                parentNode = group.parent();
            }
            return this._dataSourceMove(nodeData, group, parentNode, function(dataSource, model) {
                return this._insert(dataSource.data(), model, referenceNode.index() + 1);
            });
        },
        insertBefore: function(nodeData, referenceNode) {
            var group = referenceNode.parent(), parentNode;
            if (group.parent().is("li")) {
                parentNode = group.parent();
            }
            return this._dataSourceMove(nodeData, group, parentNode, function(dataSource, model) {
                return this._insert(dataSource.data(), model, referenceNode.index());
            });
        },
        append: function(nodeData, parentNode) {
            var that = this, group = that.root;
            if (parentNode) {
                group = subGroup(parentNode);
            }
            return that._dataSourceMove(nodeData, group, parentNode, function(dataSource, model) {
                function add() {
                    var data = dataSource.data(), index = Math.max(data.length, 0);
                    if (parentNode) {
                        that._expanded(parentNode, true);
                    }
                    return that._insert(data, model, index);
                }
                if (!dataSource.data()) {
                    dataSource.one(CHANGE, add);
                    return null;
                } else {
                    return add();
                }
            });
        },
        _remove: function(node, keepData) {
            var that = this, parentNode, prevSibling, nextSibling;
            node = $(node, that.element);
            parentNode = node.parent().parent();
            prevSibling = node.prev();
            nextSibling = node.next();
            node[keepData ? "detach" : "remove"]();
            if (parentNode.hasClass("k-item")) {
                updateNodeHtml(parentNode);
                that._updateNodeClasses(parentNode);
            }
            that._updateNodeClasses(prevSibling);
            that._updateNodeClasses(nextSibling);
            return node;
        },
        remove: function(node) {
            var dataItem = this.dataItem(node);
            if (dataItem) {
                this.dataSource.remove(dataItem);
            }
        },
        detach: function(node) {
            return this._remove(node, true);
        },
        findByText: function(text) {
            return $(this.element).find(".k-in").filter(function(i, element) {
                return $(element).text() == text;
            }).closest(NODE);
        },
        findByUid: function(uid) {
            return this.element.find(".k-item[" + kendo.attr("uid") + "=" + uid + "]");
        },
        _renderItem: function(options) {
            if (!options.group) {
                options.group = {};
            }
            options.treeview = this.options;
            options.r = this.templates;
            return this.templates.item(options);
        },
        _renderGroup: function(options) {
            var that = this;
            options.renderItems = function(options) {
                var html = "", i = 0, items = options.items, len = items ? items.length : 0, group = options.group;
                group.length = len;
                for (;i < len; i++) {
                    options.group = group;
                    options.item = items[i];
                    options.item.index = i;
                    html += that._renderItem(options);
                }
                return html;
            };
            options.r = that.templates;
            return that.templates.group(options);
        }
    });
    function TreeViewDragAndDrop(treeview) {
        var that = this;
        that.treeview = treeview;
        that.hovered = treeview.element;
        that._draggable = new ui.Draggable(treeview.element, {
            filter: "div:not(.k-state-disabled) .k-in",
            hint: function(node) {
                return treeview.templates.dragClue({
                    item: treeview.dataItem(node),
                    treeview: treeview.options
                });
            },
            cursorOffset: {
                left: 10,
                top: kendo.support.touch || kendo.support.pointers ? -40 / kendo.support.zoomLevel() : 10
            },
            dragstart: proxy(that.dragstart, that),
            dragcancel: proxy(that.dragcancel, that),
            drag: proxy(that.drag, that),
            dragend: proxy(that.dragend, that)
        });
    }
    TreeViewDragAndDrop.prototype = {
        _removeTouchHover: function() {
            var that = this;
            if (kendo.support.touch && that.hovered) {
                that.hovered.find("." + KSTATEHOVER).removeClass(KSTATEHOVER);
                that.hovered = false;
            }
        },
        _hintStatus: function(newStatus) {
            var statusElement = this._draggable.hint.find(".k-drag-status")[0];
            if (newStatus) {
                statusElement.className = "k-icon k-drag-status " + newStatus;
            } else {
                return $.trim(statusElement.className.replace(/k-(icon|drag-status)/g, ""));
            }
        },
        dragstart: function(e) {
            var that = this, treeview = that.treeview, sourceNode = that.sourceNode = e.currentTarget.closest(NODE);
            if (treeview.trigger(DRAGSTART, {
                sourceNode: sourceNode[0]
            })) {
                e.preventDefault();
            }
            that.dropHint = $("<div class='k-drop-hint' />").css(VISIBILITY, "hidden").appendTo(treeview.element);
        },
        drag: function(e) {
            var that = this, treeview = that.treeview, sourceNode = that.sourceNode, dropTarget = that.dropTarget = $(kendo.eventTarget(e)), statusClass, closestTree = dropTarget.closest(".k-treeview"), hoveredItem, hoveredItemPos, itemHeight, itemTop, itemContent, delta, insertOnTop, insertOnBottom, addChild;
            if (!closestTree.length) {
                // dragging node outside of treeview
                statusClass = "k-denied";
                that._removeTouchHover();
            } else if ($.contains(sourceNode[0], dropTarget[0])) {
                // dragging node within itself
                statusClass = "k-denied";
            } else {
                // moving or reordering node
                statusClass = "k-insert-middle";
                hoveredItem = dropTarget.closest(".k-top,.k-mid,.k-bot");
                if (hoveredItem.length) {
                    itemHeight = hoveredItem.outerHeight();
                    itemTop = kendo.getOffset(hoveredItem).top;
                    itemContent = dropTarget.closest(".k-in");
                    delta = itemHeight / (itemContent.length > 0 ? 4 : 2);
                    insertOnTop = e.y.location < itemTop + delta;
                    insertOnBottom = itemTop + itemHeight - delta < e.y.location;
                    that._removeTouchHover();
                    addChild = itemContent.length && !insertOnTop && !insertOnBottom;
                    that.hovered = addChild ? closestTree : false;
                    that.dropHint.css(VISIBILITY, addChild ? "hidden" : "visible");
                    itemContent.toggleClass(KSTATEHOVER, addChild);
                    if (addChild) {
                        statusClass = "k-add";
                    } else {
                        hoveredItemPos = hoveredItem.position();
                        hoveredItemPos.top += insertOnTop ? 0 : itemHeight;
                        that.dropHint.css(hoveredItemPos)[insertOnTop ? "prependTo" : "appendTo"](dropTarget.closest(NODE).children("div:first"));
                        if (insertOnTop && hoveredItem.hasClass("k-top")) {
                            statusClass = "k-insert-top";
                        }
                        if (insertOnBottom && hoveredItem.hasClass("k-bot")) {
                            statusClass = "k-insert-bottom";
                        }
                    }
                } else if (dropTarget[0] != that.dropHint[0]) {
                    if (closestTree[0] != treeview.element[0]) {
                        // moving node to different treeview without children
                        statusClass = "k-add";
                    } else {
                        statusClass = "k-denied";
                    }
                }
            }
            treeview.trigger(DRAG, {
                sourceNode: sourceNode[0],
                dropTarget: dropTarget[0],
                pageY: e.y.location,
                pageX: e.x.location,
                statusClass: statusClass.substring(2),
                setStatusClass: function(value) {
                    statusClass = value;
                }
            });
            if (statusClass.indexOf("k-insert") !== 0) {
                that.dropHint.css(VISIBILITY, "hidden");
            }
            that._hintStatus(statusClass);
        },
        dragcancel: function() {
            this.dropHint.remove();
        },
        dragend: function() {
            var that = this, treeview = that.treeview, dropPosition = "over", sourceNode = that.sourceNode, destinationNode, dropHint = that.dropHint, dropTarget = that.dropTarget, valid, dropPrevented;
            if (dropHint.css(VISIBILITY) == "visible") {
                dropPosition = dropHint.prevAll(".k-in").length > 0 ? "after" : "before";
                destinationNode = dropHint.closest(NODE);
            } else if (dropTarget) {
                destinationNode = dropTarget.closest(NODE);
                // moving node to root element
                if (!destinationNode.length) {
                    destinationNode = dropTarget.closest(".k-treeview");
                }
            }
            valid = that._hintStatus() != "k-denied";
            dropPrevented = treeview.trigger(DROP, {
                sourceNode: sourceNode[0],
                destinationNode: destinationNode[0],
                valid: valid,
                setValid: function(newValid) {
                    valid = newValid;
                },
                dropTarget: dropTarget[0],
                dropPosition: dropPosition
            });
            dropHint.remove();
            that._removeTouchHover();
            if (!valid || dropPrevented) {
                that._draggable.dropped = valid;
                return;
            }
            that._draggable.dropped = true;
            // perform reorder / move
            if (dropPosition == "over") {
                sourceNode = treeview.append(sourceNode, destinationNode);
            } else if (dropPosition == "before") {
                sourceNode = treeview.insertBefore(sourceNode, destinationNode);
            } else if (dropPosition == "after") {
                sourceNode = treeview.insertAfter(sourceNode, destinationNode);
            }
            treeview.trigger(DRAGEND, {
                sourceNode: sourceNode && sourceNode[0],
                destinationNode: destinationNode[0],
                dropPosition: dropPosition
            });
        },
        destroy: function() {
            this._draggable.destroy();
        }
    };
    ui.plugin(TreeView);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, Widget = kendo.ui.Widget, Draggable = kendo.ui.Draggable, extend = $.extend, format = kendo.format, parse = kendo.parseFloat, proxy = $.proxy, isArray = $.isArray, math = Math, support = kendo.support, pointers = support.pointers, CHANGE = "change", SLIDE = "slide", NS = ".slider", MOUSE_DOWN = "touchstart" + NS + " mousedown" + NS, TRACK_MOUSE_DOWN = pointers ? "MSPointerDown" + NS : "mousedown" + NS + " touchstart" + NS, MOUSE_UP = "touchend" + NS + " mouseup" + NS, MOVE_SELECTION = "moveSelection", KEY_DOWN = "keydown" + NS, CLICK = "click" + NS, MOUSE_OVER = "mouseover" + NS, FOCUS = "focus" + NS, BLUR = "blur" + NS, DRAG_HANDLE = ".k-draghandle", TRACK_SELECTOR = ".k-slider-track", TICK_SELECTOR = ".k-tick", STATE_SELECTED = "k-state-selected", STATE_DEFAULT = "k-state-default", STATE_DISABLED = "k-state-disabled", PRECISION = 3, DISABLED = "disabled", UNDEFINED = "undefined", TABINDEX = "tabindex", getTouches = kendo.getTouches;
    var SliderBase = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            options = that.options;
            that._distance = options.max - options.min;
            that._isHorizontal = options.orientation == "horizontal";
            that._isRtl = that._isHorizontal && kendo.support.isRtl(element);
            that._position = that._isHorizontal ? "left" : "bottom";
            that._size = that._isHorizontal ? "width" : "height";
            that._outerSize = that._isHorizontal ? "outerWidth" : "outerHeight";
            options.tooltip.format = options.tooltip.enabled ? options.tooltip.format || "{0}" : "{0}";
            that._createHtml();
            that.wrapper = that.element.closest(".k-slider");
            that._trackDiv = that.wrapper.find(TRACK_SELECTOR);
            that._setTrackDivWidth();
            that._maxSelection = that._trackDiv[that._size]();
            var sizeBetweenTicks = that._maxSelection / ((options.max - options.min) / options.smallStep);
            var pixelWidths = that._calculateItemsWidth(math.floor(that._distance / options.smallStep));
            if (options.tickPlacement != "none" && sizeBetweenTicks >= 2) {
                that._trackDiv.before(createSliderItems(options, that._distance));
                that._setItemsWidth(pixelWidths);
                that._setItemsTitle();
                that._setItemsLargeTick();
            }
            that._calculateSteps(pixelWidths);
            that._tabindex(that.wrapper.find(DRAG_HANDLE));
            that[options.enabled ? "enable" : "disable"]();
            var rtlDirectionSign = kendo.support.isRtl(that.wrapper) ? -1 : 1;
            that._keyMap = {
                37: step(-1 * rtlDirectionSign * options.smallStep),
                // left arrow
                40: step(-options.smallStep),
                // down arrow
                39: step(+1 * rtlDirectionSign * options.smallStep),
                // right arrow
                38: step(+options.smallStep),
                // up arrow
                35: setValue(options.max),
                // end
                36: setValue(options.min),
                // home
                33: step(+options.largeStep),
                // page up
                34: step(-options.largeStep)
            };
            kendo.notify(that);
        },
        events: [ CHANGE, SLIDE ],
        options: {
            enabled: true,
            min: 0,
            max: 10,
            smallStep: 1,
            largeStep: 5,
            orientation: "horizontal",
            tickPlacement: "both",
            tooltip: {
                enabled: true,
                format: "{0}"
            }
        },
        _setTrackDivWidth: function() {
            var that = this, trackDivPosition = parseFloat(that._trackDiv.css(that._isRtl ? "right" : that._position), 10) * 2;
            that._trackDiv[that._size](that.wrapper[that._size]() - 2 - trackDivPosition);
        },
        _setItemsWidth: function(pixelWidths) {
            var that = this, options = that.options, first = 0, last = pixelWidths.length - 1, items = that.wrapper.find(TICK_SELECTOR), i, paddingTop = 0, bordersWidth = 2, count = items.length, selection = 0;
            for (i = 0; i < count - 2; i++) {
                $(items[i + 1])[that._size](pixelWidths[i]);
            }
            if (that._isHorizontal) {
                $(items[first]).addClass("k-first")[that._size](pixelWidths[last - 1]);
                $(items[last]).addClass("k-last")[that._size](pixelWidths[last]);
            } else {
                $(items[last]).addClass("k-first")[that._size](pixelWidths[last]);
                $(items[first]).addClass("k-last")[that._size](pixelWidths[last - 1]);
            }
            if (that._distance % options.smallStep !== 0 && !that._isHorizontal) {
                for (i = 0; i < pixelWidths.length; i++) {
                    selection += pixelWidths[i];
                }
                paddingTop = that._maxSelection - selection;
                paddingTop += parseFloat(that._trackDiv.css(that._position), 10) + bordersWidth;
                that.wrapper.find(".k-slider-items").css("padding-top", paddingTop);
            }
        },
        _setItemsTitle: function() {
            var that = this, options = that.options, items = that.wrapper.find(TICK_SELECTOR), titleNumber = options.min, count = items.length, i = that._isHorizontal && !that._isRtl ? 0 : count - 1, limit = that._isHorizontal && !that._isRtl ? count : -1, increment = that._isHorizontal && !that._isRtl ? 1 : -1;
            for (;i - limit !== 0; i += increment) {
                $(items[i]).attr("title", format(options.tooltip.format, round(titleNumber)));
                titleNumber += options.smallStep;
            }
        },
        _setItemsLargeTick: function() {
            var that = this, options = that.options, i, items = that.wrapper.find(TICK_SELECTOR), item = {}, step = round(options.largeStep / options.smallStep);
            if (1e3 * options.largeStep % (1e3 * options.smallStep) === 0) {
                if (that._isHorizontal && !that._isRtl) {
                    for (i = 0; i < items.length; i = round(i + step)) {
                        item = $(items[i]);
                        item.addClass("k-tick-large").html("<span class='k-label'>" + item.attr("title") + "</span>");
                    }
                } else {
                    for (i = items.length - 1; i >= 0; i = round(i - step)) {
                        item = $(items[i]);
                        item.addClass("k-tick-large").html("<span class='k-label'>" + item.attr("title") + "</span>");
                        if (!that._isRtl) {
                            if (i !== 0 && i !== items.length - 1) {
                                item.css("line-height", item[that._size]() + "px");
                            }
                        }
                    }
                }
            }
        },
        _calculateItemsWidth: function(itemsCount) {
            var that = this, options = that.options, trackDivSize = parseFloat(that._trackDiv.css(that._size)) + 1, pixelStep = trackDivSize / that._distance, itemWidth, pixelWidths, i;
            if (that._distance / options.smallStep - math.floor(that._distance / options.smallStep) > 0) {
                trackDivSize -= that._distance % options.smallStep * pixelStep;
            }
            itemWidth = trackDivSize / itemsCount;
            pixelWidths = [];
            for (i = 0; i < itemsCount - 1; i++) {
                pixelWidths[i] = itemWidth;
            }
            pixelWidths[itemsCount - 1] = pixelWidths[itemsCount] = itemWidth / 2;
            return that._roundWidths(pixelWidths);
        },
        _roundWidths: function(pixelWidthsArray) {
            var balance = 0, count = pixelWidthsArray.length, i;
            for (i = 0; i < count; i++) {
                balance += pixelWidthsArray[i] - math.floor(pixelWidthsArray[i]);
                pixelWidthsArray[i] = math.floor(pixelWidthsArray[i]);
            }
            balance = math.round(balance);
            return this._addAdditionalSize(balance, pixelWidthsArray);
        },
        _addAdditionalSize: function(additionalSize, pixelWidthsArray) {
            if (additionalSize === 0) {
                return pixelWidthsArray;
            }
            //set step size
            var step = parseFloat(pixelWidthsArray.length - 1) / parseFloat(additionalSize == 1 ? additionalSize : additionalSize - 1), i;
            for (i = 0; i < additionalSize; i++) {
                pixelWidthsArray[parseInt(math.round(step * i), 10)] += 1;
            }
            return pixelWidthsArray;
        },
        _calculateSteps: function(pixelWidths) {
            var that = this, options = that.options, val = options.min, selection = 0, itemsCount = math.ceil(that._distance / options.smallStep), i = 1, lastItem;
            itemsCount += that._distance / options.smallStep % 1 === 0 ? 1 : 0;
            pixelWidths.splice(0, 0, pixelWidths[itemsCount - 2] * 2);
            pixelWidths.splice(itemsCount - 1, 1, pixelWidths.pop() * 2);
            that._pixelSteps = [ selection ];
            that._values = [ val ];
            if (itemsCount === 0) {
                return;
            }
            while (i < itemsCount) {
                selection += (pixelWidths[i - 1] + pixelWidths[i]) / 2;
                that._pixelSteps[i] = selection;
                that._values[i] = val += options.smallStep;
                i++;
            }
            lastItem = that._distance % options.smallStep === 0 ? itemsCount - 1 : itemsCount;
            that._pixelSteps[lastItem] = that._maxSelection;
            that._values[lastItem] = options.max;
            if (that._isRtl) {
                that._pixelSteps.reverse();
                that._values.reverse();
            }
        },
        _getValueFromPosition: function(mousePosition, dragableArea) {
            var that = this, options = that.options, step = math.max(options.smallStep * (that._maxSelection / that._distance), 0), position = 0, halfStep = step / 2, i;
            if (that._isHorizontal) {
                position = mousePosition - dragableArea.startPoint;
                if (that._isRtl) {
                    position = that._maxSelection - position;
                }
            } else {
                position = dragableArea.startPoint - mousePosition;
            }
            if (that._maxSelection - (parseInt(that._maxSelection % step, 10) - 3) / 2 < position) {
                return options.max;
            }
            for (i = 0; i < that._pixelSteps.length; i++) {
                if (math.abs(that._pixelSteps[i] - position) - 1 <= halfStep) {
                    return round(that._values[i]);
                }
            }
        },
        _getFormattedValue: function(val, drag) {
            var that = this, html = "", tooltip = that.options.tooltip, tooltipTemplate, selectionStart, selectionEnd;
            if (isArray(val)) {
                selectionStart = val[0];
                selectionEnd = val[1];
            } else if (drag && drag.type) {
                selectionStart = drag.selectionStart;
                selectionEnd = drag.selectionEnd;
            }
            if (drag) {
                tooltipTemplate = drag.tooltipTemplate;
            }
            if (!tooltipTemplate && tooltip.template) {
                tooltipTemplate = kendo.template(tooltip.template);
            }
            if (isArray(val) || drag && drag.type) {
                if (tooltipTemplate) {
                    html = tooltipTemplate({
                        selectionStart: selectionStart,
                        selectionEnd: selectionEnd
                    });
                } else {
                    selectionStart = format(tooltip.format, selectionStart);
                    selectionEnd = format(tooltip.format, selectionEnd);
                    html = selectionStart + " - " + selectionEnd;
                }
            } else {
                if (drag) {
                    drag.val = val;
                }
                if (tooltipTemplate) {
                    html = tooltipTemplate({
                        value: val
                    });
                } else {
                    html = format(tooltip.format, val);
                }
            }
            return html;
        },
        _getDraggableArea: function() {
            var that = this, offset = kendo.getOffset(that._trackDiv);
            return {
                startPoint: that._isHorizontal ? offset.left : offset.top + that._maxSelection,
                endPoint: that._isHorizontal ? offset.left + that._maxSelection : offset.top
            };
        },
        _createHtml: function() {
            var that = this, element = that.element, options = that.options, inputs = element.find("input");
            if (inputs.length == 2) {
                inputs.eq(0).val(options.selectionStart);
                inputs.eq(1).val(options.selectionEnd);
            } else {
                element.val(options.value);
            }
            element.wrap(createWrapper(options, element, that._isHorizontal)).hide();
            if (options.showButtons) {
                element.before(createButton(options, "increase", that._isHorizontal)).before(createButton(options, "decrease", that._isHorizontal));
            }
            element.before(createTrack(options, element));
        },
        _focus: function(e) {
            var that = this, target = e.target, val = that.value(), drag = that._drag;
            if (!drag) {
                if (target == that.wrapper.find(DRAG_HANDLE).eq(0)[0]) {
                    drag = that._firstHandleDrag;
                    that._activeHandle = 0;
                } else {
                    drag = that._lastHandleDrag;
                    that._activeHandle = 1;
                }
                val = val[that._activeHandle];
            }
            $(target).addClass(STATE_SELECTED);
            if (drag) {
                that._activeHandleDrag = drag;
                drag.selectionStart = that.options.selectionStart;
                drag.selectionEnd = that.options.selectionEnd;
                drag._updateTooltip(val);
            }
        },
        _focusWithMouse: function(e) {
            var that = this, target = $(e.target), idx = target.is(DRAG_HANDLE) ? target.index() : 0;
            window.setTimeout(function() {
                that.wrapper.find(DRAG_HANDLE)[idx == 2 ? 1 : 0].focus();
            }, 1);
            that._setTooltipTimeout();
        },
        _blur: function(e) {
            var that = this, drag = that._activeHandleDrag;
            $(e.target).removeClass(STATE_SELECTED);
            if (drag) {
                drag._removeTooltip();
                delete that._activeHandleDrag;
                delete that._activeHandle;
            }
        },
        _setTooltipTimeout: function() {
            var that = this;
            that._tooltipTimeout = window.setTimeout(function() {
                var drag = that._drag || that._activeHandleDrag;
                if (drag) {
                    drag._removeTooltip();
                }
            }, 300);
        },
        _clearTooltipTimeout: function() {
            var that = this;
            window.clearTimeout(this._tooltipTimeout);
            var drag = that._drag || that._activeHandleDrag;
            if (drag && drag.tooltipDiv) {
                drag.tooltipDiv.stop(true, false).css("opacity", 1);
            }
        }
    });
    function createWrapper(options, element, isHorizontal) {
        var orientationCssClass = isHorizontal ? " k-slider-horizontal" : " k-slider-vertical", style = options.style ? options.style : element.attr("style"), cssClasses = element.attr("class") ? " " + element.attr("class") : "", tickPlacementCssClass = "";
        if (options.tickPlacement == "bottomRight") {
            tickPlacementCssClass = " k-slider-bottomright";
        } else if (options.tickPlacement == "topLeft") {
            tickPlacementCssClass = " k-slider-topleft";
        }
        style = style ? " style='" + style + "'" : "";
        return "<div class='k-widget k-slider" + orientationCssClass + cssClasses + "'" + style + ">" + "<div class='k-slider-wrap" + (options.showButtons ? " k-slider-buttons" : "") + tickPlacementCssClass + "'></div></div>";
    }
    function createButton(options, type, isHorizontal) {
        var buttonCssClass = "";
        if (type == "increase") {
            buttonCssClass = isHorizontal ? "k-i-arrow-e" : "k-i-arrow-n";
        } else {
            buttonCssClass = isHorizontal ? "k-i-arrow-w" : "k-i-arrow-s";
        }
        return "<a class='k-button k-button-" + type + "'><span class='k-icon " + buttonCssClass + "' title='" + options[type + "ButtonTitle"] + "'>" + options[type + "ButtonTitle"] + "</span></a>";
    }
    function createSliderItems(options, distance) {
        var result = "<ul class='k-reset k-slider-items'>", count = math.floor(round(distance / options.smallStep)) + 1, i;
        for (i = 0; i < count; i++) {
            result += "<li class='k-tick' role='presentation'>&nbsp;</li>";
        }
        result += "</ul>";
        return result;
    }
    function createTrack(options, element) {
        var dragHandleCount = element.is("input") ? 1 : 2, firstDragHandleTitle = dragHandleCount == 2 ? options.leftDragHandleTitle : options.dragHandleTitle;
        return "<div class='k-slider-track'><div class='k-slider-selection'><!-- --></div>" + "<a href='#' class='k-draghandle' title='" + firstDragHandleTitle + "' role='slider' aria-valuemin='" + options.min + "' aria-valuemax='" + options.max + "' aria-valuenow='" + (dragHandleCount > 1 ? options.selectionStart || options.min : options.value || options.min) + "'>Drag</a>" + (dragHandleCount > 1 ? "<a href='#' class='k-draghandle' title='" + options.rightDragHandleTitle + "'role='slider' aria-valuemin='" + options.min + "' aria-valuemax='" + options.max + "' aria-valuenow='" + (options.selectionEnd || options.max) + "'>Drag</a>" : "") + "</div>";
    }
    function step(stepValue) {
        return function(value) {
            return value + stepValue;
        };
    }
    function setValue(value) {
        return function() {
            return value;
        };
    }
    function formatValue(value) {
        return (value + "").replace(".", kendo.cultures.current.numberFormat["."]);
    }
    function round(value) {
        value = parseFloat(value, 10);
        var power = math.pow(10, PRECISION || 0);
        return math.round(value * power) / power;
    }
    function parseAttr(element, name) {
        var value = parse(element.getAttribute(name));
        if (value === null) {
            value = undefined;
        }
        return value;
    }
    function defined(value) {
        return typeof value !== UNDEFINED;
    }
    var Slider = SliderBase.extend({
        init: function(element, options) {
            var that = this, dragHandle;
            element.type = "text";
            options = extend({}, {
                value: parseAttr(element, "value"),
                min: parseAttr(element, "min"),
                max: parseAttr(element, "max"),
                smallStep: parseAttr(element, "step")
            }, options);
            element = $(element);
            if (options && options.enabled === undefined) {
                options.enabled = !element.is("[disabled]");
            }
            SliderBase.fn.init.call(that, element, options);
            options = that.options;
            if (!defined(options.value) || options.value === null) {
                options.value = options.min;
                element.val(options.min);
            }
            options.value = math.max(math.min(options.value, options.max), options.min);
            dragHandle = that.wrapper.find(DRAG_HANDLE);
            new Slider.Selection(dragHandle, that, options);
            that._drag = new Slider.Drag(dragHandle, "", that, options);
        },
        options: {
            name: "Slider",
            showButtons: true,
            increaseButtonTitle: "Increase",
            decreaseButtonTitle: "Decrease",
            dragHandleTitle: "drag",
            tooltip: {
                format: "{0}"
            },
            value: null
        },
        enable: function(enable) {
            var that = this, options = that.options, clickHandler, move;
            that.disable();
            if (enable === false) {
                return;
            }
            that.wrapper.removeClass(STATE_DISABLED).addClass(STATE_DEFAULT);
            that.wrapper.find("input").removeAttr(DISABLED);
            clickHandler = function(e) {
                var touch = getTouches(e)[0];
                if (!touch) {
                    return;
                }
                var mousePosition = that._isHorizontal ? touch.location.pageX : touch.location.pageY, dragableArea = that._getDraggableArea(), target = $(e.target);
                if (target.hasClass("k-draghandle")) {
                    target.addClass(STATE_SELECTED);
                    return;
                }
                that._update(that._getValueFromPosition(mousePosition, dragableArea));
                that._focusWithMouse(e);
                that._drag.dragstart(e);
                e.preventDefault();
            };
            that.wrapper.find(TICK_SELECTOR + ", " + TRACK_SELECTOR).on(TRACK_MOUSE_DOWN, clickHandler).end().on(TRACK_MOUSE_DOWN, function() {
                $(document.documentElement).one("selectstart", kendo.preventDefault);
            });
            that.wrapper.find(DRAG_HANDLE).attr(TABINDEX, 0).on(MOUSE_UP, function() {
                that._setTooltipTimeout();
            }).on(CLICK, function(e) {
                that._focusWithMouse(e);
                e.preventDefault();
            }).on(FOCUS, proxy(that._focus, that)).on(BLUR, proxy(that._blur, that));
            move = proxy(function(sign) {
                var newVal = that._nextValueByIndex(that._valueIndex + sign * 1);
                that._setValueInRange(newVal);
                that._drag._updateTooltip(newVal);
            }, that);
            if (options.showButtons) {
                var mouseDownHandler = proxy(function(e, sign) {
                    this._clearTooltipTimeout();
                    if (e.which === 1 || support.touch && e.which === 0) {
                        move(sign);
                        this.timeout = setTimeout(proxy(function() {
                            this.timer = setInterval(function() {
                                move(sign);
                            }, 60);
                        }, this), 200);
                    }
                }, that);
                that.wrapper.find(".k-button").on(MOUSE_UP, proxy(function(e) {
                    this._clearTimer();
                    that._focusWithMouse(e);
                }, that)).on(MOUSE_OVER, function(e) {
                    $(e.currentTarget).addClass("k-state-hover");
                }).on("mouseout" + NS, proxy(function(e) {
                    $(e.currentTarget).removeClass("k-state-hover");
                    this._clearTimer();
                }, that)).eq(0).on(MOUSE_DOWN, proxy(function(e) {
                    mouseDownHandler(e, 1);
                }, that)).click(false).end().eq(1).on(MOUSE_DOWN, proxy(function(e) {
                    mouseDownHandler(e, -1);
                }, that)).click(kendo.preventDefault);
            }
            that.wrapper.find(DRAG_HANDLE).off(KEY_DOWN, false).on(KEY_DOWN, proxy(this._keydown, that));
            options.enabled = true;
        },
        disable: function() {
            var that = this;
            that.wrapper.removeClass(STATE_DEFAULT).addClass(STATE_DISABLED);
            $(that.element).prop(DISABLED, DISABLED);
            that.wrapper.find(".k-button").off(MOUSE_DOWN).on(MOUSE_DOWN, kendo.preventDefault).off(MOUSE_UP).on(MOUSE_UP, kendo.preventDefault).off("mouseleave" + NS).on("mouseleave" + NS, kendo.preventDefault).off(MOUSE_OVER).on(MOUSE_OVER, kendo.preventDefault);
            that.wrapper.find(TICK_SELECTOR + ", " + TRACK_SELECTOR).off(TRACK_MOUSE_DOWN);
            that.wrapper.find(DRAG_HANDLE).attr(TABINDEX, -1).off(MOUSE_UP).off(KEY_DOWN).off(CLICK).off(FOCUS).off(BLUR);
            that.options.enabled = false;
        },
        _update: function(val) {
            var that = this, change = that.value() != val;
            that.value(val);
            if (change) {
                that.trigger(CHANGE, {
                    value: that.options.value
                });
            }
        },
        value: function(value) {
            var that = this, options = that.options;
            value = round(value);
            if (isNaN(value)) {
                return options.value;
            }
            if (value >= options.min && value <= options.max) {
                if (options.value != value) {
                    that.element.prop("value", formatValue(value));
                    options.value = value;
                    that._refreshAriaAttr(value);
                    that._refresh();
                }
            }
        },
        _refresh: function() {
            this.trigger(MOVE_SELECTION, {
                value: this.options.value
            });
        },
        _refreshAriaAttr: function(value) {
            var that = this, drag = that._drag, formattedValue;
            if (drag && drag._tooltipDiv) {
                formattedValue = drag._tooltipDiv.text();
            } else {
                formattedValue = that._getFormattedValue(value, null);
            }
            this.wrapper.find(DRAG_HANDLE).attr("aria-valuenow", value).attr("aria-valuetext", formattedValue);
        },
        _clearTimer: function() {
            clearTimeout(this.timeout);
            clearInterval(this.timer);
        },
        _keydown: function(e) {
            var that = this;
            if (e.keyCode in that._keyMap) {
                that._clearTooltipTimeout();
                that._setValueInRange(that._keyMap[e.keyCode](that.options.value));
                that._drag._updateTooltip(that.value());
                e.preventDefault();
            }
        },
        _setValueInRange: function(val) {
            var that = this, options = that.options;
            val = round(val);
            if (isNaN(val)) {
                that._update(options.min);
                return;
            }
            val = math.max(math.min(val, options.max), options.min);
            that._update(val);
        },
        _nextValueByIndex: function(index) {
            var count = this._values.length;
            if (this._isRtl) {
                index = count - 1 - index;
            }
            return this._values[math.max(0, math.min(index, count - 1))];
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that.wrapper.off(NS).find(".k-button").off(NS).end().find(DRAG_HANDLE).off(NS).end().find(TICK_SELECTOR + ", " + TRACK_SELECTOR).off(NS).end();
            that._drag.draggable.destroy();
        }
    });
    Slider.Selection = function(dragHandle, that, options) {
        function moveSelection(val) {
            var selectionValue = val - options.min, index = that._valueIndex = math.ceil(round(selectionValue / options.smallStep)), selection = parseInt(that._pixelSteps[index], 10), selectionDiv = that._trackDiv.find(".k-slider-selection"), halfDragHanndle = parseInt(dragHandle[that._outerSize]() / 2, 10), rtlCorrection = that._isRtl ? 2 : 0;
            selectionDiv[that._size](that._isRtl ? that._maxSelection - selection : selection);
            dragHandle.css(that._position, selection - halfDragHanndle - rtlCorrection);
        }
        moveSelection(options.value);
        that.bind([ CHANGE, SLIDE, MOVE_SELECTION ], function(e) {
            moveSelection(parseFloat(e.value, 10));
        });
    };
    Slider.Drag = function(dragHandle, type, owner, options) {
        var that = this;
        that.owner = owner;
        that.options = options;
        that.dragHandle = dragHandle;
        that.dragHandleSize = dragHandle[owner._outerSize]();
        that.type = type;
        that.draggable = new Draggable(dragHandle, {
            distance: 0,
            dragstart: proxy(that._dragstart, that),
            drag: proxy(that.drag, that),
            dragend: proxy(that.dragend, that),
            dragcancel: proxy(that.dragcancel, that)
        });
        dragHandle.click(false);
    };
    Slider.Drag.prototype = {
        dragstart: function(e) {
            // HACK to initiate click on the line
            this.draggable.userEvents._start(e);
        },
        _dragstart: function(e) {
            var that = this, owner = that.owner, options = that.options;
            if (!options.enabled) {
                e.preventDefault();
                return;
            }
            owner.element.off(MOUSE_OVER);
            that.dragHandle.addClass(STATE_SELECTED);
            $(document.documentElement).css("cursor", "pointer");
            that.dragableArea = owner._getDraggableArea();
            that.step = math.max(options.smallStep * (owner._maxSelection / owner._distance), 0);
            if (that.type) {
                that.selectionStart = options.selectionStart;
                that.selectionEnd = options.selectionEnd;
                owner._setZIndex(that.type);
            } else {
                that.oldVal = that.val = options.value;
            }
            that._removeTooltip(true);
            that._createTooltip();
        },
        _createTooltip: function() {
            var that = this, owner = that.owner, tooltip = that.options.tooltip, html = "", wnd = $(window), tooltipTemplate, colloutCssClass;
            if (!tooltip.enabled) {
                return;
            }
            if (tooltip.template) {
                tooltipTemplate = that.tooltipTemplate = kendo.template(tooltip.template);
            }
            $(".k-slider-tooltip").remove();
            // if user changes window while tooltip is visible, a second one will be created
            that.tooltipDiv = $("<div class='k-widget k-tooltip k-slider-tooltip'><!-- --></div>").appendTo(document.body);
            html = owner._getFormattedValue(that.val || owner.value(), that);
            if (!that.type) {
                colloutCssClass = "k-callout-" + (owner._isHorizontal ? "s" : "e");
                that.tooltipInnerDiv = "<div class='k-callout " + colloutCssClass + "'><!-- --></div>";
                html += that.tooltipInnerDiv;
            }
            that.tooltipDiv.html(html);
            that._scrollOffset = {
                top: wnd.scrollTop(),
                left: wnd.scrollLeft()
            };
            that.moveTooltip();
        },
        drag: function(e) {
            var that = this, owner = that.owner, x = e.x.location, y = e.y.location, startPoint = that.dragableArea.startPoint, endPoint = that.dragableArea.endPoint, slideParams;
            e.preventDefault();
            if (owner._isHorizontal) {
                if (owner._isRtl) {
                    that.val = that.constrainValue(x, startPoint, endPoint, x < endPoint);
                } else {
                    that.val = that.constrainValue(x, startPoint, endPoint, x >= endPoint);
                }
            } else {
                that.val = that.constrainValue(y, endPoint, startPoint, y <= endPoint);
            }
            if (that.oldVal != that.val) {
                that.oldVal = that.val;
                if (that.type) {
                    if (that.type == "firstHandle") {
                        if (that.val < that.selectionEnd) {
                            that.selectionStart = that.val;
                        } else {
                            that.selectionStart = that.selectionEnd = that.val;
                        }
                    } else {
                        if (that.val > that.selectionStart) {
                            that.selectionEnd = that.val;
                        } else {
                            that.selectionStart = that.selectionEnd = that.val;
                        }
                    }
                    slideParams = {
                        values: [ that.selectionStart, that.selectionEnd ],
                        value: [ that.selectionStart, that.selectionEnd ]
                    };
                } else {
                    slideParams = {
                        value: that.val
                    };
                }
                owner.trigger(SLIDE, slideParams);
            }
            that._updateTooltip(that.val);
        },
        _updateTooltip: function(val) {
            var that = this, options = that.options, tooltip = options.tooltip, html = "";
            if (!tooltip.enabled) {
                return;
            }
            if (!that.tooltipDiv) {
                that._createTooltip();
            }
            html = that.owner._getFormattedValue(round(val), that);
            if (!that.type) {
                html += that.tooltipInnerDiv;
            }
            that.tooltipDiv.html(html);
            that.moveTooltip();
        },
        dragcancel: function() {
            this.owner._refresh();
            $(document.documentElement).css("cursor", "");
            return this._end();
        },
        dragend: function() {
            var that = this, owner = that.owner;
            $(document.documentElement).css("cursor", "");
            if (that.type) {
                owner._update(that.selectionStart, that.selectionEnd);
            } else {
                owner._update(that.val);
            }
            return that._end();
        },
        _end: function() {
            var that = this, owner = that.owner;
            owner._focusWithMouse({
                target: that.dragHandle[0]
            });
            owner.element.on(MOUSE_OVER);
            return false;
        },
        _removeTooltip: function(noAnimation) {
            var that = this, owner = that.owner;
            if (that.tooltipDiv && owner.options.tooltip.enabled && owner.options.enabled) {
                if (noAnimation) {
                    that.tooltipDiv.remove();
                    that.tooltipDiv = null;
                } else {
                    that.tooltipDiv.fadeOut("slow", function() {
                        $(this).remove();
                        that.tooltipDiv = null;
                    });
                }
            }
        },
        moveTooltip: function() {
            var that = this, owner = that.owner, top = 0, left = 0, dragHandle = that.dragHandle, offset = kendo.getOffset(dragHandle), margin = 8, viewport = $(window), callout = that.tooltipDiv.find(".k-callout"), width = that.tooltipDiv.outerWidth(), height = that.tooltipDiv.outerHeight(), dragHandles, sdhOffset, diff, anchorSize;
            if (that.type) {
                dragHandles = owner.wrapper.find(DRAG_HANDLE);
                offset = kendo.getOffset(dragHandles.eq(0));
                sdhOffset = kendo.getOffset(dragHandles.eq(1));
                if (owner._isHorizontal) {
                    top = sdhOffset.top;
                    left = offset.left + (sdhOffset.left - offset.left) / 2;
                } else {
                    top = offset.top + (sdhOffset.top - offset.top) / 2;
                    left = sdhOffset.left;
                }
                anchorSize = dragHandles.eq(0).outerWidth() + 2 * margin;
            } else {
                top = offset.top;
                left = offset.left;
                anchorSize = dragHandle.outerWidth() + 2 * margin;
            }
            if (owner._isHorizontal) {
                left -= parseInt((width - dragHandle[owner._outerSize]()) / 2, 10);
                top -= height + callout.height() + margin;
            } else {
                top -= parseInt((height - dragHandle[owner._outerSize]()) / 2, 10);
                left -= width + callout.width() + margin;
            }
            if (owner._isHorizontal) {
                diff = that._flip(top, height, anchorSize, viewport.outerHeight() + that._scrollOffset.top);
                top += diff;
                left += that._fit(left, width, viewport.outerWidth() + that._scrollOffset.left);
            } else {
                diff = that._flip(left, width, anchorSize, viewport.outerWidth() + that._scrollOffset.left);
                top += that._fit(top, height, viewport.outerHeight() + that._scrollOffset.top);
                left += diff;
            }
            if (diff > 0 && callout) {
                callout.removeClass();
                callout.addClass("k-callout k-callout-" + (owner._isHorizontal ? "n" : "w"));
            }
            that.tooltipDiv.css({
                top: top,
                left: left
            });
        },
        _fit: function(position, size, viewPortEnd) {
            var output = 0;
            if (position + size > viewPortEnd) {
                output = viewPortEnd - (position + size);
            }
            if (position < 0) {
                output = -position;
            }
            return output;
        },
        _flip: function(offset, size, anchorSize, viewPortEnd) {
            var output = 0;
            if (offset + size > viewPortEnd) {
                output += -(anchorSize + size);
            }
            if (offset + output < 0) {
                output += anchorSize + size;
            }
            return output;
        },
        constrainValue: function(position, min, max, maxOverflow) {
            var that = this, val = 0;
            if (min < position && position < max) {
                val = that.owner._getValueFromPosition(position, that.dragableArea);
            } else {
                if (maxOverflow) {
                    val = that.options.max;
                } else {
                    val = that.options.min;
                }
            }
            return val;
        }
    };
    kendo.ui.plugin(Slider);
    var RangeSlider = SliderBase.extend({
        init: function(element, options) {
            var that = this, inputs = $(element).find("input"), firstInput = inputs.eq(0)[0], secondInput = inputs.eq(1)[0];
            firstInput.type = "text";
            secondInput.type = "text";
            options = extend({}, {
                selectionStart: parseAttr(firstInput, "value"),
                min: parseAttr(firstInput, "min"),
                max: parseAttr(firstInput, "max"),
                smallStep: parseAttr(firstInput, "step")
            }, {
                selectionEnd: parseAttr(secondInput, "value"),
                min: parseAttr(secondInput, "min"),
                max: parseAttr(secondInput, "max"),
                smallStep: parseAttr(secondInput, "step")
            }, options);
            if (options && options.enabled === undefined) {
                options.enabled = !inputs.is("[disabled]");
            }
            SliderBase.fn.init.call(that, element, options);
            options = that.options;
            if (!defined(options.selectionStart) || options.selectionStart === null) {
                options.selectionStart = options.min;
                inputs.eq(0).val(options.min);
            }
            if (!defined(options.selectionEnd) || options.selectionEnd === null) {
                options.selectionEnd = options.max;
                inputs.eq(1).val(options.max);
            }
            var dragHandles = that.wrapper.find(DRAG_HANDLE);
            new RangeSlider.Selection(dragHandles, that, options);
            that._firstHandleDrag = new Slider.Drag(dragHandles.eq(0), "firstHandle", that, options);
            that._lastHandleDrag = new Slider.Drag(dragHandles.eq(1), "lastHandle", that, options);
        },
        options: {
            name: "RangeSlider",
            leftDragHandleTitle: "drag",
            rightDragHandleTitle: "drag",
            tooltip: {
                format: "{0}"
            },
            selectionStart: null,
            selectionEnd: null
        },
        enable: function(enable) {
            var that = this, options = that.options, clickHandler;
            that.disable();
            if (enable === false) {
                return;
            }
            that.wrapper.removeClass(STATE_DISABLED).addClass(STATE_DEFAULT);
            that.wrapper.find("input").removeAttr(DISABLED);
            clickHandler = function(e) {
                var touch = getTouches(e)[0];
                if (!touch) {
                    return;
                }
                var mousePosition = that._isHorizontal ? touch.location.pageX : touch.location.pageY, dragableArea = that._getDraggableArea(), val = that._getValueFromPosition(mousePosition, dragableArea), target = $(e.target), idx;
                if (target.hasClass("k-draghandle")) {
                    target.addClass(STATE_SELECTED);
                    return;
                }
                if (val < options.selectionStart) {
                    that._setValueInRange(val, options.selectionEnd);
                    that._firstHandleDrag.dragstart(e);
                    idx = 0;
                } else if (val > that.selectionEnd) {
                    that._setValueInRange(options.selectionStart, val);
                    that._lastHandleDrag.dragstart(e);
                    idx = 1;
                } else {
                    if (val - options.selectionStart <= options.selectionEnd - val) {
                        that._setValueInRange(val, options.selectionEnd);
                        that._firstHandleDrag.dragstart(e);
                        idx = 0;
                    } else {
                        that._setValueInRange(options.selectionStart, val);
                        that._lastHandleDrag.dragstart(e);
                        idx = 1;
                    }
                }
                that._focusWithMouse({
                    target: that.wrapper.find(DRAG_HANDLE)[idx]
                });
            };
            that.wrapper.find(TICK_SELECTOR + ", " + TRACK_SELECTOR).on(TRACK_MOUSE_DOWN, clickHandler).end().on(TRACK_MOUSE_DOWN, function() {
                $(document.documentElement).one("selectstart", kendo.preventDefault);
            });
            that.wrapper.find(DRAG_HANDLE).attr(TABINDEX, 0).on(MOUSE_UP, function() {
                that._setTooltipTimeout();
            }).on(CLICK, function(e) {
                that._focusWithMouse(e);
                e.preventDefault();
            }).on(FOCUS, proxy(that._focus, that)).on(BLUR, proxy(that._blur, that));
            that.wrapper.find(DRAG_HANDLE).off(KEY_DOWN, kendo.preventDefault).eq(0).on(KEY_DOWN, proxy(function(e) {
                this._keydown(e, "firstHandle");
            }, that)).end().eq(1).on(KEY_DOWN, proxy(function(e) {
                this._keydown(e, "lastHandle");
            }, that));
            that.options.enabled = true;
        },
        disable: function() {
            var that = this;
            that.wrapper.removeClass(STATE_DEFAULT).addClass(STATE_DISABLED);
            that.wrapper.find("input").prop(DISABLED, DISABLED);
            that.wrapper.find(TICK_SELECTOR + ", " + TRACK_SELECTOR).off(TRACK_MOUSE_DOWN);
            that.wrapper.find(DRAG_HANDLE).attr(TABINDEX, -1).off(MOUSE_UP).off(KEY_DOWN).off(CLICK).off(FOCUS).off(BLUR);
            that.options.enabled = false;
        },
        _keydown: function(e, handle) {
            var that = this, selectionStartValue = that.options.selectionStart, selectionEndValue = that.options.selectionEnd, dragSelectionStart, dragSelectionEnd, activeHandleDrag;
            if (e.keyCode in that._keyMap) {
                that._clearTooltipTimeout();
                if (handle == "firstHandle") {
                    activeHandleDrag = that._activeHandleDrag = that._firstHandleDrag;
                    selectionStartValue = that._keyMap[e.keyCode](selectionStartValue);
                    if (selectionStartValue > selectionEndValue) {
                        selectionEndValue = selectionStartValue;
                    }
                } else {
                    activeHandleDrag = that._activeHandleDrag = that._lastHandleDrag;
                    selectionEndValue = that._keyMap[e.keyCode](selectionEndValue);
                    if (selectionStartValue > selectionEndValue) {
                        selectionStartValue = selectionEndValue;
                    }
                }
                that._setValueInRange(selectionStartValue, selectionEndValue);
                dragSelectionStart = Math.max(selectionStartValue, that.options.selectionStart);
                dragSelectionEnd = Math.min(selectionEndValue, that.options.selectionEnd);
                activeHandleDrag.selectionEnd = Math.max(dragSelectionEnd, that.options.selectionStart);
                activeHandleDrag.selectionStart = Math.min(dragSelectionStart, that.options.selectionEnd);
                activeHandleDrag._updateTooltip(that.value()[that._activeHandle]);
                e.preventDefault();
            }
        },
        _update: function(selectionStart, selectionEnd) {
            var that = this, values = that.value();
            var change = values[0] != selectionStart || values[1] != selectionEnd;
            that.value([ selectionStart, selectionEnd ]);
            if (change) {
                that.trigger(CHANGE, {
                    values: [ selectionStart, selectionEnd ],
                    value: [ selectionStart, selectionEnd ]
                });
            }
        },
        value: function(value) {
            if (value && value.length) {
                return this._value(value[0], value[1]);
            } else {
                return this._value();
            }
        },
        _value: function(start, end) {
            var that = this, options = that.options, selectionStart = options.selectionStart, selectionEnd = options.selectionEnd;
            if (isNaN(start) && isNaN(end)) {
                return [ selectionStart, selectionEnd ];
            } else {
                start = round(start);
                end = round(end);
            }
            if (start >= options.min && start <= options.max && end >= options.min && end <= options.max && start <= end) {
                if (selectionStart != start || selectionEnd != end) {
                    that.element.find("input").eq(0).prop("value", formatValue(start)).end().eq(1).prop("value", formatValue(end));
                    options.selectionStart = start;
                    options.selectionEnd = end;
                    that._refresh();
                    that._refreshAriaAttr(start, end);
                }
            }
        },
        values: function(start, end) {
            if (isArray(start)) {
                return this._value(start[0], start[1]);
            } else {
                return this._value(start, end);
            }
        },
        _refresh: function() {
            var that = this, options = that.options;
            that.trigger(MOVE_SELECTION, {
                values: [ options.selectionStart, options.selectionEnd ],
                value: [ options.selectionStart, options.selectionEnd ]
            });
            if (options.selectionStart == options.max && options.selectionEnd == options.max) {
                that._setZIndex("firstHandle");
            }
        },
        _refreshAriaAttr: function(start, end) {
            var that = this, dragHandles = that.wrapper.find(DRAG_HANDLE), drag = that._activeHandleDrag, formattedValue;
            formattedValue = that._getFormattedValue([ start, end ], drag);
            dragHandles.eq(0).attr("aria-valuenow", start);
            dragHandles.eq(1).attr("aria-valuenow", end);
            dragHandles.attr("aria-valuetext", formattedValue);
        },
        _setValueInRange: function(selectionStart, selectionEnd) {
            var options = this.options;
            selectionStart = math.max(math.min(selectionStart, options.max), options.min);
            selectionEnd = math.max(math.min(selectionEnd, options.max), options.min);
            if (selectionStart == options.max && selectionEnd == options.max) {
                this._setZIndex("firstHandle");
            }
            this._update(math.min(selectionStart, selectionEnd), math.max(selectionStart, selectionEnd));
        },
        _setZIndex: function(type) {
            this.wrapper.find(DRAG_HANDLE).each(function(index) {
                $(this).css("z-index", type == "firstHandle" ? 1 - index : index);
            });
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that.wrapper.off(NS).find(TICK_SELECTOR + ", " + TRACK_SELECTOR).off(NS).end().find(DRAG_HANDLE).off(NS);
            that._firstHandleDrag.draggable.destroy();
            that._lastHandleDrag.draggable.destroy();
        }
    });
    RangeSlider.Selection = function(dragHandles, that, options) {
        function moveSelection(value) {
            value = value || [];
            var selectionStartValue = value[0] - options.min, selectionEndValue = value[1] - options.min, selectionStartIndex = math.ceil(round(selectionStartValue / options.smallStep)), selectionEndIndex = math.ceil(round(selectionEndValue / options.smallStep)), selectionStart = that._pixelSteps[selectionStartIndex], selectionEnd = that._pixelSteps[selectionEndIndex], halfHandle = parseInt(dragHandles.eq(0)[that._outerSize]() / 2, 10), rtlCorrection = that._isRtl ? 2 : 0;
            dragHandles.eq(0).css(that._position, selectionStart - halfHandle - rtlCorrection).end().eq(1).css(that._position, selectionEnd - halfHandle - rtlCorrection);
            makeSelection(selectionStart, selectionEnd);
        }
        function makeSelection(selectionStart, selectionEnd) {
            var selection, selectionPosition, selectionDiv = that._trackDiv.find(".k-slider-selection");
            selection = math.abs(selectionStart - selectionEnd);
            selectionDiv[that._size](selection);
            if (that._isRtl) {
                selectionPosition = math.max(selectionStart, selectionEnd);
                selectionDiv.css("right", that._maxSelection - selectionPosition - 1);
            } else {
                selectionPosition = math.min(selectionStart, selectionEnd);
                selectionDiv.css(that._position, selectionPosition - 1);
            }
        }
        moveSelection(that.value());
        that.bind([ CHANGE, SLIDE, MOVE_SELECTION ], function(e) {
            moveSelection(e.values);
        });
    };
    kendo.ui.plugin(RangeSlider);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.ui, keys = kendo.keys, extend = $.extend, proxy = $.proxy, Widget = ui.Widget, pxUnitsRegex = /^\d+(\.\d+)?px$/i, percentageUnitsRegex = /^\d+(\.\d+)?%$/i, NS = ".kendoSplitter", EXPAND = "expand", COLLAPSE = "collapse", CONTENTLOAD = "contentLoad", RESIZE = "resize", LAYOUTCHANGE = "layoutChange", HORIZONTAL = "horizontal", VERTICAL = "vertical", MOUSEENTER = "mouseenter", CLICK = "click", PANE = "pane", MOUSELEAVE = "mouseleave", FOCUSED = "k-state-focused", KPANE = "k-" + PANE, PANECLASS = "." + KPANE;
    function isPercentageSize(size) {
        return percentageUnitsRegex.test(size);
    }
    function isPixelSize(size) {
        return pxUnitsRegex.test(size) || /^\d+$/.test(size);
    }
    function isFluid(size) {
        return !isPercentageSize(size) && !isPixelSize(size);
    }
    function panePropertyAccessor(propertyName, triggersResize) {
        return function(pane, value) {
            var paneConfig = this.element.find(pane).data(PANE);
            if (arguments.length == 1) {
                return paneConfig[propertyName];
            }
            paneConfig[propertyName] = value;
            if (triggersResize) {
                var splitter = this.element.data("kendoSplitter");
                splitter.trigger(RESIZE);
            }
        };
    }
    var Splitter = Widget.extend({
        init: function(element, options) {
            var that = this, isHorizontal;
            Widget.fn.init.call(that, element, options);
            that.wrapper = that.element;
            isHorizontal = that.options.orientation.toLowerCase() != VERTICAL;
            that.orientation = isHorizontal ? HORIZONTAL : VERTICAL;
            that._dimension = isHorizontal ? "width" : "height";
            that._keys = {
                decrease: isHorizontal ? keys.LEFT : keys.UP,
                increase: isHorizontal ? keys.RIGHT : keys.DOWN
            };
            that._resizeStep = 10;
            that.bind(RESIZE, proxy(that._resize, that));
            that._marker = kendo.guid().substring(0, 8);
            that._initPanes();
            that._resizeHandler = function() {
                that.trigger(RESIZE);
            };
            that._attachEvents();
            $(window).on("resize", that._resizeHandler);
            that.resizing = new PaneResizing(that);
            that.element.triggerHandler("init.kendoSplitter");
        },
        events: [ EXPAND, COLLAPSE, CONTENTLOAD, RESIZE, LAYOUTCHANGE ],
        _attachEvents: function() {
            var that = this, orientation = that.options.orientation, splitbarDraggableSelector = "> .k-splitbar-draggable-" + orientation;
            // do not use delegated events to increase performance of nested elements
            that.element.find(splitbarDraggableSelector).on("keydown" + NS, $.proxy(that._keydown, that)).on("mousedown" + NS, function(e) {
                e.currentTarget.focus();
            }).on("focus" + NS, function(e) {
                $(e.currentTarget).addClass(FOCUSED);
            }).on("blur" + NS, function(e) {
                $(e.currentTarget).removeClass(FOCUSED);
                that.resizing.end();
            }).on(MOUSEENTER + NS, function() {
                $(this).addClass("k-splitbar-" + that.orientation + "-hover");
            }).on(MOUSELEAVE + NS, function() {
                $(this).removeClass("k-splitbar-" + that.orientation + "-hover");
            }).on("mousedown" + NS, function() {
                that._panes().append("<div class='k-splitter-overlay k-overlay' />");
            }).on("mouseup" + NS, function() {
                that._panes().children(".k-splitter-overlay").remove();
            }).end().on(CLICK + NS, ".k-splitbar .k-collapse-next, .k-splitbar .k-collapse-prev", that._arrowClick(COLLAPSE)).on(CLICK + NS, ".k-splitbar .k-expand-next, .k-splitbar .k-expand-prev", that._arrowClick(EXPAND)).on("dblclick" + NS, ".k-splitbar", proxy(that._togglePane, that)).parent().closest(".k-splitter").each(function() {
                var parentSplitter = $(this), splitter = parentSplitter.data("kendoSplitter");
                if (splitter) {
                    splitter.bind(RESIZE, that._resizeHandler);
                } else {
                    parentSplitter.one("init" + NS, function() {
                        $(this).data("kendoSplitter").bind(RESIZE, that._resizeHandler);
                        that._resizeHandler();
                    });
                }
            });
        },
        options: {
            name: "Splitter",
            orientation: HORIZONTAL,
            panes: []
        },
        destroy: function() {
            var that = this, orientation = that.options.orientation, splitbarDraggableSelector = "> .k-splitbar-draggable-" + orientation;
            Widget.fn.destroy.call(that);
            that.element.off(NS).find(splitbarDraggableSelector).off(NS);
            that.resizing.destroy();
            $(window).off("resize", that._resizeHandler);
            kendo.destroy(that.element);
        },
        _keydown: function(e) {
            var that = this, key = e.keyCode, resizing = that.resizing, target = $(e.currentTarget), navigationKeys = that._keys, increase = key === navigationKeys.increase, decrease = key === navigationKeys.decrease, pane;
            if (increase || decrease) {
                if (e.ctrlKey) {
                    pane = target[decrease ? "next" : "prev"]();
                    if (resizing.isResizing()) {
                        resizing.end();
                    }
                    if (!pane[that._dimension]()) {
                        that._triggerAction(EXPAND, pane);
                    } else {
                        that._triggerAction(COLLAPSE, target[decrease ? "prev" : "next"]());
                    }
                } else {
                    resizing.move((decrease ? -1 : 1) * that._resizeStep, target);
                }
                e.preventDefault();
            } else if (key === keys.ENTER) {
                resizing.end();
                e.preventDefault();
            }
        },
        _initPanes: function() {
            var that = this, panesConfig = that.options.panes || [];
            that.element.addClass("k-widget").addClass("k-splitter").children().each(function(index, pane) {
                var config = panesConfig && panesConfig[index];
                pane = $(pane).attr("role", "group").addClass(KPANE);
                pane.data(PANE, config ? config : {}).toggleClass("k-scrollable", config ? config.scrollable !== false : true);
                that.ajaxRequest(pane);
            }).end();
            that.trigger(RESIZE);
        },
        ajaxRequest: function(pane, url, data) {
            var that = this, paneConfig;
            pane = that.element.find(pane);
            paneConfig = pane.data(PANE);
            url = url || paneConfig.contentUrl;
            if (url) {
                pane.append("<span class='k-icon k-loading k-pane-loading' />");
                if (kendo.isLocalUrl(url)) {
                    jQuery.ajax({
                        url: url,
                        data: data || {},
                        type: "GET",
                        dataType: "html",
                        success: function(data) {
                            pane.html(data);
                            that.trigger(CONTENTLOAD, {
                                pane: pane[0]
                            });
                        }
                    });
                } else {
                    pane.removeClass("k-scrollable").html("<iframe src='" + url + "' frameborder='0' class='k-content-frame'>" + "This page requires frames in order to show content" + "</iframe>");
                }
            }
        },
        _triggerAction: function(type, pane) {
            if (!this.trigger(type, {
                pane: pane[0]
            })) {
                this[type](pane[0]);
            }
        },
        _togglePane: function(e) {
            var that = this, target = $(e.target), arrow;
            if (target.closest(".k-splitter")[0] != that.element[0]) {
                return;
            }
            arrow = target.children(".k-icon:not(.k-resize-handle)");
            if (arrow.length !== 1) {
                return;
            }
            if (arrow.is(".k-collapse-prev")) {
                that._triggerAction(COLLAPSE, target.prev());
            } else if (arrow.is(".k-collapse-next")) {
                that._triggerAction(COLLAPSE, target.next());
            } else if (arrow.is(".k-expand-prev")) {
                that._triggerAction(EXPAND, target.prev());
            } else if (arrow.is(".k-expand-next")) {
                that._triggerAction(EXPAND, target.next());
            }
        },
        _arrowClick: function(arrowType) {
            var that = this;
            return function(e) {
                var target = $(e.target), pane;
                if (target.closest(".k-splitter")[0] != that.element[0]) {
                    return;
                }
                if (target.is(".k-" + arrowType + "-prev")) {
                    pane = target.parent().prev();
                } else {
                    pane = target.parent().next();
                }
                that._triggerAction(arrowType, pane);
            };
        },
        _updateSplitBar: function(splitbar, previousPane, nextPane) {
            var catIconIf = function(iconType, condition) {
                return condition ? "<div class='k-icon " + iconType + "' />" : "";
            }, orientation = this.orientation, draggable = previousPane.resizable !== false && nextPane.resizable !== false, prevCollapsible = previousPane.collapsible, prevCollapsed = previousPane.collapsed, nextCollapsible = nextPane.collapsible, nextCollapsed = nextPane.collapsed;
            splitbar.addClass("k-splitbar k-state-default k-splitbar-" + orientation).attr("role", "separator").attr("aria-expanded", !(prevCollapsed || nextCollapsed)).removeClass("k-splitbar-" + orientation + "-hover").toggleClass("k-splitbar-draggable-" + orientation, draggable && !prevCollapsed && !nextCollapsed).toggleClass("k-splitbar-static-" + orientation, !draggable && !prevCollapsible && !nextCollapsible).html(catIconIf("k-collapse-prev", prevCollapsible && !prevCollapsed && !nextCollapsed) + catIconIf("k-expand-prev", prevCollapsible && prevCollapsed && !nextCollapsed) + catIconIf("k-resize-handle", draggable) + catIconIf("k-collapse-next", nextCollapsible && !nextCollapsed && !prevCollapsed) + catIconIf("k-expand-next", nextCollapsible && nextCollapsed && !prevCollapsed));
        },
        _updateSplitBars: function() {
            var that = this;
            this.element.children(".k-splitbar").each(function() {
                var splitbar = $(this), previousPane = splitbar.prev(PANECLASS).data(PANE), nextPane = splitbar.next(PANECLASS).data(PANE);
                if (!nextPane) {
                    return;
                }
                that._updateSplitBar(splitbar, previousPane, nextPane);
            });
        },
        _panes: function() {
            return this.element.children(PANECLASS);
        },
        _resize: function() {
            var that = this, element = that.element, panes = element.children(":not(.k-splitbar)"), isHorizontal = that.orientation == HORIZONTAL, splitBars = element.children(".k-splitbar"), splitBarsCount = splitBars.length, sizingProperty = isHorizontal ? "width" : "height", totalSize = element[sizingProperty]();
            if (splitBarsCount === 0) {
                splitBarsCount = panes.length - 1;
                panes.slice(0, splitBarsCount).after("<div tabindex='0' class='k-splitbar' data-marker='" + that._marker + "' />");
                that._updateSplitBars();
                splitBars = element.children(".k-splitbar");
            } else {
                that._updateSplitBars();
            }
            // discard splitbar sizes from total size
            splitBars.each(function() {
                totalSize -= this[isHorizontal ? "offsetWidth" : "offsetHeight"];
            });
            var sizedPanesWidth = 0, sizedPanesCount = 0, freeSizedPanes = $();
            panes.css({
                position: "absolute",
                top: 0
            })[sizingProperty](function() {
                var config = $(this).data(PANE) || {}, size;
                if (config.collapsed) {
                    size = 0;
                    $(this).css("overflow", "hidden");
                } else if (isFluid(config.size)) {
                    freeSizedPanes = freeSizedPanes.add(this);
                    return;
                } else {
                    // sized in px/%, not collapsed
                    size = parseInt(config.size, 10);
                    if (isPercentageSize(config.size)) {
                        size = Math.floor(size * totalSize / 100);
                    }
                }
                sizedPanesCount++;
                sizedPanesWidth += size;
                return size;
            });
            totalSize -= sizedPanesWidth;
            var freeSizePanesCount = freeSizedPanes.length, freeSizePaneWidth = Math.floor(totalSize / freeSizePanesCount);
            freeSizedPanes.slice(0, freeSizePanesCount - 1).css(sizingProperty, freeSizePaneWidth).end().eq(freeSizePanesCount - 1).css(sizingProperty, totalSize - (freeSizePanesCount - 1) * freeSizePaneWidth);
            // arrange panes
            var sum = 0, alternateSizingProperty = isHorizontal ? "height" : "width", positioningProperty = isHorizontal ? "left" : "top", sizingDomProperty = isHorizontal ? "offsetWidth" : "offsetHeight";
            if (freeSizePanesCount === 0) {
                var lastNonCollapsedPane = panes.filter(function() {
                    return !($(this).data(PANE) || {}).collapsed;
                }).last();
                lastNonCollapsedPane[sizingProperty](totalSize + lastNonCollapsedPane[0][sizingDomProperty]);
            }
            element.children().css(alternateSizingProperty, element[alternateSizingProperty]()).each(function(i, child) {
                child.style[positioningProperty] = Math.floor(sum) + "px";
                sum += child[sizingDomProperty];
            });
            that.trigger(LAYOUTCHANGE);
        },
        toggle: function(pane, expand) {
            var paneConfig;
            pane = this.element.find(pane);
            paneConfig = pane.data(PANE);
            if (!expand && !paneConfig.collapsible) {
                return;
            }
            if (arguments.length == 1) {
                expand = paneConfig.collapsed === undefined ? false : paneConfig.collapsed;
            }
            paneConfig.collapsed = !expand;
            if (paneConfig.collapsed) {
                pane.css("overflow", "hidden");
            } else {
                pane.css("overflow", "");
            }
            this.trigger(RESIZE);
            this.resizing.destroy();
            this.resizing = new PaneResizing(this);
        },
        collapse: function(pane) {
            this.toggle(pane, false);
        },
        expand: function(pane) {
            this.toggle(pane, true);
        },
        size: panePropertyAccessor("size", true),
        min: panePropertyAccessor("min"),
        max: panePropertyAccessor("max")
    });
    ui.plugin(Splitter);
    var verticalDefaults = {
        sizingProperty: "height",
        sizingDomProperty: "offsetHeight",
        alternateSizingProperty: "width",
        positioningProperty: "top",
        mousePositioningProperty: "pageY"
    };
    var horizontalDefaults = {
        sizingProperty: "width",
        sizingDomProperty: "offsetWidth",
        alternateSizingProperty: "height",
        positioningProperty: "left",
        mousePositioningProperty: "pageX"
    };
    function PaneResizing(splitter) {
        var that = this, orientation = splitter.orientation;
        that.owner = splitter;
        that._element = splitter.element;
        that.orientation = orientation;
        extend(that, orientation === HORIZONTAL ? horizontalDefaults : verticalDefaults);
        that._resizable = new kendo.ui.Resizable(splitter.element, {
            orientation: orientation,
            handle: ".k-splitbar-draggable-" + orientation + "[data-marker=" + splitter._marker + "]",
            hint: proxy(that._createHint, that),
            start: proxy(that._start, that),
            max: proxy(that._max, that),
            min: proxy(that._min, that),
            invalidClass: "k-restricted-size-" + orientation,
            resizeend: proxy(that._stop, that)
        });
    }
    PaneResizing.prototype = {
        press: function(target) {
            this._resizable.press(target);
        },
        move: function(delta, target) {
            if (!this.pressed) {
                this.press(target);
                this.pressed = true;
            }
            if (!this._resizable.target) {
                this._resizable.press(target);
            }
            this._resizable.move(delta);
        },
        end: function() {
            this._resizable.end();
            this.pressed = false;
        },
        destroy: function() {
            this._resizable.destroy();
        },
        isResizing: function() {
            return this._resizable.resizing;
        },
        _createHint: function(handle) {
            var that = this;
            return $("<div class='k-ghost-splitbar k-ghost-splitbar-" + that.orientation + " k-state-default' />").css(that.alternateSizingProperty, handle[that.alternateSizingProperty]());
        },
        _start: function(e) {
            var that = this, splitbar = $(e.currentTarget), previousPane = splitbar.prev(), nextPane = splitbar.next(), previousPaneConfig = previousPane.data(PANE), nextPaneConfig = nextPane.data(PANE), prevBoundary = parseInt(previousPane[0].style[that.positioningProperty], 10), nextBoundary = parseInt(nextPane[0].style[that.positioningProperty], 10) + nextPane[0][that.sizingDomProperty] - splitbar[0][that.sizingDomProperty], totalSize = parseInt(that._element.css(that.sizingProperty), 10), toPx = function(value) {
                var val = parseInt(value, 10);
                return (isPixelSize(value) ? val : totalSize * val / 100) || 0;
            }, prevMinSize = toPx(previousPaneConfig.min), prevMaxSize = toPx(previousPaneConfig.max) || nextBoundary - prevBoundary, nextMinSize = toPx(nextPaneConfig.min), nextMaxSize = toPx(nextPaneConfig.max) || nextBoundary - prevBoundary;
            that.previousPane = previousPane;
            that.nextPane = nextPane;
            that._maxPosition = Math.min(nextBoundary - nextMinSize, prevBoundary + prevMaxSize);
            that._minPosition = Math.max(prevBoundary + prevMinSize, nextBoundary - nextMaxSize);
        },
        _max: function() {
            return this._maxPosition;
        },
        _min: function() {
            return this._minPosition;
        },
        _stop: function(e) {
            var that = this, splitbar = $(e.currentTarget), owner = that.owner;
            owner._panes().children(".k-splitter-overlay").remove();
            if (e.keyCode !== kendo.keys.ESC) {
                var ghostPosition = e.position, previousPane = splitbar.prev(), nextPane = splitbar.next(), previousPaneConfig = previousPane.data(PANE), nextPaneConfig = nextPane.data(PANE), previousPaneNewSize = ghostPosition - parseInt(previousPane[0].style[that.positioningProperty], 10), nextPaneNewSize = parseInt(nextPane[0].style[that.positioningProperty], 10) + nextPane[0][that.sizingDomProperty] - ghostPosition - splitbar[0][that.sizingDomProperty], fluidPanesCount = that._element.children(PANECLASS).filter(function() {
                    return isFluid($(this).data(PANE).size);
                }).length;
                if (!isFluid(previousPaneConfig.size) || fluidPanesCount > 1) {
                    if (isFluid(previousPaneConfig.size)) {
                        fluidPanesCount--;
                    }
                    previousPaneConfig.size = previousPaneNewSize + "px";
                }
                if (!isFluid(nextPaneConfig.size) || fluidPanesCount > 1) {
                    nextPaneConfig.size = nextPaneNewSize + "px";
                }
                owner.trigger(RESIZE);
            }
            return false;
        }
    };
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, Widget = kendo.ui.Widget, logToConsole = kendo.logToConsole, rFileExtension = /\.([^\.]+)$/, NS = ".kendoUpload", SELECT = "select", UPLOAD = "upload", SUCCESS = "success", ERROR = "error", COMPLETE = "complete", CANCEL = "cancel", PROGRESS = "progress", REMOVE = "remove";
    var Upload = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            that.name = element.name;
            that.multiple = that.options.multiple;
            that.localization = that.options.localization;
            var activeInput = that.element;
            that.wrapper = activeInput.closest(".k-upload");
            if (that.wrapper.length === 0) {
                that.wrapper = that._wrapInput(activeInput);
            }
            that._activeInput(activeInput);
            that.toggle(that.options.enabled);
            var ns = that._ns = NS + "-" + kendo.guid();
            activeInput.closest("form").on("submit" + ns, $.proxy(that._onParentFormSubmit, that)).on("reset" + ns, $.proxy(that._onParentFormReset, that));
            if (that.options.async.saveUrl) {
                that._module = that._supportsFormData() ? new formDataUploadModule(that) : new iframeUploadModule(that);
                that._async = true;
            } else {
                that._module = new syncUploadModule(that);
            }
            if (that._supportsDrop()) {
                that._setupDropZone();
            }
            that.wrapper.delegate(".k-upload-action", "click", $.proxy(that._onFileAction, that)).delegate(".k-upload-selected", "click", $.proxy(that._onUploadSelected, that)).delegate(".k-file", "t:progress", $.proxy(that._onFileProgress, that)).delegate(".k-file", "t:upload-success", $.proxy(that._onUploadSuccess, that)).delegate(".k-file", "t:upload-error", $.proxy(that._onUploadError, that));
        },
        events: [ SELECT, UPLOAD, SUCCESS, ERROR, COMPLETE, CANCEL, PROGRESS, REMOVE ],
        options: {
            name: "Upload",
            enabled: true,
            multiple: true,
            showFileList: true,
            async: {
                removeVerb: "POST",
                autoUpload: true
            },
            localization: {
                select: "Select...",
                cancel: "Cancel",
                retry: "Retry",
                remove: "Remove",
                uploadSelectedFiles: "Upload files",
                dropFilesHere: "drop files here to upload",
                statusUploading: "uploading",
                statusUploaded: "uploaded",
                statusFailed: "failed"
            }
        },
        setOptions: function(options) {
            var that = this, activeInput = that.element;
            Widget.fn.setOptions.call(that, options);
            that.multiple = that.options.multiple;
            activeInput.attr("multiple", that._supportsMultiple() ? that.multiple : false);
            that.toggle(that.options.enabled);
        },
        enable: function(enable) {
            enable = typeof enable === "undefined" ? true : enable;
            this.toggle(enable);
        },
        disable: function() {
            this.toggle(false);
        },
        toggle: function(enable) {
            enable = typeof enable === "undefined" ? enable : !enable;
            this.wrapper.toggleClass("k-state-disabled", enable);
        },
        destroy: function() {
            var that = this;
            $(document).add($(".k-dropzone", that.wrapper)).add(that.wrapper.closest("form")).off(that._ns);
            Widget.fn.destroy.call(that);
        },
        _addInput: function(input) {
            var that = this;
            input.insertAfter(that.element).data("kendoUpload", that);
            $(that.element).hide().removeAttr("id");
            that._activeInput(input);
        },
        _activeInput: function(input) {
            var that = this, wrapper = that.wrapper;
            that.element = input;
            input.attr("multiple", that._supportsMultiple() ? that.multiple : false).attr("autocomplete", "off").click(function(e) {
                if (wrapper.hasClass("k-state-disabled")) {
                    e.preventDefault();
                }
            }).change($.proxy(that._onInputChange, that));
        },
        _onInputChange: function(e) {
            var upload = this, input = $(e.target), prevented = upload.trigger(SELECT, {
                files: inputFiles(input)
            });
            if (prevented) {
                upload._addInput(input.clone().val(""));
                input.remove();
            } else {
                input.trigger("t:select");
            }
        },
        _onDrop: function(e) {
            var dt = e.originalEvent.dataTransfer, that = this, droppedFiles = dt.files;
            stopEvent(e);
            if (droppedFiles.length > 0) {
                var prevented = that.trigger(SELECT, {
                    files: getAllFileInfo(droppedFiles)
                });
                if (!prevented) {
                    $(".k-dropzone", that.wrapper).trigger("t:select", [ droppedFiles ]);
                }
            }
        },
        _enqueueFile: function(name, data) {
            var that = this, existingFileEntries, fileEntry, fileList = $(".k-upload-files", that.wrapper);
            if (fileList.length === 0) {
                fileList = $("<ul class='k-upload-files k-reset'></ul>").appendTo(that.wrapper);
                if (!that.options.showFileList) {
                    fileList.hide();
                }
            }
            existingFileEntries = $(".k-file", fileList);
            fileEntry = $("<li class='k-file'><span class='k-filename' title='" + name + "'>" + name + "</span></li>").appendTo(fileList).data(data);
            if (that._async) {
                fileEntry.prepend("<span class='k-icon'></span>");
            }
            if (!that.multiple) {
                existingFileEntries.trigger("t:remove");
            }
            return fileEntry;
        },
        _removeFileEntry: function(fileEntry) {
            var fileList = fileEntry.closest(".k-upload-files"), allFiles;
            fileEntry.remove();
            allFiles = $(".k-file", fileList);
            if (allFiles.find("> .k-fail").length === allFiles.length) {
                this._hideUploadButton();
            }
            if (allFiles.length === 0) {
                fileList.remove();
            }
        },
        _fileAction: function(fileElement, actionKey) {
            var classDictionary = {
                remove: "k-delete",
                cancel: "k-cancel",
                retry: "k-retry"
            };
            if (!classDictionary.hasOwnProperty(actionKey)) {
                return;
            }
            this._clearFileAction(fileElement);
            fileElement.append(this._renderAction(classDictionary[actionKey], this.localization[actionKey]).addClass("k-upload-action"));
        },
        _fileState: function(fileEntry, stateKey) {
            var localization = this.localization, states = {
                uploading: {
                    cssClass: "k-loading",
                    text: localization.statusUploading
                },
                uploaded: {
                    cssClass: "k-success",
                    text: localization.statusUploaded
                },
                failed: {
                    cssClass: "k-fail",
                    text: localization.statusFailed
                }
            }, currentState = states[stateKey];
            if (currentState) {
                var icon = fileEntry.children(".k-icon").text(currentState.text);
                icon[0].className = "k-icon " + currentState.cssClass;
            }
        },
        _renderAction: function(actionClass, actionText) {
            if (actionClass !== "") {
                return $("<button type='button' class='k-button k-button-icontext'>" + "<span class='k-icon " + actionClass + "'></span>" + actionText + "</button>");
            } else {
                return $("<button type='button' class='k-button'>" + actionText + "</button>");
            }
        },
        _clearFileAction: function(fileElement) {
            fileElement.find(".k-upload-action").remove();
        },
        _onFileAction: function(e) {
            var that = this;
            if (!that.wrapper.hasClass("k-state-disabled")) {
                var button = $(e.target).closest(".k-upload-action"), icon = button.find(".k-icon"), fileEntry = button.closest(".k-file"), eventArgs = {
                    files: fileEntry.data("fileNames")
                };
                if (icon.hasClass("k-delete")) {
                    if (!that.trigger(REMOVE, eventArgs)) {
                        fileEntry.trigger("t:remove", eventArgs.data);
                    }
                } else if (icon.hasClass("k-cancel")) {
                    that.trigger(CANCEL, eventArgs);
                    fileEntry.trigger("t:cancel");
                    this._checkAllComplete();
                } else if (icon.hasClass("k-retry")) {
                    fileEntry.trigger("t:retry");
                }
            }
            return false;
        },
        _onUploadSelected: function() {
            this.wrapper.trigger("t:saveSelected");
            return false;
        },
        _onFileProgress: function(e, percentComplete) {
            var progressBar = $(".k-progress-status", e.target);
            if (progressBar.length === 0) {
                progressBar = $("<span class='k-progress'><span class='k-state-selected k-progress-status' style='width: 0;'></span></span>").appendTo($(".k-filename", e.target)).find(".k-progress-status");
            }
            progressBar.width(percentComplete + "%");
            this.trigger(PROGRESS, {
                files: getFileEntry(e).data("fileNames"),
                percentComplete: percentComplete
            });
        },
        _onUploadSuccess: function(e, response, xhr) {
            var fileEntry = getFileEntry(e);
            this._fileState(fileEntry, "uploaded");
            this.trigger(SUCCESS, {
                files: fileEntry.data("fileNames"),
                response: response,
                operation: "upload",
                XMLHttpRequest: xhr
            });
            if (this._supportsRemove()) {
                this._fileAction(fileEntry, REMOVE);
            } else {
                this._clearFileAction(fileEntry);
            }
            this._checkAllComplete();
        },
        _onUploadError: function(e, xhr) {
            var fileEntry = getFileEntry(e);
            this._fileState(fileEntry, "failed");
            this._fileAction(fileEntry, "retry");
            this.trigger(ERROR, {
                operation: "upload",
                files: fileEntry.data("fileNames"),
                XMLHttpRequest: xhr
            });
            logToConsole("Server response: " + xhr.responseText);
            this._checkAllComplete();
        },
        _showUploadButton: function() {
            var uploadButton = $(".k-upload-selected", this.wrapper);
            if (uploadButton.length === 0) {
                uploadButton = this._renderAction("", this.localization.uploadSelectedFiles).addClass("k-upload-selected");
            }
            this.wrapper.append(uploadButton);
        },
        _hideUploadButton: function() {
            $(".k-upload-selected", this.wrapper).remove();
        },
        _onParentFormSubmit: function() {
            var upload = this, element = upload.element;
            element.trigger("t:abort");
            if (!element.value) {
                var input = $(element);
                // Prevent submitting an empty input
                input.attr("disabled", "disabled");
                window.setTimeout(function() {
                    // Restore the input so the Upload remains functional
                    // in case the user cancels the form submit
                    input.removeAttr("disabled");
                }, 0);
            }
        },
        _onParentFormReset: function() {
            $(".k-upload-files", this.wrapper).remove();
        },
        _supportsFormData: function() {
            return typeof FormData != "undefined";
        },
        _supportsMultiple: function() {
            var windows = this._userAgent().indexOf("Windows") > -1;
            return !kendo.support.browser.opera && !(kendo.support.browser.safari && windows);
        },
        _supportsDrop: function() {
            var userAgent = this._userAgent().toLowerCase(), isChrome = /chrome/.test(userAgent), isSafari = !isChrome && /safari/.test(userAgent), isWindowsSafari = isSafari && /windows/.test(userAgent);
            return !isWindowsSafari && this._supportsFormData() && this.options.async.saveUrl;
        },
        _userAgent: function() {
            return navigator.userAgent;
        },
        _setupDropZone: function() {
            var that = this;
            $(".k-upload-button", this.wrapper).wrap("<div class='k-dropzone'></div>");
            var ns = that._ns;
            var dropZone = $(".k-dropzone", that.wrapper).append($("<em>" + that.localization.dropFilesHere + "</em>")).on("dragenter" + ns, stopEvent).on("dragover" + ns, function(e) {
                e.preventDefault();
            }).on("drop" + ns, $.proxy(this._onDrop, this));
            bindDragEventWrappers(dropZone, ns, function() {
                dropZone.addClass("k-dropzone-hovered");
            }, function() {
                dropZone.removeClass("k-dropzone-hovered");
            });
            bindDragEventWrappers($(document), ns, function() {
                dropZone.addClass("k-dropzone-active");
            }, function() {
                dropZone.removeClass("k-dropzone-active");
            });
        },
        _supportsRemove: function() {
            return !!this.options.async.removeUrl;
        },
        _submitRemove: function(fileNames, data, onSuccess, onError) {
            var upload = this, removeField = upload.options.async.removeField || "fileNames", params = $.extend(data, getAntiForgeryTokens());
            params[removeField] = fileNames;
            jQuery.ajax({
                type: this.options.async.removeVerb,
                dataType: "json",
                url: this.options.async.removeUrl,
                traditional: true,
                data: params,
                success: onSuccess,
                error: onError
            });
        },
        _wrapInput: function(input) {
            input.wrap("<div class='k-widget k-upload'><div class='k-button k-upload-button'></div></div>");
            input.closest(".k-button").append("<span>" + this.localization.select + "</span>");
            return input.closest(".k-upload");
        },
        _checkAllComplete: function() {
            if ($(".k-file .k-icon.k-loading", this.wrapper).length === 0) {
                this.trigger(COMPLETE);
            }
        }
    });
    // Synchronous upload module
    var syncUploadModule = function(upload) {
        this.name = "syncUploadModule";
        this.element = upload.wrapper;
        this.upload = upload;
        this.element.bind("t:select", $.proxy(this.onSelect, this)).bind("t:remove", $.proxy(this.onRemove, this)).closest("form").attr("enctype", "multipart/form-data").attr("encoding", "multipart/form-data");
    };
    syncUploadModule.prototype = {
        onSelect: function(e) {
            var upload = this.upload;
            var sourceInput = $(e.target);
            upload._addInput(sourceInput.clone().val(""));
            var file = upload._enqueueFile(getFileName(sourceInput), {
                relatedInput: sourceInput,
                fileNames: inputFiles(sourceInput)
            });
            upload._fileAction(file, REMOVE);
        },
        onRemove: function(e) {
            var fileEntry = getFileEntry(e);
            fileEntry.data("relatedInput").remove();
            this.upload._removeFileEntry(fileEntry);
        }
    };
    // Iframe upload module
    var iframeUploadModule = function(upload) {
        this.name = "iframeUploadModule";
        this.element = upload.wrapper;
        this.upload = upload;
        this.iframes = [];
        this.element.bind("t:select", $.proxy(this.onSelect, this)).bind("t:cancel", $.proxy(this.onCancel, this)).bind("t:retry", $.proxy(this.onRetry, this)).bind("t:remove", $.proxy(this.onRemove, this)).bind("t:saveSelected", $.proxy(this.onSaveSelected, this)).bind("t:abort", $.proxy(this.onAbort, this));
    };
    Upload._frameId = 0;
    iframeUploadModule.prototype = {
        onSelect: function(e) {
            var upload = this.upload, sourceInput = $(e.target);
            var fileEntry = this.prepareUpload(sourceInput);
            if (upload.options.async.autoUpload) {
                this.performUpload(fileEntry);
            } else {
                if (upload._supportsRemove()) {
                    this.upload._fileAction(fileEntry, REMOVE);
                }
                upload._showUploadButton();
            }
        },
        prepareUpload: function(sourceInput) {
            var upload = this.upload;
            var activeInput = $(upload.element);
            var name = upload.options.async.saveField || sourceInput.attr("name");
            upload._addInput(sourceInput.clone().val(""));
            sourceInput.attr("name", name);
            var iframe = this.createFrame(upload.name + "_" + Upload._frameId++);
            this.registerFrame(iframe);
            var form = this.createForm(upload.options.async.saveUrl, iframe.attr("name")).append(activeInput);
            var fileEntry = upload._enqueueFile(getFileName(sourceInput), {
                frame: iframe,
                relatedInput: activeInput,
                fileNames: inputFiles(sourceInput)
            });
            iframe.data({
                form: form,
                file: fileEntry
            });
            return fileEntry;
        },
        performUpload: function(fileEntry) {
            var e = {
                files: fileEntry.data("fileNames")
            }, iframe = fileEntry.data("frame"), upload = this.upload;
            if (!upload.trigger(UPLOAD, e)) {
                upload._hideUploadButton();
                iframe.appendTo(document.body);
                var form = iframe.data("form").appendTo(document.body);
                e.data = $.extend({}, e.data, getAntiForgeryTokens());
                for (var key in e.data) {
                    var dataInput = form.find("input[name='" + key + "']");
                    if (dataInput.length === 0) {
                        dataInput = $("<input>", {
                            type: "hidden",
                            name: key
                        }).appendTo(form);
                    }
                    dataInput.val(e.data[key]);
                }
                upload._fileAction(fileEntry, CANCEL);
                upload._fileState(fileEntry, "uploading");
                iframe.one("load", $.proxy(this.onIframeLoad, this));
                form[0].submit();
            } else {
                upload._removeFileEntry(iframe.data("file"));
                this.cleanupFrame(iframe);
                this.unregisterFrame(iframe);
            }
        },
        onSaveSelected: function() {
            var module = this;
            $(".k-file", this.element).each(function() {
                var fileEntry = $(this), started = isFileUploadStarted(fileEntry);
                if (!started) {
                    module.performUpload(fileEntry);
                }
            });
        },
        onIframeLoad: function(e) {
            var iframe = $(e.target), responseText;
            try {
                responseText = iframe.contents().text();
            } catch (ex) {
                responseText = "Error trying to get server response: " + ex;
            }
            this.processResponse(iframe, responseText);
        },
        processResponse: function(iframe, responseText) {
            var fileEntry = iframe.data("file"), module = this, fakeXHR = {
                responseText: responseText
            };
            responseText = responseText || "null";
            tryParseJSON(responseText, function(jsonResult) {
                $.extend(fakeXHR, {
                    statusText: "OK",
                    status: "200"
                });
                fileEntry.trigger("t:progress", [ 100 ]);
                fileEntry.trigger("t:upload-success", [ jsonResult, fakeXHR ]);
                module.cleanupFrame(iframe);
                module.unregisterFrame(iframe);
            }, function() {
                $.extend(fakeXHR, {
                    statusText: "error",
                    status: "500"
                });
                fileEntry.trigger("t:upload-error", [ fakeXHR ]);
            });
        },
        onCancel: function(e) {
            var iframe = $(e.target).data("frame");
            this.stopFrameSubmit(iframe);
            this.cleanupFrame(iframe);
            this.unregisterFrame(iframe);
            this.upload._removeFileEntry(iframe.data("file"));
        },
        onRetry: function(e) {
            var fileEntry = getFileEntry(e);
            this.performUpload(fileEntry);
        },
        onRemove: function(e, data) {
            var fileEntry = getFileEntry(e);
            var iframe = fileEntry.data("frame");
            if (iframe) {
                this.unregisterFrame(iframe);
                this.upload._removeFileEntry(fileEntry);
                this.cleanupFrame(iframe);
            } else {
                removeUploadedFile(fileEntry, this.upload, data);
            }
        },
        onAbort: function() {
            var element = this.element, module = this;
            $.each(this.iframes, function() {
                $("input", this.data("form")).appendTo(element);
                module.stopFrameSubmit(this[0]);
                this.data("form").remove();
                this.remove();
            });
            this.iframes = [];
        },
        createFrame: function(id) {
            return $("<iframe" + " name='" + id + "'" + " id='" + id + "'" + " style='display:none;' />");
        },
        createForm: function(action, target) {
            return $("<form enctype='multipart/form-data' method='POST'" + " action='" + action + "'" + " target='" + target + "'" + "/>");
        },
        stopFrameSubmit: function(frame) {
            if (typeof frame.stop != "undefined") {
                frame.stop();
            } else if (frame.document) {
                frame.document.execCommand("Stop");
            }
        },
        registerFrame: function(frame) {
            this.iframes.push(frame);
        },
        unregisterFrame: function(frame) {
            this.iframes = $.grep(this.iframes, function(value) {
                return value.attr("name") != frame.attr("name");
            });
        },
        cleanupFrame: function(frame) {
            var form = frame.data("form");
            frame.data("file").data("frame", null);
            setTimeout(function() {
                form.remove();
                frame.remove();
            }, 1);
        }
    };
    // FormData upload module
    var formDataUploadModule = function(upload) {
        this.name = "formDataUploadModule";
        this.element = upload.wrapper;
        this.upload = upload;
        this.element.bind("t:select", $.proxy(this.onSelect, this)).bind("t:cancel", $.proxy(this.onCancel, this)).bind("t:remove", $.proxy(this.onRemove, this)).bind("t:retry", $.proxy(this.onRetry, this)).bind("t:saveSelected", $.proxy(this.onSaveSelected, this)).bind("t:abort", $.proxy(this.onAbort, this));
    };
    formDataUploadModule.prototype = {
        onSelect: function(e, rawFiles) {
            var upload = this.upload, module = this, sourceElement = $(e.target), files = rawFiles ? getAllFileInfo(rawFiles) : this.inputFiles(sourceElement), fileEntries = this.prepareUpload(sourceElement, files);
            $.each(fileEntries, function() {
                if (upload.options.async.autoUpload) {
                    module.performUpload(this);
                } else {
                    if (upload._supportsRemove()) {
                        upload._fileAction(this, REMOVE);
                    }
                    upload._showUploadButton();
                }
            });
        },
        prepareUpload: function(sourceElement, files) {
            var fileEntries = this.enqueueFiles(files);
            if (sourceElement.is("input")) {
                $.each(fileEntries, function() {
                    $(this).data("relatedInput", sourceElement);
                });
                sourceElement.data("relatedFileEntries", fileEntries);
                this.upload._addInput(sourceElement.clone().val(""));
            }
            return fileEntries;
        },
        enqueueFiles: function(files) {
            var upload = this.upload, name, i, filesLength = files.length, currentFile, fileEntry, fileEntries = [];
            if (upload.options.async.batch === true) {
                name = $.map(files, function(file) {
                    return file.name;
                }).join(", ");
                fileEntry = upload._enqueueFile(name, {
                    fileNames: files
                });
                fileEntry.data("files", files);
                fileEntries.push(fileEntry);
            } else {
                for (i = 0; i < filesLength; i++) {
                    currentFile = files[i];
                    name = currentFile.name;
                    fileEntry = upload._enqueueFile(name, {
                        fileNames: [ currentFile ]
                    });
                    fileEntry.data("files", [ currentFile ]);
                    fileEntries.push(fileEntry);
                }
            }
            return fileEntries;
        },
        inputFiles: function(sourceInput) {
            return inputFiles(sourceInput);
        },
        performUpload: function(fileEntry) {
            var upload = this.upload, formData = this.createFormData(fileEntry.data("files")), e = {
                files: fileEntry.data("fileNames")
            };
            if (!upload.trigger(UPLOAD, e)) {
                upload._fileAction(fileEntry, CANCEL);
                upload._hideUploadButton();
                e.data = $.extend({}, e.data, getAntiForgeryTokens());
                for (var key in e.data) {
                    formData.append(key, e.data[key]);
                }
                upload._fileState(fileEntry, "uploading");
                this.postFormData(this.upload.options.async.saveUrl, formData, fileEntry);
            } else {
                this.removeFileEntry(fileEntry);
            }
        },
        onSaveSelected: function() {
            var module = this;
            $(".k-file", this.element).each(function() {
                var fileEntry = $(this), started = isFileUploadStarted(fileEntry);
                if (!started) {
                    module.performUpload(fileEntry);
                }
            });
        },
        onCancel: function(e) {
            var fileEntry = getFileEntry(e);
            this.stopUploadRequest(fileEntry);
            this.removeFileEntry(fileEntry);
        },
        onRetry: function(e) {
            var fileEntry = getFileEntry(e);
            this.performUpload(fileEntry);
        },
        onRemove: function(e, data) {
            var fileEntry = getFileEntry(e);
            if (fileEntry.children(".k-icon").is(".k-success")) {
                removeUploadedFile(fileEntry, this.upload, data);
            } else {
                this.removeFileEntry(fileEntry);
            }
        },
        postFormData: function(url, data, fileEntry) {
            var xhr = new XMLHttpRequest(), module = this;
            fileEntry.data("request", xhr);
            xhr.addEventListener("load", function(e) {
                module.onRequestSuccess.call(module, e, fileEntry);
            }, false);
            xhr.addEventListener(ERROR, function(e) {
                module.onRequestError.call(module, e, fileEntry);
            }, false);
            xhr.upload.addEventListener("progress", function(e) {
                module.onRequestProgress.call(module, e, fileEntry);
            }, false);
            xhr.open("POST", url);
            xhr.withCredentials = "true";
            xhr.send(data);
        },
        createFormData: function(files) {
            var formData = new FormData(), upload = this.upload, i, length = files.length;
            for (i = 0; i < length; i++) {
                formData.append(upload.options.async.saveField || upload.name, files[i].rawFile);
            }
            return formData;
        },
        onRequestSuccess: function(e, fileEntry) {
            var xhr = e.target, module = this;
            function raiseError() {
                fileEntry.trigger("t:upload-error", [ xhr ]);
            }
            if (xhr.status >= 200 && xhr.status <= 299) {
                tryParseJSON(xhr.responseText, function(jsonResult) {
                    fileEntry.trigger("t:progress", [ 100 ]);
                    fileEntry.trigger("t:upload-success", [ jsonResult, xhr ]);
                    module.cleanupFileEntry(fileEntry);
                }, raiseError);
            } else {
                raiseError();
            }
        },
        onRequestError: function(e, fileEntry) {
            var xhr = e.target;
            fileEntry.trigger("t:upload-error", [ xhr ]);
        },
        cleanupFileEntry: function(fileEntry) {
            var relatedInput = fileEntry.data("relatedInput"), uploadComplete = true;
            if (relatedInput) {
                $.each(relatedInput.data("relatedFileEntries") || [], function() {
                    // Exclude removed file entries and self
                    if (this.parent().length > 0 && this[0] != fileEntry[0]) {
                        uploadComplete = uploadComplete && this.children(".k-icon").is(".k-success");
                    }
                });
                if (uploadComplete) {
                    relatedInput.remove();
                }
            }
        },
        removeFileEntry: function(fileEntry) {
            this.cleanupFileEntry(fileEntry);
            this.upload._removeFileEntry(fileEntry);
        },
        onRequestProgress: function(e, fileEntry) {
            var percentComplete = Math.round(e.loaded * 100 / e.total);
            fileEntry.trigger("t:progress", [ percentComplete ]);
        },
        stopUploadRequest: function(fileEntry) {
            fileEntry.data("request").abort();
        }
    };
    // Helper functions
    function getFileName(input) {
        return $.map(inputFiles(input), function(file) {
            return file.name;
        }).join(", ");
    }
    function inputFiles($input) {
        var input = $input[0];
        if (input.files) {
            return getAllFileInfo(input.files);
        } else {
            return [ {
                name: stripPath(input.value),
                extension: getFileExtension(input.value),
                size: null
            } ];
        }
    }
    function getAllFileInfo(rawFiles) {
        return $.map(rawFiles, function(file) {
            return getFileInfo(file);
        });
    }
    function getFileInfo(rawFile) {
        // Older Firefox versions (before 3.6) use fileName and fileSize
        var fileName = rawFile.name || rawFile.fileName;
        return {
            name: fileName,
            extension: getFileExtension(fileName),
            size: rawFile.size || rawFile.fileSize,
            rawFile: rawFile
        };
    }
    function getFileExtension(fileName) {
        var matches = fileName.match(rFileExtension);
        return matches ? matches[0] : "";
    }
    function stripPath(name) {
        var slashIndex = name.lastIndexOf("\\");
        return slashIndex != -1 ? name.substr(slashIndex + 1) : name;
    }
    function removeUploadedFile(fileEntry, upload, data) {
        if (!upload._supportsRemove()) {
            return;
        }
        var files = fileEntry.data("fileNames");
        var fileNames = $.map(files, function(file) {
            return file.name;
        });
        upload._submitRemove(fileNames, data, function onSuccess(data, textStatus, xhr) {
            upload._removeFileEntry(fileEntry);
            upload.trigger(SUCCESS, {
                operation: "remove",
                files: files,
                response: data,
                XMLHttpRequest: xhr
            });
        }, function onError(xhr) {
            upload.trigger(ERROR, {
                operation: "remove",
                files: files,
                XMLHttpRequest: xhr
            });
            logToConsole("Server response: " + xhr.responseText);
        });
    }
    function tryParseJSON(input, onSuccess, onError) {
        var success = false, json = "";
        try {
            json = $.parseJSON(input);
            success = true;
        } catch (e) {
            onError();
        }
        if (success) {
            onSuccess(json);
        }
    }
    function stopEvent(e) {
        e.stopPropagation();
        e.preventDefault();
    }
    function bindDragEventWrappers(element, namespace, onDragEnter, onDragLeave) {
        var hideInterval, lastDrag;
        element.on("dragenter" + namespace, function() {
            onDragEnter();
            lastDrag = new Date();
            if (!hideInterval) {
                hideInterval = setInterval(function() {
                    var sinceLastDrag = new Date() - lastDrag;
                    if (sinceLastDrag > 100) {
                        onDragLeave();
                        clearInterval(hideInterval);
                        hideInterval = null;
                    }
                }, 100);
            }
        }).on("dragover" + namespace, function() {
            lastDrag = new Date();
        });
    }
    function isFileUploadStarted(fileEntry) {
        return fileEntry.children(".k-icon").is(".k-loading, .k-success, .k-fail");
    }
    function getFileEntry(e) {
        return $(e.target).closest(".k-file");
    }
    function getAntiForgeryTokens() {
        var tokens = {}, csrf_token = $("meta[name=csrf-token]").attr("content"), csrf_param = $("meta[name=csrf-param]").attr("content");
        $("input[name^='__RequestVerificationToken']").each(function() {
            tokens[this.name] = this.value;
        });
        if (csrf_param !== undefined && csrf_token !== undefined) {
            tokens[csrf_param] = csrf_token;
        }
        return tokens;
    }
    kendo.ui.plugin(Upload);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, Widget = kendo.ui.Widget, Draggable = kendo.ui.Draggable, isPlainObject = $.isPlainObject, activeElement = kendo._activeElement, proxy = $.proxy, extend = $.extend, each = $.each, template = kendo.template, BODY = "body", templates, NS = ".kendoWindow", // classNames
    KWINDOW = ".k-window", KWINDOWTITLE = ".k-window-title", KWINDOWTITLEBAR = KWINDOWTITLE + "bar", KWINDOWCONTENT = ".k-window-content", KWINDOWRESIZEHANDLES = ".k-resize-handle", KOVERLAY = ".k-overlay", KCONTENTFRAME = "k-content-frame", LOADING = "k-loading", KHOVERSTATE = "k-state-hover", KFOCUSEDSTATE = "k-state-focused", // constants
    VISIBLE = ":visible", HIDDEN = "hidden", CURSOR = "cursor", // events
    OPEN = "open", ACTIVATE = "activate", DEACTIVATE = "deactivate", CLOSE = "close", REFRESH = "refresh", RESIZE = "resize", DRAGSTART = "dragstart", DRAGEND = "dragend", ERROR = "error", OVERFLOW = "overflow", ZINDEX = "zIndex", MINIMIZE_MAXIMIZE = ".k-window-actions .k-i-minimize,.k-window-actions .k-i-maximize", TITLEBAR_BUTTONS = ".k-window-titlebar .k-window-action", isLocalUrl = kendo.isLocalUrl;
    function defined(x) {
        return typeof x != "undefined";
    }
    function constrain(value, low, high) {
        return Math.max(Math.min(value, high), low);
    }
    function windowObject(element, name) {
        var contentElement = element.children(KWINDOWCONTENT);
        return contentElement.data("kendoWindow") || contentElement.data("kendo" + name);
    }
    function openedModalWindows(name) {
        return $(KWINDOW).filter(function() {
            var wnd = $(this), winObj = windowObject(wnd, name);
            return winObj.options.modal && wnd.is(VISIBLE) && winObj.options.visible;
        }).sort(function(a, b) {
            return +$(a).css("zIndex") - +$(b).css("zIndex");
        });
    }
    function sizingAction(actionId, callback) {
        return function() {
            var that = this, wrapper = that.wrapper, style = wrapper[0].style, options = that.options;
            if (options.isMaximized || options.isMinimized) {
                return;
            }
            that.restoreOptions = {
                width: style.width,
                height: style.height
            };
            wrapper.find(KWINDOWRESIZEHANDLES).hide().end().find(MINIMIZE_MAXIMIZE).parent().hide().eq(0).before(templates.action({
                name: "Restore"
            }));
            callback.call(that);
            return that;
        };
    }
    var Window = Widget.extend({
        init: function(element, options) {
            var that = this, wrapper, offset, visibility, display, isVisible = false, content, windowContent, id;
            Widget.fn.init.call(that, element, options);
            options = that.options;
            element = that.element;
            content = options.content;
            that.appendTo = $(options.appendTo || document.body);
            that._animations();
            if (content && !isPlainObject(content)) {
                content = options.content = {
                    url: content
                };
            }
            // remove script blocks to prevent double-execution
            element.find("script").filter(function() {
                return !this.type || this.type.toLowerCase().indexOf("script") >= 0;
            }).remove();
            if (!element.parent().is(that.appendTo)) {
                if (element.is(VISIBLE)) {
                    offset = element.offset();
                    isVisible = true;
                } else {
                    visibility = element.css("visibility");
                    display = element.css("display");
                    element.css({
                        visibility: HIDDEN,
                        display: ""
                    });
                    offset = element.offset();
                    element.css({
                        visibility: visibility,
                        display: display
                    });
                }
            }
            if (!defined(options.visible) || options.visible === null) {
                options.visible = element.is(VISIBLE);
            }
            wrapper = that.wrapper = element.closest(KWINDOW);
            if (!element.is(".k-content") || !wrapper[0]) {
                element.addClass("k-window-content k-content");
                that._createWindow(element, options);
                wrapper = that.wrapper = element.closest(KWINDOW);
                that._dimensions();
            }
            if (offset) {
                wrapper.css({
                    top: offset.top,
                    left: offset.left
                });
            }
            if (content) {
                that.refresh(content);
            }
            if (options.visible) {
                that.toFront();
            }
            windowContent = wrapper.children(KWINDOWCONTENT);
            that._tabindex(windowContent);
            if (options.visible && options.modal) {
                that._overlay(wrapper.is(VISIBLE)).css({
                    opacity: .5
                });
            }
            wrapper.on("mouseenter" + NS, TITLEBAR_BUTTONS, function() {
                $(this).addClass(KHOVERSTATE);
            }).on("mouseleave" + NS, TITLEBAR_BUTTONS, function() {
                $(this).removeClass(KHOVERSTATE);
            }).on("click" + NS, TITLEBAR_BUTTONS, proxy(that._windowActionHandler, that));
            windowContent.on("keydown" + NS, proxy(that._keydown, that)).on("focus" + NS, function() {
                wrapper.addClass(KFOCUSEDSTATE);
            }).on("blur" + NS, function() {
                wrapper.removeClass(KFOCUSEDSTATE);
            });
            if (options.resizable) {
                wrapper.on("dblclick" + NS, KWINDOWTITLEBAR, function(e) {
                    if (!$(e.target).closest(".k-window-action").length) {
                        that.toggleMaximization();
                    }
                    return this;
                });
                each("n e s w se sw ne nw".split(" "), function(index, handler) {
                    wrapper.append(templates.resizeHandle(handler));
                });
                that.resizing = new WindowResizing(that);
            }
            if (options.draggable) {
                that.dragging = new WindowDragging(that);
            }
            id = element.attr("id");
            if (id) {
                id = id + "_wnd_title";
                wrapper.find(KWINDOWTITLEBAR).children(KWINDOWTITLE).attr("id", id);
                windowContent.attr({
                    role: "dialog",
                    "aria-labelledby": id
                });
            }
            wrapper.add(wrapper.find(".k-resize-handle,.k-window-titlebar")).on("mousedown" + NS, proxy(that.toFront, that));
            that.touchScroller = kendo.touchScroller(element);
            that._resizeHandler = function(e) {
                return that._onDocumentResize(e);
            };
            $(window).on("resize", that._resizeHandler);
            if (options.visible) {
                that.trigger(OPEN);
                that.trigger(ACTIVATE);
            }
            kendo.notify(that);
        },
        _dimensions: function() {
            var that = this, wrapper = that.wrapper, element = that.element, options = that.options;
            that.title(options.title);
            each([ "minWidth", "minHeight", "maxWidth", "maxHeight" ], function(_, prop) {
                var value = options[prop];
                if (value && value != Infinity) {
                    element.css(prop, value);
                }
            });
            if (options.width) {
                wrapper.width(constrain(parseInt(options.width, 10), options.minWidth, options.maxWidth));
            }
            if (options.height) {
                wrapper.height(constrain(parseInt(options.height, 10), options.minHeight, options.maxHeight));
            }
            if (!options.visible) {
                wrapper.hide();
            }
        },
        _animations: function() {
            var options = this.options;
            if (options.animation === false) {
                options.animation = {
                    open: {
                        effects: {}
                    },
                    close: {
                        hide: true,
                        effects: {}
                    }
                };
            }
        },
        setOptions: function(options) {
            Widget.fn.setOptions.call(this, options);
            this._animations();
            this._dimensions();
        },
        events: [ OPEN, ACTIVATE, DEACTIVATE, CLOSE, REFRESH, RESIZE, DRAGSTART, DRAGEND, ERROR ],
        options: {
            name: "Window",
            animation: {
                open: {
                    effects: {
                        zoom: {
                            direction: "in"
                        },
                        fade: {
                            direction: "in"
                        }
                    },
                    duration: 350
                },
                close: {
                    effects: {
                        zoom: {
                            direction: "out",
                            properties: {
                                scale: .7
                            }
                        },
                        fade: {
                            direction: "out"
                        }
                    },
                    duration: 350,
                    hide: true
                }
            },
            title: "",
            actions: [ "Close" ],
            modal: false,
            resizable: true,
            draggable: true,
            minWidth: 90,
            minHeight: 50,
            maxWidth: Infinity,
            maxHeight: Infinity,
            visible: null,
            height: null,
            width: null
        },
        _closable: function() {
            return $.inArray("close", $.map(this.options.actions, function(x) {
                return x.toLowerCase();
            })) > -1;
        },
        _keydown: function(e) {
            var that = this, options = that.options, keys = kendo.keys, keyCode = e.keyCode, wrapper = that.wrapper, offset, handled, distance = 10, isMaximized = that.options.isMaximized, newWidth, newHeight;
            if (e.target != e.currentTarget) {
                return;
            }
            if (keyCode == keys.ESC && that._closable()) {
                that._close(true);
            }
            if (options.draggable && !e.ctrlKey && !isMaximized) {
                offset = kendo.getOffset(wrapper);
                if (keyCode == keys.UP) {
                    handled = wrapper.css("top", offset.top - distance);
                } else if (keyCode == keys.DOWN) {
                    handled = wrapper.css("top", offset.top + distance);
                } else if (keyCode == keys.LEFT) {
                    handled = wrapper.css("left", offset.left - distance);
                } else if (keyCode == keys.RIGHT) {
                    handled = wrapper.css("left", offset.left + distance);
                }
            }
            if (options.resizable && e.ctrlKey && !isMaximized) {
                if (keyCode == keys.UP) {
                    handled = true;
                    newHeight = wrapper.height() - distance;
                } else if (keyCode == keys.DOWN) {
                    handled = true;
                    newHeight = wrapper.height() + distance;
                }
                if (keyCode == keys.LEFT) {
                    handled = true;
                    newWidth = wrapper.width() - distance;
                } else if (keyCode == keys.RIGHT) {
                    handled = true;
                    newWidth = wrapper.width() + distance;
                }
                if (handled) {
                    wrapper.css({
                        width: constrain(newWidth, options.minWidth, options.maxWidth),
                        height: constrain(newHeight, options.minHeight, options.maxHeight)
                    });
                    that.trigger(RESIZE);
                }
            }
            if (handled) {
                e.preventDefault();
            }
        },
        _overlay: function(visible) {
            var overlay = this.appendTo.children(".k-overlay"), wrapper = this.wrapper;
            if (!overlay.length) {
                overlay = $("<div class='k-overlay' />");
            }
            overlay.insertBefore(wrapper[0]).toggle(visible).css(ZINDEX, parseInt(wrapper.css(ZINDEX), 10) - 1);
            return overlay;
        },
        _windowActionHandler: function(e) {
            var target = $(e.target).closest(".k-window-action").find(".k-icon"), that = this;
            if (that._closing) {
                return;
            }
            each({
                "k-i-close": function() {
                    that._close(true);
                },
                "k-i-maximize": that.maximize,
                "k-i-minimize": that.minimize,
                "k-i-restore": that.restore,
                "k-i-refresh": that.refresh
            }, function(commandName, handler) {
                if (target.hasClass(commandName)) {
                    e.preventDefault();
                    handler.call(that);
                    return false;
                }
            });
        },
        center: function() {
            var wrapper = this.wrapper, documentWindow = $(window);
            wrapper.css({
                left: documentWindow.scrollLeft() + Math.max(0, (documentWindow.width() - wrapper.width()) / 2),
                top: documentWindow.scrollTop() + Math.max(0, (documentWindow.height() - wrapper.height()) / 2)
            });
            return this;
        },
        title: function(text) {
            var that = this, wrapper = that.wrapper, options = that.options, titleBar = wrapper.find(KWINDOWTITLEBAR), title = titleBar.children(KWINDOWTITLE), titleBarHeight = titleBar.outerHeight();
            if (!arguments.length) {
                return title.text();
            }
            if (text === false) {
                wrapper.addClass("k-window-titleless");
                titleBar.remove();
            } else {
                if (!titleBar.length) {
                    wrapper.prepend(templates.titlebar(extend(templates, options)));
                }
                wrapper.css("padding-top", titleBarHeight);
                titleBar.css("margin-top", -titleBarHeight);
            }
            title.text(text);
            return that;
        },
        content: function(html) {
            var content = this.wrapper.children(KWINDOWCONTENT), scrollContainer = content.children(".km-scroll-container");
            content = scrollContainer[0] ? scrollContainer : content;
            if (!html) {
                return content.html();
            }
            content.html(html);
            return this;
        },
        open: function() {
            var that = this, wrapper = that.wrapper, options = that.options, showOptions = options.animation.open, contentElement = wrapper.children(KWINDOWCONTENT), initialOverflow = contentElement.css(OVERFLOW), overlay;
            if (!that.trigger(OPEN)) {
                that._closing = false;
                that.toFront();
                that.element.focus();
                options.visible = true;
                if (options.modal) {
                    overlay = that._overlay(false);
                    if (showOptions.duration) {
                        overlay.kendoStop().kendoAnimate({
                            effects: "fade:in",
                            duration: showOptions.duration
                        });
                    } else {
                        overlay.css("opacity", .5).show();
                    }
                }
                if (!wrapper.is(VISIBLE)) {
                    contentElement.css(OVERFLOW, HIDDEN);
                    wrapper.show().kendoStop().kendoAnimate({
                        effects: showOptions.effects,
                        duration: showOptions.duration,
                        complete: function() {
                            that.element.focus();
                            that.trigger(ACTIVATE);
                            contentElement.css(OVERFLOW, initialOverflow);
                        }
                    });
                }
            }
            if (options.isMaximized) {
                that._documentScrollTop = $(document).scrollTop();
                $("html, body").css(OVERFLOW, HIDDEN);
            }
            return that;
        },
        _close: function(userTriggered) {
            var that = this, wrapper = that.wrapper, options = that.options, showOptions = options.animation.open, hideOptions = options.animation.close, modalWindows, shouldHideOverlay, overlay;
            if (wrapper.is(VISIBLE) && !that.trigger(CLOSE, {
                userTriggered: !!userTriggered
            })) {
                that._closing = true;
                options.visible = false;
                $(KWINDOW).each(function(i, element) {
                    var windowObject = $(element), contentElement = windowObject.find(KWINDOWCONTENT);
                    // Remove overlay set by toFront
                    if (element != wrapper && contentElement.find("> ." + KCONTENTFRAME).length > 0) {
                        contentElement.children(".k-overlay").remove();
                    }
                });
                modalWindows = openedModalWindows(options.name);
                shouldHideOverlay = options.modal && !modalWindows.length;
                overlay = options.modal ? that._overlay(true) : $(undefined);
                if (shouldHideOverlay) {
                    if (hideOptions.duration) {
                        overlay.kendoStop().kendoAnimate({
                            effects: "fade:out",
                            duration: hideOptions.duration,
                            hide: true
                        });
                    } else {
                        overlay.hide();
                    }
                } else if (modalWindows.length) {
                    windowObject(modalWindows.eq(modalWindows.length - 1), options.name)._overlay(true);
                }
                wrapper.kendoStop().kendoAnimate({
                    effects: hideOptions.effects || showOptions.effects,
                    reverse: hideOptions.reverse === true,
                    duration: hideOptions.duration,
                    complete: function() {
                        wrapper.hide();
                        that.trigger(DEACTIVATE);
                    }
                });
            }
            if (that.options.isMaximized) {
                $("html, body").css(OVERFLOW, "");
                if (that._documentScrollTop && that._documentScrollTop > 0) {
                    $(document).scrollTop(that._documentScrollTop);
                }
            }
        },
        close: function() {
            this._close(false);
            return this;
        },
        toFront: function(e) {
            var that = this, wrapper = that.wrapper, currentWindow = wrapper[0], zIndex = +wrapper.css(ZINDEX), originalZIndex = zIndex, active = activeElement(), winElement = that.element, target = e && e.target ? e.target : null;
            $(KWINDOW).each(function(i, element) {
                var windowObject = $(element), zIndexNew = windowObject.css(ZINDEX), contentElement = windowObject.find(KWINDOWCONTENT);
                if (!isNaN(zIndexNew)) {
                    zIndex = Math.max(+zIndexNew, zIndex);
                }
                // Add overlay to windows with iframes and lower z-index to prevent
                // trapping of events when resizing / dragging
                if (element != currentWindow && contentElement.find("> ." + KCONTENTFRAME).length > 0) {
                    contentElement.append(templates.overlay);
                }
            });
            if (zIndex == 10001 || originalZIndex < zIndex) {
                wrapper.css(ZINDEX, zIndex + 2);
            }
            that.element.find("> .k-overlay").remove();
            if (!$(active).is(winElement) && !$(target).is(TITLEBAR_BUTTONS + "," + TITLEBAR_BUTTONS + " .k-icon,:input") && (!winElement.find(active).length || !winElement.find(target).length)) {
                winElement.focus();
                var scrollTop = $(window).scrollTop(), windowTop = parseInt(that.wrapper.position().top, 10);
                if (windowTop > 0 && windowTop - scrollTop < 0) {
                    if (scrollTop > 0) {
                        $(window).scrollTop(windowTop);
                    } else {
                        that.wrapper.css("top", scrollTop);
                    }
                }
            }
            return that;
        },
        toggleMaximization: function() {
            if (this._closing) {
                return this;
            }
            return this[this.options.isMaximized ? "restore" : "maximize"]();
        },
        restore: function() {
            var that = this, options = that.options, restoreOptions = that.restoreOptions;
            if (!options.isMaximized && !options.isMinimized) {
                return;
            }
            that.wrapper.css({
                position: "absolute",
                left: restoreOptions.left,
                top: restoreOptions.top,
                width: restoreOptions.width,
                height: restoreOptions.height
            }).find(".k-window-content,.k-resize-handle").show().end().find(".k-window-titlebar .k-i-restore").parent().remove().end().end().find(MINIMIZE_MAXIMIZE).parent().show();
            $("html, body").css(OVERFLOW, "");
            if (this._documentScrollTop && this._documentScrollTop > 0) {
                $(document).scrollTop(this._documentScrollTop);
            }
            options.isMaximized = options.isMinimized = false;
            that.trigger(RESIZE);
            return that;
        },
        maximize: sizingAction("maximize", function() {
            var that = this, wrapper = that.wrapper, position = wrapper.position();
            extend(that.restoreOptions, {
                left: position.left,
                top: position.top
            });
            wrapper.css({
                left: 0,
                top: 0,
                position: "fixed"
            });
            this._documentScrollTop = $(document).scrollTop();
            $("html, body").css(OVERFLOW, HIDDEN);
            that.options.isMaximized = true;
            that._onDocumentResize();
        }),
        minimize: sizingAction("minimize", function() {
            var that = this;
            that.wrapper.css("height", "");
            that.element.hide();
            that.options.isMinimized = true;
        }),
        _onDocumentResize: function() {
            var that = this, wrapper = that.wrapper, wnd = $(window);
            if (!that.options.isMaximized) {
                return;
            }
            wrapper.css({
                width: wnd.width(),
                height: wnd.height() - parseInt(wrapper.css("padding-top"), 10)
            });
            that.trigger(RESIZE);
        },
        refresh: function(options) {
            var that = this, initOptions = that.options, element = $(that.element), iframe, showIframe, url;
            if (!isPlainObject(options)) {
                options = {
                    url: options
                };
            }
            options = extend({}, initOptions.content, options);
            showIframe = defined(initOptions.iframe) ? initOptions.iframe : options.iframe;
            url = options.url;
            if (url) {
                if (!defined(showIframe)) {
                    showIframe = !isLocalUrl(url);
                }
                if (!showIframe) {
                    // perform AJAX request
                    that._ajaxRequest(options);
                } else {
                    iframe = element.find("." + KCONTENTFRAME)[0];
                    if (iframe) {
                        // refresh existing iframe
                        iframe.src = url || iframe.src;
                    } else {
                        // render new iframe
                        element.html(templates.contentFrame(extend({}, initOptions, {
                            content: options
                        })));
                    }
                    element.find("." + KCONTENTFRAME).unbind("load" + NS).on("load" + NS, function() {
                        that.trigger(REFRESH);
                    });
                }
            } else {
                if (options.template) {
                    // refresh template
                    that.content(template(options.template)({}));
                }
                that.trigger(REFRESH);
            }
            return that;
        },
        _ajaxRequest: function(options) {
            var that = this, contentTemplate = options.template, refreshIcon = that.wrapper.find(".k-window-titlebar .k-i-refresh"), loadingIconTimeout = setTimeout(function() {
                refreshIcon.addClass(LOADING);
            }, 100);
            $.ajax(extend({
                type: "GET",
                dataType: "html",
                cache: false,
                error: proxy(function(xhr, status) {
                    that.trigger(ERROR, {
                        status: status,
                        xhr: xhr
                    });
                }, that),
                complete: function() {
                    clearTimeout(loadingIconTimeout);
                    refreshIcon.removeClass(LOADING);
                },
                success: proxy(function(data) {
                    if (contentTemplate) {
                        data = template(contentTemplate)(data || {});
                    }
                    that.content(data);
                    that.element.prop("scrollTop", 0);
                    that.trigger(REFRESH);
                }, that)
            }, options));
        },
        destroy: function() {
            var that = this, modalWindows, shouldHideOverlay;
            Widget.fn.destroy.call(that);
            kendo.destroy(that.wrapper);
            if (that.resizing) {
                that.resizing.destroy();
            }
            if (that.dragging) {
                that.dragging.destroy();
            }
            that.element.children("iframe").remove();
            that.wrapper.remove().add(that.wrapper.find(".k-resize-handle,.k-window-titlebar")).off(NS);
            $(window).off("resize", that._resizeHandler);
            modalWindows = openedModalWindows();
            shouldHideOverlay = that.options.modal && !modalWindows.length;
            if (shouldHideOverlay) {
                that._overlay(false).remove();
            } else if (modalWindows.length > 0) {
                windowObject(modalWindows.eq(modalWindows.length - 1), that.options.name)._overlay(true);
            }
        },
        _createWindow: function() {
            var that = this, contentHtml = that.element, options = that.options, iframeSrcAttributes, wrapper, isRtl = kendo.support.isRtl(contentHtml);
            if (options.scrollable === false) {
                contentHtml.attr("style", "overflow:hidden;");
            }
            wrapper = $(templates.wrapper(options));
            if (options.title !== false) {
                wrapper.append(templates.titlebar(extend(templates, options)));
            }
            // Collect the src attributes of all iframes and then set them to empty string.
            // This seems to fix this IE9 "feature": http://msdn.microsoft.com/en-us/library/gg622929%28v=VS.85%29.aspx?ppud=4
            iframeSrcAttributes = contentHtml.find("iframe:not(.k-content)").map(function() {
                var src = this.getAttribute("src");
                this.src = "";
                return src;
            });
            // Make sure the wrapper is appended to the body only once. IE9+ will throw exceptions if you move iframes in DOM
            wrapper.toggleClass("k-rtl", isRtl).appendTo(that.appendTo).append(contentHtml).find("iframe:not(.k-content)").each(function(index) {
                // Restore the src attribute of the iframes when they are part of the live DOM tree
                this.src = iframeSrcAttributes[index];
            });
            wrapper.find(".k-window-title").css(isRtl ? "left" : "right", wrapper.find(".k-window-actions").outerWidth() + 10);
            contentHtml.show();
        }
    });
    templates = {
        wrapper: template("<div class='k-widget k-window' />"),
        action: template("<a role='button' href='\\#' class='k-window-action k-link'>" + "<span role='presentation' class='k-icon k-i-#= name.toLowerCase() #'>#= name #</span>" + "</a>"),
        titlebar: template("<div class='k-window-titlebar k-header'>&nbsp;" + "<span class='k-window-title'>#= title #</span>" + "<div class='k-window-actions'>" + "# for (var i = 0; i < actions.length; i++) { #" + "#= action({ name: actions[i] }) #" + "# } #" + "</div>" + "</div>"),
        overlay: "<div class='k-overlay' />",
        contentFrame: template("<iframe frameborder='0' title='#= title #' class='" + KCONTENTFRAME + "' " + "src='#= content.url #'>" + "This page requires frames in order to show content" + "</iframe>"),
        resizeHandle: template("<div class='k-resize-handle k-resize-#= data #'></div>")
    };
    function WindowResizing(wnd) {
        var that = this;
        that.owner = wnd;
        that._draggable = new Draggable(wnd.wrapper, {
            filter: KWINDOWRESIZEHANDLES,
            group: wnd.wrapper.id + "-resizing",
            dragstart: proxy(that.dragstart, that),
            drag: proxy(that.drag, that),
            dragend: proxy(that.dragend, that)
        });
    }
    WindowResizing.prototype = {
        dragstart: function(e) {
            var that = this, wnd = that.owner, wrapper = wnd.wrapper;
            that.elementPadding = parseInt(wnd.wrapper.css("padding-top"), 10);
            that.initialCursorPosition = kendo.getOffset(wrapper, "position");
            that.resizeDirection = e.currentTarget.prop("className").replace("k-resize-handle k-resize-", "");
            that.initialSize = {
                width: wrapper.width(),
                height: wrapper.height()
            };
            that.containerOffset = kendo.getOffset(wnd.appendTo);
            wrapper.append(templates.overlay).find(KWINDOWRESIZEHANDLES).not(e.currentTarget).hide();
            $(BODY).css(CURSOR, e.currentTarget.css(CURSOR));
        },
        drag: function(e) {
            var that = this, wnd = that.owner, wrapper = wnd.wrapper, options = wnd.options, direction = that.resizeDirection, containerOffset = that.containerOffset, initialPosition = that.initialCursorPosition, initialSize = that.initialSize, newWidth, newHeight, windowBottom, windowRight, x = e.x.location, y = e.y.location;
            if (direction.indexOf("e") >= 0) {
                newWidth = x - initialPosition.left;
                wrapper.width(constrain(newWidth, options.minWidth, options.maxWidth));
            } else if (direction.indexOf("w") >= 0) {
                windowRight = initialPosition.left + initialSize.width;
                newWidth = constrain(windowRight - x, options.minWidth, options.maxWidth);
                wrapper.css({
                    left: windowRight - newWidth - containerOffset.left,
                    width: newWidth
                });
            }
            if (direction.indexOf("s") >= 0) {
                newHeight = y - initialPosition.top - that.elementPadding;
                wrapper.height(constrain(newHeight, options.minHeight, options.maxHeight));
            } else if (direction.indexOf("n") >= 0) {
                windowBottom = initialPosition.top + initialSize.height;
                newHeight = constrain(windowBottom - y, options.minHeight, options.maxHeight);
                wrapper.css({
                    top: windowBottom - newHeight - containerOffset.top,
                    height: newHeight
                });
            }
            wnd.trigger(RESIZE);
        },
        dragend: function(e) {
            var that = this, wnd = that.owner, wrapper = wnd.wrapper;
            wrapper.find(KOVERLAY).remove().end().find(KWINDOWRESIZEHANDLES).not(e.currentTarget).show();
            $(BODY).css(CURSOR, "");
            if (wnd.touchScroller) {
                wnd.touchScroller.reset();
            }
            if (e.keyCode == 27) {
                wrapper.css(that.initialCursorPosition).css(that.initialSize);
            }
            return false;
        },
        destroy: function() {
            this._draggable.destroy();
        }
    };
    function WindowDragging(wnd) {
        var that = this;
        that.owner = wnd;
        that._draggable = new Draggable(wnd.wrapper, {
            filter: KWINDOWTITLEBAR,
            group: wnd.wrapper.id + "-moving",
            dragstart: proxy(that.dragstart, that),
            drag: proxy(that.drag, that),
            dragend: proxy(that.dragend, that),
            dragcancel: proxy(that.dragcancel, that)
        });
    }
    WindowDragging.prototype = {
        dragstart: function(e) {
            var wnd = this.owner, element = wnd.element, actions = element.find(".k-window-actions"), containerOffset = kendo.getOffset(wnd.appendTo);
            wnd.trigger(DRAGSTART);
            wnd.initialWindowPosition = kendo.getOffset(wnd.wrapper, "position");
            wnd.startPosition = {
                left: e.x.client - wnd.initialWindowPosition.left,
                top: e.y.client - wnd.initialWindowPosition.top
            };
            if (actions.length > 0) {
                wnd.minLeftPosition = actions.outerWidth() + parseInt(actions.css("right"), 10) - element.outerWidth();
            } else {
                wnd.minLeftPosition = 20 - element.outerWidth();
            }
            wnd.minLeftPosition -= containerOffset.left;
            wnd.minTopPosition = -containerOffset.top;
            wnd.wrapper.append(templates.overlay).find(KWINDOWRESIZEHANDLES).hide();
            $(BODY).css(CURSOR, e.currentTarget.css(CURSOR));
        },
        drag: function(e) {
            var wnd = this.owner, coordinates = {
                left: Math.max(e.x.client - wnd.startPosition.left, wnd.minLeftPosition),
                top: Math.max(e.y.client - wnd.startPosition.top, wnd.minTopPosition)
            };
            $(wnd.wrapper).css(coordinates);
        },
        _finishDrag: function() {
            var wnd = this.owner;
            wnd.wrapper.find(KWINDOWRESIZEHANDLES).toggle(!wnd.options.isMinimized).end().find(KOVERLAY).remove();
            $(BODY).css(CURSOR, "");
        },
        dragcancel: function(e) {
            this._finishDrag();
            e.currentTarget.closest(KWINDOW).css(this.owner.initialWindowPosition);
        },
        dragend: function() {
            this._finishDrag();
            this.owner.trigger(DRAGEND);
            return false;
        },
        destroy: function() {
            this._draggable.destroy();
        }
    };
    kendo.ui.plugin(Window);
})(window.kendo.jQuery);

(function($, undefined) {
    // Imports ================================================================
    var doc = document, kendo = window.kendo, dataviz = kendo.dataviz = {}, Class = kendo.Class, template = kendo.template, map = $.map, noop = $.noop, indexOf = $.inArray, math = Math, deepExtend = kendo.deepExtend;
    var renderTemplate = function(definition) {
        return template(definition, {
            useWithBlock: false,
            paramName: "d"
        });
    };
    var CSS_PREFIX = "k-";
    // Constants ==============================================================
    var ANIMATION_STEP = 10, AXIS_LABEL_CLICK = "axisLabelClick", BASELINE_MARKER_SIZE = 1, BLACK = "#000", BOTTOM = "bottom", CENTER = "center", COORD_PRECISION = 3, CLIP = "clip", DEFAULT_FONT = "12px sans-serif", DEFAULT_HEIGHT = 400, DEFAULT_PRECISION = 6, DEFAULT_WIDTH = 600, DEGREE = math.PI / 180, FADEIN = "fadeIn", FORMAT_REGEX = /\{\d+:?/, HEIGHT = "height", ID_PREFIX = "k", ID_POOL_SIZE = 1e3, ID_START = 1e4, INITIAL_ANIMATION_DURATION = 600, LEFT = "left", LINEAR = "linear", MAX_VALUE = Number.MAX_VALUE, MIN_VALUE = -Number.MAX_VALUE, NONE = "none", OUTSIDE = "outside", RADIAL = "radial", RIGHT = "right", SWING = "swing", TOP = "top", UNDEFINED = "undefined", UPPERCASE_REGEX = /([A-Z])/g, WIDTH = "width", WHITE = "#fff", X = "x", Y = "y", ZERO_THRESHOLD = .2;
    function getSpacing(value) {
        var spacing = {
            top: 0,
            right: 0,
            bottom: 0,
            left: 0
        };
        if (typeof value === "number") {
            spacing[TOP] = spacing[RIGHT] = spacing[BOTTOM] = spacing[LEFT] = value;
        } else {
            spacing[TOP] = value[TOP] || 0;
            spacing[RIGHT] = value[RIGHT] || 0;
            spacing[BOTTOM] = value[BOTTOM] || 0;
            spacing[LEFT] = value[LEFT] || 0;
        }
        return spacing;
    }
    // Geometric primitives ===================================================
    var Point2D = function(x, y) {
        var point = this;
        if (!(point instanceof Point2D)) {
            return new Point2D(x, y);
        }
        point.x = round(x || 0, COORD_PRECISION);
        point.y = round(y || 0, COORD_PRECISION);
    };
    Point2D.fn = Point2D.prototype = {
        clone: function() {
            var point = this;
            return new Point2D(point.x, point.y);
        }
    };
    var Box2D = function(x1, y1, x2, y2) {
        var box = this;
        if (!(box instanceof Box2D)) {
            return new Box2D(x1, y1, x2, y2);
        }
        box.x1 = x1 || 0;
        box.x2 = x2 || 0;
        box.y1 = y1 || 0;
        box.y2 = y2 || 0;
    };
    Box2D.fn = Box2D.prototype = {
        width: function() {
            return this.x2 - this.x1;
        },
        height: function() {
            return this.y2 - this.y1;
        },
        translate: function(dx, dy) {
            var box = this;
            box.x1 += dx;
            box.x2 += dx;
            box.y1 += dy;
            box.y2 += dy;
            return box;
        },
        move: function(x, y) {
            var box = this, height = box.height(), width = box.width();
            box.x1 = x;
            box.y1 = y;
            box.x2 = box.x1 + width;
            box.y2 = box.y1 + height;
            return box;
        },
        wrap: function(targetBox) {
            var box = this;
            box.x1 = math.min(box.x1, targetBox.x1);
            box.y1 = math.min(box.y1, targetBox.y1);
            box.x2 = math.max(box.x2, targetBox.x2);
            box.y2 = math.max(box.y2, targetBox.y2);
            return box;
        },
        wrapPoint: function(point) {
            this.wrap(new Box2D(point.x, point.y, point.x, point.y));
            return this;
        },
        snapTo: function(targetBox, axis) {
            var box = this;
            if (axis == X || !axis) {
                box.x1 = targetBox.x1;
                box.x2 = targetBox.x2;
            }
            if (axis == Y || !axis) {
                box.y1 = targetBox.y1;
                box.y2 = targetBox.y2;
            }
            return box;
        },
        alignTo: function(targetBox, anchor) {
            var box = this, height = box.height(), width = box.width(), axis = anchor == TOP || anchor == BOTTOM ? Y : X, offset = axis == Y ? height : width;
            if (anchor === CENTER) {
                var targetCenter = targetBox.center();
                var center = box.center();
                box.x1 += targetCenter.x - center.x;
                box.y1 += targetCenter.y - center.y;
            } else if (anchor === TOP || anchor === LEFT) {
                box[axis + 1] = targetBox[axis + 1] - offset;
            } else {
                box[axis + 1] = targetBox[axis + 2];
            }
            box.x2 = box.x1 + width;
            box.y2 = box.y1 + height;
            return box;
        },
        shrink: function(dw, dh) {
            var box = this;
            box.x2 -= dw;
            box.y2 -= dh;
            return box;
        },
        expand: function(dw, dh) {
            this.shrink(-dw, -dh);
            return this;
        },
        pad: function(padding) {
            var box = this, spacing = getSpacing(padding);
            box.x1 -= spacing.left;
            box.x2 += spacing.right;
            box.y1 -= spacing.top;
            box.y2 += spacing.bottom;
            return box;
        },
        unpad: function(padding) {
            var box = this, spacing = getSpacing(padding);
            spacing.left = -spacing.left;
            spacing.top = -spacing.top;
            spacing.right = -spacing.right;
            spacing.bottom = -spacing.bottom;
            return box.pad(spacing);
        },
        clone: function() {
            var box = this;
            return new Box2D(box.x1, box.y1, box.x2, box.y2);
        },
        center: function() {
            var box = this;
            return {
                x: box.x1 + box.width() / 2,
                y: box.y1 + box.height() / 2
            };
        },
        containsPoint: function(point) {
            var box = this;
            return point.x >= box.x1 && point.x <= box.x2 && point.y >= box.y1 && point.y <= box.y2;
        },
        points: function() {
            var box = this;
            return [ new Point2D(box.x1, box.y1), new Point2D(box.x2, box.y1), new Point2D(box.x2, box.y2), new Point2D(box.x1, box.y2) ];
        },
        getHash: function() {
            var box = this;
            return [ box.x1, box.y1, box.x2, box.y2 ].join(",");
        }
    };
    var Ring = Class.extend({
        init: function(center, innerRadius, radius, startAngle, angle) {
            var ring = this;
            ring.c = center;
            ring.ir = innerRadius;
            ring.r = radius;
            ring.startAngle = startAngle;
            ring.angle = angle;
        },
        clone: function() {
            var r = this;
            return new Ring(r.c, r.ir, r.r, r.startAngle, r.angle);
        },
        middle: function() {
            return this.startAngle + this.angle / 2;
        },
        radius: function(newRadius, innerRadius) {
            var that = this;
            if (innerRadius) {
                that.ir = newRadius;
            } else {
                that.r = newRadius;
            }
            return that;
        },
        point: function(angle, innerRadius) {
            var ring = this, radianAngle = angle * DEGREE, ax = math.cos(radianAngle), ay = math.sin(radianAngle), radius = innerRadius ? ring.ir : ring.r, x = ring.c.x - ax * radius, y = ring.c.y - ay * radius;
            return new Point2D(x, y);
        },
        getBBox: function() {
            var ring = this, box = new Box2D(MAX_VALUE, MAX_VALUE, MIN_VALUE, MIN_VALUE), sa = round(ring.startAngle % 360), ea = round((sa + ring.angle) % 360), innerRadius = ring.ir, allAngles = [ 0, 90, 180, 270, sa, ea ].sort(numericComparer), saIndex = indexOf(sa, allAngles), eaIndex = indexOf(ea, allAngles), angles, i, point;
            if (sa == ea) {
                angles = allAngles;
            } else {
                if (saIndex < eaIndex) {
                    angles = allAngles.slice(saIndex, eaIndex + 1);
                } else {
                    angles = [].concat(allAngles.slice(0, eaIndex + 1), allAngles.slice(saIndex, allAngles.length));
                }
            }
            for (i = 0; i < angles.length; i++) {
                point = ring.point(angles[i]);
                box.wrapPoint(point);
                box.wrapPoint(point, innerRadius);
            }
            if (!innerRadius) {
                box.wrapPoint(ring.c);
            }
            return box;
        },
        expand: function(value) {
            this.r += value;
            return this;
        }
    });
    var Sector = Ring.extend({
        init: function(center, radius, startAngle, angle) {
            Ring.fn.init.call(this, center, 0, radius, startAngle, angle);
        },
        expand: function(value) {
            return Ring.fn.expand.call(this, value);
        },
        clone: function() {
            var sector = this;
            return new Sector(sector.c, sector.r, sector.startAngle, sector.angle);
        },
        radius: function(newRadius) {
            return Ring.fn.radius.call(this, newRadius);
        },
        point: function(angle) {
            return Ring.fn.point.call(this, angle);
        }
    });
    var Pin = Class.extend({
        init: function(options) {
            deepExtend(this, {
                height: 40,
                rotation: 90,
                radius: 10,
                arcAngle: 10
            }, options);
        }
    });
    // View-Model primitives ==================================================
    var ChartElement = Class.extend({
        init: function(options) {
            var element = this;
            element.children = [];
            element.options = deepExtend({}, element.options, options);
        },
        reflow: function(targetBox) {
            var element = this, children = element.children, box, i, currentChild;
            for (i = 0; i < children.length; i++) {
                currentChild = children[i];
                currentChild.reflow(targetBox);
                box = box ? box.wrap(currentChild.box) : currentChild.box.clone();
            }
            element.box = box || targetBox;
        },
        getViewElements: function(view) {
            var element = this, options = element.options, modelId = options.modelId, viewElements = [], root, children = element.children, i, child, childrenCount = children.length;
            for (i = 0; i < childrenCount; i++) {
                child = children[i];
                if (!child.discoverable) {
                    child.options = child.options || {};
                    child.options.modelId = modelId;
                }
                viewElements.push.apply(viewElements, child.getViewElements(view));
            }
            if (element.discoverable) {
                root = element.getRoot();
                if (root) {
                    root.modelMap[modelId] = element;
                }
            }
            return viewElements;
        },
        enableDiscovery: function() {
            var element = this, options = element.options;
            options.modelId = IDPool.current.alloc();
            element.discoverable = true;
        },
        destroy: function() {
            var element = this, children = element.children, root = element.getRoot(), modelId = element.options.modelId, i;
            if (root && modelId) {
                if (root.modelMap[modelId]) {
                    IDPool.current.free(modelId);
                }
                root.modelMap[modelId] = undefined;
            }
            for (i = 0; i < children.length; i++) {
                children[i].destroy();
            }
        },
        getRoot: function() {
            var parent = this.parent;
            return parent ? parent.getRoot() : null;
        },
        translateChildren: function(dx, dy) {
            var element = this, children = element.children, childrenCount = children.length, i;
            for (i = 0; i < childrenCount; i++) {
                children[i].box.translate(dx, dy);
            }
        },
        append: function() {
            var element = this, i, length = arguments.length;
            append(element.children, arguments);
            for (i = 0; i < length; i++) {
                arguments[i].parent = element;
            }
        }
    });
    var RootElement = ChartElement.extend({
        init: function(options) {
            var root = this;
            // Logical tree ID to element map
            root.modelMap = {};
            ChartElement.fn.init.call(root, options);
        },
        options: {
            width: DEFAULT_WIDTH,
            height: DEFAULT_HEIGHT,
            background: WHITE,
            border: {
                color: BLACK,
                width: 0
            },
            margin: getSpacing(5),
            zIndex: -2
        },
        reflow: function() {
            var root = this, options = root.options, children = root.children, currentBox = new Box2D(0, 0, options.width, options.height);
            root.box = currentBox.unpad(options.margin);
            for (var i = 0; i < children.length; i++) {
                children[i].reflow(currentBox);
                currentBox = boxDiff(currentBox, children[i].box);
            }
        },
        getViewElements: function(view) {
            var root = this, options = root.options, border = options.border || {}, box = root.box.clone().pad(options.margin).unpad(border.width), elements = [ view.createRect(box, {
                stroke: border.width ? border.color : "",
                strokeWidth: border.width,
                dashType: border.dashType,
                fill: options.background,
                fillOpacity: options.opacity,
                zIndex: options.zIndex
            }) ];
            return elements.concat(ChartElement.fn.getViewElements.call(root, view));
        },
        getRoot: function() {
            return this;
        }
    });
    var BoxElement = ChartElement.extend({
        init: function(options) {
            ChartElement.fn.init.call(this, options);
        },
        options: {
            align: LEFT,
            vAlign: TOP,
            margin: {},
            padding: {},
            border: {
                color: BLACK,
                width: 0
            },
            background: "",
            shrinkToFit: false,
            width: 0,
            height: 0,
            visible: true
        },
        reflow: function(targetBox) {
            var element = this, box, contentBox, options = element.options, margin = getSpacing(options.margin), padding = getSpacing(options.padding), borderWidth = options.border.width;
            function reflowPaddingBox() {
                element.align(targetBox, X, options.align);
                element.align(targetBox, Y, options.vAlign);
                element.paddingBox = box.clone().unpad(margin).unpad(borderWidth);
            }
            ChartElement.fn.reflow.call(element, targetBox);
            if (options.width && options.height) {
                box = element.box = new Box2D(0, 0, options.width, options.height);
            } else {
                box = element.box;
            }
            if (options.shrinkToFit) {
                reflowPaddingBox();
                contentBox = element.contentBox = element.paddingBox.clone().unpad(padding);
            } else {
                contentBox = element.contentBox = box.clone();
                box.pad(padding).pad(borderWidth).pad(margin);
                reflowPaddingBox();
            }
            element.translateChildren(box.x1 - contentBox.x1 + margin.left + borderWidth + padding.left, box.y1 - contentBox.y1 + margin.top + borderWidth + padding.top);
        },
        align: function(targetBox, axis, alignment) {
            var element = this, box = element.box, c1 = axis + 1, c2 = axis + 2, sizeFunc = axis === X ? WIDTH : HEIGHT, size = box[sizeFunc]();
            if (inArray(alignment, [ LEFT, TOP ])) {
                box[c1] = targetBox[c1];
                box[c2] = box[c1] + size;
            } else if (inArray(alignment, [ RIGHT, BOTTOM ])) {
                box[c2] = targetBox[c2];
                box[c1] = box[c2] - size;
            } else if (alignment == CENTER) {
                box[c1] = targetBox[c1] + (targetBox[sizeFunc]() - size) / 2;
                box[c2] = box[c1] + size;
            }
        },
        hasBox: function() {
            var options = this.options;
            return options.border.width || options.background;
        },
        getViewElements: function(view, renderOptions) {
            var boxElement = this, options = boxElement.options, elements = [];
            if (!options.visible) {
                return [];
            }
            if (boxElement.hasBox()) {
                elements.push(view.createRect(boxElement.paddingBox, deepExtend(boxElement.elementStyle(), renderOptions)));
            }
            return elements.concat(ChartElement.fn.getViewElements.call(boxElement, view));
        },
        elementStyle: function() {
            var boxElement = this, options = boxElement.options, border = options.border || {};
            return {
                id: options.id,
                stroke: border.width ? border.color : "",
                strokeWidth: border.width,
                dashType: border.dashType,
                strokeOpacity: options.opacity,
                fill: options.background,
                fillOpacity: options.opacity,
                animation: options.animation,
                zIndex: options.zIndex,
                data: {
                    modelId: options.modelId
                }
            };
        }
    });
    var Text = ChartElement.extend({
        init: function(content, options) {
            var text = this;
            ChartElement.fn.init.call(text, options);
            // Calculate size
            text.content = content;
            text.reflow(new Box2D());
        },
        options: {
            font: DEFAULT_FONT,
            color: BLACK,
            align: LEFT,
            vAlign: ""
        },
        reflow: function(targetBox) {
            var text = this, options = text.options, size, margin;
            size = options.size = measureText(text.content, {
                font: options.font
            }, options.rotation);
            text.baseline = size.baseline;
            if (options.align == LEFT) {
                text.box = new Box2D(targetBox.x1, targetBox.y1, targetBox.x1 + size.width, targetBox.y1 + size.height);
            } else if (options.align == RIGHT) {
                text.box = new Box2D(targetBox.x2 - size.width, targetBox.y1, targetBox.x2, targetBox.y1 + size.height);
            } else if (options.align == CENTER) {
                margin = (targetBox.width() - size.width) / 2;
                text.box = new Box2D(round(targetBox.x1 + margin, COORD_PRECISION), targetBox.y1, round(targetBox.x2 - margin, COORD_PRECISION), targetBox.y1 + size.height);
            }
            if (options.vAlign == CENTER) {
                margin = (targetBox.height() - size.height) / 2;
                text.box = new Box2D(text.box.x1, targetBox.y1 + margin, text.box.x2, targetBox.y2 - margin);
            } else if (options.vAlign == BOTTOM) {
                text.box = new Box2D(text.box.x1, targetBox.y2 - size.height, text.box.x2, targetBox.y2);
            } else if (options.vAlign == TOP) {
                text.box = new Box2D(text.box.x1, targetBox.y1, text.box.x2, targetBox.y1 + size.height);
            }
        },
        getViewElements: function(view) {
            var text = this, options = text.options;
            ChartElement.fn.getViewElements.call(this, view);
            return [ view.createText(text.content, deepExtend({}, options, {
                x: text.box.x1,
                y: text.box.y1,
                baseline: text.baseline,
                data: {
                    modelId: options.modelId
                }
            })) ];
        }
    });
    var TextBox = BoxElement.extend({
        init: function(content, options) {
            var textBox = this, text;
            BoxElement.fn.init.call(textBox, options);
            options = textBox.options;
            text = new Text(content, deepExtend({}, options, {
                align: LEFT,
                vAlign: TOP
            }));
            textBox.append(text);
            if (textBox.hasBox()) {
                text.options.id = uniqueId();
            }
            // Calculate size
            textBox.reflow(new Box2D());
        }
    });
    var Title = ChartElement.extend({
        init: function(options) {
            var title = this;
            ChartElement.fn.init.call(title, options);
            options = title.options;
            title.append(new TextBox(options.text, deepExtend({}, options, {
                vAlign: options.position
            })));
        },
        options: {
            color: BLACK,
            position: TOP,
            align: CENTER,
            margin: getSpacing(5),
            padding: getSpacing(5)
        },
        reflow: function(targetBox) {
            var title = this;
            ChartElement.fn.reflow.call(title, targetBox);
            title.box.snapTo(targetBox, X);
        }
    });
    Title.buildTitle = function(options, parent, defaultOptions) {
        var title;
        if (typeof options === "string") {
            options = {
                text: options
            };
        }
        options = deepExtend({
            visible: true
        }, defaultOptions, options);
        if (options && options.visible && options.text) {
            title = new Title(options);
            parent.append(title);
        }
        return title;
    };
    var AxisLabel = TextBox.extend({
        init: function(value, index, dataItem, options) {
            var label = this, text = value;
            if (options.template) {
                label.template = template(options.template);
                text = label.template({
                    value: value,
                    dataItem: dataItem
                });
            } else if (options.format) {
                text = label.formatValue(value, options);
            }
            label.text = text;
            label.value = value;
            label.index = index;
            label.dataItem = dataItem;
            TextBox.fn.init.call(label, text, deepExtend({
                id: uniqueId()
            }, options));
            label.enableDiscovery();
        },
        formatValue: function(value, options) {
            return autoFormat(options.format, value);
        },
        click: function(widget, e) {
            var label = this;
            widget.trigger(AXIS_LABEL_CLICK, {
                element: $(e.target),
                value: label.value,
                text: label.text,
                index: label.index,
                dataItem: label.dataItem,
                axis: label.parent.options
            });
        }
    });
    var Axis = ChartElement.extend({
        init: function(options) {
            var axis = this;
            ChartElement.fn.init.call(axis, options);
            if (!axis.options.visible) {
                axis.options = deepExtend({}, axis.options, {
                    labels: {
                        visible: false
                    },
                    line: {
                        visible: false
                    },
                    margin: 0,
                    majorTickSize: 0,
                    minorTickSize: 0
                });
            }
            axis.options.minorTicks = deepExtend({}, {
                color: axis.options.line.color,
                width: axis.options.line.width,
                visible: axis.options.minorTickType != NONE
            }, axis.options.minorTicks, {
                size: axis.options.minorTickSize,
                align: axis.options.minorTickType
            });
            axis.options.majorTicks = deepExtend({}, {
                color: axis.options.line.color,
                width: axis.options.line.width,
                visible: axis.options.majorTickType != NONE
            }, axis.options.majorTicks, {
                size: axis.options.majorTickSize,
                align: axis.options.majorTickType
            });
            axis.createLabels();
            axis.createTitle();
        },
        options: {
            labels: {
                visible: true,
                rotation: 0,
                mirror: false,
                step: 1,
                skip: 0
            },
            line: {
                width: 1,
                color: BLACK,
                visible: true
            },
            title: {
                visible: true,
                position: CENTER
            },
            majorTicks: {
                align: OUTSIDE,
                size: 4
            },
            minorTicks: {
                align: OUTSIDE,
                size: 3
            },
            axisCrossingValue: 0,
            majorTickType: OUTSIDE,
            minorTickType: NONE,
            minorGridLines: {
                visible: false,
                width: 1,
                color: BLACK
            },
            // TODO: Move to line or labels options
            margin: 5,
            visible: true,
            reverse: false,
            justified: true,
            _alignLines: true
        },
        // abstract labelsCount(): Number
        // abstract createAxisLabel(index, options): AxisLabel
        createLabels: function() {
            var axis = this, options = axis.options, align = options.vertical ? RIGHT : CENTER, labelOptions = deepExtend({}, options.labels, {
                align: align,
                zIndex: options.zIndex,
                modelId: options.modelId
            }), step = labelOptions.step;
            axis.labels = [];
            if (labelOptions.visible) {
                var labelsCount = axis.labelsCount(), label, i;
                for (i = labelOptions.skip; i < labelsCount; i += step) {
                    label = axis.createAxisLabel(i, labelOptions);
                    axis.append(label);
                    axis.labels.push(label);
                }
            }
        },
        destroy: function() {
            var axis = this, labels = axis.labels, i;
            for (i = 0; i < labels.length; i++) {
                labels[i].destroy();
            }
            ChartElement.fn.destroy.call(axis);
        },
        lineBox: function() {
            var axis = this, options = axis.options, box = axis.box, vertical = options.vertical, labels = axis.labels, labelSize = vertical ? HEIGHT : WIDTH, justified = options.justified, mirror = options.labels.mirror, axisX = mirror ? box.x1 : box.x2, axisY = mirror ? box.y2 : box.y1, startMargin = 0, endMargin = options.line.width;
            if (justified && labels.length > 1) {
                startMargin = labels[0].box[labelSize]() / 2;
                endMargin = last(labels).box[labelSize]() / 2;
            }
            return vertical ? new Box2D(axisX, box.y1 + startMargin, axisX, box.y2 - endMargin) : new Box2D(box.x1 + startMargin, axisY, box.x2 - endMargin, axisY);
        },
        createTitle: function() {
            var axis = this, options = axis.options, titleOptions = deepExtend({
                rotation: options.vertical ? -90 : 0,
                text: "",
                zIndex: 1
            }, options.title), title;
            if (titleOptions.visible && titleOptions.text) {
                title = new TextBox(titleOptions.text, titleOptions);
                axis.append(title);
                axis.title = title;
            }
        },
        renderTicks: function(view) {
            var axis = this, ticks = [], options = axis.options, lineBox = axis.lineBox(), mirror = options.labels.mirror, tickX, tickY, pos, lineOptions;
            function render(tickPositions, unit, tick, visible, skipUnit) {
                var skip = skipUnit / unit, i, count = tickPositions.length;
                if (visible) {
                    for (i = 0; i < count; i++) {
                        if (i % skip === 0) {
                            continue;
                        }
                        tickX = mirror ? lineBox.x2 : lineBox.x2 - tick.size;
                        tickY = mirror ? lineBox.y1 - tick.size : lineBox.y1;
                        pos = tickPositions[i];
                        lineOptions = {
                            strokeWidth: tick.width,
                            stroke: tick.color,
                            align: options._alignLines
                        };
                        if (options.vertical) {
                            ticks.push(view.createLine(tickX, pos, tickX + tick.size, pos, lineOptions));
                        } else {
                            ticks.push(view.createLine(pos, tickY, pos, tickY + tick.size, lineOptions));
                        }
                    }
                }
            }
            render(axis.getMajorTickPositions(), options.majorUnit, options.majorTicks, options.majorTicks.visible);
            render(axis.getMinorTickPositions(), options.minorUnit, options.minorTicks, options.minorTicks.visible, options.majorTicks.visible ? options.majorUnit : 0);
            return ticks;
        },
        getViewElements: function(view) {
            var axis = this, options = axis.options, line = options.line, lineBox = axis.lineBox(), childElements = ChartElement.fn.getViewElements.call(axis, view), lineOptions;
            if (line.width > 0 && line.visible) {
                lineOptions = {
                    strokeWidth: line.width,
                    stroke: line.color,
                    dashType: line.dashType,
                    zIndex: line.zIndex,
                    align: options._alignLines
                };
                childElements.push(view.createLine(lineBox.x1, lineBox.y1, lineBox.x2, lineBox.y2, lineOptions));
                append(childElements, axis.renderTicks(view));
            }
            append(childElements, axis.renderPlotBands(view));
            return childElements;
        },
        getActualTickSize: function() {
            var axis = this, options = axis.options, tickSize = 0;
            if (options.majorTicks.visible && options.minorTicks.visible) {
                tickSize = math.max(options.majorTicks.size, options.minorTicks.size);
            } else if (options.majorTicks.visible) {
                tickSize = options.majorTicks.size;
            } else if (options.minorTicks.visible) {
                tickSize = options.minorTicks.size;
            }
            return tickSize;
        },
        renderPlotBands: function(view) {
            var axis = this, options = axis.options, plotBands = options.plotBands || [], vertical = options.vertical, result = [], plotArea = axis.plotArea, slotX, slotY, from, to;
            if (plotBands.length) {
                result = map(plotBands, function(item) {
                    from = defined(item.from) ? item.from : MIN_VALUE;
                    to = defined(item.to) ? item.to : MAX_VALUE;
                    if (vertical) {
                        slotX = plotArea.axisX.lineBox();
                        slotY = axis.getSlot(item.from, item.to);
                    } else {
                        slotX = axis.getSlot(item.from, item.to);
                        slotY = plotArea.axisY.lineBox();
                    }
                    return view.createRect(new Box2D(slotX.x1, slotY.y1, slotX.x2, slotY.y2), {
                        fill: item.color,
                        fillOpacity: item.opacity,
                        zIndex: -1
                    });
                });
            }
            return result;
        },
        renderGridLines: function(view, altAxis) {
            var axis = this, modelId = axis.plotArea.options.modelId, options = axis.options, vertical = options.vertical, lineBox = altAxis.lineBox(), lineStart = lineBox[vertical ? "x1" : "y1"], lineEnd = lineBox[vertical ? "x2" : "y2"], majorTicks = axis.getMajorTickPositions(), gridLines = [], gridLine = function(pos, options) {
                return {
                    pos: pos,
                    options: options
                };
            };
            if (options.majorGridLines.visible) {
                gridLines = map(majorTicks, function(pos) {
                    return gridLine(pos, options.majorGridLines);
                });
            }
            if (options.minorGridLines.visible) {
                gridLines = gridLines.concat(map(axis.getMinorTickPositions(), function(pos) {
                    if (options.majorGridLines.visible) {
                        if (!inArray(pos, majorTicks)) {
                            return gridLine(pos, options.minorGridLines);
                        }
                    } else {
                        return gridLine(pos, options.minorGridLines);
                    }
                }));
            }
            return map(gridLines, function(line) {
                var gridLineOptions = {
                    data: {
                        modelId: modelId
                    },
                    strokeWidth: line.options.width,
                    stroke: line.options.color,
                    dashType: line.options.dashType,
                    zIndex: -1
                }, linePos = round(line.pos), altAxisBox = altAxis.lineBox();
                if (vertical) {
                    if (!altAxis.options.line.visible || altAxisBox.y1 !== linePos) {
                        return view.createLine(lineStart, linePos, lineEnd, linePos, gridLineOptions);
                    }
                } else {
                    if (!altAxis.options.line.visible || altAxisBox.x1 !== linePos) {
                        return view.createLine(linePos, lineStart, linePos, lineEnd, gridLineOptions);
                    }
                }
            });
        },
        reflow: function(box) {
            var axis = this, options = axis.options, vertical = options.vertical, labels = axis.labels, count = labels.length, space = axis.getActualTickSize() + options.margin, maxLabelHeight = 0, maxLabelWidth = 0, title = axis.title, label, i;
            for (i = 0; i < count; i++) {
                label = labels[i];
                maxLabelHeight = math.max(maxLabelHeight, label.box.height());
                maxLabelWidth = math.max(maxLabelWidth, label.box.width());
            }
            if (title) {
                if (vertical) {
                    maxLabelWidth += title.box.width();
                } else {
                    maxLabelHeight += title.box.height();
                }
            }
            if (vertical) {
                axis.box = new Box2D(box.x1, box.y1, box.x1 + maxLabelWidth + space, box.y2);
            } else {
                axis.box = new Box2D(box.x1, box.y1, box.x2, box.y1 + maxLabelHeight + space);
            }
            axis.arrangeTitle();
            axis.arrangeLabels(maxLabelWidth, maxLabelHeight);
        },
        arrangeLabels: function() {
            var axis = this, options = axis.options, labelOptions = options.labels, labels = axis.labels, labelsBetweenTicks = !options.justified, vertical = options.vertical, lineBox = axis.lineBox(), mirror = options.labels.mirror, tickPositions = axis.getMajorTickPositions(), labelOffset = axis.getActualTickSize() + options.margin, labelBox, labelY, i;
            for (i = 0; i < labels.length; i++) {
                var label = labels[i], tickIx = labelOptions.skip + labelOptions.step * i, labelSize = vertical ? label.box.height() : label.box.width(), labelPos = tickPositions[tickIx] - labelSize / 2, firstTickPosition, nextTickPosition, middle, labelX;
                if (vertical) {
                    if (labelsBetweenTicks) {
                        firstTickPosition = tickPositions[tickIx];
                        nextTickPosition = tickPositions[tickIx + 1];
                        middle = firstTickPosition + (nextTickPosition - firstTickPosition) / 2;
                        labelPos = middle - labelSize / 2;
                    }
                    labelX = lineBox.x2;
                    if (mirror) {
                        labelX += labelOffset;
                    } else {
                        labelX -= labelOffset + label.box.width();
                    }
                    labelBox = label.box.move(labelX, labelPos);
                } else {
                    if (labelsBetweenTicks) {
                        firstTickPosition = tickPositions[tickIx];
                        nextTickPosition = tickPositions[tickIx + 1];
                    } else {
                        firstTickPosition = labelPos;
                        nextTickPosition = labelPos + labelSize;
                    }
                    labelY = lineBox.y1;
                    if (mirror) {
                        labelY -= labelOffset + label.box.height();
                    } else {
                        labelY += labelOffset;
                    }
                    labelBox = new Box2D(firstTickPosition, labelY, nextTickPosition, labelY + label.box.height());
                }
                label.reflow(labelBox);
            }
        },
        arrangeTitle: function() {
            var axis = this, options = axis.options, mirror = options.labels.mirror, vertical = options.vertical, title = axis.title;
            if (title) {
                if (vertical) {
                    title.options.align = mirror ? RIGHT : LEFT;
                    title.options.vAlign = title.options.position;
                } else {
                    title.options.align = title.options.position;
                    title.options.vAlign = mirror ? TOP : BOTTOM;
                }
                title.reflow(axis.box);
            }
        },
        alignTo: function(secondAxis) {
            var axis = this, lineBox = secondAxis.lineBox(), vertical = axis.options.vertical, pos = vertical ? Y : X;
            axis.box.snapTo(lineBox, pos);
            if (vertical) {
                axis.box.shrink(0, axis.lineBox().height() - lineBox.height());
            } else {
                axis.box.shrink(axis.lineBox().width() - lineBox.width(), 0);
            }
            axis.box[pos + 1] -= axis.lineBox()[pos + 1] - lineBox[pos + 1];
            axis.box[pos + 2] -= axis.lineBox()[pos + 2] - lineBox[pos + 2];
        }
    });
    var NumericAxis = Axis.extend({
        init: function(seriesMin, seriesMax, options) {
            var axis = this, defaultOptions = axis.initDefaults(seriesMin, seriesMax, options);
            Axis.fn.init.call(axis, defaultOptions);
        },
        options: {
            type: "numeric",
            min: 0,
            max: 1,
            vertical: true,
            majorGridLines: {
                visible: true,
                width: 1,
                color: BLACK
            },
            zIndex: 1
        },
        initDefaults: function(seriesMin, seriesMax, options) {
            var axis = this, narrowRange = options.narrowRange, autoMin = axis.autoAxisMin(seriesMin, seriesMax, narrowRange), autoMax = axis.autoAxisMax(seriesMin, seriesMax, narrowRange), majorUnit = autoMajorUnit(autoMin, autoMax), autoOptions = {
                majorUnit: majorUnit
            }, userSetLimits;
            if (autoMin < 0) {
                autoMin -= majorUnit;
            }
            if (autoMax > 0) {
                autoMax += majorUnit;
            }
            autoOptions.min = floor(autoMin, majorUnit);
            autoOptions.max = ceil(autoMax, majorUnit);
            if (options) {
                userSetLimits = defined(options.min) || defined(options.max);
                if (userSetLimits) {
                    if (options.min === options.max) {
                        if (options.min > 0) {
                            options.min = 0;
                        } else {
                            options.max = 1;
                        }
                    }
                }
                if (options.majorUnit) {
                    autoOptions.min = floor(autoOptions.min, options.majorUnit);
                    autoOptions.max = ceil(autoOptions.max, options.majorUnit);
                } else if (userSetLimits) {
                    options = deepExtend(autoOptions, options);
                    // Determine an auto major unit after min/max have been set
                    autoOptions.majorUnit = autoMajorUnit(options.min, options.max);
                }
            }
            autoOptions.minorUnit = (options.majorUnit || autoOptions.majorUnit) / 5;
            return deepExtend(autoOptions, options);
        },
        range: function() {
            var options = this.options;
            return {
                min: options.min,
                max: options.max
            };
        },
        autoAxisMax: function(min, max, narrow) {
            var axisMax, diff;
            if (!min && !max) {
                return 1;
            }
            if (min <= 0 && max <= 0) {
                max = min == max ? 0 : max;
                diff = math.abs((max - min) / max);
                if (!narrow && diff > ZERO_THRESHOLD) {
                    return 0;
                }
                axisMax = math.min(0, max - (min - max) / 2);
            } else {
                min = min == max ? 0 : min;
                axisMax = max;
            }
            return axisMax;
        },
        autoAxisMin: function(min, max, narrow) {
            var axisMin, diff;
            if (!min && !max) {
                return 0;
            }
            if (min >= 0 && max >= 0) {
                min = min == max ? 0 : min;
                diff = (max - min) / max;
                if (!narrow && diff > ZERO_THRESHOLD) {
                    return 0;
                }
                axisMin = math.max(0, min - (max - min) / 2);
            } else {
                max = min == max ? 0 : max;
                axisMin = min;
            }
            return axisMin;
        },
        getDivisions: function(stepValue) {
            var options = this.options, range = options.max - options.min;
            return math.floor(round(range / stepValue, COORD_PRECISION)) + 1;
        },
        getTickPositions: function(stepValue) {
            var axis = this, options = axis.options, vertical = options.vertical, reverse = options.reverse, lineBox = axis.lineBox(), lineSize = vertical ? lineBox.height() : lineBox.width(), range = options.max - options.min, scale = lineSize / range, step = stepValue * scale, divisions = axis.getDivisions(stepValue), dir = (vertical ? -1 : 1) * (reverse ? -1 : 1), startEdge = dir === 1 ? 1 : 2, pos = lineBox[(vertical ? Y : X) + startEdge], positions = [], i;
            for (i = 0; i < divisions; i++) {
                positions.push(round(pos, COORD_PRECISION));
                pos = pos + step * dir;
            }
            return positions;
        },
        getMajorTickPositions: function() {
            var axis = this;
            return axis.getTickPositions(axis.options.majorUnit);
        },
        getMinorTickPositions: function() {
            var axis = this;
            return axis.getTickPositions(axis.options.minorUnit);
        },
        getSlot: function(a, b) {
            var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)], lineSize = vertical ? lineBox.height() : lineBox.width(), dir = reverse ? -1 : 1, step = dir * (lineSize / (options.max - options.min)), p1, p2, slotBox = new Box2D(lineBox.x1, lineBox.y1, lineBox.x1, lineBox.y1);
            if (!defined(a)) {
                a = b || 0;
            }
            if (!defined(b)) {
                b = a || 0;
            }
            a = math.max(math.min(a, options.max), options.min);
            b = math.max(math.min(b, options.max), options.min);
            if (vertical) {
                p1 = options.max - math.max(a, b);
                p2 = options.max - math.min(a, b);
            } else {
                p1 = math.min(a, b) - options.min;
                p2 = math.max(a, b) - options.min;
            }
            slotBox[valueAxis + 1] = lineStart + step * (reverse ? p2 : p1);
            slotBox[valueAxis + 2] = lineStart + step * (reverse ? p1 : p2);
            return slotBox;
        },
        getValue: function(point) {
            var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, max = options.max * 1, min = options.min * 1, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)], lineSize = vertical ? lineBox.height() : lineBox.width(), dir = reverse ? -1 : 1, offset = dir * (point[valueAxis] - lineStart), step = (max - min) / lineSize, valueOffset = offset * step, value;
            if (offset < 0 || offset > lineSize) {
                return null;
            }
            value = vertical ? max - valueOffset : min + valueOffset;
            return round(value, DEFAULT_PRECISION);
        },
        translateRange: function(delta) {
            var axis = this, options = axis.options, lineBox = axis.lineBox(), vertical = options.vertical, reverse = options.reverse, size = vertical ? lineBox.height() : lineBox.width(), range = options.max - options.min, scale = size / range, offset = round(delta / scale, DEFAULT_PRECISION);
            if ((vertical || reverse) && !(vertical && reverse)) {
                offset = -offset;
            }
            return {
                min: options.min + offset,
                max: options.max + offset
            };
        },
        scaleRange: function(delta) {
            var axis = this, options = axis.options, offset = -delta * options.majorUnit;
            return {
                min: options.min - offset,
                max: options.max + offset
            };
        },
        labelsCount: function() {
            return this.getDivisions(this.options.majorUnit);
        },
        createAxisLabel: function(index, labelOptions) {
            var axis = this, options = axis.options, value = round(options.min + index * options.majorUnit, DEFAULT_PRECISION);
            return new AxisLabel(value, index, null, labelOptions);
        }
    });
    // View base classes ======================================================
    var ViewElement = Class.extend({
        init: function(options) {
            var element = this;
            element.children = [];
            element.options = deepExtend({}, element.options, options);
        },
        render: function() {
            return this.template(this);
        },
        renderContent: function() {
            var element = this, output = "", sortedChildren = element.sortChildren(), childrenCount = sortedChildren.length, i;
            for (i = 0; i < childrenCount; i++) {
                output += sortedChildren[i].render();
            }
            return output;
        },
        sortChildren: function() {
            var element = this, children = element.children, length, i;
            for (i = 0, length = children.length; i < length; i++) {
                children[i]._childIndex = i;
            }
            return children.slice(0).sort(element.compareChildren);
        },
        refresh: $.noop,
        compareChildren: function(a, b) {
            var aValue = a.options.zIndex || 0, bValue = b.options.zIndex || 0;
            if (aValue !== bValue) {
                return aValue - bValue;
            }
            return a._childIndex - b._childIndex;
        },
        renderId: function() {
            var element = this, result = "";
            if (element.options.id) {
                result = element.renderAttr("id", element.options.id);
            }
            return result;
        },
        renderAttr: function(name, value) {
            return defined(value) ? " " + name + "='" + value + "' " : "";
        },
        renderDataAttributes: function() {
            var element = this, data = element.options.data, key, attr, output = "";
            for (key in data) {
                attr = "data-" + key.replace(UPPERCASE_REGEX, "-$1").toLowerCase();
                output += element.renderAttr(attr, data[key]);
            }
            return output;
        }
    });
    var ViewBase = ViewElement.extend({
        init: function(options) {
            var view = this;
            ViewElement.fn.init.call(view, options);
            view.definitions = {};
            view.decorators = [];
            view.animations = [];
        },
        destroy: function() {
            var animations = this.animations;
            while (animations.length > 0) {
                animations.shift().destroy();
            }
        },
        renderDefinitions: function() {
            var definitions = this.definitions, definitionId, output = "";
            for (definitionId in definitions) {
                if (definitions.hasOwnProperty(definitionId)) {
                    output += definitions[definitionId].render();
                }
            }
            return output;
        },
        decorate: function(element) {
            var decorators = this.decorators, i, length = decorators.length, currentDecorator;
            for (i = 0; i < length; i++) {
                currentDecorator = decorators[i];
                this._decorateChildren(currentDecorator, element);
                element = currentDecorator.decorate.call(currentDecorator, element);
            }
            return element;
        },
        _decorateChildren: function(decorator, element) {
            var view = this, children = element.children, i, length = children.length;
            for (i = 0; i < length; i++) {
                view._decorateChildren(decorator, children[i]);
                children[i] = decorator.decorate.call(decorator, children[i]);
            }
        },
        setupAnimations: function() {
            for (var i = 0; i < this.animations.length; i++) {
                this.animations[i].setup();
            }
        },
        playAnimations: function() {
            for (var i = 0; i < this.animations.length; i++) {
                this.animations[i].play();
            }
        },
        buildGradient: function(options) {
            var view = this, cache = view._gradientCache, hashCode, overlay, definition;
            if (!cache) {
                cache = view._gradientCache = [];
            }
            if (options) {
                hashCode = getHash(options);
                overlay = cache[hashCode];
                definition = dataviz.Gradients[options.gradient];
                if (!overlay && definition) {
                    overlay = deepExtend({
                        id: uniqueId()
                    }, definition, options);
                    cache[hashCode] = overlay;
                }
            }
            return overlay;
        },
        setDefaults: function(options) {
            var viewOptions = this.options;
            options = options || {};
            if (!defined(options.inline)) {
                options.inline = viewOptions.inline;
            }
            if (!defined(options.align)) {
                options.align = viewOptions.align;
            }
            return options;
        }
    });
    dataviz.Gradients = {
        glass: {
            type: LINEAR,
            rotation: 0,
            stops: [ {
                offset: 0,
                color: WHITE,
                opacity: 0
            }, {
                offset: .25,
                color: WHITE,
                opacity: .3
            }, {
                offset: 1,
                color: WHITE,
                opacity: 0
            } ]
        },
        sharpBevel: {
            type: RADIAL,
            stops: [ {
                offset: 0,
                color: WHITE,
                opacity: .55
            }, {
                offset: .65,
                color: WHITE,
                opacity: 0
            }, {
                offset: .95,
                color: WHITE,
                opacity: .25
            } ]
        },
        roundedBevel: {
            type: RADIAL,
            stops: [ {
                offset: .33,
                color: WHITE,
                opacity: .06
            }, {
                offset: .83,
                color: WHITE,
                opacity: .2
            }, {
                offset: .95,
                color: WHITE,
                opacity: 0
            } ]
        },
        roundedGlass: {
            type: RADIAL,
            supportVML: false,
            stops: [ {
                offset: 0,
                color: WHITE,
                opacity: 0
            }, {
                offset: .5,
                color: WHITE,
                opacity: .3
            }, {
                offset: .99,
                color: WHITE,
                opacity: 0
            } ]
        },
        sharpGlass: {
            type: RADIAL,
            supportVML: false,
            stops: [ {
                offset: 0,
                color: WHITE,
                opacity: .2
            }, {
                offset: .15,
                color: WHITE,
                opacity: .15
            }, {
                offset: .17,
                color: WHITE,
                opacity: .35
            }, {
                offset: .85,
                color: WHITE,
                opacity: .05
            }, {
                offset: .87,
                color: WHITE,
                opacity: .15
            }, {
                offset: .99,
                color: WHITE,
                opacity: 0
            } ]
        }
    };
    // Animations =============================================================
    var ElementAnimation = Class.extend({
        init: function(element, options) {
            var anim = this;
            anim.options = deepExtend({}, anim.options, options);
            anim.element = element;
        },
        options: {
            duration: INITIAL_ANIMATION_DURATION,
            easing: SWING
        },
        play: function() {
            var anim = this, options = anim.options, element = anim.element, elementId = element.options.id, domElement, delay = options.delay || 0, start = +new Date() + delay, duration = options.duration, finish = start + duration, easing = $.easing[options.easing], wallTime, time, pos, easingPos;
            setTimeout(function() {
                var loop = function() {
                    if (anim._stopped) {
                        return;
                    }
                    wallTime = +new Date();
                    time = math.min(wallTime - start, duration);
                    pos = time / duration;
                    easingPos = easing(pos, time, 0, 1, duration);
                    anim.step(easingPos);
                    if (!domElement || detached(domElement)) {
                        domElement = getElement(elementId);
                    }
                    element.refresh(domElement);
                    if (wallTime < finish) {
                        dataviz.requestFrame(loop);
                    } else {
                        anim.destroy();
                    }
                };
                loop();
            }, delay);
        },
        abort: function() {
            this._stopped = true;
        },
        destroy: function() {
            this.abort();
        },
        setup: noop,
        step: noop
    });
    var FadeAnimation = ElementAnimation.extend({
        options: {
            duration: 200,
            easing: LINEAR
        },
        setup: function() {
            var anim = this, options = anim.element.options;
            anim.targetFillOpacity = options.fillOpacity;
            anim.targetStrokeOpacity = options.strokeOpacity;
            options.fillOpacity = options.strokeOpacity = 0;
        },
        step: function(pos) {
            var anim = this, options = anim.element.options;
            options.fillOpacity = pos * anim.targetFillOpacity;
            options.strokeOpacity = pos * anim.targetStrokeOpacity;
        }
    });
    var ExpandAnimation = ElementAnimation.extend({
        options: {
            size: 0,
            easing: LINEAR
        },
        setup: function() {
            var points = this.element.points;
            points[1].x = points[2].x = points[0].x;
        },
        step: function(pos) {
            var options = this.options, size = interpolateValue(0, options.size, pos), points = this.element.points;
            // Expands rectangle to the right
            points[1].x = points[2].x = points[0].x + size;
        },
        destroy: function() {
            ElementAnimation.fn.destroy.call(this);
            // Unwrap all child elements
            this.element.destroy();
        }
    });
    var RotationAnimation = ElementAnimation.extend({
        options: {
            easing: LINEAR,
            duration: 900
        },
        setup: function() {
            var anim = this, element = anim.element, elementOptions = element.options, options = anim.options, center = options.center, start, end;
            if (elementOptions.rotation) {
                start = options.startAngle;
                end = elementOptions.rotation[0];
                options.duration = math.max(math.abs(start - end) / options.speed * 1e3, 1);
                anim.endState = end;
                elementOptions.rotation = [ start, center.x, center.y ];
            }
        },
        step: function(pos) {
            var anim = this, element = anim.element;
            if (element.options.rotation) {
                element.options.rotation[0] = interpolateValue(anim.options.startAngle, anim.endState, pos);
            }
        }
    });
    var BarAnimation = ElementAnimation.extend({
        options: {
            easing: SWING
        },
        setup: function() {
            var anim = this, element = anim.element, points = element.points, options = element.options, axis = options.vertical ? Y : X, stackBase = options.stackBase, aboveAxis = options.aboveAxis, startPosition, endState = anim.endState = {
                top: points[0].y,
                right: points[1].x,
                bottom: points[3].y,
                left: points[0].x
            };
            if (axis === Y) {
                startPosition = defined(stackBase) ? stackBase : endState[aboveAxis ? BOTTOM : TOP];
            } else {
                startPosition = defined(stackBase) ? stackBase : endState[aboveAxis ? LEFT : RIGHT];
            }
            anim.startPosition = startPosition;
            updateArray(points, axis, startPosition);
        },
        step: function(pos) {
            var anim = this, startPosition = anim.startPosition, endState = anim.endState, element = anim.element, points = element.points;
            if (element.options.vertical) {
                points[0].y = points[1].y = interpolateValue(startPosition, endState.top, pos);
                points[2].y = points[3].y = interpolateValue(startPosition, endState.bottom, pos);
            } else {
                points[0].x = points[3].x = interpolateValue(startPosition, endState.left, pos);
                points[1].x = points[2].x = interpolateValue(startPosition, endState.right, pos);
            }
        }
    });
    var BarIndicatorAnimatin = ElementAnimation.extend({
        options: {
            easing: SWING,
            duration: 1e3
        },
        setup: function() {
            var anim = this, element = anim.element, points = element.points, options = element.options.animation, vertical = options.vertical, reverse = options.reverse, axis = anim.axis = vertical ? "y" : "x", start, end, pos, endPosition = anim.options.endPosition, initialState = anim.initialState = {
                top: points[0].y,
                right: points[1].x,
                bottom: points[3].y,
                left: points[0].x
            }, initial = !defined(anim.options.endPosition);
            if (vertical) {
                pos = reverse ? "y2" : "y1";
                start = initialState[initial && !reverse ? BOTTOM : TOP];
                end = initial ? initialState[reverse ? BOTTOM : TOP] : endPosition[pos];
            } else {
                pos = reverse ? "x1" : "x2";
                start = initialState[initial && !reverse ? LEFT : RIGHT];
                end = initial ? initialState[reverse ? LEFT : RIGHT] : endPosition[pos];
            }
            anim.start = start;
            anim.end = end;
            if (initial) {
                updateArray(points, axis, anim.start);
            } else if (options.speed) {
                anim.options.duration = math.max(math.abs(anim.start - anim.end) / options.speed * 1e3, 1);
            }
        },
        step: function(pos) {
            var anim = this, start = anim.start, end = anim.end, element = anim.element, points = element.points, axis = anim.axis;
            if (element.options.animation.vertical) {
                points[0][axis] = points[1][axis] = interpolateValue(start, end, pos);
            } else {
                points[1][axis] = points[2][axis] = interpolateValue(start, end, pos);
            }
        }
    });
    var ArrowAnimation = ElementAnimation.extend({
        options: {
            easing: SWING,
            duration: 1e3
        },
        setup: function() {
            var anim = this, element = anim.element, points = element.points, options = element.options.animation, vertical = options.vertical, reverse = options.reverse, axis = vertical ? "y" : "x", startPos = axis + (reverse ? "1" : "2"), endPos = axis + (reverse ? "2" : "1"), startPosition = options.startPosition[vertical ? startPos : endPos], halfSize = options.size / 2, count = points.length, initial = !defined(anim.options.endPosition), padding = halfSize, point, end, i;
            anim.axis = axis;
            anim.endPositions = [];
            anim.startPositions = [];
            if (!initial) {
                startPosition = points[1][axis];
                end = anim.options.endPosition[vertical ? endPos : startPos];
                if (options.speed) {
                    anim.options.duration = math.max(math.abs(startPosition - end) / options.speed * 1e3, 1);
                }
            }
            for (i = 0; i < count; i++) {
                point = deepExtend({}, points[i]);
                if (initial) {
                    anim.endPositions[i] = point[axis];
                    points[i][axis] = startPosition - padding;
                } else {
                    anim.endPositions[i] = end - padding;
                }
                anim.startPositions[i] = points[i][axis];
                padding -= halfSize;
            }
        },
        step: function(pos) {
            var anim = this, startPositions = anim.startPositions, endPositions = anim.endPositions, element = anim.element, points = element.points, axis = anim.axis, count = points.length, i;
            for (i = 0; i < count; i++) {
                points[i][axis] = interpolateValue(startPositions[i], endPositions[i], pos);
            }
        }
    });
    function animationDecorator(animationName, animationType) {
        return Class.extend({
            init: function(view) {
                this.view = view;
            },
            decorate: function(element) {
                var decorator = this, view = decorator.view, animation = element.options.animation, animationObject;
                if (animation && animation.type === animationName && view.options.transitions) {
                    animationObject = element._animation = new animationType(element, animation);
                    view.animations.push(animationObject);
                }
                return element;
            }
        });
    }
    var FadeAnimationDecorator = animationDecorator(FADEIN, FadeAnimation);
    // Helper functions========================================================
    var Color = function(value) {
        var color = this, formats = Color.formats, re, processor, parts, i, channels;
        if (arguments.length === 1) {
            value = color.resolveColor(value);
            for (i = 0; i < formats.length; i++) {
                re = formats[i].re;
                processor = formats[i].process;
                parts = re.exec(value);
                if (parts) {
                    channels = processor(parts);
                    color.r = channels[0];
                    color.g = channels[1];
                    color.b = channels[2];
                }
            }
        } else {
            color.r = arguments[0];
            color.g = arguments[1];
            color.b = arguments[2];
        }
        color.r = color.normalizeByte(color.r);
        color.g = color.normalizeByte(color.g);
        color.b = color.normalizeByte(color.b);
    };
    Color.prototype = {
        toHex: function() {
            var color = this, pad = color.padDigit, r = color.r.toString(16), g = color.g.toString(16), b = color.b.toString(16);
            return "#" + pad(r) + pad(g) + pad(b);
        },
        resolveColor: function(value) {
            value = value || BLACK;
            if (value.charAt(0) == "#") {
                value = value.substr(1, 6);
            }
            value = value.replace(/ /g, "");
            value = value.toLowerCase();
            value = Color.namedColors[value] || value;
            return value;
        },
        normalizeByte: function(value) {
            return value < 0 || isNaN(value) ? 0 : value > 255 ? 255 : value;
        },
        padDigit: function(value) {
            return value.length === 1 ? "0" + value : value;
        },
        brightness: function(value) {
            var color = this, round = math.round;
            color.r = round(color.normalizeByte(color.r * value));
            color.g = round(color.normalizeByte(color.g * value));
            color.b = round(color.normalizeByte(color.b * value));
            return color;
        },
        percBrightness: function() {
            var color = this;
            return math.sqrt(.241 * color.r * color.r + .691 * color.g * color.g + .068 * color.b * color.b);
        }
    };
    Color.formats = [ {
        re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
        process: function(parts) {
            return [ parseInt(parts[1], 10), parseInt(parts[2], 10), parseInt(parts[3], 10) ];
        }
    }, {
        re: /^(\w{2})(\w{2})(\w{2})$/,
        process: function(parts) {
            return [ parseInt(parts[1], 16), parseInt(parts[2], 16), parseInt(parts[3], 16) ];
        }
    }, {
        re: /^(\w{1})(\w{1})(\w{1})$/,
        process: function(parts) {
            return [ parseInt(parts[1] + parts[1], 16), parseInt(parts[2] + parts[2], 16), parseInt(parts[3] + parts[3], 16) ];
        }
    } ];
    Color.namedColors = {
        aqua: "00ffff",
        azure: "f0ffff",
        beige: "f5f5dc",
        black: "000000",
        blue: "0000ff",
        brown: "a52a2a",
        coral: "ff7f50",
        cyan: "00ffff",
        darkblue: "00008b",
        darkcyan: "008b8b",
        darkgray: "a9a9a9",
        darkgreen: "006400",
        darkorange: "ff8c00",
        darkred: "8b0000",
        dimgray: "696969",
        fuchsia: "ff00ff",
        gold: "ffd700",
        goldenrod: "daa520",
        gray: "808080",
        green: "008000",
        greenyellow: "adff2f",
        indigo: "4b0082",
        ivory: "fffff0",
        khaki: "f0e68c",
        lightblue: "add8e6",
        lightgrey: "d3d3d3",
        lightgreen: "90ee90",
        lightpink: "ffb6c1",
        lightyellow: "ffffe0",
        lime: "00ff00",
        limegreen: "32cd32",
        linen: "faf0e6",
        magenta: "ff00ff",
        maroon: "800000",
        mediumblue: "0000cd",
        navy: "000080",
        olive: "808000",
        orange: "ffa500",
        orangered: "ff4500",
        orchid: "da70d6",
        pink: "ffc0cb",
        plum: "dda0dd",
        purple: "800080",
        red: "ff0000",
        royalblue: "4169e1",
        salmon: "fa8072",
        silver: "c0c0c0",
        skyblue: "87ceeb",
        slateblue: "6a5acd",
        slategray: "708090",
        snow: "fffafa",
        steelblue: "4682b4",
        tan: "d2b48c",
        teal: "008080",
        tomato: "ff6347",
        turquoise: "40e0d0",
        violet: "ee82ee",
        wheat: "f5deb3",
        white: "ffffff",
        whitesmoke: "f5f5f5",
        yellow: "ffff00",
        yellowgreen: "9acd32"
    };
    var IDPool = Class.extend({
        init: function(size, prefix, start) {
            this._pool = [];
            this._freed = {};
            this._size = size;
            this._id = start;
            this._prefix = prefix;
        },
        alloc: function() {
            var that = this, pool = that._pool, id;
            if (pool.length > 0) {
                id = pool.pop();
                that._freed[id] = false;
            } else {
                id = that._prefix + that._id++;
            }
            return id;
        },
        free: function(id) {
            var that = this, pool = that._pool, freed = that._freed;
            if (pool.length < that._size && !freed[id]) {
                pool.push(id);
                freed[id] = true;
            }
        }
    });
    IDPool.current = new IDPool(ID_POOL_SIZE, ID_PREFIX, ID_START);
    var LRUCache = Class.extend({
        init: function(size) {
            this._size = size;
            this._length = 0;
            this._map = {};
        },
        put: function(key, value) {
            var lru = this, map = lru._map, entry = {
                key: key,
                value: value
            };
            map[key] = entry;
            if (!lru._head) {
                lru._head = lru._tail = entry;
            } else {
                lru._tail.newer = entry;
                entry.older = lru._tail;
                lru._tail = entry;
            }
            if (lru._length >= lru._size) {
                map[lru._head.key] = null;
                lru._head = lru._head.newer;
                lru._head.older = null;
            } else {
                lru._length++;
            }
        },
        get: function(key) {
            var lru = this, entry = lru._map[key];
            if (entry) {
                if (entry === lru._head && entry !== lru._tail) {
                    lru._head = entry.newer;
                    lru._head.older = null;
                }
                if (entry !== lru._tail) {
                    if (entry.older) {
                        entry.older.newer = entry.newer;
                        entry.newer.older = entry.older;
                    }
                    entry.older = lru._tail;
                    entry.newer = null;
                    lru._tail.newer = entry;
                    lru._tail = entry;
                }
                return entry.value;
            }
        }
    });
    function measureText(text, style, rotation) {
        var styleHash = getHash(style), cacheKey = text + styleHash + rotation, cachedResult = measureText.cache.get(cacheKey), size = {
            width: 0,
            height: 0,
            baseline: 0
        };
        if (cachedResult) {
            return cachedResult;
        }
        var measureBox = measureText.measureBox, baselineMarker = measureText.baselineMarker.cloneNode(false);
        if (!measureBox) {
            measureBox = measureText.measureBox = $("<div style='position: absolute; top: -4000px; left: -4000px;" + "line-height: normal; visibility: hidden;' />").appendTo(doc.body)[0];
        }
        for (var styleKey in style) {
            measureBox.style[styleKey] = style[styleKey];
        }
        measureBox.innerHTML = text;
        measureBox.appendChild(baselineMarker);
        if ((text + "").length) {
            size = {
                width: measureBox.offsetWidth - BASELINE_MARKER_SIZE,
                height: measureBox.offsetHeight,
                baseline: baselineMarker.offsetTop + BASELINE_MARKER_SIZE
            };
        }
        if (rotation) {
            var width = size.width, height = size.height, cx = width / 2, cy = height / 2, r1 = rotatePoint(0, 0, cx, cy, rotation), r2 = rotatePoint(width, 0, cx, cy, rotation), r3 = rotatePoint(width, height, cx, cy, rotation), r4 = rotatePoint(0, height, cx, cy, rotation);
            size.normalWidth = width;
            size.normalHeight = height;
            size.width = math.max(r1.x, r2.x, r3.x, r4.x) - math.min(r1.x, r2.x, r3.x, r4.x);
            size.height = math.max(r1.y, r2.y, r3.y, r4.y) - math.min(r1.y, r2.y, r3.y, r4.y);
        }
        measureText.cache.put(cacheKey, size);
        return size;
    }
    measureText.cache = new LRUCache(1e3);
    measureText.baselineMarker = $("<div class='" + CSS_PREFIX + "baseline-marker' " + "style='display: inline-block; vertical-align: baseline;" + "width: " + BASELINE_MARKER_SIZE + "px; height: " + BASELINE_MARKER_SIZE + "px;" + "overflow: hidden;' />")[0];
    function autoMajorUnit(min, max) {
        var diff = max - min;
        if (diff === 0) {
            if (max === 0) {
                return .1;
            }
            diff = math.abs(max);
        }
        var scale = math.pow(10, math.floor(math.log(diff) / math.log(10))), relativeValue = round(diff / scale, DEFAULT_PRECISION), scaleMultiplier = 1;
        if (relativeValue < 1.904762) {
            scaleMultiplier = .2;
        } else if (relativeValue < 4.761904) {
            scaleMultiplier = .5;
        } else if (relativeValue < 9.523809) {
            scaleMultiplier = 1;
        } else {
            scaleMultiplier = 2;
        }
        return round(scale * scaleMultiplier, DEFAULT_PRECISION);
    }
    function getHash(object) {
        var hash = [];
        for (var key in object) {
            hash.push(key + object[key]);
        }
        return hash.sort().join(" ");
    }
    function uniqueId() {
        return IDPool.current.alloc();
    }
    function rotatePoint(x, y, cx, cy, angle) {
        var theta = angle * DEGREE;
        return {
            x: cx + (x - cx) * math.cos(theta) + (y - cy) * math.sin(theta),
            y: cy - (x - cx) * math.sin(theta) + (y - cy) * math.cos(theta)
        };
    }
    function boxDiff(r, s) {
        if (r.x1 == s.x1 && r.y1 == s.y1 && r.x2 == s.x2 && r.y2 == s.y2) {
            return s;
        }
        var a = math.min(r.x1, s.x1), b = math.max(r.x1, s.x1), c = math.min(r.x2, s.x2), d = math.max(r.x2, s.x2), e = math.min(r.y1, s.y1), f = math.max(r.y1, s.y1), g = math.min(r.y2, s.y2), h = math.max(r.y2, s.y2), result = [];
        // X = intersection, 0-7 = possible difference areas
        // h +-+-+-+
        // . |5|6|7|
        // g +-+-+-+
        // . |3|X|4|
        // f +-+-+-+
        // . |0|1|2|
        // e +-+-+-+
        // . a b c d
        // we'll always have rectangles 1, 3, 4 and 6
        result[0] = Box2D(b, e, c, f);
        result[1] = Box2D(a, f, b, g);
        result[2] = Box2D(c, f, d, g);
        result[3] = Box2D(b, g, c, h);
        // decide which corners
        if (r.x1 == a && r.y1 == e || s.x1 == a && s.y1 == e) {
            // corners 0 and 7
            result[4] = Box2D(a, e, b, f);
            result[5] = Box2D(c, g, d, h);
        } else {
            // corners 2 and 5
            result[4] = Box2D(c, e, d, f);
            result[5] = Box2D(a, g, b, h);
        }
        return $.grep(result, function(box) {
            return box.height() > 0 && box.width() > 0;
        })[0];
    }
    function supportsSVG() {
        return doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1");
    }
    var requestFrameFn = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
        setTimeout(callback, ANIMATION_STEP);
    };
    dataviz.requestFrame = function(callback, delay) {
        return requestFrameFn(callback, delay);
    };
    function inArray(value, array) {
        return indexOf(value, array) != -1;
    }
    function last(array) {
        return array[array.length - 1];
    }
    function append(first, second) {
        [].push.apply(first, second);
    }
    function ceil(value, step) {
        return round(math.ceil(value / step) * step, DEFAULT_PRECISION);
    }
    function floor(value, step) {
        return round(math.floor(value / step) * step, DEFAULT_PRECISION);
    }
    function round(value, precision) {
        var power = math.pow(10, precision || 0);
        return math.round(value * power) / power;
    }
    function interpolateValue(start, end, progress) {
        return round(start + (end - start) * progress, COORD_PRECISION);
    }
    function defined(value) {
        return typeof value !== UNDEFINED;
    }
    function numericComparer(a, b) {
        return a - b;
    }
    function updateArray(arr, prop, value) {
        var i, length = arr.length;
        for (i = 0; i < length; i++) {
            arr[i][prop] = value;
        }
    }
    function autoFormat(format, value) {
        if (format.match(FORMAT_REGEX)) {
            return kendo.format.apply(this, arguments);
        }
        return kendo.toString(value, format);
    }
    function getElement(modelId) {
        return doc.getElementById(modelId);
    }
    function detached(element) {
        var parent = element.parentNode;
        while (parent && parent.parentNode) {
            parent = parent.parentNode;
        }
        return parent !== doc;
    }
    // Exports ================================================================
    /**
     * @name kendo.dataviz
     * @namespace Contains Kendo DataViz.
     */
    deepExtend(kendo.dataviz, {
        init: function(element) {
            kendo.init(element, kendo.dataviz.ui);
        },
        /**
         * @name kendo.dataviz.ui
         * @namespace Contains Kendo DataViz UI widgets.
         */
        ui: {
            roles: {},
            themes: {},
            views: [],
            defaultView: function() {
                var i, views = dataviz.ui.views, length = views.length;
                for (i = 0; i < length; i++) {
                    if (views[i].available()) {
                        return views[i];
                    }
                }
                kendo.logToConsole("Warning: KendoUI DataViz cannot render. Possible causes:\n" + "- The browser does not support SVG or VML. User agent: " + navigator.userAgent + "\n" + "- The kendo.dataviz.svg.js or kendo.dataviz.vml.js scripts are not loaded");
            },
            registerView: function(viewType) {
                var defaultView = dataviz.ui.views[0];
                if (!defaultView || viewType.preference > defaultView.preference) {
                    dataviz.ui.views.unshift(viewType);
                } else {
                    dataviz.ui.views.push(viewType);
                }
            },
            plugin: function(widget) {
                kendo.ui.plugin(widget, dataviz.ui);
            }
        },
        AXIS_LABEL_CLICK: AXIS_LABEL_CLICK,
        COORD_PRECISION: COORD_PRECISION,
        DEFAULT_PRECISION: DEFAULT_PRECISION,
        DEFAULT_WIDTH: DEFAULT_WIDTH,
        DEFAULT_HEIGHT: DEFAULT_HEIGHT,
        DEFAULT_FONT: DEFAULT_FONT,
        INITIAL_ANIMATION_DURATION: INITIAL_ANIMATION_DURATION,
        CLIP: CLIP,
        Axis: Axis,
        AxisLabel: AxisLabel,
        Box2D: Box2D,
        BoxElement: BoxElement,
        ChartElement: ChartElement,
        Color: Color,
        ElementAnimation: ElementAnimation,
        ExpandAnimation: ExpandAnimation,
        ArrowAnimation: ArrowAnimation,
        BarAnimation: BarAnimation,
        BarIndicatorAnimatin: BarIndicatorAnimatin,
        FadeAnimation: FadeAnimation,
        FadeAnimationDecorator: FadeAnimationDecorator,
        IDPool: IDPool,
        LRUCache: LRUCache,
        NumericAxis: NumericAxis,
        Point2D: Point2D,
        Ring: Ring,
        Pin: Pin,
        RootElement: RootElement,
        RotationAnimation: RotationAnimation,
        Sector: Sector,
        Text: Text,
        TextBox: TextBox,
        Title: Title,
        ViewBase: ViewBase,
        ViewElement: ViewElement,
        animationDecorator: animationDecorator,
        append: append,
        autoFormat: autoFormat,
        autoMajorUnit: autoMajorUnit,
        boxDiff: boxDiff,
        defined: defined,
        getElement: getElement,
        getSpacing: getSpacing,
        inArray: inArray,
        interpolateValue: interpolateValue,
        last: last,
        measureText: measureText,
        rotatePoint: rotatePoint,
        round: round,
        ceil: ceil,
        floor: floor,
        supportsSVG: supportsSVG,
        renderTemplate: renderTemplate,
        uniqueId: uniqueId
    });
})(window.kendo.jQuery);

(function() {
    // Imports ================================================================
    var kendo = window.kendo, ui = kendo.dataviz.ui, deepExtend = kendo.deepExtend;
    // Constants ==============================================================
    var BAR_GAP = 1.5, BAR_SPACING = .4, BLACK = "#000", SANS = "Arial,Helvetica,sans-serif", SANS11 = "11px " + SANS, SANS12 = "12px " + SANS, SANS16 = "16px " + SANS, WHITE = "#fff";
    var chartBaseTheme = {
        title: {
            font: SANS16
        },
        legend: {
            labels: {
                font: SANS12
            }
        },
        seriesDefaults: {
            labels: {
                font: SANS11
            },
            donut: {
                margin: 1
            },
            line: {
                width: 4
            },
            area: {
                line: {
                    opacity: 1,
                    width: 0
                }
            },
            candlestick: {
                line: {
                    width: 1,
                    color: BLACK
                },
                border: {
                    width: 1,
                    _brightness: .8
                },
                gap: 1,
                spacing: .3,
                downColor: WHITE,
                aggregate: {
                    open: "max",
                    high: "max",
                    low: "min",
                    close: "max"
                },
                highlight: {
                    line: {
                        width: 2
                    },
                    border: {
                        width: 2,
                        opacity: 1
                    }
                }
            },
            ohlc: {
                line: {
                    width: 1
                },
                gap: 1,
                spacing: .3,
                aggregate: {
                    open: "max",
                    high: "max",
                    low: "min",
                    close: "max"
                },
                highlight: {
                    line: {
                        width: 3,
                        opacity: 1
                    }
                }
            },
            bubble: {
                opacity: .6,
                border: {
                    width: 0
                },
                labels: {
                    background: "transparent"
                }
            },
            bar: {
                gap: BAR_GAP,
                spacing: BAR_SPACING
            },
            column: {
                gap: BAR_GAP,
                spacing: BAR_SPACING
            },
            bullet: {
                gap: BAR_GAP,
                spacing: BAR_SPACING,
                target: {
                    color: "#ff0000"
                }
            },
            verticalBullet: {
                gap: BAR_GAP,
                spacing: BAR_SPACING,
                target: {
                    color: "#ff0000"
                }
            }
        },
        categoryAxis: {
            majorGridLines: {
                visible: true
            }
        },
        axisDefaults: {
            labels: {
                font: SANS12
            },
            title: {
                font: SANS16,
                margin: 5
            },
            crosshair: {
                tooltip: {
                    font: SANS12
                }
            }
        },
        tooltip: {
            font: SANS12
        },
        navigator: {
            pane: {
                height: 90,
                margin: {
                    top: 10
                }
            }
        }
    };
    var gaugeBaseTheme = {
        scale: {
            labels: {
                font: SANS12
            }
        }
    };
    var themes = ui.themes, registerTheme = ui.registerTheme = function(themeName, options) {
        var result = {};
        // Apply base theme
        result.chart = deepExtend({}, chartBaseTheme, options.chart);
        result.gauge = deepExtend({}, gaugeBaseTheme, options.gauge);
        // Copy the line/area chart settings for their vertical counterparts
        var defaults = result.chart.seriesDefaults;
        defaults.verticalLine = deepExtend({}, defaults.line);
        defaults.verticalArea = deepExtend({}, defaults.area);
        themes[themeName] = result;
    };
    registerTheme("black", {
        chart: {
            title: {
                color: WHITE
            },
            legend: {
                labels: {
                    color: WHITE
                }
            },
            seriesDefaults: {
                labels: {
                    color: WHITE
                },
                pie: {
                    overlay: {
                        gradient: "sharpBevel"
                    }
                },
                donut: {
                    overlay: {
                        gradient: "sharpGlass"
                    }
                },
                line: {
                    markers: {
                        background: "#3d3d3d"
                    }
                },
                scatter: {
                    markers: {
                        background: "#3d3d3d"
                    }
                },
                scatterLine: {
                    markers: {
                        background: "#3d3d3d"
                    }
                },
                area: {
                    opacity: .4,
                    markers: {
                        visible: false,
                        size: 6
                    }
                },
                candlestick: {
                    downColor: "#555",
                    line: {
                        color: WHITE
                    },
                    border: {
                        _brightness: 1.5,
                        opacity: 1
                    },
                    highlight: {
                        border: {
                            color: WHITE,
                            opacity: .2
                        }
                    }
                },
                ohlc: {
                    line: {
                        color: WHITE
                    }
                }
            },
            chartArea: {
                background: "#3d3d3d"
            },
            seriesColors: [ "#0081da", "#3aafff", "#99c900", "#ffeb3d", "#b20753", "#ff4195" ],
            axisDefaults: {
                line: {
                    color: "#8e8e8e"
                },
                labels: {
                    color: WHITE
                },
                majorGridLines: {
                    color: "#545454"
                },
                minorGridLines: {
                    color: "#454545"
                },
                title: {
                    color: WHITE
                },
                crosshair: {
                    color: "#8e8e8e"
                }
            }
        },
        gauge: {
            pointer: {
                color: "#0070e4"
            },
            scale: {
                rangePlaceholderColor: "#1d1d1d",
                labels: {
                    color: WHITE
                },
                minorTicks: {
                    color: WHITE
                },
                majorTicks: {
                    color: WHITE
                },
                line: {
                    color: WHITE
                }
            }
        }
    });
    registerTheme("blueopal", {
        chart: {
            title: {
                color: "#293135"
            },
            legend: {
                labels: {
                    color: "#293135"
                }
            },
            seriesDefaults: {
                labels: {
                    color: BLACK,
                    background: WHITE,
                    opacity: .5
                },
                area: {
                    opacity: .4,
                    markers: {
                        visible: false,
                        size: 6
                    }
                },
                candlestick: {
                    downColor: "#c4d0d5",
                    line: {
                        color: "#9aabb2"
                    }
                }
            },
            seriesColors: [ "#0069a5", "#0098ee", "#7bd2f6", "#ffb800", "#ff8517", "#e34a00" ],
            axisDefaults: {
                line: {
                    color: "#9aabb2"
                },
                labels: {
                    color: "#293135"
                },
                majorGridLines: {
                    color: "#c4d0d5"
                },
                minorGridLines: {
                    color: "#edf1f2"
                },
                title: {
                    color: "#293135"
                },
                crosshair: {
                    color: "#9aabb2"
                }
            }
        },
        gauge: {
            pointer: {
                color: "#005c83"
            },
            scale: {
                rangePlaceholderColor: "#daecf4",
                labels: {
                    color: "#293135"
                },
                minorTicks: {
                    color: "#293135"
                },
                majorTicks: {
                    color: "#293135"
                },
                line: {
                    color: "#293135"
                }
            }
        }
    });
    registerTheme("highcontrast", {
        chart: {
            title: {
                color: "#ffffff"
            },
            legend: {
                labels: {
                    color: "#ffffff"
                }
            },
            seriesDefaults: {
                labels: {
                    color: "#ffffff"
                },
                pie: {
                    overlay: {
                        gradient: "sharpGlass"
                    }
                },
                donut: {
                    overlay: {
                        gradient: "sharpGlass"
                    }
                },
                line: {
                    markers: {
                        background: "#2c232b"
                    }
                },
                scatter: {
                    markers: {
                        background: "#2c232b"
                    }
                },
                scatterLine: {
                    markers: {
                        background: "#2c232b"
                    }
                },
                area: {
                    opacity: .5,
                    markers: {
                        visible: false,
                        size: 6
                    }
                },
                candlestick: {
                    downColor: "#664e62",
                    line: {
                        color: "#ffffff"
                    },
                    border: {
                        _brightness: 1.5,
                        opacity: 1
                    },
                    highlight: {
                        border: {
                            color: "#ffffff",
                            opacity: 1
                        }
                    }
                },
                ohlc: {
                    line: {
                        color: "#ffffff"
                    }
                }
            },
            chartArea: {
                background: "#2c232b"
            },
            seriesColors: [ "#a7008f", "#ffb800", "#3aafff", "#99c900", "#b20753", "#ff4195" ],
            axisDefaults: {
                line: {
                    color: "#ffffff"
                },
                labels: {
                    color: "#ffffff"
                },
                majorGridLines: {
                    color: "#664e62"
                },
                minorGridLines: {
                    color: "#4f394b"
                },
                title: {
                    color: "#ffffff"
                },
                crosshair: {
                    color: "#ffffff"
                }
            }
        },
        gauge: {
            pointer: {
                color: "#a7008f"
            },
            scale: {
                rangePlaceholderColor: "#2c232b",
                labels: {
                    color: "#ffffff"
                },
                minorTicks: {
                    color: "#2c232b"
                },
                majorTicks: {
                    color: "#664e62"
                },
                line: {
                    color: "#ffffff"
                }
            }
        }
    });
    registerTheme("default", {
        chart: {
            title: {
                color: "#8e8e8e"
            },
            legend: {
                labels: {
                    color: "#232323"
                }
            },
            seriesDefaults: {
                labels: {
                    color: BLACK,
                    background: WHITE,
                    opacity: .5
                },
                area: {
                    opacity: .4,
                    markers: {
                        visible: false,
                        size: 6
                    }
                },
                candlestick: {
                    downColor: "#dedede",
                    line: {
                        color: "#8d8d8d"
                    }
                }
            },
            seriesColors: [ "#ff6800", "#a0a700", "#ff8d00", "#678900", "#ffb53c", "#396000" ],
            axisDefaults: {
                line: {
                    color: "#8e8e8e"
                },
                labels: {
                    color: "#232323"
                },
                minorGridLines: {
                    color: "#f0f0f0"
                },
                majorGridLines: {
                    color: "#dfdfdf"
                },
                title: {
                    color: "#232323"
                },
                crosshair: {
                    color: "#8e8e8e"
                }
            }
        },
        gauge: {
            pointer: {
                color: "#ea7001"
            },
            scale: {
                rangePlaceholderColor: "#dedede",
                labels: {
                    color: "#2e2e2e"
                },
                minorTicks: {
                    color: "#2e2e2e"
                },
                majorTicks: {
                    color: "#2e2e2e"
                },
                line: {
                    color: "#2e2e2e"
                }
            }
        }
    });
    registerTheme("silver", {
        chart: {
            title: {
                color: "#4e5968"
            },
            legend: {
                labels: {
                    color: "#4e5968"
                }
            },
            seriesDefaults: {
                labels: {
                    color: "#293135",
                    background: "#eaeaec",
                    opacity: .5
                },
                line: {
                    markers: {
                        background: "#eaeaec"
                    }
                },
                scatter: {
                    markers: {
                        background: "#eaeaec"
                    }
                },
                scatterLine: {
                    markers: {
                        background: "#eaeaec"
                    }
                },
                pie: {
                    connectors: {
                        color: "#A6B1C0"
                    }
                },
                donut: {
                    connectors: {
                        color: "#A6B1C0"
                    }
                },
                area: {
                    opacity: .4,
                    markers: {
                        visible: false,
                        size: 6
                    }
                },
                candlestick: {
                    downColor: "#a6afbe"
                }
            },
            chartArea: {
                background: "#eaeaec"
            },
            seriesColors: [ "#007bc3", "#76b800", "#ffae00", "#ef4c00", "#a419b7", "#430B62" ],
            axisDefaults: {
                line: {
                    color: "#a6b1c0"
                },
                labels: {
                    color: "#4e5968"
                },
                majorGridLines: {
                    color: "#dcdcdf"
                },
                minorGridLines: {
                    color: "#eeeeef"
                },
                title: {
                    color: "#4e5968"
                },
                crosshair: {
                    color: "#a6b1c0"
                }
            }
        },
        gauge: {
            pointer: {
                color: "#0879c0"
            },
            scale: {
                rangePlaceholderColor: "#f3f3f4",
                labels: {
                    color: "#515967"
                },
                minorTicks: {
                    color: "#515967"
                },
                majorTicks: {
                    color: "#515967"
                },
                line: {
                    color: "#515967"
                }
            }
        }
    });
    registerTheme("metro", {
        chart: {
            title: {
                color: "#777777"
            },
            legend: {
                labels: {
                    color: "#777777"
                }
            },
            seriesDefaults: {
                labels: {
                    color: BLACK
                },
                area: {
                    opacity: .4,
                    markers: {
                        visible: false,
                        size: 6
                    }
                },
                candlestick: {
                    downColor: "#c7c7c7",
                    line: {
                        color: "#787878"
                    }
                },
                overlay: {
                    gradient: "none"
                },
                border: {
                    _brightness: 1
                }
            },
            seriesColors: [ "#8ebc00", "#309b46", "#25a0da", "#ff6900", "#e61e26", "#d8e404", "#16aba9", "#7e51a1", "#313131", "#ed1691" ],
            axisDefaults: {
                line: {
                    color: "#c7c7c7"
                },
                labels: {
                    color: "#777777"
                },
                minorGridLines: {
                    color: "#c7c7c7"
                },
                majorGridLines: {
                    color: "#c7c7c7"
                },
                title: {
                    color: "#777777"
                },
                crosshair: {
                    color: "#c7c7c7"
                }
            }
        },
        gauge: {
            pointer: {
                color: "#8ebc00"
            },
            scale: {
                rangePlaceholderColor: "#e6e6e6",
                labels: {
                    color: "#777"
                },
                minorTicks: {
                    color: "#777"
                },
                majorTicks: {
                    color: "#777"
                },
                line: {
                    color: "#777"
                }
            }
        }
    });
    registerTheme("metroblack", {
        chart: {
            title: {
                color: "#ffffff"
            },
            legend: {
                labels: {
                    color: "#ffffff"
                }
            },
            seriesDefaults: {
                border: {
                    _brightness: 1
                },
                labels: {
                    color: "#ffffff"
                },
                line: {
                    markers: {
                        background: "#0e0e0e"
                    }
                },
                bubble: {
                    opacity: .6
                },
                scatter: {
                    markers: {
                        background: "#0e0e0e"
                    }
                },
                scatterLine: {
                    markers: {
                        background: "#0e0e0e"
                    }
                },
                area: {
                    opacity: .4,
                    markers: {
                        visible: false,
                        size: 6
                    }
                },
                candlestick: {
                    downColor: "#828282",
                    line: {
                        color: "#ffffff"
                    }
                },
                overlay: {
                    gradient: "none"
                }
            },
            chartArea: {
                background: "#0e0e0e"
            },
            seriesColors: [ "#00aba9", "#309b46", "#8ebc00", "#ff6900", "#e61e26", "#d8e404", "#25a0da", "#7e51a1", "#313131", "#ed1691" ],
            axisDefaults: {
                line: {
                    color: "#cecece"
                },
                labels: {
                    color: "#ffffff"
                },
                minorGridLines: {
                    color: "#2d2d2d"
                },
                majorGridLines: {
                    color: "#333333"
                },
                title: {
                    color: "#ffffff"
                },
                crosshair: {
                    color: "#cecece"
                }
            }
        },
        gauge: {
            pointer: {
                color: "#00aba9"
            },
            scale: {
                rangePlaceholderColor: "#2d2d2d",
                labels: {
                    color: "#ffffff"
                },
                minorTicks: {
                    color: "#333333"
                },
                majorTicks: {
                    color: "#cecece"
                },
                line: {
                    color: "#cecece"
                }
            }
        }
    });
    registerTheme("moonlight", {
        chart: {
            title: {
                color: "#ffffff"
            },
            legend: {
                labels: {
                    color: "#ffffff"
                }
            },
            seriesDefaults: {
                labels: {
                    color: "#ffffff"
                },
                pie: {
                    overlay: {
                        gradient: "sharpBevel"
                    }
                },
                donut: {
                    overlay: {
                        gradient: "sharpGlass"
                    }
                },
                line: {
                    markers: {
                        background: "#212a33"
                    }
                },
                bubble: {
                    opacity: .6
                },
                scatter: {
                    markers: {
                        background: "#212a33"
                    }
                },
                scatterLine: {
                    markers: {
                        background: "#212a33"
                    }
                },
                area: {
                    opacity: .3,
                    markers: {
                        visible: false,
                        size: 6
                    }
                },
                candlestick: {
                    downColor: "#757d87",
                    line: {
                        color: "#ea9d06"
                    },
                    border: {
                        _brightness: 1.5,
                        opacity: 1
                    },
                    highlight: {
                        border: {
                            color: WHITE,
                            opacity: .2
                        }
                    }
                },
                ohlc: {
                    line: {
                        color: "#ea9d06"
                    }
                }
            },
            chartArea: {
                background: "#212a33"
            },
            seriesColors: [ "#ffca08", "#ff710f", "#ed2e24", "#ff9f03", "#e13c02", "#a00201" ],
            axisDefaults: {
                line: {
                    color: "#8c909e"
                },
                minorTicks: {
                    color: "#8c909e"
                },
                majorTicks: {
                    color: "#8c909e"
                },
                labels: {
                    color: "#ffffff"
                },
                majorGridLines: {
                    color: "#3e424d"
                },
                minorGridLines: {
                    color: "#2f3640"
                },
                title: {
                    color: "#ffffff"
                },
                crosshair: {
                    color: "#8c909e"
                }
            }
        },
        gauge: {
            pointer: {
                color: "#f4af03"
            },
            scale: {
                rangePlaceholderColor: "#2f3640",
                labels: {
                    color: WHITE
                },
                minorTicks: {
                    color: "#8c909e"
                },
                majorTicks: {
                    color: "#8c909e"
                },
                line: {
                    color: "#8c909e"
                }
            }
        }
    });
    registerTheme("uniform", {
        chart: {
            title: {
                color: "#686868"
            },
            legend: {
                labels: {
                    color: "#686868"
                }
            },
            seriesDefaults: {
                labels: {
                    color: "#686868"
                },
                pie: {
                    overlay: {
                        gradient: "sharpBevel"
                    }
                },
                donut: {
                    overlay: {
                        gradient: "sharpGlass"
                    }
                },
                line: {
                    markers: {
                        background: "#ffffff"
                    }
                },
                bubble: {
                    opacity: .6
                },
                scatter: {
                    markers: {
                        background: "#ffffff"
                    }
                },
                scatterLine: {
                    markers: {
                        background: "#ffffff"
                    }
                },
                area: {
                    opacity: .3,
                    markers: {
                        visible: false,
                        size: 6
                    }
                },
                candlestick: {
                    downColor: "#cccccc",
                    line: {
                        color: "#cccccc"
                    },
                    border: {
                        _brightness: 1.5,
                        opacity: 1
                    },
                    highlight: {
                        border: {
                            color: "#cccccc",
                            opacity: .2
                        }
                    }
                },
                ohlc: {
                    line: {
                        color: "#cccccc"
                    }
                }
            },
            chartArea: {
                background: "#ffffff"
            },
            seriesColors: [ "#527aa3", "#6f91b3", "#8ca7c2", "#a8bdd1", "#c5d3e0", "#e2e9f0" ],
            axisDefaults: {
                line: {
                    color: "#9e9e9e"
                },
                minorTicks: {
                    color: "#aaaaaa"
                },
                majorTicks: {
                    color: "#888888"
                },
                labels: {
                    color: "#686868"
                },
                majorGridLines: {
                    color: "#dadada"
                },
                minorGridLines: {
                    color: "#e7e7e7"
                },
                title: {
                    color: "#686868"
                },
                crosshair: {
                    color: "#9e9e9e"
                }
            }
        },
        gauge: {
            pointer: {
                color: "#527aa3"
            },
            scale: {
                rangePlaceholderColor: "#e7e7e7",
                labels: {
                    color: "#686868"
                },
                minorTicks: {
                    color: "#aaaaaa"
                },
                majorTicks: {
                    color: "#888888"
                },
                line: {
                    color: "#9e9e9e"
                }
            }
        }
    });
    registerTheme("bootstrap", {
        chart: {
            title: {
                color: "#343434"
            },
            legend: {
                labels: {
                    color: "#343434"
                }
            },
            seriesDefaults: {
                labels: {
                    color: "#343434"
                },
                pie: {
                    overlay: {
                        gradient: "sharpBevel"
                    }
                },
                donut: {
                    overlay: {
                        gradient: "sharpGlass"
                    }
                },
                line: {
                    markers: {
                        background: "#ffffff"
                    }
                },
                bubble: {
                    opacity: .6
                },
                scatter: {
                    markers: {
                        background: "#ffffff"
                    }
                },
                scatterLine: {
                    markers: {
                        background: "#ffffff"
                    }
                },
                area: {
                    opacity: .3,
                    markers: {
                        visible: false,
                        size: 6
                    }
                },
                candlestick: {
                    downColor: "#d0d0d0",
                    line: {
                        color: "#d0d0d0"
                    },
                    border: {
                        _brightness: 1.5,
                        opacity: 1
                    },
                    highlight: {
                        border: {
                            color: "#b8b8b8",
                            opacity: .2
                        }
                    }
                },
                ohlc: {
                    line: {
                        color: "#d0d0d0"
                    }
                }
            },
            chartArea: {
                background: "#ffffff"
            },
            seriesColors: [ "#006dcc", "#49AFCD", "#5BB75B", "#FAA732", "#DA4F49", "#363636" ],
            axisDefaults: {
                line: {
                    color: "#b8b8b8"
                },
                minorTicks: {
                    color: "#dddddd"
                },
                majorTicks: {
                    color: "#b8b8b8"
                },
                labels: {
                    color: "#343434"
                },
                majorGridLines: {
                    color: "#b8b8b8"
                },
                minorGridLines: {
                    color: "#dddddd"
                },
                title: {
                    color: "#343434"
                },
                crosshair: {
                    color: "#b8b8b8"
                }
            }
        },
        gauge: {
            pointer: {
                color: "#0044cc"
            },
            scale: {
                rangePlaceholderColor: "#b8b8b8",
                labels: {
                    color: "#343434"
                },
                minorTicks: {
                    color: "#dddddd"
                },
                majorTicks: {
                    color: "#b8b8b8"
                },
                line: {
                    color: "#b8b8b8"
                }
            }
        }
    });
})(window.kendo.jQuery);

(function($, undefined) {
    // Imports ================================================================
    var each = $.each, isArray = $.isArray, map = $.map, math = Math, extend = $.extend, proxy = $.proxy, kendo = window.kendo, Class = kendo.Class, Observable = kendo.Observable, DataSource = kendo.data.DataSource, Widget = kendo.ui.Widget, template = kendo.template, deepExtend = kendo.deepExtend, getter = kendo.getter, dataviz = kendo.dataviz, Axis = dataviz.Axis, AxisLabel = dataviz.AxisLabel, BarAnimation = dataviz.BarAnimation, Box2D = dataviz.Box2D, BoxElement = dataviz.BoxElement, ChartElement = dataviz.ChartElement, Color = dataviz.Color, ElementAnimation = dataviz.ElementAnimation, NumericAxis = dataviz.NumericAxis, Point2D = dataviz.Point2D, RootElement = dataviz.RootElement, Ring = dataviz.Ring, Text = dataviz.Text, TextBox = dataviz.TextBox, Title = dataviz.Title, animationDecorator = dataviz.animationDecorator, append = dataviz.append, autoFormat = dataviz.autoFormat, defined = dataviz.defined, getElement = dataviz.getElement, getSpacing = dataviz.getSpacing, inArray = dataviz.inArray, interpolateValue = dataviz.interpolateValue, last = dataviz.last, round = dataviz.round, renderTemplate = dataviz.renderTemplate, uniqueId = dataviz.uniqueId;
    // Constants ==============================================================
    var NS = ".kendoChart", ABOVE = "above", AREA = "area", AUTO = "auto", FIT = "fit", AXIS_LABEL_CLICK = dataviz.AXIS_LABEL_CLICK, BAR = "bar", BAR_BORDER_BRIGHTNESS = .8, BELOW = "below", BLACK = "#000", BOTH = "both", BOTTOM = "bottom", BUBBLE = "bubble", BULLET = "bullet", CANDLESTICK = "candlestick", CATEGORY = "category", CENTER = "center", CHANGE = "change", CIRCLE = "circle", CLICK_NS = "click" + NS, CLIP = dataviz.CLIP, COLUMN = "column", COORD_PRECISION = dataviz.COORD_PRECISION, CSS_PREFIX = "k-", DATABOUND = "dataBound", DATE = "date", DATE_REGEXP = /^\/Date\((.*?)\)\/$/, DAYS = "days", DEFAULT_FONT = dataviz.DEFAULT_FONT, DEFAULT_HEIGHT = dataviz.DEFAULT_HEIGHT, DEFAULT_PRECISION = dataviz.DEFAULT_PRECISION, DEFAULT_WIDTH = dataviz.DEFAULT_WIDTH, DEGREE = math.PI / 180, DONUT = "donut", DONUT_SECTOR_ANIM_DELAY = 50, DRAG = "drag", DRAG_END = "dragEnd", DRAG_START = "dragStart", FADEIN = "fadeIn", GLASS = "glass", HOURS = "hours", INITIAL_ANIMATION_DURATION = dataviz.INITIAL_ANIMATION_DURATION, INSIDE_BASE = "insideBase", INSIDE_END = "insideEnd", INTERPOLATE = "interpolate", LEFT = "left", LINE = "line", LINE_MARKER_SIZE = 8, MAX_VALUE = Number.MAX_VALUE, MIN_VALUE = -Number.MAX_VALUE, MINUTES = "minutes", MONTHS = "months", MOUSELEAVE_NS = "mouseleave" + NS, MOUSEMOVE_TRACKING = "mousemove.tracking", MOUSEOVER_NS = "mouseover" + NS, MOUSEMOVE_NS = "mousemove" + NS, MOUSEMOVE_THROTTLE = 20, MOUSEWHEEL_DELAY = 150, MOUSEWHEEL_NS = "DOMMouseScroll" + NS + " mousewheel" + NS, OHLC = "ohlc", OUTSIDE_END = "outsideEnd", OUTLINE_SUFFIX = "_outline", PIE = "pie", PIE_SECTOR_ANIM_DELAY = 70, PLOT_AREA_CLICK = "plotAreaClick", RIGHT = "right", ROUNDED_BEVEL = "roundedBevel", ROUNDED_GLASS = "roundedGlass", SCATTER = "scatter", SCATTER_LINE = "scatterLine", SELECT_START = "selectStart", SELECT = "select", SELECT_END = "selectEnd", SERIES_CLICK = "seriesClick", SERIES_HOVER = "seriesHover", STRING = "string", TIME_PER_MINUTE = 6e4, TIME_PER_HOUR = 60 * TIME_PER_MINUTE, TIME_PER_DAY = 24 * TIME_PER_HOUR, TIME_PER_WEEK = 7 * TIME_PER_DAY, TIME_PER_MONTH = 31 * TIME_PER_DAY, TIME_PER_YEAR = 365 * TIME_PER_DAY, TIME_PER_UNIT = {
        years: TIME_PER_YEAR,
        months: TIME_PER_MONTH,
        weeks: TIME_PER_WEEK,
        days: TIME_PER_DAY,
        hours: TIME_PER_HOUR,
        minutes: TIME_PER_MINUTE
    }, TOP = "top", TOOLTIP_ANIMATION_DURATION = 150, TOOLTIP_OFFSET = 5, TOOLTIP_SHOW_DELAY = 100, TOOLTIP_HIDE_DELAY = 100, TOOLTIP_INVERSE = "tooltip-inverse", TOUCH_START_NS = "touchstart" + NS, TRIANGLE = "triangle", VALUE = "value", VERTICAL_AREA = "verticalArea", VERTICAL_BULLET = "verticalBullet", VERTICAL_LINE = "verticalLine", WEEKS = "weeks", WHITE = "#fff", X = "x", Y = "y", YEARS = "years", ZERO = "zero", ZOOM_ACCELERATION = 3, ZOOM_START = "zoomStart", ZOOM = "zoom", ZOOM_END = "zoomEnd", CATEGORICAL_CHARTS = [ BAR, COLUMN, LINE, VERTICAL_LINE, AREA, VERTICAL_AREA, CANDLESTICK, OHLC, BULLET, VERTICAL_BULLET ], XY_CHARTS = [ SCATTER, SCATTER_LINE, BUBBLE ], BASE_UNITS = [ MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS ];
    var DateLabelFormats = {
        minutes: "HH:mm",
        hours: "HH:mm",
        days: "M/d",
        weeks: "M/d",
        months: "MMM 'yy",
        years: "yyyy"
    };
    // Chart ==================================================================
    var Chart = Widget.extend({
        init: function(element, userOptions) {
            var chart = this, options, themeOptions, themes = dataviz.ui.themes || {}, theme, themeName;
            kendo.destroy(element);
            Widget.fn.init.call(chart, element);
            options = deepExtend({}, chart.options, userOptions);
            chart.element.addClass(CSS_PREFIX + options.name.toLowerCase()).css("position", "relative");
            // Used by the ThemeBuilder
            chart._originalOptions = deepExtend({}, options);
            themeName = options.theme;
            theme = themes[themeName] || themes[themeName.toLowerCase()];
            themeOptions = themeName && theme ? theme.chart : {};
            resolveAxisAliases(options);
            chart._applyDefaults(options, themeOptions);
            chart.options = deepExtend({}, themeOptions, options);
            applySeriesColors(chart.options);
            chart.bind(chart.events, chart.options);
            chart.wrapper = chart.element;
            chart._initDataSource(userOptions);
            kendo.notify(chart, dataviz.ui);
        },
        _initDataSource: function(userOptions) {
            var chart = this, dataSourceOptions = (userOptions || {}).dataSource;
            chart._dataChangeHandler = proxy(chart._onDataChanged, chart);
            chart.dataSource = DataSource.create(dataSourceOptions).bind(CHANGE, chart._dataChangeHandler);
            chart._redraw();
            chart._attachEvents();
            if (dataSourceOptions && chart.options.autoBind) {
                chart.dataSource.fetch();
            }
        },
        setDataSource: function(dataSource) {
            var chart = this;
            chart.dataSource.unbind(CHANGE, chart._dataChangeHandler);
            chart.dataSource = dataSource;
            dataSource.bind(CHANGE, chart._dataChangeHandler);
            if (chart.options.autoBind) {
                dataSource.fetch();
            }
        },
        events: [ DATABOUND, SERIES_CLICK, SERIES_HOVER, AXIS_LABEL_CLICK, PLOT_AREA_CLICK, DRAG_START, DRAG, DRAG_END, ZOOM_START, ZOOM, ZOOM_END, SELECT_START, SELECT, SELECT_END ],
        items: function() {
            return $();
        },
        options: {
            name: "Chart",
            theme: "default",
            chartArea: {},
            legend: {
                visible: true,
                labels: {}
            },
            categoryAxis: {},
            autoBind: true,
            seriesDefaults: {
                type: COLUMN,
                data: [],
                highlight: {
                    visible: true
                },
                groupNameTemplate: "#= group.value + (kendo.dataviz.defined(series.name) ? ': ' + series.name : '') #",
                labels: {}
            },
            series: [],
            tooltip: {
                visible: false
            },
            transitions: true,
            valueAxis: {},
            plotArea: {},
            title: {},
            xAxis: {},
            yAxis: {}
        },
        refresh: function() {
            var chart = this;
            chart._applyDefaults(chart.options);
            delete chart._sourceSeries;
            chart._onDataChanged();
        },
        redraw: function(paneName) {
            var chart = this, pane, plotArea;
            chart._applyDefaults(chart.options);
            if (paneName) {
                plotArea = chart._model._plotArea;
                pane = plotArea.findPane(paneName);
                plotArea.redraw(pane);
            } else {
                chart._redraw();
            }
        },
        _redraw: function() {
            var chart = this, model = chart._getModel(), viewType = dataviz.ui.defaultView(), view;
            chart._destroyView();
            chart._model = model;
            chart._plotArea = model._plotArea;
            if (viewType) {
                view = chart._view = viewType.fromModel(model);
                chart._viewElement = chart._renderView(view);
                chart._tooltip = chart._createTooltip();
                chart._highlight = new Highlight(view, chart._viewElement);
                chart._setupSelection();
            }
        },
        _sharedTooltip: function() {
            var chart = this, options = chart.options;
            return chart._plotArea instanceof CategoricalPlotArea && options.tooltip.shared;
        },
        _createTooltip: function() {
            var chart = this, options = chart.options, element = chart.element, tooltip;
            if (chart._sharedTooltip()) {
                tooltip = new SharedTooltip(element, chart._plotArea, options.tooltip);
            } else {
                tooltip = new Tooltip(element, options.tooltip);
            }
            return tooltip;
        },
        _renderView: function() {
            var chart = this;
            return chart._view.renderTo(chart.element[0]);
        },
        svg: function() {
            var model = this._getModel(), view = dataviz.SVGView.fromModel(model);
            return view.render();
        },
        _applyDefaults: function(options, themeOptions) {
            applyAxisDefaults(options, themeOptions);
            applySeriesDefaults(options, themeOptions);
        },
        _getModel: function() {
            var chart = this, options = chart.options, model = new RootElement(chart._modelOptions()), plotArea;
            model.parent = chart;
            Title.buildTitle(options.title, model);
            plotArea = model._plotArea = chart._createPlotArea();
            if (options.legend.visible) {
                model.append(new Legend(plotArea.options.legend));
            }
            model.append(plotArea);
            model.reflow();
            return model;
        },
        _modelOptions: function() {
            var chart = this, options = chart.options, element = chart.element, height = math.floor(element.height()), width = math.floor(element.width());
            return deepExtend({
                width: width || DEFAULT_WIDTH,
                height: height || DEFAULT_HEIGHT,
                transitions: options.transitions
            }, options.chartArea);
        },
        _createPlotArea: function() {
            var chart = this, options = chart.options, series = options.series, i, length = series.length, currentSeries, categoricalSeries = [], xySeries = [], pieSeries = [], donutSeries = [], bulletSeries = [], plotArea;
            for (i = 0; i < length; i++) {
                currentSeries = series[i];
                if (inArray(currentSeries.type, CATEGORICAL_CHARTS)) {
                    categoricalSeries.push(currentSeries);
                } else if (inArray(currentSeries.type, XY_CHARTS)) {
                    xySeries.push(currentSeries);
                } else if (currentSeries.type === PIE) {
                    pieSeries.push(currentSeries);
                } else if (currentSeries.type === DONUT) {
                    donutSeries.push(currentSeries);
                } else if (currentSeries.type === BULLET) {
                    bulletSeries.push(currentSeries);
                }
            }
            if (pieSeries.length > 0) {
                plotArea = new PiePlotArea(pieSeries, options);
            } else if (donutSeries.length > 0) {
                plotArea = new DonutPlotArea(donutSeries, options);
            } else if (xySeries.length > 0) {
                plotArea = new XYPlotArea(xySeries, options);
            } else {
                plotArea = new CategoricalPlotArea(categoricalSeries, options);
            }
            return plotArea;
        },
        _setupSelection: function() {
            var chart = this, plotArea = chart._plotArea, axes = plotArea.axes, selections = chart._selections = [], selection, i, axis, min, max, options;
            if (!chart._selectStartHandler) {
                chart._selectStartHandler = proxy(chart._selectStart, chart);
                chart._selectHandler = proxy(chart._select, chart);
                chart._selectEndHandler = proxy(chart._selectEnd, chart);
            }
            for (i = 0; i < axes.length; i++) {
                axis = axes[i];
                options = axis.options;
                if (axis instanceof CategoryAxis && options.select && !options.vertical) {
                    min = 0;
                    max = options.categories.length - 1;
                    if (axis instanceof DateCategoryAxis) {
                        min = options.categories[min];
                        max = options.categories[max];
                    }
                    if (!options.justified) {
                        if (axis instanceof DateCategoryAxis) {
                            max = addDuration(max, 1, options.baseUnit, options.weekStartDay);
                        } else {
                            max++;
                        }
                    }
                    selection = new Selection(chart, axis, deepExtend({
                        min: min,
                        max: max
                    }, options.select));
                    selection.bind(SELECT_START, chart._selectStartHandler);
                    selection.bind(SELECT, chart._selectHandler);
                    selection.bind(SELECT_END, chart._selectEndHandler);
                    selections.push(selection);
                }
            }
        },
        _selectStart: function(e) {
            return this.trigger(SELECT_START, e);
        },
        _select: function(e) {
            return this.trigger(SELECT, e);
        },
        _selectEnd: function(e) {
            return this.trigger(SELECT_END, e);
        },
        _attachEvents: function() {
            var chart = this, element = chart.element;
            element.on(CLICK_NS, proxy(chart._click, chart));
            element.on(MOUSEOVER_NS, proxy(chart._mouseover, chart));
            element.on(MOUSEWHEEL_NS, proxy(chart._mousewheel, chart));
            element.on(TOUCH_START_NS, proxy(chart._tap, chart));
            element.on(MOUSELEAVE_NS, proxy(chart._mouseleave, chart));
            if (chart._plotArea.crosshairs.length || chart._tooltip && chart._sharedTooltip()) {
                element.on(MOUSEMOVE_NS, proxy(chart._mousemove, chart));
            }
            if (kendo.UserEvents) {
                chart._userEvents = new kendo.UserEvents(element, {
                    global: true,
                    threshold: 5,
                    filter: ":not(.k-selector)",
                    multiTouch: false,
                    start: proxy(chart._start, chart),
                    move: proxy(chart._move, chart),
                    end: proxy(chart._end, chart)
                });
            }
        },
        _start: function(e) {
            var chart = this, events = chart._events;
            if (defined(events[DRAG_START] || events[DRAG] || events[DRAG_END])) {
                chart._startNavigation(e, DRAG_START);
            }
        },
        _move: function(e) {
            var chart = this, state = chart._navState, axes, ranges = {}, i, currentAxis, axisName, axis, delta;
            if (state) {
                e.preventDefault();
                axes = state.axes;
                for (i = 0; i < axes.length; i++) {
                    currentAxis = axes[i];
                    axisName = currentAxis.options.name;
                    if (axisName) {
                        axis = currentAxis.options.vertical ? e.y : e.x;
                        delta = axis.startLocation - axis.location;
                        if (delta !== 0) {
                            ranges[currentAxis.options.name] = currentAxis.translateRange(delta);
                        }
                    }
                }
                state.axisRanges = ranges;
                chart.trigger(DRAG, {
                    axisRanges: ranges,
                    originalEvent: e
                });
            }
        },
        _end: function(e) {
            this._endNavigation(e, DRAG_END);
        },
        _mousewheel: function(e) {
            var chart = this, origEvent = e.originalEvent, prevented, delta = mwDelta(e), totalDelta, state = chart._navState, axes, i, currentAxis, axisName, ranges = {};
            if (!state) {
                prevented = chart._startNavigation(origEvent, ZOOM_START);
                if (!prevented) {
                    state = chart._navState;
                }
            }
            if (state) {
                totalDelta = state.totalDelta || delta;
                state.totalDelta = totalDelta + delta;
                axes = chart._navState.axes;
                for (i = 0; i < axes.length; i++) {
                    currentAxis = axes[i];
                    axisName = currentAxis.options.name;
                    if (axisName) {
                        ranges[axisName] = currentAxis.scaleRange(totalDelta);
                    }
                }
                chart.trigger(ZOOM, {
                    delta: delta,
                    axisRanges: ranges,
                    originalEvent: e
                });
                if (chart._mwTimeout) {
                    clearTimeout(chart._mwTimeout);
                }
                chart._mwTimeout = setTimeout(function() {
                    chart._endNavigation(e, ZOOM_END);
                }, MOUSEWHEEL_DELAY);
            }
        },
        _startNavigation: function(e, chartEvent) {
            var chart = this, coords = chart._eventCoordinates(e), plotArea = chart._model._plotArea, pane = plotArea.findPointPane(coords), axes = plotArea.axes.slice(0), i, currentAxis, inAxis = false, prevented;
            if (!pane) {
                return;
            }
            for (i = 0; i < axes.length; i++) {
                currentAxis = axes[i];
                if (currentAxis.box.containsPoint(coords)) {
                    inAxis = true;
                    break;
                }
            }
            if (!inAxis && plotArea.backgroundBox().containsPoint(coords)) {
                prevented = chart.trigger(chartEvent, {
                    axisRanges: axisRanges(axes),
                    originalEvent: e
                });
                if (prevented) {
                    chart._userEvents.cancel();
                } else {
                    chart._suppressHover = true;
                    chart._unsetActivePoint();
                    chart._navState = {
                        pane: pane,
                        axes: axes
                    };
                }
            }
        },
        _endNavigation: function(e, chartEvent) {
            var chart = this;
            if (chart._navState) {
                chart.trigger(chartEvent, {
                    axisRanges: chart._navState.axisRanges,
                    originalEvent: e
                });
                chart._suppressHover = false;
                chart._navState = null;
            }
        },
        _getChartElement: function(e) {
            var chart = this, modelId = $(e.target).data("modelId"), model = chart._model, element;
            if (modelId) {
                element = model.modelMap[modelId];
            }
            if (element && element.aliasFor) {
                element = element.aliasFor(e, chart._eventCoordinates(e));
            }
            return element;
        },
        _eventCoordinates: function(e) {
            var chart = this, isTouch = defined((e.x || {}).client), clientX = isTouch ? e.x.client : e.clientX, clientY = isTouch ? e.y.client : e.clientY;
            return chart._toModelCoordinates(clientX, clientY);
        },
        _toModelCoordinates: function(clientX, clientY) {
            var element = this.element, offset = element.offset(), paddingLeft = parseInt(element.css("paddingLeft"), 10), paddingTop = parseInt(element.css("paddingTop"), 10), win = $(window);
            return new Point2D(clientX - offset.left - paddingLeft + win.scrollLeft(), clientY - offset.top - paddingTop + win.scrollTop());
        },
        _click: function(e) {
            var chart = this, element = chart._getChartElement(e);
            while (element) {
                if (element.click) {
                    element.click(chart, e);
                }
                element = element.parent;
            }
        },
        _startHover: function(e) {
            var chart = this, tooltip = chart._tooltip, highlight = chart._highlight, tooltipOptions = chart.options.tooltip, point;
            if (chart._suppressHover || !highlight || inArray(e.target, highlight._overlays) || chart._sharedTooltip()) {
                return;
            }
            point = chart._getChartElement(e);
            if (point && point.hover) {
                point.hover(chart, e);
                chart._activePoint = point;
                tooltipOptions = deepExtend({}, tooltipOptions, point.options.tooltip);
                if (tooltipOptions.visible) {
                    tooltip.show(point);
                }
                highlight.show(point);
                return true;
            }
        },
        _mouseover: function(e) {
            var chart = this;
            if (chart._startHover(e)) {
                $(document).on(MOUSEMOVE_TRACKING, proxy(chart._mouseMoveTracking, chart));
            }
        },
        _mouseMoveTracking: function(e) {
            var chart = this, options = chart.options, tooltip = chart._tooltip, highlight = chart._highlight, coords = chart._eventCoordinates(e), point = chart._activePoint, tooltipOptions, owner, seriesPoint;
            if (chart._plotArea.box.containsPoint(coords)) {
                if (point && point.series && (point.series.type === LINE || point.series.type === AREA)) {
                    owner = point.parent;
                    seriesPoint = owner.getNearestPoint(coords.x, coords.y, point.seriesIx);
                    if (seriesPoint && seriesPoint != point) {
                        seriesPoint.hover(chart, e);
                        chart._activePoint = seriesPoint;
                        tooltipOptions = deepExtend({}, options.tooltip, point.options.tooltip);
                        if (tooltipOptions.visible) {
                            tooltip.show(seriesPoint);
                        }
                        highlight.show(seriesPoint);
                    }
                }
            } else {
                $(document).off(MOUSEMOVE_TRACKING);
                chart._unsetActivePoint();
            }
        },
        _mousemove: function(e) {
            var chart = this, now = new Date(), timestamp = chart._mousemove.timestamp;
            if (!timestamp || now - timestamp > MOUSEMOVE_THROTTLE) {
                var coords = chart._eventCoordinates(e);
                chart._trackCrosshairs(coords);
                if (chart._sharedTooltip()) {
                    chart._trackSharedTooltip(coords);
                }
                chart._mousemove.timestamp = now;
            }
        },
        _trackCrosshairs: function(coords) {
            var crosshairs = this._plotArea.crosshairs, i, current;
            for (i = 0; i < crosshairs.length; i++) {
                current = crosshairs[i];
                if (current.box.containsPoint(coords)) {
                    current.showAt(coords);
                } else {
                    current.hide();
                }
            }
        },
        _trackSharedTooltip: function(coords) {
            var chart = this, options = chart.options, plotArea = chart._plotArea, categoryAxis = plotArea.categoryAxis, tooltip = chart._tooltip, tooltipOptions = options.tooltip, highlight = chart._highlight, index, points;
            index = categoryAxis.getCategoryIndex(coords);
            if (index !== chart._tooltipCategoryIx) {
                points = plotArea.pointsByCategoryIndex(index);
                if (points.length > 0) {
                    if (tooltipOptions.visible) {
                        tooltip.showAt(points, coords);
                    }
                    highlight.show(points);
                } else {
                    tooltip.hide();
                }
                chart._tooltipCategoryIx = index;
            }
        },
        _mouseleave: function() {
            var chart = this, plotArea = chart._plotArea, crosshairs = plotArea.crosshairs, tooltip = chart._tooltip, highlight = chart._highlight, i;
            for (i = 0; i < crosshairs.length; i++) {
                crosshairs[i].hide();
            }
            setTimeout(proxy(tooltip.hide, tooltip), TOOLTIP_HIDE_DELAY);
            highlight.hide();
            chart._tooltipCategoryIx = null;
        },
        _unsetActivePoint: function() {
            var chart = this, tooltip = chart._tooltip, highlight = chart._highlight;
            chart._activePoint = null;
            if (tooltip) {
                tooltip.hide();
            }
            if (highlight) {
                highlight.hide();
            }
        },
        _onDataChanged: function() {
            var chart = this, options = chart.options, series = chart._sourceSeries || options.series, seriesIx, seriesLength = series.length, data = chart.dataSource.view(), grouped = (chart.dataSource.group() || []).length > 0, categoriesData = grouped ? (data[0] || []).items : data, processedSeries = [], currentSeries;
            for (seriesIx = 0; seriesIx < seriesLength; seriesIx++) {
                currentSeries = series[seriesIx];
                if (chart.isBindable(currentSeries)) {
                    if (currentSeries.autoBind !== false) {
                        currentSeries.data = data;
                    }
                    append(processedSeries, grouped ? chart._createGroupedSeries(currentSeries, data) : [ currentSeries ]);
                } else {
                    processedSeries.push(currentSeries);
                }
            }
            chart._sourceSeries = series;
            options.series = processedSeries;
            applySeriesColors(chart.options);
            chart._bindCategories(categoriesData);
            chart.trigger(DATABOUND);
            chart._redraw();
        },
        _bindCategories: function(data) {
            var chart = this, options = chart.options, definitions = [].concat(options.categoryAxis), axisIx, axis;
            for (axisIx = 0; axisIx < definitions.length; axisIx++) {
                axis = definitions[axisIx];
                if (axis.autoBind !== false) {
                    chart._bindCategoryAxis(axis, data);
                }
            }
        },
        _bindCategoryAxis: function(axis, data) {
            var categoryIx, category, row;
            if (axis.field) {
                axis.categories = [];
                for (categoryIx = 0; categoryIx < data.length; categoryIx++) {
                    row = data[categoryIx];
                    category = getField(axis.field, row);
                    if (categoryIx === 0) {
                        axis.categories = [ category ];
                        axis.dataItems = [ row ];
                    } else {
                        axis.categories.push(category);
                        axis.dataItems.push(row);
                    }
                }
            }
        },
        isBindable: function(series) {
            var valueFields = valueFieldsBySeriesType(series.type), result = true, field, i;
            for (i = 0; i < valueFields.length; i++) {
                field = valueFields[i];
                if (field === VALUE) {
                    field = "field";
                } else {
                    field = field + "Field";
                }
                if (!series[field]) {
                    result = false;
                    break;
                }
            }
            return result;
        },
        _createGroupedSeries: function(series, data) {
            var groupSeries = [], nameTemplate, group, groupIx, dataLength = data.length, seriesClone;
            if (series.groupNameTemplate) {
                nameTemplate = template(series.groupNameTemplate);
            }
            for (groupIx = 0; groupIx < dataLength; groupIx++) {
                seriesClone = deepExtend({}, series);
                seriesClone.color = undefined;
                groupSeries.push(seriesClone);
                group = data[groupIx];
                seriesClone.data = group.items;
                if (nameTemplate) {
                    seriesClone.name = nameTemplate({
                        series: seriesClone,
                        group: group
                    });
                }
            }
            return groupSeries;
        },
        _tap: function(e) {
            var chart = this;
            if (!chart._startHover(e)) {
                chart._unsetActivePoint();
            }
            chart._click(e);
        },
        destroy: function() {
            var chart = this, dataSource = chart.dataSource;
            chart.element.off(NS);
            dataSource.unbind(CHANGE, chart._dataChangeHandler);
            $(document).off(MOUSEMOVE_TRACKING);
            if (chart._userEvents) {
                chart._userEvents.destroy();
            }
            chart._destroyView();
            Widget.fn.destroy.call(chart);
        },
        _destroyView: function() {
            var chart = this, pool = dataviz.IDPool.current, model = chart._model, view = chart._view, viewElement = chart._viewElement, selections = chart._selections;
            if (model) {
                model.destroy();
            }
            if (view) {
                view.destroy();
            }
            if (viewElement) {
                $("[id]", viewElement).each(function() {
                    pool.free($(this).attr("id"));
                });
            }
            if (selections) {
                while (selections.length > 0) {
                    selections.shift().destroy();
                }
            }
        }
    });
    var BarLabel = ChartElement.extend({
        init: function(content, options) {
            var barLabel = this;
            ChartElement.fn.init.call(barLabel, options);
            barLabel.append(new TextBox(content, barLabel.options));
        },
        options: {
            position: OUTSIDE_END,
            margin: getSpacing(3),
            padding: getSpacing(4),
            color: BLACK,
            background: "",
            border: {
                width: 1,
                color: ""
            },
            aboveAxis: true,
            vertical: false,
            animation: {
                type: FADEIN,
                delay: INITIAL_ANIMATION_DURATION
            },
            zIndex: 1
        },
        reflow: function(targetBox) {
            var barLabel = this, options = barLabel.options, vertical = options.vertical, aboveAxis = options.aboveAxis, text = barLabel.children[0], box = text.box, padding = text.options.padding;
            text.options.align = vertical ? CENTER : LEFT;
            text.options.vAlign = vertical ? TOP : CENTER;
            if (options.position == INSIDE_END) {
                if (vertical) {
                    text.options.vAlign = TOP;
                    if (!aboveAxis && box.height() < targetBox.height()) {
                        text.options.vAlign = BOTTOM;
                    }
                } else {
                    text.options.align = aboveAxis ? RIGHT : LEFT;
                }
            } else if (options.position == CENTER) {
                text.options.vAlign = CENTER;
                text.options.align = CENTER;
            } else if (options.position == INSIDE_BASE) {
                if (vertical) {
                    text.options.vAlign = aboveAxis ? BOTTOM : TOP;
                } else {
                    text.options.align = aboveAxis ? LEFT : RIGHT;
                }
            } else if (options.position == OUTSIDE_END) {
                if (vertical) {
                    if (aboveAxis) {
                        targetBox = new Box2D(targetBox.x1, targetBox.y1 - box.height(), targetBox.x2, targetBox.y1);
                    } else {
                        targetBox = new Box2D(targetBox.x1, targetBox.y2, targetBox.x2, targetBox.y2 + box.height());
                    }
                } else {
                    text.options.align = CENTER;
                    if (aboveAxis) {
                        targetBox = new Box2D(targetBox.x2 + box.width(), targetBox.y1, targetBox.x2, targetBox.y2);
                    } else {
                        targetBox = new Box2D(targetBox.x1 - box.width(), targetBox.y1, targetBox.x1, targetBox.y2);
                    }
                }
            }
            if (vertical) {
                padding.left = padding.right = (targetBox.width() - text.contentBox.width()) / 2;
            } else {
                padding.top = padding.bottom = (targetBox.height() - text.contentBox.height()) / 2;
            }
            text.reflow(targetBox);
        }
    });
    var Legend = ChartElement.extend({
        init: function(options) {
            var legend = this;
            ChartElement.fn.init.call(legend, options);
            legend.createLabels();
        },
        options: {
            position: RIGHT,
            items: [],
            labels: {},
            offsetX: 0,
            offsetY: 0,
            margin: getSpacing(10),
            padding: getSpacing(5),
            border: {
                color: BLACK,
                width: 0
            },
            background: "",
            zIndex: 1
        },
        createLabels: function() {
            var legend = this, items = legend.options.items, count = items.length, label, name, i;
            for (i = 0; i < count; i++) {
                name = items[i].name;
                label = new Text(name, legend.options.labels);
                legend.append(label);
            }
        },
        reflow: function(targetBox) {
            var legend = this, options = legend.options, childrenCount = legend.children.length;
            if (childrenCount === 0) {
                legend.box = targetBox.clone();
                return;
            }
            if (options.position == "custom") {
                legend.customLayout(targetBox);
                return;
            }
            if (options.position == TOP || options.position == BOTTOM) {
                legend.horizontalLayout(targetBox);
            } else {
                legend.verticalLayout(targetBox);
            }
        },
        getViewElements: function(view) {
            var legend = this, children = legend.children, options = legend.options, items = options.items, count = items.length, markerSize = legend.markerSize(), group = view.createGroup({
                zIndex: options.zIndex
            }), border = options.border || {}, padding, markerBox, labelBox, color, label, box, i;
            append(group.children, ChartElement.fn.getViewElements.call(legend, view));
            for (i = 0; i < count; i++) {
                color = items[i].color;
                label = children[i];
                markerBox = new Box2D();
                box = label.box;
                labelBox = labelBox ? labelBox.wrap(box) : box.clone();
                markerBox.x1 = box.x1 - markerSize * 2;
                markerBox.x2 = markerBox.x1 + markerSize;
                if (options.position == TOP || options.position == BOTTOM) {
                    markerBox.y1 = box.y1 + markerSize / 2;
                } else {
                    markerBox.y1 = box.y1 + (box.height() - markerSize) / 2;
                }
                markerBox.y2 = markerBox.y1 + markerSize;
                group.children.push(view.createRect(markerBox, {
                    fill: color,
                    stroke: color
                }));
            }
            if (children.length > 0) {
                padding = getSpacing(options.padding);
                padding.left += markerSize * 2;
                labelBox.pad(padding);
                group.children.unshift(view.createRect(labelBox, {
                    stroke: border.width ? border.color : "",
                    strokeWidth: border.width,
                    dashType: border.dashType,
                    fill: options.background
                }));
            }
            return [ group ];
        },
        verticalLayout: function(targetBox) {
            var legend = this, options = legend.options, children = legend.children, childrenCount = children.length, labelBox = children[0].box.clone(), offsetX, offsetY, margin = getSpacing(options.margin), markerSpace = legend.markerSize() * 2, label, i;
            // Position labels below each other
            for (i = 1; i < childrenCount; i++) {
                label = legend.children[i];
                label.box.alignTo(legend.children[i - 1].box, BOTTOM);
                labelBox.wrap(label.box);
            }
            // Vertical center is calculated relative to the container, not the parent!
            if (options.position == LEFT) {
                offsetX = targetBox.x1 + markerSpace + margin.left;
                offsetY = (targetBox.y2 - labelBox.height()) / 2;
                labelBox.x2 += markerSpace + margin.left + margin.right;
            } else {
                offsetX = targetBox.x2 - labelBox.width() - margin.right;
                offsetY = (targetBox.y2 - labelBox.height()) / 2;
                labelBox.translate(offsetX, offsetY);
                labelBox.x1 -= markerSpace + margin.left;
            }
            legend.translateChildren(offsetX + options.offsetX, offsetY + options.offsetY);
            var labelBoxWidth = labelBox.width();
            labelBox.x1 = math.max(targetBox.x1, labelBox.x1);
            labelBox.x2 = labelBox.x1 + labelBoxWidth;
            labelBox.y1 = targetBox.y1;
            labelBox.y2 = targetBox.y2;
            legend.box = labelBox;
        },
        horizontalLayout: function(targetBox) {
            var legend = this, options = legend.options, children = legend.children, childrenCount = children.length, box = children[0].box.clone(), markerWidth = legend.markerSize() * 3, offsetX, offsetY, margin = getSpacing(options.margin), boxWidth = children[0].box.width() + markerWidth, plotAreaWidth = targetBox.width(), label, labelY = 0, i;
            // Position labels next to each other
            for (i = 1; i < childrenCount; i++) {
                label = children[i];
                boxWidth += label.box.width() + markerWidth;
                if (boxWidth > plotAreaWidth - markerWidth) {
                    label.box = new Box2D(box.x1, box.y2, box.x1 + label.box.width(), box.y2 + label.box.height());
                    boxWidth = label.box.width() + markerWidth;
                    labelY = label.box.y1;
                } else {
                    label.box.alignTo(children[i - 1].box, RIGHT);
                    label.box.y2 = labelY + label.box.height();
                    label.box.y1 = labelY;
                    label.box.translate(markerWidth, 0);
                }
                box.wrap(label.box);
            }
            offsetX = (targetBox.width() - box.width() + markerWidth) / 2;
            if (options.position === TOP) {
                offsetY = targetBox.y1 + margin.top;
                box.y2 = targetBox.y1 + box.height() + margin.top + margin.bottom;
                box.y1 = targetBox.y1;
            } else {
                offsetY = targetBox.y2 - box.height() - margin.bottom;
                box.y1 = targetBox.y2 - box.height() - margin.top - margin.bottom;
                box.y2 = targetBox.y2;
            }
            legend.translateChildren(offsetX + options.offsetX, offsetY + options.offsetY);
            box.x1 = targetBox.x1;
            box.x2 = targetBox.x2;
            legend.box = box;
        },
        customLayout: function(targetBox) {
            var legend = this, options = legend.options, children = legend.children, childrenCount = children.length, labelBox = children[0].box.clone(), markerWidth = legend.markerSize() * 2, i;
            // Position labels next to each other
            for (i = 1; i < childrenCount; i++) {
                labelBox = legend.children[i].box;
                labelBox.alignTo(legend.children[i - 1].box, BOTTOM);
                labelBox.wrap(labelBox);
            }
            legend.translateChildren(options.offsetX + markerWidth, options.offsetY);
            legend.box = targetBox;
        },
        markerSize: function() {
            var legend = this, children = legend.children;
            if (children.length > 0) {
                return children[0].box.height() / 2;
            } else {
                return 0;
            }
        }
    });
    var CategoryAxis = Axis.extend({
        init: function(options) {
            var axis = this;
            Axis.fn.init.call(axis, options);
            options = axis.options;
            options.categories = options.categories.slice(0);
        },
        options: {
            type: CATEGORY,
            categories: [],
            vertical: false,
            majorGridLines: {
                visible: false,
                width: 1,
                color: BLACK
            },
            zIndex: 1,
            justified: false
        },
        range: function() {
            return {
                min: 0,
                max: this.options.categories.length
            };
        },
        getTickPositions: function(itemsCount) {
            var axis = this, options = axis.options, vertical = options.vertical, justified = options.justified, lineBox = axis.lineBox(), size = vertical ? lineBox.height() : lineBox.width(), intervals = itemsCount - (justified ? 1 : 0), step = size / intervals, dim = vertical ? Y : X, pos = lineBox[dim + 1], positions = [], i;
            for (i = 0; i < itemsCount; i++) {
                positions.push(round(pos, COORD_PRECISION));
                pos += step;
            }
            if (!justified) {
                positions.push(lineBox[dim + 2]);
            }
            return options.reverse ? positions.reverse() : positions;
        },
        getMajorTickPositions: function() {
            var axis = this;
            return axis.getTickPositions(axis.options.categories.length);
        },
        getMinorTickPositions: function() {
            var axis = this;
            return axis.getTickPositions(axis.options.categories.length * 2);
        },
        getSlot: function(from, to) {
            var axis = this, options = axis.options, majorTicks = axis.getMajorTickPositions(), reverse = options.reverse, justified = options.justified, valueAxis = options.vertical ? Y : X, lineBox = axis.lineBox(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)], lineEnd = lineBox[valueAxis + (reverse ? 1 : 2)], slotBox = lineBox.clone(), intervals = math.max(1, majorTicks.length - (justified ? 0 : 1)), p1, p2, slotSize;
            from = defined(from) ? from : 0;
            to = defined(to) ? to : from;
            from = clipValue(from, 0, intervals);
            to = clipValue(to - 1, from, intervals);
            // Fixes transient bug caused by iOS 6.0 JIT
            // (one can never be too sure)
            to = math.max(from, to);
            p1 = from === 0 ? lineStart : majorTicks[from];
            p2 = justified ? p1 : majorTicks[to];
            slotSize = to - from;
            if (slotSize > 0 || from === to) {
                p2 = majorTicks[to + 1] || lineEnd;
            }
            if (justified) {
                if (from === intervals) {
                    p1 = p2;
                } else {
                    p2 = p1;
                }
            }
            slotBox[valueAxis + 1] = reverse ? p2 : p1;
            slotBox[valueAxis + 2] = reverse ? p1 : p2;
            return slotBox;
        },
        getCategoryIndex: function(point) {
            var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), lineStart = lineBox[valueAxis + 1], lineEnd = lineBox[valueAxis + 2], pos = point[valueAxis], majorTicks = axis.getMajorTickPositions(), diff = MAX_VALUE, tickPos, nextTickPos, i, categoryIx;
            if (pos < lineStart || pos > lineEnd) {
                return null;
            }
            for (i = 0; i < majorTicks.length; i++) {
                tickPos = majorTicks[i];
                nextTickPos = majorTicks[i + 1];
                if (!defined(nextTickPos)) {
                    nextTickPos = reverse ? lineStart : lineEnd;
                }
                if (reverse) {
                    tickPos = nextTickPos;
                    nextTickPos = majorTicks[i];
                }
                if (options.justified) {
                    if (pos === nextTickPos) {
                        categoryIx = math.max(0, vertical ? majorTicks.length - i - 1 : i + 1);
                        break;
                    }
                    if (math.abs(pos - tickPos) < diff) {
                        diff = pos - tickPos;
                        categoryIx = i;
                    }
                } else {
                    if (pos >= tickPos && pos <= nextTickPos) {
                        categoryIx = i;
                        break;
                    }
                }
            }
            return categoryIx;
        },
        getCategory: function(point) {
            var index = this.getCategoryIndex(point);
            if (index === null) {
                return null;
            }
            return this.options.categories[index];
        },
        translateRange: function(delta) {
            var axis = this, options = axis.options, lineBox = axis.lineBox(), size = options.vertical ? lineBox.height() : lineBox.width(), range = options.categories.length, scale = size / range, offset = round(delta / scale, DEFAULT_PRECISION);
            return {
                min: offset,
                max: range + offset
            };
        },
        scaleRange: function(scale) {
            var axis = this, options = axis.options, range = options.categories.length, delta = scale * range;
            return {
                min: -delta,
                max: range + delta
            };
        },
        labelsCount: function() {
            return this.options.categories.length;
        },
        createAxisLabel: function(index, labelOptions) {
            var axis = this, options = axis.options, dataItem = options.dataItems ? options.dataItems[index] : null, category = defined(options.categories[index]) ? options.categories[index] : "";
            return new AxisLabel(category, index, dataItem, labelOptions);
        }
    });
    var AxisDateLabel = AxisLabel.extend({
        formatValue: function(value, options) {
            return kendo.toString(value, options.format, options.culture);
        }
    });
    var DateCategoryAxis = CategoryAxis.extend({
        init: function(options) {
            var axis = this, baseUnit, useDefault;
            options = options || {};
            options = deepExtend({
                roundToBaseUnit: true
            }, options, {
                min: toDate(options.min),
                max: toDate(options.max)
            });
            if (options.categories && options.categories.length > 0) {
                baseUnit = (options.baseUnit || "").toLowerCase();
                useDefault = baseUnit !== FIT && !inArray(baseUnit, BASE_UNITS);
                if (useDefault) {
                    options.baseUnit = axis.defaultBaseUnit(options);
                }
                if (baseUnit === FIT || options.baseUnitStep === AUTO) {
                    axis.autoBaseUnit(options);
                }
                axis.groupCategories(options);
            }
            CategoryAxis.fn.init.call(axis, options);
        },
        options: {
            type: DATE,
            labels: {
                dateFormats: DateLabelFormats
            },
            autoBaseUnitSteps: {
                minutes: [ 1, 2, 5, 15, 30 ],
                hours: [ 1, 2, 3 ],
                days: [ 1, 2, 3 ],
                weeks: [ 1, 2 ],
                months: [ 1, 2, 3, 6 ],
                years: [ 1, 2, 3, 5, 10, 25, 50 ]
            },
            maxDateGroups: 10
        },
        translateRange: function(delta) {
            var axis = this, range = CategoryAxis.fn.translateRange.call(axis, delta), options = axis.options, baseUnit = options.baseUnit, offset = math.round(range.min), weekStartDay = options.weekStartDay;
            return {
                min: addDuration(options.min, offset, baseUnit, weekStartDay),
                max: addDuration(options.max, offset, baseUnit, weekStartDay)
            };
        },
        scaleRange: function(delta) {
            var axis = this, options = axis.options, rounds = math.abs(delta), from = options.min, to = options.max, range, step;
            while (rounds--) {
                range = dateDiff(from, to);
                step = math.round(range * .1);
                if (delta < 0) {
                    from = addTicks(from, step);
                    to = addTicks(to, -step);
                } else {
                    from = addTicks(from, -step);
                    to = addTicks(to, step);
                }
            }
            return {
                min: from,
                max: to
            };
        },
        defaultBaseUnit: function(options) {
            var categories = options.categories, count = defined(categories) ? categories.length : 0, categoryIx, cat, diff, minDiff = MAX_VALUE, lastCat, unit;
            for (categoryIx = 0; categoryIx < count; categoryIx++) {
                cat = toDate(categories[categoryIx]);
                if (cat && lastCat) {
                    diff = dateDiff(cat, lastCat);
                    if (diff > 0) {
                        minDiff = math.min(minDiff, diff);
                        if (minDiff >= TIME_PER_YEAR) {
                            unit = YEARS;
                        } else if (minDiff >= TIME_PER_MONTH - TIME_PER_DAY * 3) {
                            unit = MONTHS;
                        } else if (minDiff >= TIME_PER_WEEK) {
                            unit = WEEKS;
                        } else if (minDiff >= TIME_PER_DAY) {
                            unit = DAYS;
                        } else if (minDiff >= TIME_PER_HOUR) {
                            unit = HOURS;
                        } else {
                            unit = MINUTES;
                        }
                    }
                }
                lastCat = cat;
            }
            return unit || DAYS;
        },
        range: function(options) {
            options = options || this.options;
            var categories = toDate(options.categories), autoUnit = options.baseUnit === FIT, baseUnit = autoUnit ? BASE_UNITS[0] : options.baseUnit, min = toTime(options.min), max = toTime(options.max), categoryLimits = sparseArrayLimits(categories), minCategory = toTime(categoryLimits.min), maxCategory = toTime(categoryLimits.max);
            if (options.roundToBaseUnit) {
                return {
                    min: addDuration(min || minCategory, 0, baseUnit, options.weekStartDay),
                    max: addDuration(max || maxCategory, 1, baseUnit, options.weekStartDay)
                };
            } else {
                return {
                    min: toDate(min || minCategory),
                    max: toDate(max || maxCategory)
                };
            }
        },
        autoBaseUnit: function(options) {
            var axis = this, range = axis.range(options), autoUnit = options.baseUnit === FIT, autoUnitIx = 0, baseUnit = autoUnit ? BASE_UNITS[autoUnitIx++] : options.baseUnit, span = range.max - range.min, units = span / TIME_PER_UNIT[baseUnit], totalUnits = units, maxDateGroups = options.maxDateGroups || axis.options.maxDateGroups, autoBaseUnitSteps = deepExtend({}, axis.options.autoBaseUnitSteps, options.autoBaseUnitSteps), unitSteps, step, nextStep;
            while (units > maxDateGroups) {
                unitSteps = unitSteps || autoBaseUnitSteps[baseUnit].slice(0);
                nextStep = unitSteps.shift();
                if (nextStep) {
                    step = nextStep;
                    units = totalUnits / step;
                } else if (autoUnit) {
                    baseUnit = BASE_UNITS[autoUnitIx++] || last(BASE_UNITS);
                    totalUnits = span / TIME_PER_UNIT[baseUnit];
                    unitSteps = null;
                } else {
                    if (units > maxDateGroups) {
                        step = math.ceil(totalUnits / maxDateGroups);
                    }
                    break;
                }
            }
            options.baseUnitStep = step;
            options.baseUnit = baseUnit;
        },
        getMajorTickPositions: function() {
            var axis = this, options = axis.options, categories = options.categories, positions = [];
            if (options.roundToBaseUnit || categories.length === 0) {
                positions = CategoryAxis.fn.getMajorTickPositions.call(axis);
            } else {
                var vertical = options.vertical, reverse = options.reverse, lineBox = axis.lineBox(), lineSize = vertical ? lineBox.height() : lineBox.width(), startTime = categories[0].getTime(), range = axis.range(axis.options), timeRange = range.max - range.min, scale = lineSize / timeRange, divisions = categories.length, dir = (vertical ? -1 : 1) * (reverse ? -1 : 1), startEdge = dir === 1 ? 1 : 2, endEdge = dir === 1 ? 2 : 1, startPos = lineBox[(vertical ? Y : X) + startEdge], endPos = lineBox[(vertical ? Y : X) + endEdge], pos = startPos, i, timePos;
                for (i = 0; i < divisions; i++) {
                    timePos = categories[i] - startTime;
                    pos = startPos + timePos * scale * dir;
                    positions.push(round(pos, COORD_PRECISION));
                }
                if (last(positions) !== endPos) {
                    positions.push(endPos);
                }
            }
            return positions;
        },
        groupCategories: function(options) {
            var axis = this, categories = toDate(options.categories), baseUnit = options.baseUnit, baseUnitStep = options.baseUnitStep || 1, range = axis.range(options), round = options.roundToBaseUnit, end, date, nextDate, groups = [], categoryMap = axis.categoryMap = [], categoryIndicies, lastCategoryIndicies = [], categoryIx, categoryDate;
            end = round ? addDuration(range.max, baseUnitStep - 1, baseUnit, options.weekStartDay) : range.max;
            if (dateEquals(range.min, range.max)) {
                end = toDate(toTime(end) + 1);
            }
            for (date = range.min; date < end; date = nextDate) {
                nextDate = addDuration(date, baseUnitStep, baseUnit, options.weekStartDay);
                groups.push(date);
                categoryIndicies = [];
                for (categoryIx = lteDateIndex(categories, date); categoryIx < categories.length; categoryIx++) {
                    categoryDate = categories[categoryIx];
                    if (categoryDate && categoryDate >= date) {
                        if (categoryDate < nextDate) {
                            if (options.justified && dateEquals(categoryDate, end)) {
                                lastCategoryIndicies.push(categoryIx);
                            } else {
                                categoryIndicies.push(categoryIx);
                            }
                        } else if (!round && dateEquals(nextDate, end)) {
                            lastCategoryIndicies.push(categoryIx);
                        } else {
                            break;
                        }
                    }
                }
                categoryMap.push(categoryIndicies);
            }
            if (lastCategoryIndicies.length) {
                groups.push(end);
                categoryMap.push(lastCategoryIndicies);
            }
            if (!options.max && (last(categoryMap) || []).length === 0) {
                // Drop the last group if the user has not requested it
                categoryMap.pop();
                groups.pop();
            }
            options.min = groups[0];
            options.max = round ? last(groups) : end;
            options.categories = groups;
        },
        createAxisLabel: function(index, labelOptions) {
            var options = this.options, dataItem = options.dataItems ? options.dataItems[index] : null, date = options.categories[index], baseUnit = options.baseUnit, visible = true, unitFormat = labelOptions.dateFormats[baseUnit];
            if (options.justified) {
                var roundedDate = floorDate(date, baseUnit, options.weekStartDay);
                visible = dateEquals(roundedDate, date);
            }
            labelOptions = deepExtend({
                format: unitFormat
            }, labelOptions, {
                visible: visible
            });
            return new AxisDateLabel(date, index, dataItem, labelOptions);
        }
    });
    var DateValueAxis = Axis.extend({
        init: function(seriesMin, seriesMax, options) {
            var axis = this;
            options = options || {};
            deepExtend(options, {
                min: toDate(options.min),
                max: toDate(options.max),
                axisCrossingValue: toDate(options.axisCrossingValues || options.axisCrossingValue)
            });
            options = axis.applyDefaults(toDate(seriesMin), toDate(seriesMax), options);
            Axis.fn.init.call(axis, options);
        },
        options: {
            type: DATE,
            labels: {
                dateFormats: DateLabelFormats
            }
        },
        applyDefaults: function(seriesMin, seriesMax, options) {
            var axis = this, min = options.min || seriesMin, max = options.max || seriesMax, baseUnit = options.baseUnit || axis.timeUnits(max - min), baseUnitTime = TIME_PER_UNIT[baseUnit], autoMin = floorDate(toTime(min) - 1, baseUnit) || toDate(max), autoMax = ceilDate(toTime(max) + 1, baseUnit), userMajorUnit = options.majorUnit ? options.majorUnit : undefined, majorUnit = userMajorUnit || dataviz.ceil(dataviz.autoMajorUnit(autoMin.getTime(), autoMax.getTime()), baseUnitTime) / baseUnitTime, actualUnits = duration(autoMin, autoMax, baseUnit), totalUnits = dataviz.ceil(actualUnits, majorUnit), unitsToAdd = totalUnits - actualUnits, head = math.floor(unitsToAdd / 2), tail = unitsToAdd - head;
            if (!options.baseUnit) {
                delete options.baseUnit;
            }
            return deepExtend({
                baseUnit: baseUnit,
                min: addDuration(autoMin, -head, baseUnit),
                max: addDuration(autoMax, tail, baseUnit),
                minorUnit: majorUnit / 5
            }, options, {
                majorUnit: majorUnit
            });
        },
        range: function() {
            var options = this.options;
            return {
                min: options.min,
                max: options.max
            };
        },
        getDivisions: function(stepValue) {
            var options = this.options;
            return math.floor(duration(options.min, options.max, options.baseUnit) / stepValue + 1);
        },
        getTickPositions: function(stepValue) {
            var axis = this, options = axis.options, vertical = options.vertical, reverse = options.reverse, lineBox = axis.lineBox(), lineSize = vertical ? lineBox.height() : lineBox.width(), timeRange = duration(options.min, options.max, options.baseUnit), scale = lineSize / timeRange, step = stepValue * scale, divisions = axis.getDivisions(stepValue), dir = (vertical ? -1 : 1) * (reverse ? -1 : 1), startEdge = dir === 1 ? 1 : 2, pos = lineBox[(vertical ? Y : X) + startEdge], positions = [], i;
            for (i = 0; i < divisions; i++) {
                positions.push(round(pos, COORD_PRECISION));
                pos = pos + step * dir;
            }
            return positions;
        },
        getMajorTickPositions: function() {
            var axis = this;
            return axis.getTickPositions(axis.options.majorUnit);
        },
        getMinorTickPositions: function() {
            var axis = this;
            return axis.getTickPositions(axis.options.minorUnit);
        },
        getSlot: function(a, b) {
            return NumericAxis.fn.getSlot.call(this, toDate(a), toDate(b));
        },
        getValue: function(point) {
            var value = NumericAxis.fn.getValue.call(this, point);
            return value !== null ? toDate(value) : null;
        },
        labelsCount: function() {
            return this.getDivisions(this.options.majorUnit);
        },
        createAxisLabel: function(index, labelOptions) {
            var options = this.options, offset = index * options.majorUnit, date = addDuration(options.min, offset, options.baseUnit), unitFormat = labelOptions.dateFormats[options.baseUnit];
            labelOptions.format = labelOptions.format || unitFormat;
            return new AxisDateLabel(date, index, null, labelOptions);
        },
        timeUnits: function(delta) {
            var unit = HOURS;
            if (delta >= TIME_PER_YEAR) {
                unit = YEARS;
            } else if (delta >= TIME_PER_MONTH) {
                unit = MONTHS;
            } else if (delta >= TIME_PER_WEEK) {
                unit = WEEKS;
            } else if (delta >= TIME_PER_DAY) {
                unit = DAYS;
            }
            return unit;
        },
        translateRange: function(delta) {
            var axis = this, options = axis.options, baseUnit = options.baseUnit, weekStartDay = options.weekStartDay, lineBox = axis.lineBox(), size = options.vertical ? lineBox.height() : lineBox.width(), range = axis.range(), scale = size / (range.max - range.min), offset = round(delta / scale, DEFAULT_PRECISION), from = addTicks(options.min, offset), to = addTicks(options.max, offset);
            return {
                min: addDuration(from, 0, baseUnit, weekStartDay),
                max: addDuration(to, 0, baseUnit, weekStartDay)
            };
        },
        scaleRange: function(delta) {
            var axis = this, options = axis.options, baseUnit = options.baseUnit, weekStartDay = options.weekStartDay, rounds = math.abs(delta), from = options.min, to = options.max, range, step;
            while (rounds--) {
                range = dateDiff(from, to);
                step = math.round(range * .1);
                if (delta < 0) {
                    from = addTicks(from, step);
                    to = addTicks(to, -step);
                } else {
                    from = addTicks(from, -step);
                    to = addTicks(to, step);
                }
                from = addDuration(from, 0, baseUnit, weekStartDay);
                to = addDuration(to, 1, baseUnit, weekStartDay);
            }
            return {
                min: from,
                max: to
            };
        }
    });
    var ClusterLayout = ChartElement.extend({
        init: function(options) {
            var cluster = this;
            ChartElement.fn.init.call(cluster, options);
        },
        options: {
            vertical: false,
            gap: 0,
            spacing: 0
        },
        reflow: function(box) {
            var cluster = this, options = cluster.options, vertical = options.vertical, axis = vertical ? Y : X, children = cluster.children, gap = options.gap, spacing = options.spacing, count = children.length, slots = count + gap + spacing * (count - 1), slotSize = (vertical ? box.height() : box.width()) / slots, position = box[axis + 1] + slotSize * (gap / 2), childBox, i;
            for (i = 0; i < count; i++) {
                childBox = (children[i].box || box).clone();
                childBox[axis + 1] = position;
                childBox[axis + 2] = position + slotSize;
                children[i].reflow(childBox);
                if (i < count - 1) {
                    position += slotSize * spacing;
                }
                position += slotSize;
            }
        }
    });
    var StackLayout = ChartElement.extend({
        init: function(options) {
            var stack = this;
            ChartElement.fn.init.call(stack, options);
        },
        options: {
            vertical: true,
            isReversed: false
        },
        reflow: function(targetBox) {
            var stack = this, options = stack.options, vertical = options.vertical, positionAxis = vertical ? X : Y, stackAxis = vertical ? Y : X, stackBase = targetBox[stackAxis + 2], children = stack.children, box = stack.box = new Box2D(), childrenCount = children.length, stackDirection, i;
            if (options.isReversed) {
                stackDirection = vertical ? BOTTOM : LEFT;
            } else {
                stackDirection = vertical ? TOP : RIGHT;
            }
            for (i = 0; i < childrenCount; i++) {
                var currentChild = children[i], childBox = currentChild.box.clone();
                childBox.snapTo(targetBox, positionAxis);
                if (currentChild.options) {
                    currentChild.options.stackBase = stackBase;
                }
                if (i === 0) {
                    box = stack.box = childBox.clone();
                } else {
                    childBox.alignTo(children[i - 1].box, stackDirection);
                }
                currentChild.reflow(childBox);
                box.wrap(childBox);
            }
        }
    });
    var PointEventsMixin = {
        click: function(chart, e) {
            var point = this;
            chart.trigger(SERIES_CLICK, {
                value: point.value,
                category: point.category,
                series: point.series,
                dataItem: point.dataItem,
                element: $(e.target)
            });
        },
        hover: function(chart, e) {
            var point = this;
            chart.trigger(SERIES_HOVER, {
                value: point.value,
                category: point.category,
                series: point.series,
                dataItem: point.dataItem,
                element: $(e.target)
            });
        }
    };
    var Bar = ChartElement.extend({
        init: function(value, options) {
            var bar = this;
            ChartElement.fn.init.call(bar, options);
            bar.value = value;
            if (value < 0 && options.negativeColor) {
                bar.options.color = options.negativeColor;
            }
            bar.options.id = uniqueId();
            bar.enableDiscovery();
        },
        options: {
            color: WHITE,
            border: {
                width: 1
            },
            vertical: true,
            overlay: {
                gradient: GLASS
            },
            aboveAxis: true,
            labels: {
                visible: false
            },
            animation: {
                type: BAR
            },
            opacity: 1
        },
        render: function() {
            var bar = this, value = bar.value, options = bar.options, labels = options.labels, labelText = value, labelTemplate;
            if (bar._rendered) {
                return;
            } else {
                bar._rendered = true;
            }
            if (labels.visible && value) {
                if (labels.template) {
                    labelTemplate = template(labels.template);
                    labelText = labelTemplate({
                        dataItem: bar.dataItem,
                        category: bar.category,
                        value: bar.value,
                        series: bar.series
                    });
                } else if (labels.format) {
                    labelText = autoFormat(labels.format, labelText);
                }
                bar.append(new BarLabel(labelText, deepExtend({
                    vertical: options.vertical,
                    id: uniqueId()
                }, options.labels)));
            }
        },
        reflow: function(targetBox) {
            this.render();
            var bar = this, options = bar.options, children = bar.children, label = children[0];
            bar.box = targetBox;
            if (label) {
                label.options.aboveAxis = options.aboveAxis;
                label.reflow(targetBox);
            }
        },
        getViewElements: function(view) {
            var bar = this, options = bar.options, vertical = options.vertical, border = options.border.width > 0 ? {
                stroke: bar.getBorderColor(),
                strokeWidth: options.border.width,
                strokeOpacity: options.border.opacity,
                dashType: options.border.dashType
            } : {}, box = bar.box, rectStyle = deepExtend({
                id: options.id,
                fill: options.color,
                fillOpacity: options.opacity,
                strokeOpacity: options.opacity,
                vertical: options.vertical,
                aboveAxis: options.aboveAxis,
                stackBase: options.stackBase,
                animation: options.animation,
                data: {
                    modelId: options.modelId
                }
            }, border), elements = [];
            if (box.width() > 0 && box.height() > 0) {
                if (options.overlay) {
                    rectStyle.overlay = deepExtend({
                        rotation: vertical ? 0 : 90
                    }, options.overlay);
                }
                elements.push(view.createRect(box, rectStyle));
            }
            append(elements, ChartElement.fn.getViewElements.call(bar, view));
            return elements;
        },
        highlightOverlay: function(view, options) {
            var bar = this, box = bar.box;
            options = deepExtend({
                data: {
                    modelId: bar.options.modelId
                }
            }, options);
            options.id = null;
            return view.createRect(box, options);
        },
        getBorderColor: function() {
            var bar = this, options = bar.options, color = options.color, border = options.border, borderColor = border.color, brightness = border._brightness || BAR_BORDER_BRIGHTNESS;
            if (!defined(borderColor)) {
                borderColor = new Color(color).brightness(brightness).toHex();
            }
            return borderColor;
        },
        tooltipAnchor: function(tooltipWidth, tooltipHeight) {
            var bar = this, options = bar.options, box = bar.box, vertical = options.vertical, aboveAxis = options.aboveAxis, x, y;
            if (vertical) {
                x = box.x2 + TOOLTIP_OFFSET;
                y = aboveAxis ? box.y1 : box.y2 - tooltipHeight;
            } else {
                if (options.isStacked) {
                    x = aboveAxis ? box.x2 - tooltipWidth : box.x1;
                    y = box.y1 - tooltipHeight - TOOLTIP_OFFSET;
                } else {
                    x = aboveAxis ? box.x2 + TOOLTIP_OFFSET : box.x1 - tooltipWidth - TOOLTIP_OFFSET;
                    y = box.y1;
                }
            }
            return new Point2D(x, y);
        },
        formatValue: function(format) {
            var point = this;
            return point.owner.formatPointValue(point, format);
        }
    });
    deepExtend(Bar.fn, PointEventsMixin);
    var CategoricalChart = ChartElement.extend({
        init: function(plotArea, options) {
            var chart = this;
            ChartElement.fn.init.call(chart, options);
            chart.plotArea = plotArea;
            chart.categoryAxis = plotArea.seriesCategoryAxis(options.series[0]);
            // Value axis ranges grouped by axis name, e.g.:
            // primary: { min: 0, max: 1 }
            chart.valueAxisRanges = {};
            chart.points = [];
            chart.categoryPoints = [];
            chart.seriesPoints = [];
            chart.render();
        },
        options: {
            series: [],
            invertAxes: false,
            isStacked: false
        },
        render: function() {
            var chart = this;
            chart.traverseDataPoints(proxy(chart.addValue, chart));
        },
        addValue: function(data, category, categoryIx, series, seriesIx) {
            var chart = this, value = data.value, point, categoryPoints = chart.categoryPoints[categoryIx], seriesPoints = chart.seriesPoints[seriesIx];
            if (!categoryPoints) {
                chart.categoryPoints[categoryIx] = categoryPoints = [];
            }
            if (!seriesPoints) {
                chart.seriesPoints[seriesIx] = seriesPoints = [];
            }
            chart.updateRange(value, categoryIx, series);
            point = chart.createPoint(data, category, categoryIx, series, seriesIx);
            if (point) {
                point.category = category;
                point.series = series;
                point.seriesIx = seriesIx;
                point.owner = chart;
                point.dataItem = series.data[categoryIx];
            }
            chart.points.push(point);
            seriesPoints.push(point);
            categoryPoints.push(point);
        },
        updateRange: function(value, categoryIx, series) {
            var chart = this, axisName = series.axis, axisRange = chart.valueAxisRanges[axisName];
            if (defined(value) && !isNaN(value)) {
                axisRange = chart.valueAxisRanges[axisName] = axisRange || {
                    min: MAX_VALUE,
                    max: MIN_VALUE
                };
                axisRange.min = math.min(axisRange.min, value);
                axisRange.max = math.max(axisRange.max, value);
            }
        },
        seriesValueAxis: function(series) {
            var plotArea = this.plotArea, axisName = series.axis, axis = axisName ? plotArea.namedValueAxes[axisName] : plotArea.valueAxis;
            if (!axis) {
                throw new Error("Unable to locate value axis with name " + axisName);
            }
            return axis;
        },
        reflow: function(targetBox) {
            var chart = this, options = chart.options, invertAxes = options.invertAxes, pointIx = 0, categorySlots = chart.categorySlots = [], chartPoints = chart.points, categoryAxis = chart.categoryAxis, valueAxis, axisCrossingValue, point;
            chart.traverseDataPoints(function(data, category, categoryIx, currentSeries) {
                var value = chart.pointValue(data);
                valueAxis = chart.seriesValueAxis(currentSeries);
                axisCrossingValue = chart.categoryAxisCrossingValue(valueAxis);
                point = chartPoints[pointIx++];
                if (point && point.plotValue) {
                    value = point.plotValue;
                }
                var categorySlot = chart.categorySlot(categoryAxis, categoryIx, valueAxis), valueSlot = chart.valueSlot(valueAxis, value, axisCrossingValue), slotX = invertAxes ? valueSlot : categorySlot, slotY = invertAxes ? categorySlot : valueSlot, pointSlot = new Box2D(slotX.x1, slotY.y1, slotX.x2, slotY.y2), aboveAxis = valueAxis.options.reverse ? value < axisCrossingValue : value >= axisCrossingValue;
                if (point) {
                    point.options.aboveAxis = aboveAxis;
                    point.reflow(pointSlot);
                }
                if (!categorySlots[categoryIx]) {
                    categorySlots[categoryIx] = categorySlot;
                }
            });
            chart.reflowCategories(categorySlots);
            chart.box = targetBox;
        },
        categoryAxisCrossingValue: function(valueAxis) {
            var categoryAxis = this.categoryAxis, options = valueAxis.options, crossingValues = [].concat(options.axisCrossingValues || options.axisCrossingValue);
            return crossingValues[categoryAxis.axisIndex || 0] || 0;
        },
        reflowCategories: function() {},
        valueSlot: function(valueAxis, value, axisCrossingValue) {
            return valueAxis.getSlot(value, axisCrossingValue);
        },
        categorySlot: function(categoryAxis, categoryIx) {
            return categoryAxis.getSlot(categoryIx);
        },
        traverseDataPoints: function(callback) {
            var chart = this, options = chart.options, series = options.series, categories = chart.categoryAxis.options.categories || [], count = categoriesCount(series), bindableFields = chart.bindableFields(), categoryIx, seriesIx, pointData, currentCategory, currentSeries, seriesCount = series.length;
            for (categoryIx = 0; categoryIx < count; categoryIx++) {
                for (seriesIx = 0; seriesIx < seriesCount; seriesIx++) {
                    currentCategory = categories[categoryIx];
                    currentSeries = series[seriesIx];
                    pointData = bindPoint(currentSeries, categoryIx, bindableFields);
                    callback(pointData, currentCategory, categoryIx, currentSeries, seriesIx);
                }
            }
        },
        bindableFields: function() {
            return [];
        },
        formatPointValue: function(point, format) {
            return autoFormat(format, point.value);
        },
        pointValue: function(data) {
            return data.value;
        }
    });
    var BarChart = CategoricalChart.extend({
        init: function(plotArea, options) {
            var chart = this;
            chart._groupTotals = {};
            chart._groups = [];
            CategoricalChart.fn.init.call(chart, plotArea, options);
        },
        render: function() {
            var chart = this;
            CategoricalChart.fn.render.apply(chart);
            chart.computeAxisRanges();
        },
        createPoint: function(data, category, categoryIx, series) {
            var barChart = this, value = data.value, options = barChart.options, children = barChart.children, isStacked = barChart.options.isStacked, labelOptions = deepExtend({}, series.labels), bar, cluster;
            if (isStacked) {
                if (labelOptions.position == OUTSIDE_END) {
                    labelOptions.position = INSIDE_END;
                }
            }
            bar = new Bar(value, deepExtend({}, {
                vertical: !options.invertAxes,
                overlay: series.overlay,
                labels: labelOptions,
                isStacked: isStacked
            }, series, {
                color: data.fields.color || undefined
            }));
            cluster = children[categoryIx];
            if (!cluster) {
                cluster = new ClusterLayout({
                    vertical: options.invertAxes,
                    gap: options.gap,
                    spacing: options.spacing
                });
                barChart.append(cluster);
            }
            if (isStacked) {
                var stackWrap = barChart.getStackWrap(series, cluster), positiveStack, negativeStack;
                if (stackWrap.children.length === 0) {
                    positiveStack = new StackLayout({
                        vertical: !options.invertAxes
                    });
                    negativeStack = new StackLayout({
                        vertical: !options.invertAxes,
                        isReversed: true
                    });
                    stackWrap.append(positiveStack, negativeStack);
                } else {
                    positiveStack = stackWrap.children[0];
                    negativeStack = stackWrap.children[1];
                }
                if (value > 0) {
                    positiveStack.append(bar);
                } else {
                    negativeStack.append(bar);
                }
            } else {
                cluster.append(bar);
            }
            return bar;
        },
        getStackWrap: function(series, cluster) {
            var wraps = cluster.children, stackGroup = series.stack, stackWrap, i, length = wraps.length;
            if (typeof stackGroup === STRING) {
                for (i = 0; i < length; i++) {
                    if (wraps[i]._stackGroup === stackGroup) {
                        stackWrap = wraps[i];
                        break;
                    }
                }
            } else {
                stackWrap = wraps[0];
            }
            if (!stackWrap) {
                stackWrap = new ChartElement();
                stackWrap._stackGroup = stackGroup;
                cluster.append(stackWrap);
            }
            return stackWrap;
        },
        updateRange: function(value, categoryIx, series) {
            var chart = this, isStacked = chart.options.isStacked, totals = chart.groupTotals(series.stack), positive = totals.positive, negative = totals.negative;
            if (defined(value)) {
                if (isStacked) {
                    incrementSlot(value > 0 ? positive : negative, categoryIx, value);
                } else {
                    CategoricalChart.fn.updateRange.apply(chart, arguments);
                }
            }
        },
        computeAxisRanges: function() {
            var chart = this, isStacked = chart.options.isStacked, axisName, categoryTotals;
            if (isStacked) {
                axisName = chart.options.series[0].axis;
                categoryTotals = chart.categoryTotals();
                chart.valueAxisRanges[axisName] = {
                    min: sparseArrayMin(categoryTotals.negative.concat(0)),
                    max: sparseArrayMax(categoryTotals.positive.concat(0))
                };
            }
        },
        seriesValueAxis: function(series) {
            var chart = this, options = chart.options;
            return CategoricalChart.fn.seriesValueAxis.call(chart, options.isStacked ? chart.options.series[0] : series);
        },
        valueSlot: function(valueAxis, value, axisCrossingValue) {
            return valueAxis.getSlot(value, this.options.isStacked ? 0 : axisCrossingValue);
        },
        categorySlot: function(categoryAxis, categoryIx, valueAxis) {
            var chart = this, options = chart.options, categorySlot = categoryAxis.getSlot(categoryIx), stackAxis, zeroSlot;
            if (options.isStacked) {
                zeroSlot = valueAxis.getSlot(0, 0);
                stackAxis = options.invertAxes ? X : Y;
                categorySlot[stackAxis + 1] = categorySlot[stackAxis + 2] = zeroSlot[stackAxis + 1];
            }
            return categorySlot;
        },
        reflow: function(targetBox) {
            var chart = this;
            chart.setStacksDirection();
            CategoricalChart.fn.reflow.call(chart, targetBox);
        },
        setStacksDirection: function() {
            var chart = this, options = chart.options, series = options.series, count = categoriesCount(series), clusters = chart.children, categoryIx, seriesIx, currentSeries, valueAxis, seriesCount = series.length;
            for (seriesIx = 0; seriesIx < seriesCount; seriesIx++) {
                currentSeries = series[seriesIx];
                valueAxis = chart.seriesValueAxis(currentSeries);
                for (categoryIx = 0; categoryIx < count; categoryIx++) {
                    var cluster = clusters[categoryIx], stackWrap = chart.getStackWrap(currentSeries, cluster), stacks = stackWrap.children, positiveStack = stacks[0], negativeStack = stacks[1];
                    if (positiveStack && negativeStack) {
                        positiveStack.options.isReversed = valueAxis.options.reverse;
                        negativeStack.options.isReversed = !valueAxis.options.reverse;
                    }
                }
            }
        },
        reflowCategories: function(categorySlots) {
            var chart = this, children = chart.children, childrenLength = children.length, i;
            for (i = 0; i < childrenLength; i++) {
                children[i].reflow(categorySlots[i]);
            }
        },
        groupTotals: function(stackGroup) {
            var chart = this, groupName = typeof stackGroup === STRING ? stackGroup : "default", totals = chart._groupTotals[groupName];
            if (!totals) {
                totals = chart._groupTotals[groupName] = {
                    positive: [],
                    negative: []
                };
                chart._groups.push(groupName);
            }
            return totals;
        },
        categoryTotals: function() {
            var chart = this, groups = chart._groups, groupTotals = chart._groupTotals, name, totals, categoryTotals = {
                positive: [],
                negative: []
            }, i, length = groups.length;
            for (i = 0; i < length; i++) {
                name = groups[i];
                totals = groupTotals[name];
                append(categoryTotals.positive, totals.positive);
                append(categoryTotals.negative, totals.negative);
            }
            return categoryTotals;
        },
        bindableFields: function() {
            return [ "color" ];
        }
    });
    var BulletChart = CategoricalChart.extend({
        init: function(plotArea, options) {
            var chart = this;
            chart.wrapData(options);
            CategoricalChart.fn.init.call(chart, plotArea, options);
        },
        wrapData: function(options) {
            var series = options.series, i, data, seriesItem;
            for (i = 0; i < series.length; i++) {
                seriesItem = series[i];
                data = seriesItem.data;
                if (data && !isArray(data[0]) && typeof data[0] != "object") {
                    seriesItem.data = [ data ];
                }
            }
        },
        addValue: function(data, category, categoryIx, series, seriesIx) {
            var chart = this, categoryPoints = chart.categoryPoints[categoryIx], seriesPoints = chart.seriesPoints[seriesIx], point;
            if (!categoryPoints) {
                chart.categoryPoints[categoryIx] = categoryPoints = [];
            }
            if (!seriesPoints) {
                chart.seriesPoints[seriesIx] = seriesPoints = [];
            }
            chart.updateRange(data.value, categoryIx, series);
            point = chart.createPoint(data.value, categoryIx, series);
            if (point) {
                point.category = category;
                point.series = series;
                point.seriesIx = seriesIx;
                point.owner = chart;
                point.dataItem = series.data[categoryIx];
            }
            chart.points.push(point);
            seriesPoints.push(point);
            categoryPoints.push(point);
        },
        reflowCategories: function(categorySlots) {
            var chart = this, children = chart.children, childrenLength = children.length, i;
            for (i = 0; i < childrenLength; i++) {
                children[i].reflow(categorySlots[i]);
            }
        },
        createPoint: function(data, categoryIx, series) {
            var chart = this, options = chart.options, children = chart.children, bullet, cluster;
            bullet = new Bullet(data, deepExtend({}, {
                vertical: !options.invertAxes,
                overlay: series.overlay,
                categoryIx: categoryIx,
                invertAxes: options.invertAxes
            }, series));
            cluster = children[categoryIx];
            if (!cluster) {
                cluster = new ClusterLayout({
                    vertical: options.invertAxes,
                    gap: options.gap,
                    spacing: options.spacing
                });
                chart.append(cluster);
            }
            cluster.append(bullet);
            return bullet;
        },
        updateRange: function(value, categoryIx, series) {
            var chart = this, axisName = series.axis, current = value.current, target = value.target, axisRange = chart.valueAxisRanges[axisName];
            if (defined(current) && !isNaN(current) && defined(target && !isNaN(target))) {
                axisRange = chart.valueAxisRanges[axisName] = axisRange || {
                    min: MAX_VALUE,
                    max: MIN_VALUE
                };
                axisRange.min = math.min.apply(math, [ axisRange.min, current, target ]);
                axisRange.max = math.max.apply(math, [ axisRange.max, current, target ]);
            }
        },
        formatPointValue: function(point, format) {
            return autoFormat(format, point.value.current, point.value.target);
        },
        pointValue: function(data) {
            return data.value.current;
        }
    });
    var Bullet = ChartElement.extend({
        init: function(value, options) {
            var bullet = this;
            ChartElement.fn.init.call(bullet, options);
            bullet.value = value;
            bullet.options.id = uniqueId();
            bullet.enableDiscovery();
            bullet.render();
        },
        options: {
            color: WHITE,
            border: {
                width: 1
            },
            vertical: false,
            animation: {
                type: BAR
            },
            opacity: 1,
            target: {
                shape: "",
                border: {
                    width: 0,
                    color: "green"
                },
                line: {
                    width: 2
                }
            },
            tooltip: {
                format: "Current: {0}</br>Target: {1}"
            }
        },
        render: function() {
            var bullet = this, options = bullet.options;
            bullet.target = new Target({
                id: bullet.options.id,
                type: options.target.shape,
                background: options.target.color || options.color,
                opacity: options.opacity,
                zIndex: options.zIndex,
                border: options.target.border,
                vAlign: TOP,
                align: RIGHT
            });
            bullet.append(bullet.target);
        },
        reflow: function(box) {
            var bullet = this, options = bullet.options, chart = bullet.owner, target = bullet.target, invertAxes = options.invertAxes, valueAxis = chart.seriesValueAxis(bullet.options), axisCrossingValue = chart.categoryAxisCrossingValue(valueAxis), categorySlot = chart.categorySlot(chart.categoryAxis, options.categoryIx, valueAxis), targetValueSlot = chart.valueSlot(valueAxis, bullet.value.target, axisCrossingValue), targetSlotX = invertAxes ? targetValueSlot : categorySlot, targetSlotY = invertAxes ? categorySlot : targetValueSlot, targetSlot = new Box2D(targetSlotX.x1, targetSlotY.y1, targetSlotX.x2, targetSlotY.y2);
            target.options.height = invertAxes ? targetSlot.height() : options.target.line.width;
            target.options.width = invertAxes ? options.target.line.width : targetSlot.width();
            target.reflow(targetSlot);
            bullet.box = box;
        },
        getViewElements: function(view) {
            var bullet = this, options = bullet.options, vertical = options.vertical, border = options.border.width > 0 ? {
                stroke: options.border.color || options.color,
                strokeWidth: options.border.width,
                dashType: options.border.dashType
            } : {}, box = bullet.box, rectStyle = deepExtend({
                id: options.id,
                fill: options.color,
                fillOpacity: options.opacity,
                strokeOpacity: options.opacity,
                vertical: options.vertical,
                aboveAxis: options.aboveAxis,
                animation: options.animation,
                data: {
                    modelId: options.modelId
                }
            }, border), elements = [];
            if (box.width() > 0 && box.height() > 0) {
                if (options.overlay) {
                    rectStyle.overlay = deepExtend({
                        rotation: vertical ? 0 : 90
                    }, options.overlay);
                }
                elements.push(view.createRect(box, rectStyle));
            }
            append(elements, ChartElement.fn.getViewElements.call(bullet, view));
            return elements;
        },
        tooltipAnchor: function(tooltipWidth, tooltipHeight) {
            var bar = this, options = bar.options, box = bar.box, vertical = options.vertical, aboveAxis = options.aboveAxis, x, y;
            if (vertical) {
                x = box.x2 + TOOLTIP_OFFSET;
                y = aboveAxis ? box.y1 : box.y2 - tooltipHeight;
            } else {
                if (options.isStacked) {
                    x = aboveAxis ? box.x2 - tooltipWidth : box.x1;
                    y = box.y1 - tooltipHeight - TOOLTIP_OFFSET;
                } else {
                    x = aboveAxis ? box.x2 + TOOLTIP_OFFSET : box.x1 - tooltipWidth - TOOLTIP_OFFSET;
                    y = box.y1;
                }
            }
            return new Point2D(x, y);
        },
        highlightOverlay: function(view, options) {
            var bullet = this, box = bullet.box;
            options = deepExtend({
                data: {
                    modelId: bullet.options.modelId
                }
            }, options);
            options.id = null;
            return view.createRect(box, options);
        },
        formatValue: function(format) {
            var bullet = this;
            return bullet.owner.formatPointValue(bullet, format);
        }
    });
    deepExtend(Bullet.fn, PointEventsMixin);
    var ShapeElement = BoxElement.extend({
        options: {
            type: CIRCLE,
            align: CENTER,
            vAlign: CENTER
        },
        getViewElements: function(view, renderOptions) {
            var marker = this, options = marker.options, type = options.type, box = marker.paddingBox, element, elementOptions, halfWidth = box.width() / 2;
            if (!options.visible || !marker.hasBox()) {
                return [];
            }
            elementOptions = deepExtend(marker.elementStyle(), renderOptions);
            if (type === TRIANGLE) {
                element = view.createPolyline([ new Point2D(box.x1 + halfWidth, box.y1), new Point2D(box.x1, box.y2), new Point2D(box.x2, box.y2) ], true, elementOptions);
            } else if (type === CIRCLE) {
                element = view.createCircle(new Point2D(round(box.x1 + halfWidth, COORD_PRECISION), round(box.y1 + box.height() / 2, COORD_PRECISION)), halfWidth, elementOptions);
            } else {
                element = view.createRect(box, elementOptions);
            }
            return [ element ];
        }
    });
    var Target = ShapeElement.extend();
    deepExtend(Target.fn, PointEventsMixin);
    var LinePoint = ChartElement.extend({
        init: function(value, options) {
            var point = this;
            ChartElement.fn.init.call(point, options);
            point.value = value;
            point.options.id = uniqueId();
            point.enableDiscovery();
        },
        options: {
            aboveAxis: true,
            vertical: true,
            markers: {
                visible: true,
                background: WHITE,
                size: LINE_MARKER_SIZE,
                type: CIRCLE,
                border: {
                    width: 2
                },
                opacity: 1
            },
            labels: {
                visible: false,
                position: ABOVE,
                margin: getSpacing(3),
                padding: getSpacing(4),
                animation: {
                    type: FADEIN,
                    delay: INITIAL_ANIMATION_DURATION
                }
            }
        },
        render: function() {
            var point = this, options = point.options, markers = options.markers, labels = options.labels, markerBackground = markers.background, markerBorder = deepExtend({}, markers.border), labelText = point.value;
            if (point._rendered) {
                return;
            } else {
                point._rendered = true;
            }
            if (!defined(markerBorder.color)) {
                markerBorder.color = new Color(markerBackground).brightness(BAR_BORDER_BRIGHTNESS).toHex();
            }
            point.marker = new ShapeElement({
                id: point.options.id,
                visible: markers.visible && markers.size,
                type: markers.type,
                width: markers.size,
                height: markers.size,
                background: markerBackground,
                border: markerBorder,
                opacity: markers.opacity,
                zIndex: markers.zIndex,
                animation: markers.animation
            });
            point.append(point.marker);
            if (labels.visible) {
                if (labels.template) {
                    var labelTemplate = template(labels.template);
                    labelText = labelTemplate({
                        dataItem: point.dataItem,
                        category: point.category,
                        value: point.value,
                        series: point.series
                    });
                } else if (labels.format) {
                    labelText = point.formatValue(labels.format);
                }
                point.label = new TextBox(labelText, deepExtend({
                    id: uniqueId(),
                    align: CENTER,
                    vAlign: CENTER,
                    margin: {
                        left: 5,
                        right: 5
                    }
                }, labels));
                point.append(point.label);
            }
        },
        markerBox: function() {
            return this.marker.box;
        },
        reflow: function(targetBox) {
            var point = this, options = point.options, vertical = options.vertical, aboveAxis = options.aboveAxis, childBox;
            point.render();
            point.box = targetBox;
            childBox = targetBox.clone();
            if (vertical) {
                if (aboveAxis) {
                    childBox.y1 -= childBox.height();
                } else {
                    childBox.y2 += childBox.height();
                }
            } else {
                if (aboveAxis) {
                    childBox.x1 += childBox.width();
                } else {
                    childBox.x2 -= childBox.width();
                }
            }
            point.marker.reflow(childBox);
            point.reflowLabel(childBox);
        },
        reflowLabel: function(box) {
            var point = this, options = point.options, marker = point.marker, label = point.label, anchor = options.labels.position;
            if (label) {
                anchor = anchor === ABOVE ? TOP : anchor;
                anchor = anchor === BELOW ? BOTTOM : anchor;
                label.reflow(box);
                label.box.alignTo(marker.box, anchor);
                label.reflow(label.box);
            }
        },
        highlightOverlay: function(view, options) {
            var element = this, marker = element.marker;
            options = deepExtend({
                data: {
                    modelId: element.options.modelId
                }
            }, options);
            return marker.getViewElements(view, deepExtend(options, {
                id: null,
                fill: marker.options.border.color,
                fillOpacity: 1,
                strokeOpacity: 0
            }))[0];
        },
        tooltipAnchor: function(tooltipWidth, tooltipHeight) {
            var point = this, markerBox = point.marker.box, aboveAxis = point.options.aboveAxis;
            return new Point2D(markerBox.x2 + TOOLTIP_OFFSET, aboveAxis ? markerBox.y1 - tooltipHeight : markerBox.y2);
        },
        formatValue: function(format) {
            var point = this;
            return point.owner.formatPointValue(point, format);
        }
    });
    deepExtend(LinePoint.fn, PointEventsMixin);
    var Bubble = LinePoint.extend({
        init: function(value, options) {
            var point = this;
            LinePoint.fn.init.call(point, value, options);
            point.category = value.category;
        },
        options: {
            labels: {
                position: CENTER
            },
            highlight: {
                opacity: 1,
                border: {
                    width: 1,
                    opacity: 1
                }
            }
        },
        highlightOverlay: function(view) {
            var element = this, options = element.options, highlight = options.highlight, borderWidth = highlight.border.width, markers = options.markers, center = element.box.center(), radius = markers.size / 2 - borderWidth / 2, borderColor = highlight.border.color || new Color(markers.background).brightness(BAR_BORDER_BRIGHTNESS).toHex();
            return view.createCircle(center, radius, {
                id: null,
                data: {
                    modelId: element.options.modelId
                },
                stroke: borderColor,
                strokeWidth: borderWidth,
                strokeOpacity: highlight.border.opacity
            });
        },
        toggleHighlight: function(view) {
            var element = this, opacity = element.options.highlight.opacity;
            element.highlighted = !element.highlighted;
            var marker = element.marker.getViewElements(view, {
                fillOpacity: element.highlighted ? opacity : undefined
            })[0];
            marker.refresh(getElement(this.options.id));
        }
    });
    var LineSegment = ChartElement.extend({
        init: function(linePoints, series, seriesIx) {
            var segment = this;
            ChartElement.fn.init.call(segment);
            segment.linePoints = linePoints;
            segment.series = series;
            segment.seriesIx = seriesIx;
            segment.options.id = uniqueId();
            segment.enableDiscovery();
        },
        options: {},
        points: function(visualPoints) {
            var segment = this, linePoints = segment.linePoints.concat(visualPoints || []), points = [], i, length = linePoints.length, pointCenter;
            for (i = 0; i < length; i++) {
                pointCenter = linePoints[i].markerBox().center();
                points.push(new Point2D(pointCenter.x, pointCenter.y));
            }
            return points;
        },
        getViewElements: function(view) {
            var segment = this, series = segment.series;
            ChartElement.fn.getViewElements.call(segment, view);
            return [ view.createPolyline(segment.points(), false, {
                id: segment.options.id,
                stroke: series.color,
                strokeWidth: series.width,
                strokeOpacity: series.opacity,
                fill: "",
                dashType: series.dashType,
                data: {
                    modelId: segment.options.modelId
                },
                zIndex: -1
            }) ];
        },
        aliasFor: function(e, coords) {
            var segment = this, seriesIx = segment.seriesIx;
            return segment.parent.getNearestPoint(coords.x, coords.y, seriesIx);
        }
    });
    var LineChartMixin = {
        renderSegments: function() {
            var chart = this, options = chart.options, series = options.series, seriesPoints = chart.seriesPoints, currentSeries, seriesIx, seriesCount = seriesPoints.length, currentSeriesPoints, linePoints, point, pointIx, pointCount, segments = [];
            for (seriesIx = 0; seriesIx < seriesCount; seriesIx++) {
                currentSeriesPoints = seriesPoints[seriesIx];
                pointCount = currentSeriesPoints.length;
                currentSeries = series[seriesIx];
                linePoints = [];
                for (pointIx = 0; pointIx < pointCount; pointIx++) {
                    point = currentSeriesPoints[pointIx];
                    if (point) {
                        linePoints.push(point);
                    } else if (chart.seriesMissingValues(currentSeries) !== INTERPOLATE) {
                        if (linePoints.length > 1) {
                            segments.push(chart.createSegment(linePoints, currentSeries, seriesIx, last(segments)));
                        }
                        linePoints = [];
                    }
                }
                if (linePoints.length > 1) {
                    segments.push(chart.createSegment(linePoints, currentSeries, seriesIx, last(segments)));
                }
            }
            chart._segments = segments;
            chart.append.apply(chart, segments);
        },
        seriesMissingValues: function(series) {
            var missingValues = series.missingValues, assumeZero = !missingValues && this.options.isStacked;
            return assumeZero ? ZERO : missingValues;
        },
        createSegment: function(linePoints, currentSeries, seriesIx) {
            return new LineSegment(linePoints, currentSeries, seriesIx);
        },
        getNearestPoint: function(x, y, seriesIx) {
            var chart = this, invertAxes = chart.options.invertAxes, axis = invertAxes ? Y : X, pos = invertAxes ? y : x, points = chart.seriesPoints[seriesIx], nearestPointDistance = MAX_VALUE, pointsLength = points.length, currentPoint, pointBox, pointDistance, nearestPoint, i;
            for (i = 0; i < pointsLength; i++) {
                currentPoint = points[i];
                if (currentPoint && defined(currentPoint.value) && currentPoint.value !== null) {
                    pointBox = currentPoint.box;
                    pointDistance = math.abs(pointBox.center()[axis] - pos);
                    if (pointDistance < nearestPointDistance) {
                        nearestPoint = currentPoint;
                        nearestPointDistance = pointDistance;
                    }
                }
            }
            return nearestPoint;
        }
    };
    var LineChart = CategoricalChart.extend({
        init: function(plotArea, options) {
            var chart = this;
            chart._stackAxisRange = {
                min: MAX_VALUE,
                max: MIN_VALUE
            };
            chart._categoryTotals = [];
            chart.enableDiscovery();
            CategoricalChart.fn.init.call(chart, plotArea, options);
        },
        render: function() {
            var chart = this;
            CategoricalChart.fn.render.apply(chart);
            chart.computeAxisRanges();
            chart.renderSegments();
        },
        createPoint: function(data, category, categoryIx, series) {
            var chart = this, value = data.value, options = chart.options, isStacked = options.isStacked, categoryPoints = chart.categoryPoints[categoryIx], missingValues = chart.seriesMissingValues(series), stackPoint, plotValue = 0, fields = data.fields;
            if (!defined(value) || value === null) {
                if (missingValues === ZERO) {
                    value = 0;
                } else {
                    return null;
                }
            }
            var point = new LinePoint(value, deepExtend({
                vertical: !options.invertAxes,
                markers: {
                    border: {
                        color: series.color
                    }
                }
            }, series, {
                color: fields.color,
                markers: {
                    border: {
                        color: fields.color
                    }
                }
            }));
            if (isStacked) {
                stackPoint = lastValue(categoryPoints);
                if (stackPoint) {
                    plotValue = stackPoint.plotValue;
                }
                point.plotValue = value + plotValue;
            }
            chart.append(point);
            return point;
        },
        updateRange: function(value, categoryIx) {
            var chart = this, isStacked = chart.options.isStacked, stackAxisRange = chart._stackAxisRange, totals = chart._categoryTotals, totalsLimits;
            if (defined(value)) {
                if (isStacked) {
                    incrementSlot(totals, categoryIx, value);
                    totalsLimits = sparseArrayLimits(totals);
                    stackAxisRange.min = math.min(stackAxisRange.min, totalsLimits.min);
                    stackAxisRange.max = math.max(stackAxisRange.max, totalsLimits.max);
                } else {
                    CategoricalChart.fn.updateRange.apply(chart, arguments);
                }
            }
        },
        computeAxisRanges: function() {
            var chart = this, isStacked = chart.options.isStacked, axisName;
            if (isStacked) {
                axisName = chart.options.series[0].axis;
                chart.valueAxisRanges[axisName] = chart._stackAxisRange;
            }
        },
        getViewElements: function(view) {
            var chart = this, elements = CategoricalChart.fn.getViewElements.call(chart, view), group = view.createGroup({
                animation: {
                    type: CLIP
                }
            });
            group.children = elements;
            return [ group ];
        },
        bindableFields: function() {
            return [ "color" ];
        }
    });
    deepExtend(LineChart.fn, LineChartMixin);
    var AreaSegment = LineSegment.extend({
        init: function(linePoints, stackPoints, currentSeries, seriesIx) {
            var segment = this;
            segment.stackPoints = stackPoints;
            LineSegment.fn.init.call(segment, linePoints, currentSeries, seriesIx);
        },
        points: function() {
            var segment = this, chart = segment.parent, stack = chart.options.isStacked && segment.seriesIx > 0, plotArea = chart.plotArea, invertAxes = chart.options.invertAxes, valueAxis = chart.seriesValueAxis(segment.series), valueAxisLineBox = valueAxis.lineBox(), categoryAxis = plotArea.seriesCategoryAxis(segment.series), categoryAxisLineBox = categoryAxis.lineBox(), end = invertAxes ? categoryAxisLineBox.x1 : categoryAxisLineBox.y1, stackPoints = segment.stackPoints, points = LineSegment.fn.points.call(segment, stackPoints), firstPoint, lastPoint;
            if (invertAxes) {
                end = clipValue(end, valueAxisLineBox.x1, valueAxisLineBox.x2);
            } else {
                end = clipValue(end, valueAxisLineBox.y1, valueAxisLineBox.y2);
            }
            if (!stack && points.length > 1) {
                firstPoint = points[0];
                lastPoint = last(points);
                if (invertAxes) {
                    points.unshift(new Point2D(end, firstPoint.y));
                    points.push(new Point2D(end, lastPoint.y));
                } else {
                    points.unshift(new Point2D(firstPoint.x, end));
                    points.push(new Point2D(lastPoint.x, end));
                }
            }
            return points;
        },
        getViewElements: function(view) {
            var segment = this, series = segment.series, lineOptions = deepExtend({
                color: series.color,
                opacity: series.opacity
            }, series.line), linePoints = LineSegment.fn.points.call(segment), areaPoints = segment.points();
            ChartElement.fn.getViewElements.call(segment, view);
            return [ view.createPolyline(areaPoints, false, {
                id: segment.options.id,
                fillOpacity: series.opacity,
                fill: series.color,
                stack: series.stack,
                data: {
                    modelId: segment.options.modelId
                },
                zIndex: -1
            }), view.createPolyline(linePoints, false, {
                stroke: lineOptions.color,
                strokeWidth: lineOptions.width,
                strokeOpacity: lineOptions.opacity,
                dashType: lineOptions.dashType,
                data: {
                    modelId: segment.options.modelId
                },
                strokeLineCap: "butt",
                zIndex: -1
            }) ];
        }
    });
    var AreaChart = LineChart.extend({
        createSegment: function(linePoints, currentSeries, seriesIx, prevSegment) {
            var chart = this, options = chart.options, stackPoints;
            if (options.isStacked && seriesIx > 0 && prevSegment) {
                stackPoints = prevSegment.linePoints.slice(0).reverse();
            }
            return new AreaSegment(linePoints, stackPoints, currentSeries, seriesIx);
        },
        seriesMissingValues: function(series) {
            return series.missingValues || ZERO;
        }
    });
    var ScatterChart = ChartElement.extend({
        init: function(plotArea, options) {
            var chart = this;
            ChartElement.fn.init.call(chart, options);
            chart.plotArea = plotArea;
            // X and Y axis ranges grouped by name, e.g.:
            // primary: { min: 0, max: 1 }
            chart.xAxisRanges = {};
            chart.yAxisRanges = {};
            chart.points = [];
            chart.seriesPoints = [];
            chart.render();
        },
        options: {
            series: [],
            tooltip: {
                format: "{0}, {1}"
            },
            labels: {
                format: "{0}, {1}"
            }
        },
        render: function() {
            var chart = this;
            chart.traverseDataPoints(proxy(chart.addValue, chart));
        },
        addValue: function(value, fields) {
            var chart = this, point, x = value.x, y = value.y, seriesIx = fields.seriesIx, seriesPoints = chart.seriesPoints[seriesIx];
            chart.updateRange(value, fields.series);
            if (defined(x) && x !== null && defined(y) && y !== null) {
                point = chart.createPoint(value, fields.series, seriesIx, fields);
                if (point) {
                    extend(point, fields);
                }
            }
            chart.points.push(point);
            seriesPoints.push(point);
        },
        updateRange: function(value, series) {
            var chart = this, x = value.x, y = value.y, xAxisName = series.xAxis, yAxisName = series.yAxis, xAxisRange = chart.xAxisRanges[xAxisName], yAxisRange = chart.yAxisRanges[yAxisName];
            if (defined(x) && x !== null) {
                xAxisRange = chart.xAxisRanges[xAxisName] = xAxisRange || {
                    min: MAX_VALUE,
                    max: MIN_VALUE
                };
                if (typeof x === STRING) {
                    x = toDate(x);
                }
                xAxisRange.min = math.min(xAxisRange.min, x);
                xAxisRange.max = math.max(xAxisRange.max, x);
            }
            if (defined(y) && y !== null) {
                yAxisRange = chart.yAxisRanges[yAxisName] = yAxisRange || {
                    min: MAX_VALUE,
                    max: MIN_VALUE
                };
                if (typeof y === STRING) {
                    y = toDate(y);
                }
                yAxisRange.min = math.min(yAxisRange.min, y);
                yAxisRange.max = math.max(yAxisRange.max, y);
            }
        },
        createPoint: function(value, series, seriesIx, fields) {
            var chart = this, point;
            point = new LinePoint(value, deepExtend({
                markers: {
                    border: {
                        color: series.color
                    },
                    opacity: series.opacity
                },
                tooltip: {
                    format: chart.options.tooltip.format
                },
                labels: {
                    format: chart.options.labels.format
                }
            }, series, {
                color: fields.color,
                markers: {
                    border: {
                        color: fields.color
                    }
                }
            }));
            chart.append(point);
            return point;
        },
        seriesAxes: function(series) {
            var plotArea = this.plotArea, xAxisName = series.xAxis, xAxis = xAxisName ? plotArea.namedXAxes[xAxisName] : plotArea.axisX, yAxisName = series.yAxis, yAxis = yAxisName ? plotArea.namedYAxes[yAxisName] : plotArea.axisY;
            if (!xAxis) {
                throw new Error("Unable to locate X axis with name " + xAxisName);
            }
            if (!yAxis) {
                throw new Error("Unable to locate Y axis with name " + yAxisName);
            }
            return {
                x: xAxis,
                y: yAxis
            };
        },
        reflow: function(targetBox) {
            var chart = this, chartPoints = chart.points, pointIx = 0, point, seriesAxes;
            chart.traverseDataPoints(function(value, fields) {
                point = chartPoints[pointIx++];
                seriesAxes = chart.seriesAxes(fields.series);
                var slotX = seriesAxes.x.getSlot(value.x, value.x), slotY = seriesAxes.y.getSlot(value.y, value.y), pointSlot = new Box2D(slotX.x1, slotY.y1, slotX.x2, slotY.y2);
                if (point) {
                    point.reflow(pointSlot);
                }
            });
            chart.box = targetBox;
        },
        getViewElements: function(view) {
            var chart = this, elements = ChartElement.fn.getViewElements.call(chart, view), group = view.createGroup({
                animation: {
                    type: CLIP
                }
            });
            group.children = elements;
            return [ group ];
        },
        traverseDataPoints: function(callback) {
            var chart = this, options = chart.options, series = options.series, seriesPoints = chart.seriesPoints, bindableFields = chart.bindableFields(), pointIx, seriesIx, currentSeries, currentSeriesPoints, pointData, value, fields;
            for (seriesIx = 0; seriesIx < series.length; seriesIx++) {
                currentSeries = series[seriesIx];
                currentSeriesPoints = seriesPoints[seriesIx];
                if (!currentSeriesPoints) {
                    seriesPoints[seriesIx] = [];
                }
                for (pointIx = 0; pointIx < currentSeries.data.length; pointIx++) {
                    pointData = bindPoint(currentSeries, pointIx, bindableFields);
                    value = pointData.value;
                    fields = pointData.fields;
                    callback(value, deepExtend({
                        pointIx: pointIx,
                        series: currentSeries,
                        seriesIx: seriesIx,
                        dataItem: currentSeries.data[pointIx],
                        owner: chart
                    }, fields));
                }
            }
        },
        bindableFields: function() {
            return [ "color" ];
        },
        formatPointValue: function(point, format) {
            var value = point.value;
            return autoFormat(format, value.x, value.y);
        }
    });
    var ScatterLineChart = ScatterChart.extend({
        render: function() {
            var chart = this;
            ScatterChart.fn.render.call(chart);
            chart.renderSegments();
        }
    });
    deepExtend(ScatterLineChart.fn, LineChartMixin);
    var BubbleChart = ScatterChart.extend({
        options: {
            tooltip: {
                format: "{3}"
            },
            labels: {
                format: "{3}"
            }
        },
        addValue: function(value, fields) {
            var chart = this, color, series = fields.series, negativeValues = series.negativeValues, seriesColors = chart.plotArea.options.seriesColors || [], visible = true;
            color = fields.color || series.color || seriesColors[fields.pointIx % seriesColors.length];
            if (value.size < 0) {
                color = negativeValues.color || color;
                visible = negativeValues.visible;
            }
            fields.color = color;
            if (visible) {
                ScatterChart.fn.addValue.call(this, value, fields);
            }
        },
        reflow: function(box) {
            var chart = this;
            chart.updateBubblesSize(box);
            ScatterChart.fn.reflow.call(chart, box);
        },
        createPoint: function(value, series, seriesIx, fields) {
            var chart = this, point, pointsCount = series.data.length, delay = fields.pointIx * (INITIAL_ANIMATION_DURATION / pointsCount), animationOptions = {
                delay: delay,
                duration: INITIAL_ANIMATION_DURATION - delay,
                type: BUBBLE
            };
            point = new Bubble(value, deepExtend({
                tooltip: {
                    format: chart.options.tooltip.format
                },
                labels: {
                    format: chart.options.labels.format,
                    animation: animationOptions
                }
            }, series, {
                color: fields.color,
                markers: {
                    type: CIRCLE,
                    background: fields.color,
                    border: series.border,
                    opacity: series.opacity,
                    animation: animationOptions
                }
            }));
            chart.append(point);
            return point;
        },
        updateBubblesSize: function(box) {
            var chart = this, options = chart.options, series = options.series, boxSize = math.min(box.width(), box.height()), seriesIx, pointIx;
            for (seriesIx = 0; seriesIx < series.length; seriesIx++) {
                var currentSeries = series[seriesIx], seriesPoints = chart.seriesPoints[seriesIx], seriesMaxSize = chart.maxSize(seriesPoints), minSize = currentSeries.minSize || math.max(boxSize * .02, 10), maxSize = currentSeries.maxSize || boxSize * .2, minR = minSize / 2, maxR = maxSize / 2, minArea = math.PI * minR * minR, maxArea = math.PI * maxR * maxR, areaRange = maxArea - minArea, areaRatio = areaRange / seriesMaxSize;
                for (pointIx = 0; pointIx < seriesPoints.length; pointIx++) {
                    var point = seriesPoints[pointIx], area = math.abs(point.value.size) * areaRatio, r = math.sqrt((minArea + area) / math.PI);
                    deepExtend(point.options, {
                        markers: {
                            size: r * 2,
                            zIndex: maxR - r
                        },
                        labels: {
                            zIndex: maxR - r + 1
                        }
                    });
                }
            }
        },
        maxSize: function(seriesPoints) {
            var length = seriesPoints.length, max = 0, i, size;
            for (i = 0; i < length; i++) {
                size = seriesPoints[i].value.size;
                max = math.max(max, math.abs(size));
            }
            return max;
        },
        bindableFields: function() {
            return [ "color", "category", "visibleInLegend" ];
        },
        getViewElements: function(view) {
            var chart = this, elements = ChartElement.fn.getViewElements.call(chart, view), group = view.createGroup();
            group.children = elements;
            return [ group ];
        },
        formatPointValue: function(point, format) {
            var value = point.value;
            return autoFormat(format, value.x, value.y, value.size, point.category);
        }
    });
    var Candlestick = ChartElement.extend({
        init: function(value, options) {
            var point = this;
            ChartElement.fn.init.call(point, options);
            point.value = value;
            point.options.id = uniqueId();
            point.enableDiscovery();
        },
        options: {
            border: {
                _brightness: .8
            },
            line: {
                width: 2
            },
            overlay: {
                gradient: GLASS
            },
            tooltip: {
                format: "<table style='text-align: left;'>" + "<th colspan='2'>{4:d}</th>" + "<tr><td>Open:</td><td>{0:C}</td></tr>" + "<tr><td>High:</td><td>{1:C}</td></tr>" + "<tr><td>Low:</td><td>{2:C}</td></tr>" + "<tr><td>Close:</td><td>{3:C}</td></tr>" + "</table>"
            },
            highlight: {
                opacity: 1,
                border: {
                    width: 1,
                    opacity: 1
                },
                line: {
                    width: 1,
                    opacity: 1
                }
            }
        },
        reflow: function(box) {
            var point = this, options = point.options, chart = point.owner, value = point.value, valueAxis = chart.seriesValueAxis(options), points = [], mid, ocSlot, lhSlot;
            ocSlot = valueAxis.getSlot(value.open, value.close);
            lhSlot = valueAxis.getSlot(value.low, value.high);
            ocSlot.x1 = lhSlot.x1 = box.x1;
            ocSlot.x2 = lhSlot.x2 = box.x2;
            point.realBody = ocSlot;
            mid = lhSlot.center().x;
            points.push([ new Point2D(mid, lhSlot.y1), new Point2D(mid, ocSlot.y1) ]);
            points.push([ new Point2D(mid, ocSlot.y2), new Point2D(mid, lhSlot.y2) ]);
            point.lowHighLinePoints = points;
            point.box = lhSlot.clone().wrap(ocSlot);
        },
        getViewElements: function(view) {
            var point = this, options = point.options, elements = [], border = options.border.width > 0 ? {
                stroke: point.getBorderColor(),
                strokeWidth: options.border.width,
                dashType: options.border.dashType,
                strokeOpacity: defined(options.border.opacity) ? options.border.opacity : options.opacity
            } : {}, rectStyle = deepExtend({
                fill: point.color,
                fillOpacity: options.opacity
            }, border), lineStyle = {
                strokeOpacity: defined(options.line.opacity) ? options.line.opacity : options.opacity,
                strokeWidth: options.line.width,
                stroke: options.line.color || point.color,
                dashType: options.line.dashType,
                strokeLineCap: "butt"
            }, group = view.createGroup({
                animation: {
                    type: CLIP
                }
            });
            if (options.overlay) {
                rectStyle.overlay = deepExtend({
                    rotation: 0
                }, options.overlay);
            }
            elements.push(view.createRect(point.realBody, rectStyle));
            elements.push(view.createPolyline(point.lowHighLinePoints[0], false, lineStyle));
            elements.push(view.createPolyline(point.lowHighLinePoints[1], false, lineStyle));
            elements.push(point.createOverlayRect(view, options));
            append(elements, ChartElement.fn.getViewElements.call(point, view));
            group.children = elements;
            return [ group ];
        },
        getBorderColor: function() {
            var point = this, options = point.options, color = point.color, border = options.border, borderColor = border.color;
            if (!defined(borderColor)) {
                borderColor = new Color(color).brightness(border._brightness).toHex();
            }
            return borderColor;
        },
        createOverlayRect: function(view, options) {
            return view.createRect(this.box, {
                data: {
                    modelId: options.modelId
                },
                fill: "#fff",
                fillOpacity: 0
            });
        },
        highlightOverlay: function(view, options) {
            var point = this, pointOptions = point.options, highlight = pointOptions.highlight, border = highlight.border, borderColor = point.getBorderColor(), line = highlight.line, data = {
                data: {
                    modelId: pointOptions.modelId
                }
            }, rectStyle = deepExtend({}, data, options, {
                stroke: borderColor,
                strokeOpacity: border.opacity,
                strokeWidth: border.width
            }), lineStyle = deepExtend({}, data, {
                stroke: line.color || borderColor,
                strokeWidth: line.width,
                strokeOpacity: line.opacity,
                strokeLineCap: "butt"
            }), group = view.createGroup();
            group.children.push(view.createRect(point.realBody, rectStyle));
            group.children.push(view.createPolyline(point.lowHighLinePoints[0], false, lineStyle));
            group.children.push(view.createPolyline(point.lowHighLinePoints[1], false, lineStyle));
            return group;
        },
        tooltipAnchor: function() {
            var point = this, box = point.box;
            return new Point2D(box.x2 + TOOLTIP_OFFSET, box.y1 + TOOLTIP_OFFSET);
        },
        formatValue: function(format) {
            var point = this;
            return point.owner.formatPointValue(point, format);
        }
    });
    deepExtend(Candlestick.fn, PointEventsMixin);
    var CandlestickChart = CategoricalChart.extend({
        options: {},
        bindableFields: function() {
            return [ "color", "downColor" ];
        },
        reflowCategories: function(categorySlots) {
            var chart = this, children = chart.children, childrenLength = children.length, i;
            for (i = 0; i < childrenLength; i++) {
                children[i].reflow(categorySlots[i]);
            }
        },
        addValue: function(data, category, categoryIx, series, seriesIx) {
            var chart = this, options = chart.options, value = data.value, children = chart.children, pointColor = data.fields.color || series.color, valueParts = this.splitValue(value), hasValue = validNumbers(valueParts), categoryPoints = chart.categoryPoints[categoryIx], point, cluster;
            if (!categoryPoints) {
                chart.categoryPoints[categoryIx] = categoryPoints = [];
            }
            if (hasValue) {
                point = chart.createPoint(value, series);
            }
            cluster = children[categoryIx];
            if (!cluster) {
                cluster = new ClusterLayout({
                    vertical: options.invertAxes,
                    gap: options.gap,
                    spacing: options.spacing
                });
                chart.append(cluster);
            }
            if (point) {
                chart.updateRange(value, categoryIx, series);
                cluster.append(point);
                if (series.type == CANDLESTICK) {
                    if (value.open > value.close) {
                        pointColor = data.fields.downColor || series.downColor;
                    }
                }
                point.color = pointColor;
                point.categoryIx = categoryIx;
                point.category = category;
                point.series = series;
                point.seriesIx = seriesIx;
                point.owner = chart;
                point.dataItem = series.data[categoryIx];
            }
            chart.points.push(point);
            categoryPoints.push(point);
        },
        createPoint: function(value, series) {
            return new Candlestick(value, series);
        },
        splitValue: function(value) {
            return [ value.low, value.open, value.close, value.high ];
        },
        updateRange: function(value, categoryIx, series) {
            var chart = this, axisName = series.axis, axisRange = chart.valueAxisRanges[axisName], parts = chart.splitValue(value);
            axisRange = chart.valueAxisRanges[axisName] = axisRange || {
                min: MAX_VALUE,
                max: MIN_VALUE
            };
            axisRange = chart.valueAxisRanges[axisName] = {
                min: math.min.apply(math, parts.concat([ axisRange.min ])),
                max: math.max.apply(math, parts.concat([ axisRange.max ]))
            };
        },
        formatPointValue: function(point, format) {
            var value = point.value;
            return autoFormat(format, value.open, value.high, value.low, value.close, point.category);
        }
    });
    var OHLCPoint = Candlestick.extend({
        reflow: function(box) {
            var point = this, options = point.options, chart = point.owner, value = point.value, valueAxis = chart.seriesValueAxis(options), oPoints = [], cPoints = [], lhPoints = [], mid, oSlot, cSlot, lhSlot;
            lhSlot = valueAxis.getSlot(value.low, value.high);
            oSlot = valueAxis.getSlot(value.open, value.open);
            cSlot = valueAxis.getSlot(value.close, value.close);
            oSlot.x1 = cSlot.x1 = lhSlot.x1 = box.x1;
            oSlot.x2 = cSlot.x2 = lhSlot.x2 = box.x2;
            mid = lhSlot.center().x;
            oPoints.push(new Point2D(oSlot.x1, oSlot.y1));
            oPoints.push(new Point2D(mid, oSlot.y1));
            cPoints.push(new Point2D(mid, cSlot.y1));
            cPoints.push(new Point2D(cSlot.x2, cSlot.y1));
            lhPoints.push(new Point2D(mid, lhSlot.y1));
            lhPoints.push(new Point2D(mid, lhSlot.y2));
            point.oPoints = oPoints;
            point.cPoints = cPoints;
            point.lhPoints = lhPoints;
            point.box = lhSlot.clone().wrap(oSlot.clone().wrap(cSlot));
        },
        getViewElements: function(view) {
            var point = this, options = point.options, elements = [], lineStyle = {
                strokeOpacity: options.opacity,
                zIndex: -1,
                strokeWidth: options.width,
                stroke: point.color,
                dashType: options.dashType
            }, group = view.createGroup({
                animation: {
                    type: CLIP
                }
            });
            elements.push(point.createOverlayRect(view, options));
            elements.push(view.createPolyline(point.oPoints, true, lineStyle));
            elements.push(view.createPolyline(point.cPoints, true, lineStyle));
            elements.push(view.createPolyline(point.lhPoints, true, lineStyle));
            append(elements, ChartElement.fn.getViewElements.call(point, view));
            group.children = elements;
            return [ group ];
        },
        highlightOverlay: function(view) {
            var point = this, pointOptions = point.options, highlight = pointOptions.highlight, data = {
                data: {
                    modelId: pointOptions.modelId
                }
            }, lineStyle = deepExtend(data, {
                strokeWidth: highlight.line.width,
                strokeOpacity: highlight.line.opacity,
                stroke: highlight.line.color || point.color
            }), group = view.createGroup();
            group.children.push(view.createPolyline(point.oPoints, true, lineStyle));
            group.children.push(view.createPolyline(point.cPoints, true, lineStyle));
            group.children.push(view.createPolyline(point.lhPoints, true, lineStyle));
            return group;
        }
    });
    var OHLCChart = CandlestickChart.extend({
        createPoint: function(value, series) {
            return new OHLCPoint(value, series);
        },
        bindableFields: function() {
            return [ "color" ];
        }
    });
    var PieSegment = ChartElement.extend({
        init: function(value, sector, options) {
            var segment = this;
            segment.value = value;
            segment.sector = sector;
            segment.enableDiscovery();
            ChartElement.fn.init.call(segment, options);
        },
        options: {
            color: WHITE,
            overlay: {
                gradient: ROUNDED_BEVEL
            },
            border: {
                width: .5
            },
            labels: {
                visible: false,
                distance: 35,
                font: DEFAULT_FONT,
                margin: getSpacing(.5),
                align: CIRCLE,
                zIndex: 1,
                position: OUTSIDE_END
            },
            animation: {
                type: PIE
            },
            highlight: {
                visible: true,
                border: {
                    width: 1
                }
            }
        },
        render: function() {
            var segment = this, options = segment.options, labels = options.labels, labelText = segment.value, labelTemplate;
            if (segment._rendered) {
                return;
            } else {
                segment._rendered = true;
            }
            if (labels.template) {
                labelTemplate = template(labels.template);
                labelText = labelTemplate({
                    dataItem: segment.dataItem,
                    category: segment.category,
                    value: segment.value,
                    series: segment.series,
                    percentage: segment.percentage
                });
            } else if (labels.format) {
                labelText = autoFormat(labels.format, labelText);
            }
            if (labels.visible && labelText) {
                segment.label = new TextBox(labelText, deepExtend({}, labels, {
                    id: uniqueId(),
                    align: CENTER,
                    vAlign: "",
                    animation: {
                        type: FADEIN,
                        delay: segment.animationDelay
                    }
                }));
                segment.append(segment.label);
            }
        },
        reflow: function(targetBox) {
            var segment = this;
            segment.render();
            segment.box = targetBox;
            segment.reflowLabel();
        },
        reflowLabel: function() {
            var segment = this, sector = segment.sector.clone(), options = segment.options, label = segment.label, labelsOptions = options.labels, labelsDistance = labelsOptions.distance, angle = sector.middle(), lp, x1, labelWidth, labelHeight;
            if (label) {
                labelHeight = label.box.height();
                labelWidth = label.box.width();
                if (labelsOptions.position == CENTER) {
                    sector.r = math.abs((sector.r - labelHeight) / 2) + labelHeight;
                    lp = sector.point(angle);
                    label.reflow(new Box2D(lp.x, lp.y - labelHeight / 2, lp.x, lp.y));
                } else if (labelsOptions.position == INSIDE_END) {
                    sector.r = sector.r - labelHeight / 2;
                    lp = sector.point(angle);
                    label.reflow(new Box2D(lp.x, lp.y - labelHeight / 2, lp.x, lp.y));
                } else {
                    lp = sector.clone().expand(labelsDistance).point(angle);
                    if (lp.x >= sector.c.x) {
                        x1 = lp.x + labelWidth;
                        label.orientation = RIGHT;
                    } else {
                        x1 = lp.x - labelWidth;
                        label.orientation = LEFT;
                    }
                    label.reflow(new Box2D(x1, lp.y - labelHeight, lp.x, lp.y));
                }
            }
        },
        getViewElements: function(view) {
            var segment = this, sector = segment.sector, options = segment.options, borderOptions = options.border || {}, border = borderOptions.width > 0 ? {
                stroke: borderOptions.color,
                strokeWidth: borderOptions.width,
                strokeOpacity: borderOptions.opacity,
                dashType: borderOptions.dashType
            } : {}, elements = [], overlay = options.overlay;
            if (overlay) {
                overlay = deepExtend({}, options.overlay, {
                    r: sector.r,
                    ir: sector.ir,
                    cx: sector.c.x,
                    cy: sector.c.y,
                    bbox: sector.getBBox()
                });
            }
            if (segment.value) {
                elements.push(segment.createSegment(view, sector, deepExtend({
                    id: options.id,
                    fill: options.color,
                    overlay: overlay,
                    fillOpacity: options.opacity,
                    strokeOpacity: options.opacity,
                    animation: deepExtend(options.animation, {
                        delay: segment.animationDelay
                    }),
                    data: {
                        modelId: options.modelId
                    },
                    zIndex: options.zIndex,
                    singleSegment: (segment.options.data || []).length === 1
                }, border)));
            }
            append(elements, ChartElement.fn.getViewElements.call(segment, view));
            return elements;
        },
        createSegment: function(view, sector, options) {
            if (options.singleSegment) {
                return view.createCircle(sector.c, sector.r, options);
            } else {
                return view.createSector(sector, options);
            }
        },
        highlightOverlay: function(view, options) {
            var segment = this, highlight = segment.options.highlight || {}, border = highlight.border || {}, outlineId = segment.options.id + OUTLINE_SUFFIX, element;
            options = deepExtend({}, options, {
                id: outlineId
            });
            if (segment.value !== 0) {
                element = segment.createSegment(view, segment.sector, deepExtend({}, options, {
                    fill: highlight.color,
                    fillOpacity: highlight.opacity,
                    strokeOpacity: border.opacity,
                    strokeWidth: border.width,
                    stroke: border.color,
                    id: null,
                    data: {
                        modelId: segment.options.modelId
                    }
                }));
            }
            return element;
        },
        tooltipAnchor: function(tooltipWidth, tooltipHeight) {
            var point = this, sector = point.sector.clone().expand(TOOLTIP_OFFSET), w = tooltipWidth / 2, h = tooltipHeight / 2, midAndle = sector.middle(), pointAngle = midAndle * DEGREE, lp = sector.point(midAndle), cx = lp.x - w, cy = lp.y - h, sa = math.sin(pointAngle), ca = math.cos(pointAngle);
            if (math.abs(sa) < .9) {
                cx += w * -ca / math.abs(ca);
            }
            if (math.abs(ca) < .9) {
                cy += h * -sa / math.abs(sa);
            }
            return new Point2D(cx, cy);
        },
        formatValue: function(format) {
            var point = this;
            return point.owner.formatPointValue(point, format);
        }
    });
    deepExtend(PieSegment.fn, PointEventsMixin);
    var PieChart = ChartElement.extend({
        init: function(plotArea, options) {
            var chart = this;
            ChartElement.fn.init.call(chart, options);
            chart.plotArea = plotArea;
            chart.segments = [];
            chart.legendItems = [];
            chart.render();
        },
        options: {
            startAngle: 90,
            connectors: {
                width: 1,
                color: "#939393",
                padding: 4
            }
        },
        render: function() {
            var chart = this;
            chart.traverseDataPoints(proxy(chart.addValue, chart));
        },
        traverseDataPoints: function(callback) {
            var chart = this, options = chart.options, colors = chart.plotArea.options.seriesColors || [], colorsCount = colors.length, series = options.series, seriesCount = series.length, overlayId = uniqueId(), bindableFields = chart.bindableFields(), currentSeries, pointData, fields, seriesIx, angle, data, anglePerValue, value, explode, total, currentAngle, i;
            for (seriesIx = 0; seriesIx < seriesCount; seriesIx++) {
                currentSeries = series[seriesIx];
                data = currentSeries.data;
                total = chart.pointsTotal(currentSeries);
                anglePerValue = 360 / total;
                if (defined(currentSeries.startAngle)) {
                    currentAngle = currentSeries.startAngle;
                } else {
                    currentAngle = options.startAngle;
                }
                if (seriesIx != seriesCount - 1) {
                    if (currentSeries.labels.position == OUTSIDE_END) {
                        currentSeries.labels.position = CENTER;
                    }
                }
                for (i = 0; i < data.length; i++) {
                    pointData = bindPoint(currentSeries, i, bindableFields);
                    value = pointData.value;
                    fields = pointData.fields;
                    angle = round(value * anglePerValue, DEFAULT_PRECISION);
                    explode = data.length != 1 && !!fields.explode;
                    currentSeries.color = fields.color || colors[i % colorsCount];
                    callback(value, new Ring(null, 0, 0, currentAngle, angle), {
                        owner: chart,
                        category: fields.category || "",
                        categoryIx: i,
                        series: currentSeries,
                        seriesIx: seriesIx,
                        dataItem: data[i],
                        percentage: value / total,
                        explode: explode,
                        visibleInLegend: fields.visibleInLegend,
                        overlay: {
                            id: overlayId + seriesIx
                        },
                        zIndex: seriesCount - seriesIx,
                        animationDelay: chart.animationDelay(i, seriesIx, seriesCount)
                    });
                    currentAngle += angle;
                }
            }
        },
        bindableFields: function() {
            return [ "category", "color", "explode", "visibleInLegend" ];
        },
        addValue: function(value, sector, fields) {
            var chart = this, segment;
            chart.createLegendItem(value, fields);
            if (!value) {
                return;
            }
            segment = new PieSegment(value, sector, fields.series);
            segment.options.id = uniqueId();
            extend(segment, fields);
            chart.append(segment);
            chart.segments.push(segment);
        },
        createLegendItem: function(value, point) {
            var chart = this, options = (chart.options.legend || {}).labels || {}, text, labelTemplate;
            if (point && point.visibleInLegend !== false) {
                text = point.category || "";
                if ((options || {}).template) {
                    labelTemplate = template(options.template);
                    text = labelTemplate({
                        text: text,
                        series: point.series,
                        dataItem: point.dataItem,
                        percentage: point.percentage,
                        value: value
                    });
                }
                chart.legendItems.push({
                    name: text,
                    color: point.series.color
                });
            }
        },
        pointsTotal: function(series) {
            var data = series.data, length = data.length, sum = 0, i;
            for (i = 0; i < length; i++) {
                sum += bindPoint(series, i).value;
            }
            return sum;
        },
        reflow: function(targetBox) {
            var chart = this, options = chart.options, box = targetBox.clone(), space = 5, minWidth = math.min(box.width(), box.height()), halfMinWidth = minWidth / 2, defaultPadding = minWidth - minWidth * .85, padding = defined(options.padding) ? options.padding : defaultPadding, newBox = new Box2D(box.x1, box.y1, box.x1 + minWidth, box.y1 + minWidth), newBoxCenter = newBox.center(), seriesConfigs = chart.seriesConfigs || [], boxCenter = box.center(), segments = chart.segments, count = segments.length, seriesCount = options.series.length, leftSideLabels = [], rightSideLabels = [], seriesConfig, seriesIndex, label, segment, sector, r, i, c;
            padding = padding > halfMinWidth - space ? halfMinWidth - space : padding;
            newBox.translate(boxCenter.x - newBoxCenter.x, boxCenter.y - newBoxCenter.y);
            r = halfMinWidth - padding;
            c = new Point2D(r + newBox.x1 + padding, r + newBox.y1 + padding);
            for (i = 0; i < count; i++) {
                segment = segments[i];
                sector = segment.sector;
                sector.r = r;
                sector.c = c;
                seriesIndex = segment.seriesIx;
                if (seriesConfigs.length) {
                    seriesConfig = seriesConfigs[seriesIndex];
                    sector.ir = seriesConfig.ir;
                    sector.r = seriesConfig.r;
                }
                if (seriesIndex == seriesCount - 1 && segment.explode) {
                    sector.c = sector.clone().radius(sector.r * .15).point(sector.middle());
                }
                segment.reflow(newBox);
                label = segment.label;
                if (label) {
                    if (label.options.position === OUTSIDE_END) {
                        if (seriesIndex == seriesCount - 1) {
                            if (label.orientation === RIGHT) {
                                rightSideLabels.push(label);
                            } else {
                                leftSideLabels.push(label);
                            }
                        }
                    }
                }
            }
            if (leftSideLabels.length > 0) {
                leftSideLabels.sort(chart.labelComparator(true));
                chart.leftLabelsReflow(leftSideLabels);
            }
            if (rightSideLabels.length > 0) {
                rightSideLabels.sort(chart.labelComparator(false));
                chart.rightLabelsReflow(rightSideLabels);
            }
            chart.box = newBox;
        },
        leftLabelsReflow: function(labels) {
            var chart = this, distances = chart.distanceBetweenLabels(labels);
            chart.distributeLabels(distances, labels);
        },
        rightLabelsReflow: function(labels) {
            var chart = this, distances = chart.distanceBetweenLabels(labels);
            chart.distributeLabels(distances, labels);
        },
        distanceBetweenLabels: function(labels) {
            var chart = this, segments = chart.segments, segment = segments[segments.length - 1], sector = segment.sector, firstBox = labels[0].box, count = labels.length - 1, lr = sector.r + segment.options.labels.distance, distances = [], secondBox, distance, i;
            distance = round(firstBox.y1 - (sector.c.y - lr - firstBox.height() - firstBox.height() / 2));
            distances.push(distance);
            for (i = 0; i < count; i++) {
                firstBox = labels[i].box;
                secondBox = labels[i + 1].box;
                distance = round(secondBox.y1 - firstBox.y2);
                distances.push(distance);
            }
            distance = round(sector.c.y + lr - labels[count].box.y2 - labels[count].box.height() / 2);
            distances.push(distance);
            return distances;
        },
        distributeLabels: function(distances, labels) {
            var chart = this, count = distances.length, remaining, left, right, i;
            for (i = 0; i < count; i++) {
                left = right = i;
                remaining = -distances[i];
                while (remaining > 0 && (left >= 0 || right < count)) {
                    remaining = chart._takeDistance(distances, i, --left, remaining);
                    remaining = chart._takeDistance(distances, i, ++right, remaining);
                }
            }
            chart.reflowLabels(distances, labels);
        },
        _takeDistance: function(distances, anchor, position, amount) {
            if (distances[position] > 0) {
                var available = math.min(distances[position], amount);
                amount -= available;
                distances[position] -= available;
                distances[anchor] += available;
            }
            return amount;
        },
        reflowLabels: function(distances, labels) {
            var chart = this, segments = chart.segments, segment = segments[segments.length - 1], sector = segment.sector, labelsCount = labels.length, labelOptions = segment.options.labels, labelDistance = labelOptions.distance, boxY = sector.c.y - (sector.r + labelDistance) - labels[0].box.height(), label, boxX, box, i;
            distances[0] += 2;
            for (i = 0; i < labelsCount; i++) {
                label = labels[i];
                boxY += distances[i];
                box = label.box;
                boxX = chart.hAlignLabel(box.x2, sector.clone().expand(labelDistance), boxY, boxY + box.height(), label.orientation == RIGHT);
                if (label.orientation == RIGHT) {
                    if (labelOptions.align !== CIRCLE) {
                        boxX = sector.r + sector.c.x + labelDistance;
                    }
                    label.reflow(new Box2D(boxX + box.width(), boxY, boxX, boxY));
                } else {
                    if (labelOptions.align !== CIRCLE) {
                        boxX = sector.c.x - sector.r - labelDistance;
                    }
                    label.reflow(new Box2D(boxX - box.width(), boxY, boxX, boxY));
                }
                boxY += box.height();
            }
        },
        getViewElements: function(view) {
            var chart = this, options = chart.options, connectors = options.connectors, segments = chart.segments, connectorLine, lines = [], count = segments.length, space = 4, sector, angle, points, segment, seriesIx, label, i;
            for (i = 0; i < count; i++) {
                segment = segments[i];
                sector = segment.sector;
                angle = sector.middle();
                label = segment.label;
                seriesIx = {
                    seriesId: segment.seriesIx
                };
                if (label) {
                    points = [];
                    if (label.options.position === OUTSIDE_END && segment.value !== 0) {
                        var box = label.box, centerPoint = sector.c, start = sector.point(angle), middle = new Point2D(box.x1, box.center().y), sr, end, crossing;
                        start = sector.clone().expand(connectors.padding).point(angle);
                        points.push(start);
                        if (label.orientation == RIGHT) {
                            end = new Point2D(box.x1 - connectors.padding, box.center().y);
                            crossing = intersection(centerPoint, start, middle, end);
                            middle = new Point2D(end.x - space, end.y);
                            crossing = crossing || middle;
                            crossing.x = math.min(crossing.x, middle.x);
                            if (chart.pointInCircle(crossing, sector.c, sector.r + space) || crossing.x < sector.c.x) {
                                sr = sector.c.x + sector.r + space;
                                if (segment.options.labels.align !== COLUMN) {
                                    if (sr < middle.x) {
                                        points.push(new Point2D(sr, start.y));
                                    } else {
                                        points.push(new Point2D(start.x + space * 2, start.y));
                                    }
                                } else {
                                    points.push(new Point2D(sr, start.y));
                                }
                                points.push(new Point2D(middle.x, end.y));
                            } else {
                                crossing.y = end.y;
                                points.push(crossing);
                            }
                        } else {
                            end = new Point2D(box.x2 + connectors.padding, box.center().y);
                            crossing = intersection(centerPoint, start, middle, end);
                            middle = new Point2D(end.x + space, end.y);
                            crossing = crossing || middle;
                            crossing.x = math.max(crossing.x, middle.x);
                            if (chart.pointInCircle(crossing, sector.c, sector.r + space) || crossing.x > sector.c.x) {
                                sr = sector.c.x - sector.r - space;
                                if (segment.options.labels.align !== COLUMN) {
                                    if (sr > middle.x) {
                                        points.push(new Point2D(sr, start.y));
                                    } else {
                                        points.push(new Point2D(start.x - space * 2, start.y));
                                    }
                                } else {
                                    points.push(new Point2D(sr, start.y));
                                }
                                points.push(new Point2D(middle.x, end.y));
                            } else {
                                crossing.y = end.y;
                                points.push(crossing);
                            }
                        }
                        points.push(end);
                        connectorLine = view.createPolyline(points, false, {
                            id: uniqueId(),
                            stroke: connectors.color,
                            strokeWidth: connectors.width,
                            animation: {
                                type: FADEIN,
                                delay: segment.animationDelay
                            },
                            data: {
                                modelId: segment.options.modelId
                            }
                        });
                        lines.push(connectorLine);
                    }
                }
            }
            append(lines, ChartElement.fn.getViewElements.call(chart, view));
            return lines;
        },
        labelComparator: function(reverse) {
            reverse = reverse ? -1 : 1;
            return function(a, b) {
                a = (a.parent.sector.middle() + 270) % 360;
                b = (b.parent.sector.middle() + 270) % 360;
                return (a - b) * reverse;
            };
        },
        hAlignLabel: function(originalX, sector, y1, y2, direction) {
            var cx = sector.c.x, cy = sector.c.y, r = sector.r, t = math.min(math.abs(cy - y1), math.abs(cy - y2));
            if (t > r) {
                return originalX;
            } else {
                return cx + math.sqrt(r * r - t * t) * (direction ? 1 : -1);
            }
        },
        pointInCircle: function(point, c, r) {
            return sqr(c.x - point.x) + sqr(c.y - point.y) < sqr(r);
        },
        formatPointValue: function(point, format) {
            return autoFormat(format, point.value);
        },
        animationDelay: function(categoryIndex) {
            return categoryIndex * PIE_SECTOR_ANIM_DELAY;
        }
    });
    var DonutSegment = PieSegment.extend({
        options: {
            overlay: {
                gradient: ROUNDED_GLASS
            },
            labels: {
                position: CENTER
            },
            animation: {
                type: PIE
            }
        },
        reflowLabel: function() {
            var segment = this, sector = segment.sector.clone(), options = segment.options, label = segment.label, labelsOptions = options.labels, lp, angle = sector.middle(), labelHeight;
            if (label) {
                labelHeight = label.box.height();
                if (labelsOptions.position == CENTER) {
                    sector.r -= (sector.r - sector.ir) / 2;
                    lp = sector.point(angle);
                    label.reflow(new Box2D(lp.x, lp.y - labelHeight / 2, lp.x, lp.y));
                } else {
                    PieSegment.fn.reflowLabel.call(segment);
                }
            }
        },
        createSegment: function(view, sector, options) {
            return view.createRing(sector, options);
        }
    });
    deepExtend(DonutSegment.fn, PointEventsMixin);
    var DonutChart = PieChart.extend({
        options: {
            startAngle: 90,
            connectors: {
                width: 1,
                color: "#939393",
                padding: 4
            }
        },
        addValue: function(value, sector, fields) {
            var chart = this, segment;
            chart.createLegendItem(value, fields);
            if (!value) {
                return;
            }
            segment = new DonutSegment(value, sector, fields.series);
            segment.options.id = uniqueId();
            extend(segment, fields);
            chart.append(segment);
            chart.segments.push(segment);
        },
        reflow: function(targetBox) {
            var chart = this, options = chart.options, box = targetBox.clone(), space = 5, minWidth = math.min(box.width(), box.height()), halfMinWidth = minWidth / 2, defaultPadding = minWidth - minWidth * .85, padding = defined(options.padding) ? options.padding : defaultPadding, series = options.series, currentSeries, seriesCount = series.length, seriesWithoutSize = 0, holeSize, totalSize, size, margin = 0, i, r, ir = 0, currentSize = 0;
            chart.seriesConfigs = [];
            padding = padding > halfMinWidth - space ? halfMinWidth - space : padding, totalSize = halfMinWidth - padding;
            for (i = 0; i < seriesCount; i++) {
                currentSeries = series[i];
                if (i === 0) {
                    if (defined(currentSeries.holeSize)) {
                        holeSize = currentSeries.holeSize;
                        totalSize -= currentSeries.holeSize;
                    }
                }
                if (defined(currentSeries.size)) {
                    totalSize -= currentSeries.size;
                } else {
                    seriesWithoutSize++;
                }
                if (defined(currentSeries.margin) && i != seriesCount - 1) {
                    totalSize -= currentSeries.margin;
                }
            }
            if (!defined(holeSize)) {
                currentSize = (halfMinWidth - padding) / (seriesCount + .75);
                holeSize = currentSize * .75;
                totalSize -= holeSize;
            }
            ir = holeSize;
            for (i = 0; i < seriesCount; i++) {
                currentSeries = series[i];
                size = defined(currentSeries.size) ? currentSeries.size : totalSize / seriesWithoutSize;
                ir += margin;
                r = ir + size;
                chart.seriesConfigs.push({
                    ir: ir,
                    r: r
                });
                margin = currentSeries.margin || 0;
                ir = r;
            }
            PieChart.fn.reflow.call(chart, targetBox);
        },
        animationDelay: function(categoryIndex, seriesIndex, seriesCount) {
            return categoryIndex * DONUT_SECTOR_ANIM_DELAY + INITIAL_ANIMATION_DURATION * (seriesIndex + 1) / (seriesCount + 1);
        }
    });
    var Pane = BoxElement.extend({
        init: function(options) {
            var pane = this;
            BoxElement.fn.init.call(pane, options);
            options = pane.options;
            options.id = uniqueId();
            pane.title = Title.buildTitle(options.title, pane, Pane.fn.options.title);
            pane.content = new ChartElement();
            pane.append(pane.content);
            pane.axes = [];
            pane.charts = [];
        },
        options: {
            zIndex: -1,
            shrinkToFit: true,
            title: {
                align: LEFT
            },
            visible: true
        },
        appendAxis: function(axis) {
            var pane = this;
            pane.content.append(axis);
            pane.axes.push(axis);
            axis.pane = pane;
        },
        appendChart: function(chart) {
            var pane = this;
            pane.charts.push(chart);
            pane.content.append(chart);
            chart.pane = pane;
        },
        empty: function() {
            var pane = this, plotArea = pane.parent, i;
            if (plotArea) {
                for (i = 0; i < pane.axes.length; i++) {
                    plotArea.removeAxis(pane.axes[i]);
                }
                for (i = 0; i < pane.charts.length; i++) {
                    plotArea.removeChart(pane.charts[i]);
                }
            }
            pane.axes = [];
            pane.charts = [];
            pane.content.destroy();
            pane.content.children = [];
        },
        reflow: function(targetBox) {
            var pane = this;
            // Content (such as charts) is rendered, but excluded from reflows
            if (last(pane.children) === pane.content) {
                pane.children.pop();
            }
            BoxElement.fn.reflow.call(pane, targetBox);
            if (pane.title) {
                pane.contentBox.y1 += pane.title.box.height();
            }
        },
        getViewElements: function(view) {
            var pane = this, elements = CategoricalChart.fn.getViewElements.call(pane, view), group = view.createGroup({
                id: pane.options.id
            }), result = [];
            group.children = elements.concat(pane.renderGridLines(view), pane.content.getViewElements(view));
            pane.view = view;
            if (pane.options.visible) {
                result = [ group ];
            }
            return result;
        },
        renderGridLines: function(view) {
            var pane = this, axes = pane.axes, allAxes = axes.concat(pane.parent.axes), vGridLines = [], hGridLines = [], gridLines, i, j, axis, vertical, altAxis;
            for (i = 0; i < axes.length; i++) {
                axis = axes[i];
                vertical = axis.options.vertical;
                gridLines = vertical ? vGridLines : hGridLines;
                for (j = 0; j < allAxes.length; j++) {
                    if (gridLines.length === 0) {
                        altAxis = allAxes[j];
                        if (vertical !== altAxis.options.vertical) {
                            append(gridLines, axis.renderGridLines(view, altAxis, axis));
                        }
                    }
                }
            }
            return vGridLines.concat(hGridLines);
        },
        refresh: function() {
            var pane = this, view = pane.view, element = getElement(pane.options.id);
            if (view && element) {
                element.parentNode.replaceChild(view.renderElement(pane.getViewElements(view)[0]), element);
            }
        }
    });
    var PlotAreaBase = ChartElement.extend({
        init: function(series, options) {
            var plotArea = this;
            ChartElement.fn.init.call(plotArea, options);
            plotArea.series = series;
            plotArea.charts = [];
            plotArea.options.legend.items = [];
            plotArea.axes = [];
            plotArea.crosshairs = [];
            plotArea.options.id = uniqueId();
            plotArea.enableDiscovery();
            plotArea.createPanes();
            plotArea.render();
            plotArea.createCrosshairs();
        },
        options: {
            series: [],
            plotArea: {
                margin: {}
            },
            background: "",
            border: {
                color: BLACK,
                width: 0
            },
            legend: {}
        },
        createPanes: function() {
            var plotArea = this, panes = [], paneOptions = plotArea.options.panes || [], i, panesLength = math.max(paneOptions.length, 1), currentPane;
            for (i = 0; i < panesLength; i++) {
                currentPane = new Pane(paneOptions[i]);
                currentPane.paneIndex = i;
                panes.push(currentPane);
                plotArea.append(currentPane);
            }
            plotArea.panes = panes;
        },
        destroy: function() {
            var plotArea = this, charts = plotArea.charts, axes = plotArea.axes, i;
            for (i = 0; i < charts.length; i++) {
                charts[i].destroy();
            }
            for (i = 0; i < axes.length; i++) {
                axes[i].destroy();
            }
            ChartElement.fn.destroy.call(plotArea);
        },
        createCrosshairs: function() {
            var plotArea = this, panes = plotArea.panes, i, j, pane, axis, currentCrosshair;
            for (i = 0; i < panes.length; i++) {
                pane = panes[i];
                for (j = 0; j < pane.axes.length; j++) {
                    axis = pane.axes[j];
                    if (axis.options.crosshair && axis.options.crosshair.visible) {
                        currentCrosshair = new Crosshair(axis, axis.options.crosshair);
                        plotArea.crosshairs.push(currentCrosshair);
                        plotArea.append(currentCrosshair);
                    }
                }
            }
        },
        findPane: function(name) {
            var plotArea = this, panes = plotArea.panes, i, matchingPane;
            for (i = 0; i < panes.length; i++) {
                if (panes[i].options.name === name) {
                    matchingPane = panes[i];
                    break;
                }
            }
            return matchingPane || panes[0];
        },
        findPointPane: function(point) {
            var plotArea = this, panes = plotArea.panes, i, matchingPane;
            for (i = 0; i < panes.length; i++) {
                if (panes[i].box.containsPoint(point)) {
                    matchingPane = panes[i];
                    break;
                }
            }
            return matchingPane;
        },
        appendAxis: function(axis) {
            var plotArea = this, pane = plotArea.findPane(axis.options.pane);
            pane.appendAxis(axis);
            plotArea.axes.push(axis);
            axis.plotArea = plotArea;
        },
        removeAxis: function(axisToRemove) {
            var plotArea = this, i, axis, filteredAxes = [];
            for (i = 0; i < plotArea.axes.length; i++) {
                axis = plotArea.axes[i];
                if (axisToRemove !== axis) {
                    filteredAxes.push(axis);
                }
            }
            plotArea.axes = filteredAxes;
        },
        appendChart: function(chart, pane) {
            var plotArea = this;
            plotArea.charts.push(chart);
            plotArea.addToLegend(chart);
            if (pane) {
                pane.appendChart(chart);
            } else {
                plotArea.append(chart);
            }
        },
        removeChart: function(chartToRemove) {
            var plotArea = this, i, chart, filteredCharts = [];
            for (i = 0; i < plotArea.charts.length; i++) {
                chart = plotArea.charts[i];
                if (chart !== chartToRemove) {
                    filteredCharts.push(chart);
                }
            }
            plotArea.charts = filteredCharts;
        },
        addToLegend: function(chart) {
            var series = chart.options.series, count = series.length, data = [], i, currentSeries, text, labelTemplate, labels = this.options.legend.labels || {};
            if (chart.legendItems) {
                data = chart.legendItems;
            } else {
                for (i = 0; i < count; i++) {
                    currentSeries = series[i];
                    if (currentSeries.visibleInLegend !== false) {
                        text = currentSeries.name || "";
                        if (labels.template) {
                            labelTemplate = template(labels.template);
                            text = labelTemplate({
                                text: text,
                                series: currentSeries
                            });
                        }
                        data.push({
                            name: text,
                            color: currentSeries.color
                        });
                    }
                }
            }
            append(this.options.legend.items, data);
        },
        groupAxes: function(panes) {
            var xAxes = [], yAxes = [], paneAxes, axis, paneIx, axisIx;
            for (paneIx = 0; paneIx < panes.length; paneIx++) {
                paneAxes = panes[paneIx].axes;
                for (axisIx = 0; axisIx < paneAxes.length; axisIx++) {
                    axis = paneAxes[axisIx];
                    if (axis.options.vertical) {
                        yAxes.push(axis);
                    } else {
                        xAxes.push(axis);
                    }
                }
            }
            return {
                x: xAxes,
                y: yAxes,
                any: xAxes.concat(yAxes)
            };
        },
        groupSeriesByPane: function() {
            var plotArea = this, series = plotArea.series, seriesByPane = {}, i, pane, currentSeries;
            for (i = 0; i < series.length; i++) {
                currentSeries = series[i];
                pane = plotArea.seriesPaneName(currentSeries);
                if (seriesByPane[pane]) {
                    seriesByPane[pane].push(currentSeries);
                } else {
                    seriesByPane[pane] = [ currentSeries ];
                }
            }
            return seriesByPane;
        },
        filterSeriesByType: function(series, types) {
            var i, currentSeries, result = [];
            types = [].concat(types);
            for (i = 0; i < series.length; i++) {
                currentSeries = series[i];
                if (inArray(currentSeries.type, types)) {
                    result.push(currentSeries);
                }
            }
            return result;
        },
        reflow: function(targetBox) {
            var plotArea = this, options = plotArea.options.plotArea, panes = plotArea.panes, margin = getSpacing(options.margin);
            plotArea.box = targetBox.clone().unpad(margin);
            plotArea.reflowPanes();
            plotArea.reflowAxes(panes);
            plotArea.reflowCharts(panes);
        },
        redraw: function(panes) {
            var plotArea = this, i;
            panes = [].concat(panes);
            for (i = 0; i < panes.length; i++) {
                panes[i].empty();
            }
            plotArea.render(panes);
            plotArea.reflowAxes(plotArea.panes);
            plotArea.reflowCharts(panes);
            for (i = 0; i < panes.length; i++) {
                panes[i].refresh();
            }
        },
        axisCrossingValues: function(axis, crossingAxes) {
            var options = axis.options, crossingValues = [].concat(options.axisCrossingValues || options.axisCrossingValue), valuesToAdd = crossingAxes.length - crossingValues.length, defaultValue = crossingValues[0] || 0, i;
            for (i = 0; i < valuesToAdd; i++) {
                crossingValues.push(defaultValue);
            }
            return crossingValues;
        },
        alignAxisTo: function(axis, targetAxis, crossingValue, targetCrossingValue) {
            var slot = axis.getSlot(crossingValue, crossingValue), slotEdge = axis.options.reverse ? 2 : 1, targetSlot = targetAxis.getSlot(targetCrossingValue, targetCrossingValue), targetEdge = targetAxis.options.reverse ? 2 : 1, axisBox = axis.box.translate(targetSlot[X + targetEdge] - slot[X + slotEdge], targetSlot[Y + targetEdge] - slot[Y + slotEdge]);
            if (axis.pane !== targetAxis.pane) {
                axisBox.translate(0, axis.pane.box.y1 - targetAxis.pane.box.y1);
            }
            axis.reflow(axisBox);
        },
        alignAxes: function(xAxes, yAxes) {
            var plotArea = this, xAnchor = xAxes[0], yAnchor = yAxes[0], xAnchorCrossings = plotArea.axisCrossingValues(xAnchor, yAxes), yAnchorCrossings = plotArea.axisCrossingValues(yAnchor, xAxes), leftAnchors = {}, rightAnchors = {}, topAnchors = {}, bottomAnchors = {}, pane, paneId, axis, i;
            for (i = 0; i < yAxes.length; i++) {
                axis = yAxes[i];
                pane = axis.pane;
                paneId = pane.options.id;
                plotArea.alignAxisTo(axis, xAnchor, yAnchorCrossings[i], xAnchorCrossings[i]);
                if (axis.options._overlap) {
                    continue;
                }
                if (round(axis.lineBox().x1) === round(xAnchor.lineBox().x1)) {
                    if (leftAnchors[paneId]) {
                        axis.reflow(axis.box.alignTo(leftAnchors[paneId].box, LEFT).translate(-axis.options.margin, 0));
                    }
                    leftAnchors[paneId] = axis;
                }
                if (round(axis.lineBox().x2) === round(xAnchor.lineBox().x2)) {
                    if (!axis._mirrored) {
                        axis.options.labels.mirror = !axis.options.labels.mirror;
                        axis._mirrored = true;
                    }
                    plotArea.alignAxisTo(axis, xAnchor, yAnchorCrossings[i], xAnchorCrossings[i]);
                    if (rightAnchors[paneId]) {
                        axis.reflow(axis.box.alignTo(rightAnchors[paneId].box, RIGHT).translate(axis.options.margin, 0));
                    }
                    rightAnchors[paneId] = axis;
                }
                if (i !== 0 && yAnchor.pane === axis.pane) {
                    axis.alignTo(yAnchor);
                }
            }
            for (i = 0; i < xAxes.length; i++) {
                axis = xAxes[i];
                pane = axis.pane;
                paneId = pane.options.id;
                plotArea.alignAxisTo(axis, yAnchor, xAnchorCrossings[i], yAnchorCrossings[i]);
                if (axis.options._overlap) {
                    continue;
                }
                if (round(axis.lineBox().y1) === round(yAnchor.lineBox().y1)) {
                    if (!axis._mirrored) {
                        axis.options.labels.mirror = !axis.options.labels.mirror;
                        axis._mirrored = true;
                    }
                    plotArea.alignAxisTo(axis, yAnchor, xAnchorCrossings[i], yAnchorCrossings[i]);
                    if (topAnchors[paneId]) {
                        axis.reflow(axis.box.alignTo(topAnchors[paneId].box, TOP).translate(0, -axis.options.margin));
                    }
                    topAnchors[paneId] = axis;
                }
                if (round(axis.lineBox().y2, COORD_PRECISION) === round(yAnchor.lineBox().y2, COORD_PRECISION)) {
                    if (bottomAnchors[paneId]) {
                        axis.reflow(axis.box.alignTo(bottomAnchors[paneId].box, BOTTOM).translate(0, axis.options.margin));
                    }
                    bottomAnchors[paneId] = axis;
                }
                if (i !== 0) {
                    axis.alignTo(xAnchor);
                }
            }
        },
        shrinkAxisWidth: function(panes) {
            var plotArea = this, axes = plotArea.groupAxes(panes).any, axisBox = axisGroupBox(axes), overflowX = 0, i, currentPane, currentAxis;
            for (i = 0; i < panes.length; i++) {
                currentPane = panes[i];
                if (currentPane.axes.length > 0) {
                    overflowX = math.max(overflowX, axisBox.width() - currentPane.contentBox.width());
                }
            }
            for (i = 0; i < axes.length; i++) {
                currentAxis = axes[i];
                if (!currentAxis.options.vertical) {
                    currentAxis.reflow(currentAxis.box.shrink(overflowX, 0));
                }
            }
        },
        shrinkAxisHeight: function(panes) {
            var i, currentPane, axes, overflowY, j, currentAxis;
            for (i = 0; i < panes.length; i++) {
                currentPane = panes[i];
                axes = currentPane.axes, overflowY = math.max(0, axisGroupBox(axes).height() - currentPane.contentBox.height());
                for (j = 0; j < axes.length; j++) {
                    currentAxis = axes[j];
                    if (currentAxis.options.vertical) {
                        currentAxis.reflow(currentAxis.box.shrink(0, overflowY));
                    }
                }
            }
        },
        fitAxes: function(panes) {
            var plotArea = this, axes = plotArea.groupAxes(panes).any, offsetX = 0, paneAxes, paneBox, axisBox, offsetY, currentPane, currentAxis, i, j;
            for (i = 0; i < panes.length; i++) {
                currentPane = panes[i];
                paneAxes = currentPane.axes;
                paneBox = currentPane.contentBox;
                if (paneAxes.length > 0) {
                    axisBox = axisGroupBox(paneAxes);
                    // OffsetX is calculated and applied globally
                    offsetX = math.max(offsetX, paneBox.x1 - axisBox.x1);
                    // OffsetY is calculated and applied per pane
                    offsetY = math.max(paneBox.y1 - axisBox.y1, paneBox.y2 - axisBox.y2);
                    for (j = 0; j < paneAxes.length; j++) {
                        currentAxis = paneAxes[j];
                        currentAxis.reflow(currentAxis.box.translate(0, offsetY));
                    }
                }
            }
            for (i = 0; i < axes.length; i++) {
                currentAxis = axes[i];
                currentAxis.reflow(currentAxis.box.translate(offsetX, 0));
            }
        },
        reflowAxes: function(panes) {
            var plotArea = this, i, axes = plotArea.groupAxes(panes);
            for (i = 0; i < panes.length; i++) {
                plotArea.reflowPaneAxes(panes[i]);
            }
            if (axes.x.length > 0 && axes.y.length > 0) {
                plotArea.alignAxes(axes.x, axes.y);
                plotArea.shrinkAxisWidth(panes);
                plotArea.alignAxes(axes.x, axes.y);
                plotArea.shrinkAxisHeight(panes);
                plotArea.alignAxes(axes.x, axes.y);
                plotArea.fitAxes(panes);
            }
        },
        reflowPaneAxes: function(pane) {
            var axes = pane.axes, i, length = axes.length;
            if (length > 0) {
                for (i = 0; i < length; i++) {
                    axes[i].reflow(pane.contentBox);
                }
            }
        },
        reflowCharts: function(panes) {
            var plotArea = this, charts = plotArea.charts, count = charts.length, box = plotArea.box, chartPane, i;
            for (i = 0; i < count; i++) {
                chartPane = charts[i].pane;
                if (!chartPane || inArray(chartPane, panes)) {
                    charts[i].reflow(box);
                }
            }
        },
        reflowPanes: function() {
            var plotArea = this, box = plotArea.box, panes = plotArea.panes, i, panesLength = panes.length, currentPane, paneBox, remainingHeight = box.height(), remainingPanes = panesLength, autoHeightPanes = 0, top = box.y1, height, percents;
            for (i = 0; i < panesLength; i++) {
                currentPane = panes[i];
                height = currentPane.options.height;
                currentPane.options.width = box.width();
                if (!currentPane.options.height) {
                    autoHeightPanes++;
                } else {
                    if (height.indexOf && height.indexOf("%")) {
                        percents = parseInt(height, 10) / 100;
                        currentPane.options.height = percents * box.height();
                    }
                    currentPane.reflow(box.clone());
                    remainingHeight -= currentPane.options.height;
                }
            }
            for (i = 0; i < panesLength; i++) {
                currentPane = panes[i];
                if (!currentPane.options.height) {
                    currentPane.options.height = remainingHeight / autoHeightPanes;
                }
            }
            for (i = 0; i < panesLength; i++) {
                currentPane = panes[i];
                paneBox = box.clone().move(box.x1, top);
                currentPane.reflow(paneBox);
                remainingPanes--;
                top += currentPane.options.height;
            }
        },
        backgroundBox: function() {
            var plotArea = this, axes = plotArea.axes, axesCount = axes.length, lineBox, box, i, j, axisA, axisB;
            for (i = 0; i < axesCount; i++) {
                axisA = axes[i];
                for (j = 0; j < axesCount; j++) {
                    axisB = axes[j];
                    if (axisA.options.vertical !== axisB.options.vertical) {
                        lineBox = axisA.lineBox().clone().wrap(axisB.lineBox());
                        if (!box) {
                            box = lineBox;
                        } else {
                            box = box.wrap(lineBox);
                        }
                    }
                }
            }
            return box || plotArea.box;
        },
        getViewElements: function(view) {
            var plotArea = this, bgBox = plotArea.backgroundBox(), options = plotArea.options, userOptions = options.plotArea, border = userOptions.border || {}, elements = ChartElement.fn.getViewElements.call(plotArea, view);
            append(elements, [ view.createRect(bgBox, {
                fill: userOptions.background,
                fillOpacity: userOptions.opacity,
                zIndex: -2,
                strokeWidth: .1
            }), view.createRect(bgBox, {
                id: options.id,
                data: {
                    modelId: options.modelId
                },
                stroke: border.width ? border.color : "",
                strokeWidth: border.width,
                fill: WHITE,
                fillOpacity: 0,
                zIndex: -1,
                dashType: border.dashType
            }) ]);
            return elements;
        },
        pointsByCategoryIndex: function(categoryIndex) {
            var charts = this.charts, result = [], i, j, points, point, chart;
            if (categoryIndex !== null) {
                for (i = 0; i < charts.length; i++) {
                    chart = charts[i];
                    if (chart.pane.options.name === "_navigator") {
                        continue;
                    }
                    points = charts[i].categoryPoints[categoryIndex];
                    if (points && points.length) {
                        for (j = 0; j < points.length; j++) {
                            point = points[j];
                            if (point && defined(point.value) && point.value !== null) {
                                result.push(point);
                            }
                        }
                    }
                }
            }
            return result;
        },
        paneByPoint: function(point) {
            var plotArea = this, panes = plotArea.panes, pane, i;
            for (i = 0; i < panes.length; i++) {
                pane = panes[i];
                if (pane.box.containsPoint(point)) {
                    return pane;
                }
            }
        }
    });
    var CategoricalPlotArea = PlotAreaBase.extend({
        init: function(series, options) {
            var plotArea = this;
            plotArea.namedCategoryAxes = {};
            plotArea.namedValueAxes = {};
            plotArea.valueAxisRangeTracker = new AxisGroupRangeTracker();
            if (series.length > 0) {
                plotArea.invertAxes = inArray(series[0].type, [ BAR, BULLET, VERTICAL_LINE, VERTICAL_AREA ]);
            }
            PlotAreaBase.fn.init.call(plotArea, series, options);
        },
        options: {
            categoryAxis: {
                categories: []
            },
            valueAxis: {}
        },
        render: function(panes) {
            var plotArea = this;
            panes = panes || plotArea.panes;
            plotArea.createCategoryAxes(panes);
            plotArea.aggregateDateSeries(panes);
            plotArea.createCharts(panes);
            plotArea.createValueAxes(panes);
        },
        removeAxis: function(axis) {
            var plotArea = this, axisName = axis.options.name;
            PlotAreaBase.fn.removeAxis.call(plotArea, axis);
            if (axis instanceof CategoryAxis) {
                delete plotArea.namedCategoryAxes[axisName];
            } else {
                plotArea.valueAxisRangeTracker.reset(axisName);
                delete plotArea.namedValueAxes[axisName];
            }
            if (axis === plotArea.categoryAxis) {
                delete plotArea.categoryAxis;
            }
            if (axis === plotArea.valueAxis) {
                delete plotArea.valueAxis;
            }
        },
        createCharts: function(panes) {
            var plotArea = this, seriesByPane = plotArea.groupSeriesByPane(), i, pane, paneSeries;
            for (i = 0; i < panes.length; i++) {
                pane = panes[i];
                paneSeries = seriesByPane[pane.options.name || "default"];
                if (!paneSeries) {
                    continue;
                }
                plotArea.createAreaChart(plotArea.filterSeriesByType(paneSeries, [ AREA, VERTICAL_AREA ]), pane);
                plotArea.createBarChart(plotArea.filterSeriesByType(paneSeries, [ COLUMN, BAR ]), pane);
                plotArea.createLineChart(plotArea.filterSeriesByType(paneSeries, [ LINE, VERTICAL_LINE ]), pane);
                plotArea.createCandlestickChart(plotArea.filterSeriesByType(paneSeries, CANDLESTICK), pane);
                plotArea.createOHLCChart(plotArea.filterSeriesByType(paneSeries, OHLC), pane);
                plotArea.createBulletChart(plotArea.filterSeriesByType(paneSeries, [ BULLET, VERTICAL_BULLET ]), pane);
            }
        },
        aggregateDateSeries: function(panes) {
            var plotArea = this, series = plotArea.srcSeries || plotArea.series, processedSeries = [], categoryAxis, axisPane, categories, categoryMap, groupIx, categoryIndicies, seriesIx, currentSeries, seriesClone, srcData, data, srcValues, i, categoryIx, pointData, value, srcDataItems;
            for (seriesIx = 0; seriesIx < series.length; seriesIx++) {
                currentSeries = series[seriesIx];
                seriesClone = deepExtend({}, currentSeries);
                categoryAxis = plotArea.seriesCategoryAxis(currentSeries);
                axisPane = plotArea.findPane(categoryAxis.options.pane);
                if (inArray(axisPane, panes) && equalsIgnoreCase(categoryAxis.options.type, DATE)) {
                    categories = categoryAxis.options.categories;
                    categoryMap = categoryAxis.categoryMap;
                    srcData = seriesClone.data;
                    seriesClone.data = data = [];
                    for (groupIx = 0; groupIx < categories.length; groupIx++) {
                        categoryIndicies = categoryMap[groupIx];
                        srcValues = [];
                        srcDataItems = [];
                        for (i = 0; i < categoryIndicies.length; i++) {
                            categoryIx = categoryIndicies[i];
                            pointData = bindPoint(currentSeries, categoryIx);
                            value = pointData.value;
                            if (defined(value)) {
                                srcValues.push(pointData.value);
                            }
                            srcDataItems.push(currentSeries.data[categoryIx]);
                        }
                        if (srcValues.length > 1) {
                            data[groupIx] = calculateAggregates(srcValues, currentSeries, srcDataItems);
                        } else {
                            data[groupIx] = srcData[categoryIndicies[0]];
                        }
                    }
                }
                processedSeries.push(seriesClone);
            }
            plotArea.srcSeries = series;
            plotArea.series = processedSeries;
        },
        appendChart: function(chart, pane) {
            var plotArea = this, series = chart.options.series, categoryAxis = plotArea.seriesCategoryAxis(series[0]), categories = categoryAxis.options.categories, categoriesToAdd = math.max(0, categoriesCount(series) - categories.length);
            while (categoriesToAdd--) {
                categories.push("");
            }
            plotArea.valueAxisRangeTracker.update(chart.valueAxisRanges);
            PlotAreaBase.fn.appendChart.call(plotArea, chart, pane);
        },
        // TODO: Refactor, optionally use series.pane option
        seriesPaneName: function(series) {
            var plotArea = this, options = plotArea.options, axisName = series.axis, axisOptions = [].concat(options.valueAxis), axis = $.grep(axisOptions, function(a) {
                return a.name === axisName;
            })[0], panes = options.panes || [ {} ], defaultPaneName = (panes[0] || {}).name || "default", paneName = (axis || {}).pane || defaultPaneName;
            return paneName;
        },
        seriesCategoryAxis: function(series) {
            var plotArea = this, axisName = series.categoryAxis, axis = axisName ? plotArea.namedCategoryAxes[axisName] : plotArea.categoryAxis;
            if (!axis) {
                throw new Error("Unable to locate category axis with name " + axisName);
            }
            return axis;
        },
        createBarChart: function(series, pane) {
            if (series.length === 0) {
                return;
            }
            var plotArea = this, firstSeries = series[0], barChart = new BarChart(plotArea, {
                series: series,
                invertAxes: plotArea.invertAxes,
                isStacked: firstSeries.stack && series.length > 1,
                gap: firstSeries.gap,
                spacing: firstSeries.spacing
            });
            plotArea.appendChart(barChart, pane);
        },
        createBulletChart: function(series, pane) {
            if (series.length === 0) {
                return;
            }
            var plotArea = this, firstSeries = series[0], bulletChart = new BulletChart(plotArea, {
                series: series,
                invertAxes: plotArea.invertAxes,
                gap: firstSeries.gap,
                spacing: firstSeries.spacing
            });
            plotArea.appendChart(bulletChart, pane);
        },
        createLineChart: function(series, pane) {
            if (series.length === 0) {
                return;
            }
            var plotArea = this, firstSeries = series[0], lineChart = new LineChart(plotArea, {
                invertAxes: plotArea.invertAxes,
                isStacked: firstSeries.stack && series.length > 1,
                series: series
            });
            plotArea.appendChart(lineChart, pane);
        },
        createAreaChart: function(series, pane) {
            if (series.length === 0) {
                return;
            }
            var plotArea = this, firstSeries = series[0], areaChart = new AreaChart(plotArea, {
                invertAxes: plotArea.invertAxes,
                isStacked: firstSeries.stack && series.length > 1,
                series: series
            });
            plotArea.appendChart(areaChart, pane);
        },
        createOHLCChart: function(series, pane) {
            if (series.length === 0) {
                return;
            }
            var plotArea = this, firstSeries = series[0], chart = new OHLCChart(plotArea, {
                invertAxes: plotArea.invertAxes,
                gap: firstSeries.gap,
                series: series,
                spacing: firstSeries.spacing
            });
            plotArea.appendChart(chart, pane);
        },
        createCandlestickChart: function(series, pane) {
            if (series.length === 0) {
                return;
            }
            var plotArea = this, firstSeries = series[0], chart = new CandlestickChart(plotArea, {
                invertAxes: plotArea.invertAxes,
                gap: firstSeries.gap,
                series: series,
                spacing: firstSeries.spacing
            });
            plotArea.appendChart(chart, pane);
        },
        axisRequiresRounding: function(categoryAxisName, categoryAxisIndex) {
            var plotArea = this, centeredSeries = plotArea.filterSeriesByType(plotArea.series, [ BAR, COLUMN, OHLC, CANDLESTICK ]), seriesIx, seriesAxis;
            for (seriesIx = 0; seriesIx < centeredSeries.length; seriesIx++) {
                seriesAxis = centeredSeries[seriesIx].categoryAxis || "";
                if (seriesAxis === categoryAxisName || !seriesAxis && categoryAxisIndex === 0) {
                    return true;
                }
            }
        },
        createCategoryAxes: function(panes) {
            var plotArea = this, invertAxes = plotArea.invertAxes, definitions = [].concat(plotArea.options.categoryAxis), i, axisOptions, axisPane, categories, type, name, dateCategory, categoryAxis, axes = [], primaryAxis;
            for (i = 0; i < definitions.length; i++) {
                axisOptions = definitions[i];
                axisPane = plotArea.findPane(axisOptions.pane);
                if (inArray(axisPane, panes)) {
                    name = axisOptions.name;
                    categories = axisOptions.categories || [];
                    dateCategory = categories[0] instanceof Date;
                    type = axisOptions.type || "";
                    axisOptions = deepExtend({
                        vertical: invertAxes,
                        axisCrossingValue: invertAxes ? MAX_VALUE : 0
                    }, axisOptions);
                    if (!defined(axisOptions.justified)) {
                        axisOptions.justified = plotArea.inJustified();
                    }
                    if (plotArea.axisRequiresRounding(name, i)) {
                        axisOptions.justified = false;
                        axisOptions.roundToBaseUnit = true;
                    }
                    if (!type && dateCategory || equalsIgnoreCase(type, DATE)) {
                        categoryAxis = new DateCategoryAxis(axisOptions);
                    } else {
                        categoryAxis = new CategoryAxis(axisOptions);
                    }
                    if (name) {
                        if (plotArea.namedCategoryAxes[name]) {
                            throw new Error("Category axis with name " + name + " is already defined");
                        }
                        plotArea.namedCategoryAxes[name] = categoryAxis;
                    }
                    categoryAxis.axisIndex = i;
                    axes.push(categoryAxis);
                    plotArea.appendAxis(categoryAxis);
                }
            }
            primaryAxis = plotArea.categoryAxis || axes[0];
            plotArea.categoryAxis = primaryAxis;
            if (invertAxes) {
                plotArea.axisY = primaryAxis;
            } else {
                plotArea.axisX = primaryAxis;
            }
        },
        inJustified: function() {
            var plotArea = this, series = plotArea.series, i, currentSeries;
            for (i = 0; i < series.length; i++) {
                currentSeries = series[i];
                if (currentSeries.type !== AREA) {
                    return false;
                }
            }
            return true;
        },
        createValueAxes: function(panes) {
            var plotArea = this, tracker = plotArea.valueAxisRangeTracker, defaultRange = tracker.query(), definitions = [].concat(plotArea.options.valueAxis), invertAxes = plotArea.invertAxes, baseOptions = {
                vertical: !invertAxes
            }, axisOptions, axisPane, valueAxis, primaryAxis, axes = [], range, name, i;
            for (i = 0; i < definitions.length; i++) {
                axisOptions = definitions[i];
                axisPane = plotArea.findPane(axisOptions.pane);
                if (inArray(axisPane, panes)) {
                    name = axisOptions.name;
                    range = tracker.query(name) || defaultRange || {
                        min: 0,
                        max: 1
                    };
                    if (i === 0 && range && defaultRange) {
                        range.min = math.min(range.min, defaultRange.min);
                        range.max = math.max(range.max, defaultRange.max);
                    }
                    valueAxis = new NumericAxis(range.min, range.max, deepExtend({}, baseOptions, axisOptions));
                    if (name) {
                        if (plotArea.namedValueAxes[name]) {
                            throw new Error("Value axis with name " + name + " is already defined");
                        }
                        plotArea.namedValueAxes[name] = valueAxis;
                    }
                    valueAxis.axisIndex = i;
                    axes.push(valueAxis);
                    plotArea.appendAxis(valueAxis);
                }
            }
            primaryAxis = plotArea.valueAxis || axes[0];
            plotArea.valueAxis = primaryAxis;
            if (invertAxes) {
                plotArea.axisX = primaryAxis;
            } else {
                plotArea.axisY = primaryAxis;
            }
        },
        click: function(chart, e) {
            var plotArea = this, coords = chart._eventCoordinates(e), point = new Point2D(coords.x, coords.y), pane = plotArea.pointPane(point), allAxes, i, axis, categories = [], values = [];
            if (!pane) {
                return;
            }
            allAxes = pane.axes;
            for (i = 0; i < allAxes.length; i++) {
                axis = allAxes[i];
                if (axis.getValue) {
                    appendIfNotNull(values, axis.getValue(point));
                } else {
                    appendIfNotNull(categories, axis.getCategory(point));
                }
            }
            if (categories.length === 0) {
                appendIfNotNull(categories, plotArea.categoryAxis.getCategory(point));
            }
            if (categories.length > 0 && values.length > 0) {
                chart.trigger(PLOT_AREA_CLICK, {
                    element: $(e.target),
                    category: singleItemOrArray(categories),
                    value: singleItemOrArray(values)
                });
            }
        },
        pointPane: function(point) {
            var plotArea = this, panes = plotArea.panes, currentPane, i;
            for (i = 0; i < panes.length; i++) {
                currentPane = panes[i];
                if (currentPane.contentBox.containsPoint(point)) {
                    return currentPane;
                }
            }
        }
    });
    var AxisGroupRangeTracker = Class.extend({
        init: function() {
            var tracker = this;
            tracker.axisRanges = {};
        },
        update: function(chartAxisRanges) {
            var tracker = this, axisRanges = tracker.axisRanges, range, chartRange, axisName;
            for (axisName in chartAxisRanges) {
                range = axisRanges[axisName];
                chartRange = chartAxisRanges[axisName];
                axisRanges[axisName] = range = range || {
                    min: MAX_VALUE,
                    max: MIN_VALUE
                };
                range.min = math.min(range.min, chartRange.min);
                range.max = math.max(range.max, chartRange.max);
            }
        },
        reset: function(axisName) {
            this.axisRanges[axisName] = undefined;
        },
        query: function(axisName) {
            return this.axisRanges[axisName];
        }
    });
    var XYPlotArea = PlotAreaBase.extend({
        init: function(series, options) {
            var plotArea = this;
            plotArea.namedXAxes = {};
            plotArea.namedYAxes = {};
            plotArea.xAxisRangeTracker = new AxisGroupRangeTracker();
            plotArea.yAxisRangeTracker = new AxisGroupRangeTracker();
            PlotAreaBase.fn.init.call(plotArea, series, options);
        },
        options: {
            xAxis: {},
            yAxis: {}
        },
        render: function(panes) {
            var plotArea = this, seriesByPane = plotArea.groupSeriesByPane(), i, pane, paneSeries;
            panes = panes || plotArea.panes;
            for (i = 0; i < panes.length; i++) {
                pane = panes[i];
                paneSeries = seriesByPane[pane.options.name || "default"];
                if (!paneSeries) {
                    continue;
                }
                plotArea.createScatterChart(plotArea.filterSeriesByType(paneSeries, SCATTER), pane);
                plotArea.createScatterLineChart(plotArea.filterSeriesByType(paneSeries, SCATTER_LINE), pane);
                plotArea.createBubbleChart(plotArea.filterSeriesByType(paneSeries, BUBBLE), pane);
            }
            plotArea.createAxes(panes);
        },
        appendChart: function(chart, pane) {
            var plotArea = this;
            plotArea.xAxisRangeTracker.update(chart.xAxisRanges);
            plotArea.yAxisRangeTracker.update(chart.yAxisRanges);
            PlotAreaBase.fn.appendChart.call(plotArea, chart, pane);
        },
        removeAxis: function(axis) {
            var plotArea = this, axisName = axis.options.name;
            PlotAreaBase.fn.removeAxis.call(plotArea, axis);
            if (axis.options.vertical) {
                plotArea.yAxisRangeTracker.reset(axisName);
                delete plotArea.namedYAxes[axisName];
            } else {
                plotArea.xAxisRangeTracker.reset(axisName);
                delete plotArea.namedXAxes[axisName];
            }
            if (axis === plotArea.axisX) {
                delete plotArea.axisX;
            }
            if (axis === plotArea.axisY) {
                delete plotArea.axisY;
            }
        },
        // TODO: Refactor, optionally use series.pane option
        seriesPaneName: function(series) {
            var plotArea = this, options = plotArea.options, xAxisName = series.xAxis, xAxisOptions = [].concat(options.xAxis), xAxis = $.grep(xAxisOptions, function(a) {
                return a.name === xAxisName;
            })[0], yAxisName = series.yAxis, yAxisOptions = [].concat(options.yAxis), yAxis = $.grep(yAxisOptions, function(a) {
                return a.name === yAxisName;
            })[0], panes = options.panes || [ {} ], defaultPaneName = panes[0].name || "default", paneName = (xAxis || {}).pane || (yAxis || {}).pane || defaultPaneName;
            return paneName;
        },
        createScatterChart: function(series, pane) {
            var plotArea = this;
            if (series.length > 0) {
                plotArea.appendChart(new ScatterChart(plotArea, {
                    series: series
                }), pane);
            }
        },
        createScatterLineChart: function(series, pane) {
            var plotArea = this;
            if (series.length > 0) {
                plotArea.appendChart(new ScatterLineChart(plotArea, {
                    series: series
                }), pane);
            }
        },
        createBubbleChart: function(series, pane) {
            var plotArea = this;
            if (series.length > 0) {
                plotArea.appendChart(new BubbleChart(plotArea, {
                    series: series
                }), pane);
            }
        },
        createXYAxis: function(options, vertical, axisIndex) {
            var plotArea = this, axisName = options.name, namedAxes = vertical ? plotArea.namedYAxes : plotArea.namedXAxes, tracker = vertical ? plotArea.yAxisRangeTracker : plotArea.xAxisRangeTracker, defaultRange = tracker.query(), range = tracker.query(axisName) || defaultRange || {
                min: 0,
                max: 1
            }, axisOptions = deepExtend({}, options, {
                vertical: vertical
            }), axis, seriesIx, series = plotArea.series, currentSeries, seriesAxisName, firstPointValue, typeSamples = [ axisOptions.min, axisOptions.max ], inferredDate, i;
            for (seriesIx = 0; seriesIx < series.length; seriesIx++) {
                currentSeries = series[seriesIx];
                seriesAxisName = currentSeries[vertical ? "yAxis" : "xAxis"];
                if (seriesAxisName == axisOptions.name || axisIndex === 0 && !seriesAxisName) {
                    firstPointValue = bindPoint(currentSeries, 0).value;
                    typeSamples.push(firstPointValue[vertical ? "y" : "x"]);
                    break;
                }
            }
            if (axisIndex === 0 && defaultRange) {
                range.min = math.min(range.min, defaultRange.min);
                range.max = math.max(range.max, defaultRange.max);
            }
            for (i = 0; i < typeSamples.length; i++) {
                if (typeSamples[i] instanceof Date) {
                    inferredDate = true;
                    break;
                }
            }
            if (equalsIgnoreCase(axisOptions.type, DATE) || !axisOptions.type && inferredDate) {
                axis = new DateValueAxis(range.min, range.max, axisOptions);
            } else {
                axis = new NumericAxis(range.min, range.max, axisOptions);
            }
            if (axisName) {
                if (namedAxes[axisName]) {
                    throw new Error((vertical ? "Y" : "X") + " axis with name " + axisName + " is already defined");
                }
                namedAxes[axisName] = axis;
            }
            plotArea.appendAxis(axis);
            return axis;
        },
        createAxes: function(panes) {
            var plotArea = this, options = plotArea.options, axisPane, xAxesOptions = [].concat(options.xAxis), xAxes = [], yAxesOptions = [].concat(options.yAxis), yAxes = [];
            each(xAxesOptions, function(i) {
                axisPane = plotArea.findPane(this.pane);
                if (inArray(axisPane, panes)) {
                    xAxes.push(plotArea.createXYAxis(this, false, i));
                }
            });
            each(yAxesOptions, function(i) {
                axisPane = plotArea.findPane(this.pane);
                if (inArray(axisPane, panes)) {
                    yAxes.push(plotArea.createXYAxis(this, true, i));
                }
            });
            plotArea.axisX = plotArea.axisX || xAxes[0];
            plotArea.axisY = plotArea.axisY || yAxes[0];
        },
        click: function(chart, e) {
            var plotArea = this, coords = chart._eventCoordinates(e), point = new Point2D(coords.x, coords.y), allAxes = plotArea.axes, i, length = allAxes.length, axis, xValues = [], yValues = [], currentValue, values;
            for (i = 0; i < length; i++) {
                axis = allAxes[i];
                values = axis.options.vertical ? yValues : xValues;
                currentValue = axis.getValue(point);
                if (currentValue !== null) {
                    values.push(currentValue);
                }
            }
            if (xValues.length > 0 && yValues.length > 0) {
                chart.trigger(PLOT_AREA_CLICK, {
                    element: $(e.target),
                    x: singleItemOrArray(xValues),
                    y: singleItemOrArray(yValues)
                });
            }
        }
    });
    var PiePlotArea = PlotAreaBase.extend({
        render: function() {
            var plotArea = this, series = plotArea.series;
            plotArea.createPieChart(series);
        },
        createPieChart: function(series) {
            var plotArea = this, firstSeries = series[0], pieChart = new PieChart(plotArea, {
                series: series,
                padding: firstSeries.padding,
                startAngle: firstSeries.startAngle,
                connectors: firstSeries.connectors,
                legend: plotArea.options.legend
            });
            plotArea.appendChart(pieChart);
        }
    });
    var DonutPlotArea = PiePlotArea.extend({
        render: function() {
            var plotArea = this, series = plotArea.series;
            plotArea.createDonutChart(series);
        },
        createDonutChart: function(series) {
            var plotArea = this, firstSeries = series[0], donutChart = new DonutChart(plotArea, {
                series: series,
                padding: firstSeries.padding,
                connectors: firstSeries.connectors,
                legend: plotArea.options.legend
            });
            plotArea.appendChart(donutChart);
        }
    });
    var PieAnimation = ElementAnimation.extend({
        options: {
            easing: "easeOutElastic",
            duration: INITIAL_ANIMATION_DURATION
        },
        setup: function() {
            var element = this.element, sector = element.config, startRadius;
            if (element.options.singleSegment) {
                sector = element;
            }
            this.endRadius = sector.r;
            startRadius = this.startRadius = sector.ir || 0;
            sector.r = startRadius;
        },
        step: function(pos) {
            var animation = this, element = animation.element, endRadius = animation.endRadius, sector = element.config, startRadius = animation.startRadius;
            if (element.options.singleSegment) {
                sector = element;
            }
            sector.r = interpolateValue(startRadius, endRadius, pos);
        }
    });
    var BubbleAnimation = ElementAnimation.extend({
        options: {
            easing: "easeOutElastic",
            duration: INITIAL_ANIMATION_DURATION
        },
        setup: function() {
            var circle = this.element;
            circle.endRadius = circle.radius;
            circle.radius = 0;
        },
        step: function(pos) {
            var circle = this.element, endRadius = circle.endRadius;
            circle.radius = interpolateValue(0, endRadius, pos);
        }
    });
    var BarAnimationDecorator = animationDecorator(BAR, BarAnimation), PieAnimationDecorator = animationDecorator(PIE, PieAnimation), BubbleAnimationDecorator = animationDecorator(BUBBLE, BubbleAnimation);
    var Highlight = Class.extend({
        init: function(view, viewElement, options) {
            var highlight = this;
            highlight.options = deepExtend({}, highlight.options, options);
            highlight.view = view;
            highlight.viewElement = viewElement;
            highlight._overlays = [];
        },
        options: {
            fill: WHITE,
            fillOpacity: .2,
            stroke: WHITE,
            strokeWidth: 1,
            strokeOpacity: .2
        },
        show: function(points) {
            var highlight = this, view = highlight.view, viewElement = highlight.viewElement, overlay, overlays = highlight._overlays, overlayElement, i, point, pointOptions;
            highlight.hide();
            highlight._points = points = [].concat(points);
            for (i = 0; i < points.length; i++) {
                point = points[i];
                pointOptions = point.options;
                if (!pointOptions || pointOptions.highlight.visible) {
                    if (point.highlightOverlay) {
                        overlay = point.highlightOverlay(view, highlight.options);
                        if (overlay) {
                            overlayElement = view.renderElement(overlay);
                            viewElement.appendChild(overlayElement);
                            overlays.push(overlayElement);
                        }
                    }
                    if (point.toggleHighlight) {
                        point.toggleHighlight(view);
                    }
                }
            }
        },
        hide: function() {
            var highlight = this, points = highlight._points, overlays = highlight._overlays, overlay, i, point, pointOptions;
            while (overlays.length) {
                overlay = highlight._overlays.pop();
                overlay.parentNode.removeChild(overlay);
            }
            if (points) {
                for (i = 0; i < points.length; i++) {
                    point = points[i];
                    pointOptions = point.options;
                    if (!pointOptions || pointOptions.highlight.visible) {
                        if (point.toggleHighlight) {
                            point.toggleHighlight(highlight.view);
                        }
                    }
                }
            }
            highlight._points = [];
        }
    });
    var BaseTooltip = Class.extend({
        init: function(chartElement, options) {
            var tooltip = this;
            tooltip.options = deepExtend({}, tooltip.options, options);
            tooltip.chartElement = chartElement;
            tooltip.chartPadding = {
                top: parseInt(chartElement.css("paddingTop"), 10),
                left: parseInt(chartElement.css("paddingLeft"), 10)
            };
            tooltip.template = BaseTooltip.template;
            if (!tooltip.template) {
                tooltip.template = BaseTooltip.template = renderTemplate("<div class='" + CSS_PREFIX + "tooltip' " + "style='display:none; position: absolute; font: #= d.font #;" + "border: #= d.border.width #px solid;" + "opacity: #= d.opacity #; filter: alpha(opacity=#= d.opacity * 100 #);'>" + "</div>");
            }
            tooltip.element = $(tooltip.template(tooltip.options)).appendTo(chartElement);
            tooltip._moveProxy = proxy(tooltip.move, tooltip);
        },
        options: {
            border: {
                width: 1
            },
            opacity: 1,
            animation: {
                duration: TOOLTIP_ANIMATION_DURATION
            }
        },
        move: function() {
            var tooltip = this, options = tooltip.options, element = tooltip.element, offset = tooltip._offset();
            if (!tooltip.visible) {
                element.css({
                    top: offset.top,
                    left: offset.left
                });
            }
            element.stop(true, true).show().animate({
                left: offset.left,
                top: offset.top
            }, options.animation.duration);
            tooltip.visible = true;
        },
        _offset: function() {
            var tooltip = this, element = tooltip.element, anchor = tooltip.anchor, chartPadding = tooltip.chartPadding, top = round(anchor.y + chartPadding.top), left = round(anchor.x + chartPadding.left), zoomLevel = kendo.support.zoomLevel(), viewport = $(window), offsetTop = window.pageYOffset || document.documentElement.scrollTop || 0, offsetLeft = window.pageXOffset || document.documentElement.scrollLeft || 0;
            offsetTop = tooltip.chartElement.offset().top - offsetTop;
            offsetLeft = tooltip.chartElement.offset().left - offsetLeft;
            top += tooltip._currentPosition(top + offsetTop, element.outerHeight(), viewport.outerHeight() / zoomLevel);
            left += tooltip._currentPosition(left + offsetLeft, element.outerWidth(), viewport.outerWidth() / zoomLevel);
            return {
                top: top,
                left: left
            };
        },
        setStyle: function(options) {
            this.element.css({
                backgroundColor: options.background,
                borderColor: options.border.color || options.background,
                font: options.font,
                color: options.color,
                opacity: options.opacity,
                borderWidth: options.border.width
            });
        },
        show: function() {
            var tooltip = this;
            tooltip.showTimeout = setTimeout(tooltip._moveProxy, TOOLTIP_SHOW_DELAY);
        },
        hide: function() {
            var tooltip = this;
            clearTimeout(tooltip.showTimeout);
            if (tooltip.visible) {
                tooltip._hideElement();
                tooltip.point = null;
                tooltip.visible = false;
                tooltip.index = null;
            }
        },
        _hideElement: function() {
            this.element.fadeOut();
        },
        _pointContent: function(point) {
            var tooltip = this, options = deepExtend({}, tooltip.options, point.options.tooltip), content, tooltipTemplate;
            if (defined(point.value)) {
                content = point.value.toString();
            }
            if (options.template) {
                tooltipTemplate = template(options.template);
                content = tooltipTemplate({
                    value: point.value,
                    category: point.category,
                    series: point.series,
                    dataItem: point.dataItem,
                    percentage: point.percentage
                });
            } else if (options.format) {
                content = point.formatValue(options.format);
            }
            return content;
        },
        _pointAnchor: function(point) {
            var tooltip = this, element = tooltip.element;
            return point.tooltipAnchor(element.outerWidth(), element.outerHeight());
        },
        _currentPosition: function(offset, size, viewPortSize) {
            var output = 0;
            if (offset + size > viewPortSize) {
                output = viewPortSize - (offset + size);
            }
            if (offset < 0) {
                output = -offset;
            }
            return output;
        },
        _updateStyle: function(options, pointOptions) {
            if (!defined(options.background)) {
                options.background = pointOptions.color;
            }
            if (!defined(options.color)) {
                var tooltip = this, element = tooltip.element, brightnessValue = new Color(options.background).percBrightness();
                if (brightnessValue > 180) {
                    element.addClass(CSS_PREFIX + TOOLTIP_INVERSE);
                } else {
                    element.removeClass(CSS_PREFIX + TOOLTIP_INVERSE);
                }
            }
        }
    });
    var Tooltip = BaseTooltip.extend({
        show: function(point) {
            var tooltip = this, options = deepExtend({}, tooltip.options, point.options.tooltip);
            if (!point) {
                return;
            }
            tooltip.element.html(tooltip._pointContent(point));
            tooltip.anchor = tooltip._pointAnchor(point);
            tooltip._updateStyle(options, point.options);
            tooltip.setStyle(options);
            BaseTooltip.fn.show.call(tooltip, point);
        }
    });
    var SharedTooltip = BaseTooltip.extend({
        init: function(element, plotArea, options) {
            var tooltip = this;
            BaseTooltip.fn.init.call(tooltip, element, options);
            tooltip.plotArea = plotArea;
        },
        options: {
            sharedTemplate: "<table>" + "<th colspan='2'>#= categoryText #</th>" + "# for(var i = 0; i < points.length; i++) { #" + "# var point = points[i]; #" + "<tr>" + "# if(point.series.name) { #<td>#= point.series.name #:</td> # } #" + "<td>#= content(point) #</td>" + "</tr>" + "# } #" + "</table>",
            categoryFormat: "{0:d}"
        },
        showAt: function(points, coords) {
            var tooltip = this, options = tooltip.options, plotArea = tooltip.plotArea, axis = plotArea.categoryAxis, index = axis.getCategoryIndex(coords), category = axis.getCategory(coords), slot = axis.getSlot(index), content;
            content = tooltip._content(points, category);
            tooltip.element.html(content);
            tooltip.anchor = tooltip._slotAnchor(coords, slot);
            tooltip._updateStyle(options, points[0].options);
            tooltip.setStyle(options);
            BaseTooltip.fn.show.call(tooltip);
        },
        _slotAnchor: function(point, slot) {
            var tooltip = this, plotArea = tooltip.plotArea, axis = plotArea.categoryAxis, anchor, hCenter = point.y - tooltip.element.height() / 2;
            if (axis.options.vertical) {
                anchor = Point2D(point.x, hCenter);
            } else {
                anchor = Point2D(slot.center().x, hCenter);
            }
            return anchor;
        },
        _content: function(points, category) {
            var tooltip = this, template, content;
            template = kendo.template(tooltip.options.sharedTemplate);
            content = template({
                points: points,
                category: category,
                categoryText: autoFormat(tooltip.options.categoryFormat, category),
                content: tooltip._pointContent
            });
            return content;
        }
    });
    var Crosshair = ChartElement.extend({
        init: function(axis, options) {
            var crosshair = this;
            ChartElement.fn.init.call(crosshair, options);
            crosshair.axis = axis;
            if (!crosshair.options.id) {
                crosshair.options.id = uniqueId();
            }
            crosshair._visible = false;
            crosshair.stickyMode = axis instanceof CategoryAxis;
        },
        options: {
            color: BLACK,
            width: 1,
            zIndex: -1,
            tooltip: {
                visible: false
            }
        },
        repaint: function() {
            var crosshair = this, element = crosshair.element;
            crosshair.getViewElements(crosshair._view);
            element = crosshair.element;
            element.refresh(getElement(crosshair.options.id));
        },
        showAt: function(point) {
            var crosshair = this;
            crosshair.updateAxisReference();
            crosshair._visible = true;
            crosshair.point = point;
            crosshair.repaint();
            if (crosshair.options.tooltip.visible) {
                if (!crosshair.tooltip) {
                    crosshair.tooltip = new CrosshairTooltip(crosshair, deepExtend({}, crosshair.options.tooltip, {
                        stickyMode: crosshair.stickyMode
                    }));
                }
                crosshair.tooltip.showAt(point);
            }
        },
        hide: function() {
            var crosshair = this;
            if (crosshair._visible) {
                crosshair._visible = false;
                crosshair.repaint();
                if (crosshair.tooltip) {
                    crosshair.tooltip.hide();
                }
            }
        },
        linePoints: function() {
            var crosshair = this, axis = crosshair.axis, vertical = axis.options.vertical, box = crosshair.getBox(), point = crosshair.point, dim = vertical ? Y : X, slot, lineStart, lineEnd;
            lineStart = Point2D(box.x1, box.y1);
            if (vertical) {
                lineEnd = Point2D(box.x2, box.y1);
            } else {
                lineEnd = Point2D(box.x1, box.y2);
            }
            if (point) {
                if (crosshair.stickyMode) {
                    slot = axis.getSlot(axis.getCategoryIndex(point));
                    lineStart[dim] = lineEnd[dim] = slot.center()[dim];
                } else {
                    lineStart[dim] = lineEnd[dim] = point[dim];
                }
            }
            crosshair.box = box;
            return [ lineStart, lineEnd ];
        },
        getBox: function() {
            var crosshair = this, axis = crosshair.axis, axes = axis.pane.axes, length = axes.length, vertical = axis.options.vertical, box = axis.lineBox().clone(), dim = vertical ? X : Y, axisLineBox, currentAxis, i;
            for (i = 0; i < length; i++) {
                currentAxis = axes[i];
                if (currentAxis.options.vertical != vertical) {
                    if (!axisLineBox) {
                        axisLineBox = currentAxis.lineBox().clone();
                    } else {
                        axisLineBox.wrap(currentAxis.lineBox());
                    }
                }
            }
            box[dim + 1] = axisLineBox[dim + 1];
            box[dim + 2] = axisLineBox[dim + 2];
            return box;
        },
        getViewElements: function(view) {
            var crosshair = this, options = crosshair.options, elements = [];
            crosshair.points = crosshair.linePoints();
            crosshair.element = view.createPolyline(crosshair.points, false, {
                id: options.id,
                stroke: options.color,
                strokeWidth: options.width,
                strokeOpacity: options.opacity,
                dashType: options.dashType,
                zIndex: options.zIndex,
                visible: crosshair._visible
            });
            elements.push(crosshair.element);
            crosshair._view = view;
            append(elements, ChartElement.fn.getViewElements.call(crosshair, view));
            return elements;
        },
        updateAxisReference: function() {
            var crosshair = this, axis = crosshair.axis, plotArea = axis.plotArea, axes = plotArea.axes, currentAxis, i;
            for (i = 0; i < axes.length; i++) {
                currentAxis = axes[i];
                if (defined(axis.axisIndex) && axis instanceof NumericAxis != currentAxis instanceof CategoryAxis && axis.axisIndex === currentAxis.axisIndex) {
                    crosshair.axis = currentAxis;
                    break;
                }
            }
        }
    });
    var CrosshairTooltip = BaseTooltip.extend({
        init: function(crosshair, options) {
            var tooltip = this, chartElement = crosshair.axis.getRoot().parent.element;
            tooltip.crosshair = crosshair;
            BaseTooltip.fn.init.call(tooltip, chartElement, deepExtend({}, tooltip.options, {
                background: crosshair.axis.plotArea.options.seriesColors[0]
            }, options));
            tooltip._updateStyle(tooltip.options, {});
            tooltip.setStyle(tooltip.options);
        },
        options: {
            padding: 10
        },
        showAt: function(point) {
            var tooltip = this, element = tooltip.element;
            tooltip.point = point;
            tooltip.element.html(tooltip.content(point));
            tooltip.anchor = tooltip.getAnchor(element.outerWidth(), element.outerHeight());
            tooltip.move();
        },
        move: function() {
            var tooltip = this, element = tooltip.element, offset = tooltip._offset();
            element.css({
                top: offset.top,
                left: offset.left
            }).show();
        },
        content: function(point) {
            var tooltip = this, options = tooltip.options, axis = tooltip.crosshair.axis, axisOptions = axis.options, content, value, tooltipTemplate;
            value = content = axis[options.stickyMode ? "getCategory" : "getValue"](point);
            if (options.template) {
                tooltipTemplate = template(options.template);
                content = tooltipTemplate({
                    value: value
                });
            } else if (options.format) {
                content = autoFormat(options.format, value);
            } else {
                if (axisOptions.type === DATE) {
                    content = autoFormat(axisOptions.labels.dateFormats[axisOptions.baseUnit], value);
                }
            }
            return content;
        },
        getAnchor: function(width, height) {
            var tooltip = this, options = tooltip.options, position = options.position, vertical = tooltip.crosshair.axis.options.vertical, points = tooltip.crosshair.points, fPoint = points[0], sPoint = points[1], halfWidth = width / 2, halfHeight = height / 2, padding = options.padding, x, y;
            if (vertical) {
                if (position === LEFT) {
                    x = fPoint.x - width - padding;
                    y = fPoint.y - halfHeight;
                } else {
                    x = sPoint.x + padding;
                    y = sPoint.y - halfHeight;
                }
            } else {
                if (position === BOTTOM) {
                    x = sPoint.x - halfWidth;
                    y = sPoint.y + padding;
                } else {
                    x = fPoint.x - halfWidth;
                    y = fPoint.y - height - padding;
                }
            }
            return Point2D(x, y);
        },
        hide: function() {
            this.element.hide();
            this.point = null;
        }
    });
    var Aggregates = {
        max: function(values) {
            var result = math.max.apply(math, values);
            if (isNaN(result)) {
                return sparseArrayMax(values);
            } else {
                return result;
            }
        },
        min: function(values) {
            var result = math.min.apply(math, values);
            if (isNaN(result)) {
                return sparseArrayMin(values);
            } else {
                return result;
            }
        },
        sum: function(values) {
            var i, length = values.length, sum = 0, n;
            for (i = 0; i < length; i++) {
                n = values[i];
                if (defined(n) && !isNaN(n)) {
                    sum += n;
                }
            }
            return sum;
        },
        count: function(values) {
            return values.length;
        },
        avg: function(values) {
            return Aggregates.sum(values) / Aggregates.count(values);
        }
    };
    var Selection = Observable.extend({
        init: function(chart, categoryAxis, options) {
            var that = this, chartElement = chart.element, categoryAxisLineBox = categoryAxis.lineBox(), valueAxis = that.getValueAxis(categoryAxis), valueAxisLineBox = valueAxis.lineBox(), selectorPrefix = "." + CSS_PREFIX, wrapper, padding;
            Observable.fn.init.call(that);
            that.options = deepExtend({}, that.options, options);
            options = that.options;
            that.chart = chart;
            that.chartElement = chartElement;
            that.categoryAxis = categoryAxis;
            that._dateAxis = that.categoryAxis instanceof DateCategoryAxis, that.valueAxis = valueAxis;
            if (that._dateAxis) {
                deepExtend(options, {
                    min: toDate(options.min),
                    max: toDate(options.max),
                    from: toDate(options.from),
                    to: toDate(options.to)
                });
            }
            that.template = Selection.template;
            if (!that.template) {
                that.template = Selection.template = renderTemplate("<div class='" + CSS_PREFIX + "selector' " + "style='width: #= d.width #px; height: #= d.height #px;" + " top: #= d.offset.top #px; left: #= d.offset.left #px;'>" + "<div class='" + CSS_PREFIX + "mask'></div>" + "<div class='" + CSS_PREFIX + "mask'></div>" + "<div class='" + CSS_PREFIX + "selection'>" + "<div class='" + CSS_PREFIX + "handle " + CSS_PREFIX + "leftHandle'><div></div></div>" + "<div class='" + CSS_PREFIX + "handle " + CSS_PREFIX + "rightHandle'><div></div></div>" + "</div></div>");
            }
            padding = {
                left: parseInt(chartElement.css("paddingLeft"), 10),
                right: parseInt(chartElement.css("paddingTop"), 10)
            };
            that.options = deepExtend({}, {
                width: categoryAxisLineBox.width(),
                height: valueAxisLineBox.height(),
                padding: padding,
                offset: {
                    left: valueAxisLineBox.x2 + padding.left,
                    top: valueAxisLineBox.y1 + padding.right
                },
                from: options.min,
                to: options.max
            }, options);
            if (that.options.visible) {
                that.wrapper = wrapper = $(that.template(that.options)).appendTo(chartElement);
                that.selection = wrapper.find(selectorPrefix + "selection");
                that.leftMask = wrapper.find(selectorPrefix + "mask").first();
                that.rightMask = wrapper.find(selectorPrefix + "mask").last();
                that.leftHandle = wrapper.find(selectorPrefix + "leftHandle");
                that.rightHandle = wrapper.find(selectorPrefix + "rightHandle");
                that.options.selection = {
                    border: {
                        left: parseFloat(that.selection.css("border-left-width"), 10),
                        right: parseFloat(that.selection.css("border-right-width"), 10)
                    }
                };
                that.leftHandle.css("top", (that.selection.height() - that.leftHandle.height()) / 2);
                that.rightHandle.css("top", (that.selection.height() - that.rightHandle.height()) / 2);
                that.set(that._index(options.from), that._index(options.to));
                that.bind(that.events, that.options);
                that.wrapper[0].style.cssText = that.wrapper[0].style.cssText;
                that.wrapper.on(MOUSEWHEEL_NS, proxy(that._mousewheel, that));
                if (kendo.UserEvents) {
                    that.userEvents = new kendo.UserEvents(that.wrapper, {
                        global: true,
                        threshold: 5,
                        stopPropagation: true,
                        multiTouch: true,
                        start: proxy(that._start, that),
                        move: proxy(that._move, that),
                        end: proxy(that._end, that),
                        tap: proxy(that._tap, that),
                        gesturestart: proxy(that._gesturechange, that),
                        gesturechange: proxy(that._gesturechange, that)
                    });
                } else {
                    that.leftHandle.add(that.rightHandle).removeClass(CSS_PREFIX + "handle");
                }
            }
        },
        events: [ SELECT_START, SELECT, SELECT_END ],
        options: {
            visible: true,
            mousewheel: {
                zoom: BOTH
            },
            min: MIN_VALUE,
            max: MAX_VALUE
        },
        destroy: function() {
            var that = this, userEvents = that.userEvents;
            if (userEvents) {
                userEvents.destroy();
            }
        },
        _rangeEventArgs: function(range) {
            var that = this;
            return {
                axis: that.categoryAxis.options,
                from: that._value(range.from),
                to: that._value(range.to)
            };
        },
        _start: function(e) {
            var that = this, options = that.options, target = $(e.event.target), args;
            if (that._state || !target) {
                return;
            }
            that.chart._unsetActivePoint();
            that._state = {
                moveTarget: target.parents(".k-handle").add(target).first(),
                startLocation: e.x ? e.x.location : 0,
                range: {
                    from: that._index(options.from),
                    to: that._index(options.to)
                }
            };
            args = that._rangeEventArgs({
                from: that._index(options.from),
                to: that._index(options.to)
            });
            if (that.trigger(SELECT_START, args)) {
                that.userEvents.cancel();
                that._state = null;
            }
        },
        _move: function(e) {
            if (!this._state) {
                return;
            }
            var that = this, state = that._state, options = that.options, categories = that.categoryAxis.options.categories, from = that._index(options.from), to = that._index(options.to), min = that._index(options.min), max = that._index(options.max), delta = state.startLocation - e.x.location, range = state.range, oldRange = {
                from: range.from,
                to: range.to
            }, span = range.to - range.from, target = state.moveTarget, scale = that.wrapper.width() / (categories.length - 1), offset = math.round(delta / scale);
            if (!target) {
                return;
            }
            e.preventDefault();
            if (target.is(".k-selection")) {
                range.from = math.min(math.max(min, from - offset), max - span);
                range.to = math.min(range.from + span, max);
            } else if (target.is(".k-leftHandle")) {
                range.from = math.min(math.max(min, from - offset), max - 1);
                range.to = math.max(range.from + 1, range.to);
            } else if (target.is(".k-rightHandle")) {
                range.to = math.min(math.max(min + 1, to - offset), max);
                range.from = math.min(range.to - 1, range.from);
            }
            if (range.from !== oldRange.from || range.to !== oldRange.to) {
                that.move(range.from, range.to);
                that.trigger(SELECT, that._rangeEventArgs(range));
            }
        },
        _end: function() {
            var that = this, range = that._state.range;
            delete that._state;
            that.set(range.from, range.to);
            that.trigger(SELECT_END, that._rangeEventArgs(range));
        },
        _gesturechange: function(e) {
            if (!this._state) {
                return;
            }
            var that = this, chart = that.chart, state = that._state, options = that.options, categoryAxis = that.categoryAxis, range = state.range, p0 = chart._toModelCoordinates(e.touches[0].x.location).x, p1 = chart._toModelCoordinates(e.touches[1].x.location).x, left = math.min(p0, p1), right = math.max(p0, p1);
            e.preventDefault();
            state.moveTarget = null;
            range.from = categoryAxis.getCategoryIndex(new dataviz.Point2D(left)) || options.min;
            range.to = categoryAxis.getCategoryIndex(new dataviz.Point2D(right)) || options.max;
            that.move(range.from, range.to);
        },
        _tap: function(e) {
            var that = this, options = that.options, coords = that.chart._eventCoordinates(e), categoryAxis = that.categoryAxis, categoryIx = categoryAxis.getCategoryIndex(new dataviz.Point2D(coords.x, categoryAxis.box.y1)), from = that._index(options.from), to = that._index(options.to), min = that._index(options.min), max = that._index(options.max), span = to - from, mid = from + span / 2, offset = math.round(mid - categoryIx), range = {}, rightClick = e.event.which === 3;
            if (that._state || rightClick) {
                return;
            }
            e.preventDefault();
            that.chart._unsetActivePoint();
            if (!categoryAxis.options.justified) {
                offset--;
            }
            range.from = math.min(math.max(min, from - offset), max - span);
            range.to = math.min(range.from + span, max);
            that._start(e);
            if (that._state) {
                that._state.range = range;
                that.trigger(SELECT, that._rangeEventArgs(range));
                that._end();
            }
        },
        _mousewheel: function(e) {
            var that = this, options = that.options, delta = mwDelta(e);
            that._start({
                event: {
                    target: that.selection
                }
            });
            if (that._state) {
                var range = that._state.range;
                e.preventDefault();
                e.stopPropagation();
                if (math.abs(delta) > 1) {
                    delta *= ZOOM_ACCELERATION;
                }
                if (options.mousewheel.reverse) {
                    delta *= -1;
                }
                if (that.expand(delta)) {
                    that.trigger(SELECT, {
                        axis: that.categoryAxis.options,
                        delta: delta,
                        originalEvent: e,
                        from: that._value(range.from),
                        to: that._value(range.to)
                    });
                }
                if (that._mwTimeout) {
                    clearTimeout(that._mwTimeout);
                }
                that._mwTimeout = setTimeout(function() {
                    that._end();
                }, MOUSEWHEEL_DELAY);
            }
        },
        _index: function(value) {
            var that = this, categoryAxis = that.categoryAxis, categories = categoryAxis.options.categories, index = value;
            if (value instanceof Date) {
                index = lteDateIndex(categories, value);
                if (!categoryAxis.options.justified && value > last(categories)) {
                    index += 1;
                }
            }
            return index;
        },
        _value: function(index) {
            var that = this, categoryAxis = this.categoryAxis, categories = categoryAxis.options.categories, value = index;
            if (that._dateAxis) {
                if (index > categories.length - 1) {
                    value = that.options.max;
                } else {
                    value = categories[index];
                }
            }
            return value;
        },
        _slot: function(value) {
            var that = this, categoryAxis = this.categoryAxis;
            return categoryAxis.getSlot(that._index(value));
        },
        move: function(from, to) {
            var that = this, options = that.options, offset = options.offset, padding = options.padding, border = options.selection.border, leftMaskWidth, rightMaskWidth, box, distance;
            box = that._slot(from);
            leftMaskWidth = round(box.x1 - offset.left + padding.left);
            that.leftMask.width(leftMaskWidth);
            that.selection.css("left", leftMaskWidth);
            box = that._slot(to);
            rightMaskWidth = round(options.width - (box.x1 - offset.left + padding.left));
            that.rightMask.width(rightMaskWidth);
            distance = options.width - rightMaskWidth;
            if (distance != options.width) {
                distance += border.right;
            }
            that.rightMask.css("left", distance);
            that.selection.width(math.max(options.width - (leftMaskWidth + rightMaskWidth) - border.right, 0));
        },
        set: function(from, to) {
            var that = this, options = that.options, min = that._index(options.min), max = that._index(options.max);
            from = clipValue(that._index(from), min, max);
            to = clipValue(that._index(to), from + 1, max);
            if (options.visible) {
                that.move(from, to);
            }
            options.from = that._value(from);
            options.to = that._value(to);
        },
        expand: function(delta) {
            var that = this, options = that.options, min = that._index(options.min), max = that._index(options.max), zDir = options.mousewheel.zoom, from = that._index(options.from), to = that._index(options.to), range = {
                from: from,
                to: to
            }, oldRange = deepExtend({}, range);
            if (that._state) {
                range = that._state.range;
            }
            if (zDir !== RIGHT) {
                range.from = clipValue(clipValue(from - delta, 0, to - 1), min, max);
            }
            if (zDir !== LEFT) {
                range.to = clipValue(clipValue(to + delta, range.from + 1, max), min, max);
            }
            if (range.from !== oldRange.from || range.to !== oldRange.to) {
                that.set(range.from, range.to);
                return true;
            }
        },
        getValueAxis: function(categoryAxis) {
            var axes = categoryAxis.pane.axes, axesCount = axes.length, i, axis;
            for (i = 0; i < axesCount; i++) {
                axis = axes[i];
                if (axis.options.vertical !== categoryAxis.options.vertical) {
                    return axis;
                }
            }
        }
    });
    function calculateAggregates(values, series, dataItems) {
        var aggregate = series.aggregate, result;
        function execSimple(values, aggregate, series) {
            var result, aggregateType = typeof aggregate;
            if (aggregateType === STRING) {
                result = Aggregates[aggregate](values);
            } else if (aggregateType === "function") {
                result = aggregate(values, series, dataItems);
            } else {
                result = Aggregates.max(values);
            }
            return result;
        }
        function execComposite(values, aggregate, series) {
            var valueFields = valueFieldsBySeriesType(series.type), valueFieldsCount = valueFields.length, count = values.length, i, j, field, result = [], data = [];
            for (i = 0; i < valueFieldsCount; i++) {
                field = valueFields[i];
                for (j = 0; j < count; j++) {
                    data.push(values[j][field]);
                }
                result.push(execSimple(data, aggregate[field], series));
                data = [];
            }
            return result;
        }
        if (typeof aggregate === "object") {
            result = execComposite(values, aggregate, series);
        } else {
            result = execSimple(values, aggregate, series);
        }
        return result;
    }
    function sparseArrayMin(arr) {
        return sparseArrayLimits(arr).min;
    }
    function sparseArrayMax(arr) {
        return sparseArrayLimits(arr).max;
    }
    function sparseArrayLimits(arr) {
        var min = MAX_VALUE, max = MIN_VALUE, i, length = arr.length, n;
        for (i = 0; i < length; i++) {
            n = arr[i];
            if (n !== null && isFinite(n)) {
                min = math.min(min, n);
                max = math.max(max, n);
            }
        }
        return {
            min: min === MAX_VALUE ? undefined : min,
            max: max === MIN_VALUE ? undefined : max
        };
    }
    function intersection(a1, a2, b1, b2) {
        var result, ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x), u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y), ua;
        if (u_b !== 0) {
            ua = ua_t / u_b;
            result = new Point2D(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y));
        }
        return result;
    }
    function applySeriesDefaults(options, themeOptions) {
        var series = options.series, i, seriesLength = series.length, seriesType, seriesDefaults = options.seriesDefaults, commonDefaults = deepExtend({}, options.seriesDefaults), themeSeriesDefaults = themeOptions ? deepExtend({}, themeOptions.seriesDefaults) : {}, commonThemeDefaults = deepExtend({}, themeSeriesDefaults);
        cleanupNestedSeriesDefaults(commonDefaults);
        cleanupNestedSeriesDefaults(commonThemeDefaults);
        for (i = 0; i < seriesLength; i++) {
            seriesType = series[i].type || options.seriesDefaults.type;
            series[i] = deepExtend({}, commonThemeDefaults, themeSeriesDefaults[seriesType], {
                tooltip: options.tooltip
            }, commonDefaults, seriesDefaults[seriesType], series[i]);
        }
    }
    function cleanupNestedSeriesDefaults(seriesDefaults) {
        delete seriesDefaults.bar;
        delete seriesDefaults.column;
        delete seriesDefaults.line;
        delete seriesDefaults.verticalLine;
        delete seriesDefaults.pie;
        delete seriesDefaults.donut;
        delete seriesDefaults.area;
        delete seriesDefaults.verticalArea;
        delete seriesDefaults.scatter;
        delete seriesDefaults.scatterLine;
        delete seriesDefaults.bubble;
        delete seriesDefaults.candlestick;
        delete seriesDefaults.ohlc;
        delete seriesDefaults.bullet;
        delete seriesDefaults.verticalBullet;
    }
    function applySeriesColors(options) {
        var series = options.series, i, seriesLength = series.length, colors = options.seriesColors || [];
        for (i = 0; i < seriesLength; i++) {
            series[i].color = series[i].color || colors[i % colors.length];
        }
    }
    function resolveAxisAliases(options) {
        var alias;
        each([ CATEGORY, VALUE, X, Y ], function() {
            alias = this + "Axes";
            if (options[alias]) {
                options[this + "Axis"] = options[alias];
                delete options[alias];
            }
        });
    }
    function applyAxisDefaults(options, themeOptions) {
        var themeAxisDefaults = (themeOptions || {}).axisDefaults || {};
        each([ CATEGORY, VALUE, X, Y ], function() {
            var axisName = this + "Axis", axes = [].concat(options[axisName]), axisDefaults = options.axisDefaults || {};
            axes = $.map(axes, function(axisOptions) {
                var axisColor = (axisOptions || {}).color;
                var result = deepExtend({}, themeAxisDefaults, themeAxisDefaults[axisName], axisDefaults, axisDefaults[axisName], {
                    line: {
                        color: axisColor
                    },
                    labels: {
                        color: axisColor
                    },
                    title: {
                        color: axisColor
                    }
                }, axisOptions);
                delete result[axisName];
                return result;
            });
            options[axisName] = axes.length > 1 ? axes : axes[0];
        });
    }
    function incrementSlot(slots, index, value) {
        slots[index] = (slots[index] || 0) + value;
    }
    function categoriesCount(series) {
        var seriesCount = series.length, categories = 0, i;
        for (i = 0; i < seriesCount; i++) {
            categories = math.max(categories, series[i].data.length);
        }
        return categories;
    }
    function sqr(value) {
        return value * value;
    }
    extend($.easing, {
        easeOutElastic: function(n, d, first, diff) {
            var s = 1.70158, p = 0, a = diff;
            if (n === 0) {
                return first;
            }
            if (n === 1) {
                return first + diff;
            }
            if (!p) {
                p = .5;
            }
            if (a < math.abs(diff)) {
                a = diff;
                s = p / 4;
            } else {
                s = p / (2 * math.PI) * math.asin(diff / a);
            }
            return a * math.pow(2, -10 * n) * math.sin((n * 1 - s) * 1.1 * math.PI / p) + diff + first;
        }
    });
    function getField(field, row) {
        if (row === null) {
            return row;
        }
        var get = getter(field, true);
        return get(row);
    }
    function toDate(value) {
        var result, aspDate, i;
        if (value instanceof Date) {
            result = value;
        } else if (typeof value === STRING) {
            aspDate = DATE_REGEXP.exec(value);
            result = new Date(aspDate ? parseInt(aspDate[1], 10) : value);
        } else if (value) {
            if (value.length) {
                result = [];
                for (i = 0; i < value.length; i++) {
                    result.push(toDate(value[i]));
                }
            } else {
                result = new Date(value);
            }
        }
        return result;
    }
    function toTime(value) {
        if (isArray(value)) {
            return map(value, toTime);
        } else if (value) {
            return toDate(value).getTime();
        }
    }
    function addDuration(date, value, unit, weekStartDay) {
        var result = date;
        if (date) {
            date = toDate(date);
            if (unit === YEARS) {
                result = new Date(date.getFullYear() + value, 0, 1);
            } else if (unit === MONTHS) {
                result = new Date(date.getFullYear(), date.getMonth() + value, 1);
            } else if (unit === WEEKS) {
                result = addDuration(startOfWeek(date, weekStartDay), value * 7, DAYS);
            } else if (unit === DAYS) {
                result = new Date(date.getFullYear(), date.getMonth(), date.getDate() + value);
            } else if (unit === HOURS) {
                result = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours() + value);
                if (value > 0 && dateEquals(date, result)) {
                    result = addDuration(date, value + 1, unit, weekStartDay);
                }
            } else if (unit === MINUTES) {
                result = new Date(date.getTime() + value * TIME_PER_MINUTE);
                result.setSeconds(0);
            }
        }
        return result;
    }
    function startOfWeek(date, weekStartDay) {
        var day = date.getDay(), daysToSubtract = 0;
        weekStartDay = weekStartDay || 0;
        while (day !== weekStartDay) {
            if (day === 0) {
                day = 6;
            } else {
                day--;
            }
            daysToSubtract++;
        }
        return addTicks(date, -daysToSubtract * TIME_PER_DAY);
    }
    function floorDate(date, unit, weekStartDay) {
        date = toDate(date);
        return addDuration(date, 0, unit, weekStartDay);
    }
    function ceilDate(date, unit, weekStartDay) {
        date = toDate(date);
        if (date && floorDate(date, unit, weekStartDay).getTime() === date.getTime()) {
            return date;
        }
        return addDuration(date, 1, unit, weekStartDay);
    }
    function dateDiff(a, b) {
        var diff = a.getTime() - b, offsetDiff = a.getTimezoneOffset() - b.getTimezoneOffset();
        return diff - offsetDiff * TIME_PER_MINUTE;
    }
    function addTicks(date, ticks) {
        var tzOffsetBefore = date.getTimezoneOffset(), result = new Date(date.getTime() + ticks), tzOffsetDiff = result.getTimezoneOffset() - tzOffsetBefore;
        return new Date(result.getTime() + tzOffsetDiff * TIME_PER_MINUTE);
    }
    function duration(a, b, unit) {
        var diff;
        if (unit === YEARS) {
            diff = b.getFullYear() - a.getFullYear();
        } else if (unit === MONTHS) {
            diff = duration(a, b, YEARS) * 12 + b.getMonth() - a.getMonth();
        } else if (unit === DAYS) {
            diff = math.floor(dateDiff(b, a) / TIME_PER_DAY);
        } else {
            diff = math.floor((b - a) / TIME_PER_UNIT[unit]);
        }
        return diff;
    }
    function valueFieldsBySeriesType(type) {
        var result = [ VALUE ];
        if (inArray(type, [ CANDLESTICK, OHLC ])) {
            result = [ "open", "high", "low", "close" ];
        } else if (inArray(type, [ BULLET, VERTICAL_BULLET ])) {
            result = [ "current", "target" ];
        } else if (inArray(type, XY_CHARTS)) {
            result = [ X, Y ];
            if (type === BUBBLE) {
                result.push("size");
            }
        }
        return result;
    }
    function bindPoint(series, pointIx, pointFields) {
        var pointData = series.data[pointIx], fieldData, fields = {}, srcValueFields, srcPointFields, valueFields = valueFieldsBySeriesType(series.type), value, result = {
            value: pointData
        };
        if (defined(pointData)) {
            if (isArray(pointData)) {
                fieldData = pointData.slice(valueFields.length);
                value = bindFromArray(pointData, valueFields);
                fields = bindFromArray(fieldData, pointFields);
            } else if (typeof pointData === "object") {
                srcValueFields = mapSeriesFields(series, valueFields);
                srcPointFields = mapSeriesFields(series, pointFields);
                value = bindFromObject(pointData, valueFields, srcValueFields);
                fields = bindFromObject(pointData, pointFields, srcPointFields);
            }
        } else {
            value = bindFromObject({}, valueFields);
        }
        if (defined(value)) {
            if (valueFields.length === 1) {
                value = value[valueFields[0]];
            }
            result.value = value;
        }
        result.fields = fields;
        return result;
    }
    function bindFromArray(array, fields) {
        var value = {}, i, length;
        if (fields) {
            length = math.min(fields.length, array.length);
            for (i = 0; i < length; i++) {
                value[fields[i]] = array[i];
            }
        }
        return value;
    }
    function bindFromObject(object, fields, srcFields) {
        var value = {}, i, length, fieldName, srcFieldName;
        if (fields) {
            length = fields.length;
            srcFields = srcFields || fields;
            for (i = 0; i < length; i++) {
                fieldName = fields[i];
                srcFieldName = srcFields[i];
                value[fieldName] = getField(srcFieldName, object);
            }
        }
        return value;
    }
    function mapSeriesFields(series, fields) {
        var i, length, fieldName, sourceFields, sourceFieldName;
        if (fields) {
            length = fields.length;
            sourceFields = [];
            for (i = 0; i < length; i++) {
                fieldName = fields[i];
                sourceFieldName = fieldName === VALUE ? "field" : fieldName + "Field";
                sourceFields.push(series[sourceFieldName] || fieldName);
            }
        }
        return sourceFields;
    }
    function singleItemOrArray(array) {
        return array.length === 1 ? array[0] : array;
    }
    function clipValue(value, min, max) {
        return math.max(math.min(value, max), min);
    }
    function axisGroupBox(axes) {
        var length = axes.length, box, i, axisBox;
        if (length > 0) {
            for (i = 0; i < length; i++) {
                axisBox = axes[i].box;
                if (!box) {
                    box = axisBox.clone();
                } else {
                    box.wrap(axisBox);
                }
            }
        }
        return box || Box2D();
    }
    function equalsIgnoreCase(a, b) {
        if (a && b) {
            return a.toLowerCase() === b.toLowerCase();
        }
        return a === b;
    }
    function dateEquals(a, b) {
        if (a && b) {
            return toTime(a) === toTime(b);
        }
        return a === b;
    }
    function lastValue(array) {
        var i = array.length, value;
        while (i--) {
            value = array[i];
            if (defined(value) && value !== null) {
                return value;
            }
        }
    }
    function appendIfNotNull(array, element) {
        if (element !== null) {
            array.push(element);
        }
    }
    function lteDateIndex(sortedDates, date) {
        var low = 0, high = sortedDates.length - 1, i, currentDate;
        while (low <= high) {
            i = math.floor((low + high) / 2);
            currentDate = sortedDates[i];
            if (currentDate < date) {
                low = i + 1;
                continue;
            }
            if (currentDate > date) {
                high = i - 1;
                continue;
            }
            return i;
        }
        if (sortedDates[i] <= date) {
            return i;
        } else {
            return i - 1;
        }
    }
    function validNumbers(values) {
        var valid = true, i, val, length = values.length;
        for (i = 0; i < length; i++) {
            val = values[i];
            if (typeof val !== "number" || isNaN(val)) {
                valid = false;
                break;
            }
        }
        return valid;
    }
    function axisRanges(axes) {
        var i, axis, axisName, ranges = {};
        for (i = 0; i < axes.length; i++) {
            axis = axes[i];
            axisName = axis.options.name;
            if (axisName) {
                ranges[axisName] = axis.range();
            }
        }
        return ranges;
    }
    function mwDelta(e) {
        var origEvent = e.originalEvent, delta = 0;
        if (origEvent.wheelDelta) {
            delta = -origEvent.wheelDelta / 120;
            delta = delta > 0 ? math.ceil(delta) : math.floor(delta);
        }
        if (origEvent.detail) {
            delta = round(origEvent.detail / 3);
        }
        return delta;
    }
    // Exports ================================================================
    dataviz.ui.plugin(Chart);
    deepExtend(dataviz, {
        Aggregates: Aggregates,
        AreaChart: AreaChart,
        Bar: Bar,
        BarAnimationDecorator: BarAnimationDecorator,
        BarChart: BarChart,
        BarLabel: BarLabel,
        BubbleAnimationDecorator: BubbleAnimationDecorator,
        BubbleChart: BubbleChart,
        CandlestickChart: CandlestickChart,
        Candlestick: Candlestick,
        CategoricalPlotArea: CategoricalPlotArea,
        CategoryAxis: CategoryAxis,
        ClusterLayout: ClusterLayout,
        Crosshair: Crosshair,
        CrosshairTooltip: CrosshairTooltip,
        DateCategoryAxis: DateCategoryAxis,
        DateValueAxis: DateValueAxis,
        DonutChart: DonutChart,
        DonutPlotArea: DonutPlotArea,
        DonutSegment: DonutSegment,
        Highlight: Highlight,
        SharedTooltip: SharedTooltip,
        Legend: Legend,
        LineChart: LineChart,
        LinePoint: LinePoint,
        Pane: Pane,
        PieAnimation: PieAnimation,
        PieAnimationDecorator: PieAnimationDecorator,
        PieChart: PieChart,
        PiePlotArea: PiePlotArea,
        PieSegment: PieSegment,
        ScatterChart: ScatterChart,
        ScatterLineChart: ScatterLineChart,
        Selection: Selection,
        ShapeElement: ShapeElement,
        StackLayout: StackLayout,
        Tooltip: Tooltip,
        OHLCChart: OHLCChart,
        OHLCPoint: OHLCPoint,
        XYPlotArea: XYPlotArea,
        addDuration: addDuration,
        axisGroupBox: axisGroupBox,
        validNumbers: validNumbers,
        bindPoint: bindPoint,
        categoriesCount: categoriesCount,
        ceilDate: ceilDate,
        duration: duration,
        floorDate: floorDate,
        lteDateIndex: lteDateIndex,
        sparseArrayLimits: sparseArrayLimits,
        toDate: toDate,
        toTime: toTime
    });
})(window.kendo.jQuery);

(function($, undefined) {
    // Imports ================================================================
    var math = Math, kendo = window.kendo, Widget = kendo.ui.Widget, deepExtend = kendo.deepExtend, dataviz = kendo.dataviz, Axis = dataviz.Axis, Box2D = dataviz.Box2D, ChartElement = dataviz.ChartElement, NumericAxis = dataviz.NumericAxis, Pin = dataviz.Pin, Ring = dataviz.Ring, RootElement = dataviz.RootElement, RotationAnimation = dataviz.RotationAnimation, BarIndicatorAnimatin = dataviz.BarIndicatorAnimatin, ArrowAnimation = dataviz.ArrowAnimation, append = dataviz.append, animationDecorator = dataviz.animationDecorator, autoMajorUnit = dataviz.autoMajorUnit, getElement = dataviz.getElement, getSpacing = dataviz.getSpacing, defined = dataviz.defined, rotatePoint = dataviz.rotatePoint, Point2D = dataviz.Point2D, round = dataviz.round, uniqueId = dataviz.uniqueId;
    // Constants ==============================================================
    var ANGULAR_SPEED = 150, ARROW = "arrow", ARROW_POINTER = "arrowPointer", BAR_INDICATOR = "barIndicator", BLACK = "#000", CAP_SIZE = .05, COORD_PRECISION = dataviz.COORD_PRECISION, MAX_VALUE = Number.MAX_VALUE, MIN_VALUE = -Number.MAX_VALUE, DEFAULT_HEIGHT = 200, DEFAULT_LINE_WIDTH = .5, DEFAULT_WIDTH = 200, DEFAULT_MIN_WIDTH = 60, DEFAULT_MIN_HEIGHT = 60, DEGREE = math.PI / 180, INSIDE = "inside", NEEDLE = "needle", OUTSIDE = "outside", RADIAL_POINTER = "radialPointer", ROTATION_ORIGIN = 90;
    // Gauge ==================================================================
    var Pointer = ChartElement.extend({
        init: function(scale, options) {
            var pointer = this, scaleOptions = scale.options;
            ChartElement.fn.init.call(pointer, options);
            options = pointer.options;
            if (!options.id) {
                options.id = uniqueId();
            }
            options.fill = options.color;
            pointer.scale = scale;
            if (defined(options.value)) {
                options.value = math.min(math.max(options.value, scaleOptions.min), scaleOptions.max);
            } else {
                options.value = scaleOptions.min;
            }
        },
        options: {
            color: BLACK
        },
        value: function(newValue) {
            var pointer = this, options = pointer.options, value = options.value, scaleOptions = pointer.scale.options;
            if (arguments.length === 0) {
                return value;
            }
            options._oldValue = options.value;
            options.value = math.min(math.max(newValue, scaleOptions.min), scaleOptions.max);
            pointer.repaint();
        }
    });
    var RadialPointer = Pointer.extend({
        options: {
            shape: NEEDLE,
            cap: {
                size: CAP_SIZE
            },
            arrow: {
                width: 16,
                height: 14
            },
            animation: {
                type: RADIAL_POINTER,
                speed: ANGULAR_SPEED
            }
        },
        reflow: function() {
            var pointer = this, options = pointer.options, scale = pointer.scale, ring = scale.ring, c = ring.c, capSize = ring.r * options.cap.size;
            pointer.box = new Box2D(c.x - capSize, c.y - capSize, c.x + capSize, c.y + capSize);
        },
        repaint: function() {
            var pointer = this, scale = pointer.scale, options = pointer.options, needle = pointer.elements[0], animationOptions = options.animation, minSlotAngle = scale.slotAngle(scale.options.min), oldAngle = scale.slotAngle(options._oldValue) - minSlotAngle, animation = needle._animation;
            needle.options.rotation[0] = scale.slotAngle(options.value) - minSlotAngle;
            if (animation) {
                animation.abort();
            }
            if (animationOptions.transitions === false) {
                needle.refresh(getElement(options.id));
            } else {
                animation = needle._animation = new RotationAnimation(needle, deepExtend(animationOptions, {
                    startAngle: oldAngle,
                    reverse: scale.options.reverse
                }));
                animation.setup();
                animation.play();
            }
        },
        _renderNeedle: function(view, box, center, pointRotation) {
            var pointer = this, options = pointer.options, scale = pointer.scale, capSize = scale.ring.r * options.cap.size;
            return [ view.createPolyline([ rotatePoint((box.x1 + box.x2) / 2, box.y1 + scale.options.minorTicks.size, center.x, center.y, pointRotation), rotatePoint(center.x - capSize / 2, center.y, center.x, center.y, pointRotation), rotatePoint(center.x + capSize / 2, center.y, center.x, center.y, pointRotation) ], true, options), view.createCircle(center, capSize, {
                fill: options.cap.color || options.color
            }) ];
        },
        _renderArrow: function(view, box, center, pointRotation) {
            var pointer = this, options = pointer.options, scale = pointer.scale, ring = scale.ring.clone(), trackWidth = 5, arrowOptions = options.arrow, height = arrowOptions.height;
            ring.ir = ring.r - trackWidth;
            return [ view.createPin(new Pin({
                origin: rotatePoint((box.x1 + box.x2) / 2, box.y1 + height, center.x, center.y, pointRotation),
                height: arrowOptions.height,
                radius: trackWidth,
                rotation: pointRotation,
                arcAngle: 180
            }), options), view.createRing(ring, {
                fill: options.color
            }) ];
        },
        renderPointer: function(view) {
            var pointer = this, scale = pointer.scale, ring = scale.ring, c = ring.c, r = ring.r, shape, options = pointer.options, box = new Box2D(c.x - r, c.y - r, c.x + r, c.y + r), center = box.center(), minAngle = scale.slotAngle(scale.options.min), pointRotation = ROTATION_ORIGIN - minAngle;
            if (options.animation !== false) {
                deepExtend(options.animation, {
                    startAngle: 0,
                    center: center,
                    reverse: scale.options.reverse
                });
            }
            deepExtend(options, {
                rotation: [ scale.slotAngle(options.value) - minAngle, center.x, center.y ]
            });
            if (options.shape == ARROW) {
                shape = pointer._renderArrow(view, box, center, pointRotation);
            } else {
                shape = pointer._renderNeedle(view, box, center, pointRotation);
            }
            return shape;
        },
        getViewElements: function(view) {
            var pointer = this, elements = pointer.renderPointer(view);
            pointer.elements = elements;
            return elements;
        }
    });
    var RadialScale = NumericAxis.extend({
        init: function(options) {
            var scale = this;
            scale.options = deepExtend({}, scale.options, options);
            scale.options.majorUnit = scale.options.majorUnit || autoMajorUnit(scale.options.min, scale.options.max);
            Axis.fn.init.call(scale, scale.options);
            scale.options.minorUnit = scale.options.minorUnit || scale.options.majorUnit / 10;
        },
        options: {
            min: 0,
            max: 100,
            majorTicks: {
                size: 15,
                align: INSIDE,
                color: BLACK,
                width: DEFAULT_LINE_WIDTH,
                visible: true
            },
            minorTicks: {
                size: 10,
                align: INSIDE,
                color: BLACK,
                width: DEFAULT_LINE_WIDTH,
                visible: true
            },
            startAngle: -30,
            endAngle: 210,
            labels: {
                position: INSIDE,
                padding: 2
            }
        },
        reflow: function(box) {
            var scale = this, options = scale.options, center = box.center(), radius = math.min(box.height(), box.width()) / 2, ring = scale.ring || new dataviz.Ring(center, radius - options.majorTicks.size, radius, options.startAngle, options.endAngle - options.startAngle);
            scale.ring = ring;
            scale.box = ring.getBBox();
            scale.arrangeLabels();
        },
        slotAngle: function(value) {
            var options = this.options, startAngle = options.startAngle, reverse = options.reverse, angle = options.endAngle - startAngle, min = options.min, max = options.max, result;
            if (reverse) {
                result = options.endAngle - (value - min) / (max - min) * angle;
            } else {
                result = (value - min) / (max - min) * angle + startAngle;
            }
            return result;
        },
        renderTicks: function(view) {
            var scale = this, ticks = [], majorTickRing = scale.ring, minorTickRing = majorTickRing.clone(), options = scale.options, minorTickSize = options.minorTicks.size;
            function renderTickRing(ring, unit, tickOptions, visible, skipUnit) {
                var tickAngles = scale.tickAngles(ring, unit), i, innerPoint, outerPoint, skip = skipUnit / unit, count = tickAngles.length;
                if (visible) {
                    for (i = 0; i < count; i++) {
                        if (i % skip === 0) {
                            continue;
                        }
                        outerPoint = ring.point(tickAngles[i]);
                        innerPoint = ring.point(tickAngles[i], true);
                        ticks.push(view.createLine(innerPoint.x, innerPoint.y, outerPoint.x, outerPoint.y, {
                            align: false,
                            stroke: tickOptions.color,
                            strokeWidth: tickOptions.width
                        }));
                    }
                }
            }
            renderTickRing(majorTickRing, options.majorUnit, options.majorTicks, options.majorTicks.visible);
            if (options.labels.position == INSIDE) {
                minorTickRing.radius(minorTickRing.r - minorTickSize, true);
            } else {
                minorTickRing.radius(minorTickRing.ir + minorTickSize);
            }
            renderTickRing(minorTickRing, options.minorUnit, options.minorTicks, options.minorTicks.visible, options.majorUnit);
            return ticks;
        },
        arrangeLabels: function() {
            var scale = this, options = scale.options, ring = scale.ring.clone(), tickAngels = scale.tickAngles(ring, options.majorUnit), labels = scale.labels, count = labels.length, labelsOptions = options.labels, padding = labelsOptions.padding, rangeDistance = ring.r * .05, rangeSize = options.rangeSize = options.rangeSize || ring.r * .1, ranges = options.ranges || [], halfWidth, halfHeight, labelAngle, angle, label, lp, i, cx, cy, isInside;
            if (typeof scale.options.rangeDistance != "undefined") {
                rangeDistance = scale.options.rangeDistance;
            } else {
                scale.options.rangeDistance = rangeDistance;
            }
            if (labelsOptions.position === INSIDE && ranges.length) {
                ring.r -= rangeSize + rangeDistance;
                ring.ir -= rangeSize + rangeDistance;
            }
            for (i = 0; i < count; i++) {
                label = labels[i];
                halfWidth = label.box.width() / 2;
                halfHeight = label.box.height() / 2;
                angle = tickAngels[i];
                labelAngle = angle * DEGREE;
                isInside = labelsOptions.position === INSIDE;
                lp = ring.point(angle, isInside);
                cx = lp.x + math.cos(labelAngle) * (halfWidth + padding) * (isInside ? 1 : -1);
                cy = lp.y + math.sin(labelAngle) * (halfHeight + padding) * (isInside ? 1 : -1);
                label.reflow(new Box2D(cx - halfWidth, cy - halfHeight, cx + halfWidth, cy + halfHeight));
                scale.box.wrap(label.box);
            }
        },
        tickAngles: function(ring, stepValue) {
            var scale = this, options = scale.options, reverse = options.reverse, range = options.max - options.min, angle = ring.angle, pos = ring.startAngle, tickCount = range / stepValue, step = angle / tickCount, positions = [], i;
            if (reverse) {
                pos += angle;
                step = -step;
            }
            for (i = 0; i < tickCount; i++) {
                positions.push(round(pos, COORD_PRECISION));
                pos += step;
            }
            if (round(pos) <= options.endAngle) {
                positions.push(pos);
            }
            return positions;
        },
        renderRanges: function(view) {
            var scale = this, result = [], from, to, segments = scale.rangeSegments(), segmentsCount = segments.length, reverse = scale.options.reverse, segment, ringRadius, i;
            if (segmentsCount) {
                ringRadius = scale.getRadius();
                for (i = 0; i < segmentsCount; i++) {
                    segment = segments[i];
                    from = scale.slotAngle(segment[reverse ? "to" : "from"]);
                    to = scale.slotAngle(segment[!reverse ? "to" : "from"]);
                    if (to - from !== 0) {
                        result.push(view.createRing(new Ring(scale.ring.c, ringRadius.inner, ringRadius.outer, from, to - from), {
                            fill: segment.color,
                            fillOpacity: segment.opacity,
                            zIndex: -1
                        }));
                    }
                }
            }
            return result;
        },
        rangeSegments: function() {
            var gauge = this, options = gauge.options, ranges = options.ranges || [], count = ranges.length, range, segmentsCount, defaultColor = options.rangePlaceholderColor, segments = [], segment, min = options.min, max = options.max, i, j;
            function rangeSegment(from, to, color) {
                return {
                    from: from,
                    to: to,
                    color: color
                };
            }
            if (count) {
                segments.push(rangeSegment(min, max, defaultColor));
                for (i = 0; i < count; i++) {
                    range = getRange(ranges[i], min, max);
                    segmentsCount = segments.length;
                    for (j = 0; j < segmentsCount; j++) {
                        segment = segments[j];
                        if (segment.from <= range.from && range.from <= segment.to) {
                            segments.push(rangeSegment(range.from, range.to, range.color));
                            if (segment.from <= range.to && range.to <= segment.to) {
                                segments.push(rangeSegment(range.to, segment.to, defaultColor));
                            }
                            segment.to = range.from;
                            break;
                        }
                    }
                }
            }
            return segments;
        },
        getRadius: function() {
            var scale = this, options = scale.options, rangeSize = options.rangeSize, rangeDistance = options.rangeDistance, ring = scale.ring, ir, r;
            if (options.labels.position === OUTSIDE) {
                r = ring.ir - rangeDistance;
                ir = r - rangeSize;
            } else {
                r = ring.r;
                ir = r - rangeSize;
                // move the ticks with a range distance and a range size
                ring.r -= rangeSize + rangeDistance;
                ring.ir -= rangeSize + rangeDistance;
            }
            return {
                inner: ir,
                outer: r
            };
        },
        getViewElements: function(view) {
            var scale = this, childElements = ChartElement.fn.getViewElements.call(scale, view);
            append(childElements, scale.renderRanges(view));
            append(childElements, scale.renderTicks(view));
            return childElements;
        }
    });
    var RadialGaugePlotArea = ChartElement.extend({
        init: function(options) {
            ChartElement.fn.init.call(this, options);
            this.render();
        },
        options: {
            margin: {},
            background: "",
            border: {
                color: BLACK,
                width: 0
            },
            minorTicks: {
                align: INSIDE
            }
        },
        reflow: function(box) {
            var plotArea = this, scale = plotArea.scale, pointer = plotArea.pointer, plotBox;
            scale.reflow(box);
            plotBox = scale.box.clone();
            pointer.scale = scale;
            pointer.reflow();
            plotBox.wrap(pointer.box);
            plotArea.box = plotBox;
            plotArea.fitScale(box);
            plotArea.alignScale(box);
        },
        alignScale: function(box) {
            var plotArea = this, plotBoxCenter = plotArea.box.center(), boxCenter = box.center(), paddingX = plotBoxCenter.x - boxCenter.x, paddingY = plotBoxCenter.y - boxCenter.y, scale = plotArea.scale, pointer = plotArea.pointer;
            scale.ring.c.x -= paddingX;
            scale.ring.c.y -= paddingY;
            scale.reflow(box);
            pointer.reflow();
            plotArea.box = scale.box.clone().wrap(pointer.box);
        },
        fitScale: function(box) {
            var plotArea = this, scale = plotArea.scale, ring = scale.ring, plotAreaBox = plotArea.box, step = math.abs(plotArea.getDiff(plotAreaBox, box)), min = round(step, COORD_PRECISION), max = round(-step, COORD_PRECISION), minDiff, midDiff, maxDiff, mid, i = 0;
            while (i < 100) {
                i++;
                if (min != mid) {
                    minDiff = plotArea.getPlotBox(min, box, ring);
                    if (0 <= minDiff && minDiff <= 2) {
                        break;
                    }
                }
                if (max != mid) {
                    maxDiff = plotArea.getPlotBox(max, box, ring);
                    if (0 <= maxDiff && maxDiff <= 2) {
                        break;
                    }
                }
                if (minDiff > 0 && maxDiff > 0) {
                    mid = min * 2;
                } else if (minDiff < 0 && maxDiff < 0) {
                    mid = max * 2;
                } else {
                    mid = round((min + max) / 2 || 1, COORD_PRECISION);
                }
                midDiff = plotArea.getPlotBox(mid, box, ring);
                if (0 <= midDiff && midDiff <= 2) {
                    break;
                }
                if (midDiff > 0) {
                    max = mid;
                    maxDiff = midDiff;
                } else {
                    min = mid;
                    minDiff = midDiff;
                }
            }
        },
        getPlotBox: function(step, box, ring) {
            var plotArea = this, scale = plotArea.scale, pointer = plotArea.pointer;
            ring = ring.clone();
            ring.r += step;
            ring.ir += step;
            scale.ring = ring;
            scale.reflow(box);
            pointer.scale = scale;
            pointer.reflow();
            plotArea.box = scale.box.clone().wrap(pointer.box);
            return plotArea.getDiff(plotArea.box, box);
        },
        getDiff: function(plotBox, box) {
            return math.min(box.width() - plotBox.width(), box.height() - plotBox.height());
        },
        render: function() {
            var plotArea = this, options = plotArea.options, scale;
            scale = plotArea.scale = new RadialScale(options.scale);
            plotArea.append(plotArea.scale);
            plotArea.pointer = new RadialPointer(scale, deepExtend({}, options.pointer, {
                animation: {
                    transitions: options.transitions
                }
            }));
            plotArea.append(plotArea.pointer);
        }
    });
    var LinearScale = NumericAxis.extend({
        init: function(options) {
            var scale = this;
            scale.options = deepExtend({}, scale.options, options);
            scale.options = deepExtend({}, scale.options, {
                labels: {
                    mirror: scale.options.mirror
                }
            });
            scale.options.majorUnit = scale.options.majorUnit || autoMajorUnit(scale.options.min, scale.options.max);
            Axis.fn.init.call(scale, scale.options);
            scale.options.minorUnit = scale.options.minorUnit || scale.options.majorUnit / 10;
        },
        options: {
            min: 0,
            max: 50,
            majorTicks: {
                size: 15,
                align: INSIDE,
                color: BLACK,
                width: DEFAULT_LINE_WIDTH,
                visible: true
            },
            minorTicks: {
                size: 10,
                align: INSIDE,
                color: BLACK,
                width: DEFAULT_LINE_WIDTH,
                visible: true
            },
            line: {
                width: DEFAULT_LINE_WIDTH
            },
            labels: {
                position: INSIDE,
                padding: 2
            },
            mirror: false,
            _alignLines: false
        },
        renderRanges: function(view) {
            var scale = this, options = scale.options, min = options.min, max = options.max, ranges = options.ranges || [], vertical = options.vertical, mirror = options.labels.mirror, result = [], count = ranges.length, range, slotX, slotY, i, rangeSize = options.rangeSize || options.minorTicks.size / 2, slot;
            if (count) {
                for (i = 0; i < count; i++) {
                    range = getRange(ranges[i], min, max);
                    slot = scale.getSlot(range.from, range.to);
                    slotX = vertical ? scale.lineBox() : slot;
                    slotY = vertical ? slot : scale.lineBox();
                    if (vertical) {
                        slotX.x1 -= rangeSize * (mirror ? -1 : 1);
                    } else {
                        slotY.y2 += rangeSize * (mirror ? -1 : 1);
                    }
                    result.push(view.createRect(new Box2D(slotX.x1, slotY.y1, slotX.x2, slotY.y2), {
                        fill: range.color,
                        fillOpacity: range.opacity
                    }));
                }
            }
            return result;
        },
        getViewElements: function(view) {
            var scale = this, elements = NumericAxis.fn.getViewElements.call(scale, view);
            append(elements, scale.renderRanges(view));
            return elements;
        }
    });
    var LinearPointer = Pointer.extend({
        init: function(scale, options) {
            var pointer = this;
            Pointer.fn.init.call(pointer, scale, options);
            pointer.options = deepExtend({
                size: pointer.pointerSize(),
                track: {
                    visible: defined(options.track)
                }
            }, pointer.options);
        },
        options: {
            shape: BAR_INDICATOR,
            track: {
                border: {
                    width: 1
                }
            },
            color: BLACK,
            border: {
                width: 1
            },
            opacity: 1,
            margin: getSpacing(3),
            animation: {
                type: BAR_INDICATOR
            },
            visible: true
        },
        repaint: function() {
            var pointer = this, scale = pointer.scale, options = pointer.options, element = pointer.element, animation = element._animation;
            if (animation) {
                animation.abort();
            }
            if (options.animation.transitions === false) {
                pointer.getViewElements(pointer._view);
                element.points = pointer.element.points;
                element.refresh(getElement(options.id));
            } else {
                options.animation = deepExtend({}, options.animation, {
                    endPosition: scale.getSlot(scale.options.min, options.value),
                    reverse: scale.options.reverse
                });
                if (options.shape === ARROW) {
                    animation = element._animation = new ArrowAnimation(element, options.animation);
                } else {
                    animation = element._animation = new BarIndicatorAnimatin(element, options.animation);
                }
                animation.setup();
                animation.play();
            }
        },
        reflow: function() {
            var pointer = this, options = pointer.options, scale = pointer.scale, scaleLine = scale.lineBox(), trackSize = options.track.size || options.size, pointerHalfSize = options.size / 2, mirror = scale.options.mirror, margin = getSpacing(options.margin), vertical = scale.options.vertical, space = vertical ? margin[mirror ? "left" : "right"] : margin[mirror ? "bottom" : "top"], pointerBox, pointerRangeBox, trackBox;
            space = mirror ? -space : space;
            if (vertical) {
                trackBox = new Box2D(scaleLine.x1 + space, scaleLine.y1, scaleLine.x1 + space, scaleLine.y2);
                if (mirror) {
                    trackBox.x1 -= trackSize;
                } else {
                    trackBox.x2 += trackSize;
                }
                if (options.shape !== BAR_INDICATOR) {
                    pointerRangeBox = new Box2D(scaleLine.x2 + space, scaleLine.y1 - pointerHalfSize, scaleLine.x2 + space, scaleLine.y2 + pointerHalfSize);
                    pointerBox = pointerRangeBox;
                }
            } else {
                trackBox = new Box2D(scaleLine.x1, scaleLine.y1 - space, scaleLine.x2, scaleLine.y1 - space);
                if (mirror) {
                    trackBox.y2 += trackSize;
                } else {
                    trackBox.y1 -= trackSize;
                }
                if (options.shape !== BAR_INDICATOR) {
                    pointerRangeBox = new Box2D(scaleLine.x1 - pointerHalfSize, scaleLine.y1 - space, scaleLine.x2 + pointerHalfSize, scaleLine.y1 - space);
                    pointerBox = pointerRangeBox;
                }
            }
            pointer.trackBox = trackBox;
            pointer.pointerRangeBox = pointerRangeBox;
            pointer.box = pointerBox || trackBox.clone().pad(options.border.width);
        },
        renderPointer: function(view) {
            var pointer = this, scale = pointer.scale, options = pointer.options, border = defined(options.border) ? {
                stroke: options.border.width ? options.border.color || options.color : "",
                strokeWidth: options.border.width,
                dashType: options.border.dashType
            } : {}, element, elementOptions = deepExtend({
                fill: options.color,
                fillOpacity: options.opacity,
                animation: deepExtend(options.animation, {
                    startPosition: scale.getSlot(scale.options.min, options.value),
                    size: options.size,
                    vertical: scale.options.vertical,
                    reverse: scale.options.reverse
                }),
                id: options.id,
                zIndex: 2,
                align: false
            }, border), shape = pointer.pointerShape(options.value);
            if (options.shape === ARROW) {
                elementOptions.animation.type = ARROW_POINTER;
                element = view.createPolyline(shape, true, elementOptions);
            } else {
                element = view.createRect(shape, elementOptions);
            }
            return element;
        },
        pointerShape: function(value) {
            var pointer = this, options = pointer.options, scale = pointer.scale, slot = scale.getSlot(value, scale.options.min), size = options.size, pointerRangeBox = pointer.pointerRangeBox, vertical = scale.options.vertical, halfSize = size / 2, shape, sign = scale.options.mirror ? -1 : 1, reverse = scale.options.reverse, pos, trackBox;
            if (options.shape == ARROW) {
                if (vertical) {
                    pos = reverse ? "y2" : "y1";
                    shape = [ new Point2D(pointerRangeBox.x1, slot[pos] - halfSize), new Point2D(pointerRangeBox.x1 - sign * size, slot[pos]), new Point2D(pointerRangeBox.x1, slot[pos] + halfSize) ];
                } else {
                    pos = reverse ? "x1" : "x2";
                    shape = [ new Point2D(slot[pos] - halfSize, pointerRangeBox.y2), new Point2D(slot[pos], pointerRangeBox.y2 + sign * size), new Point2D(slot[pos] + halfSize, pointerRangeBox.y2) ];
                }
            } else {
                trackBox = pointer.trackBox;
                if (vertical) {
                    shape = new Box2D(trackBox.x1, slot.y1, trackBox.x1 + size, slot.y2);
                } else {
                    shape = new Box2D(slot.x1, trackBox.y1, slot.x2, trackBox.y1 + size);
                }
            }
            return shape;
        },
        pointerSize: function() {
            var pointer = this, options = pointer.options, scale = pointer.scale, tickSize = scale.options.majorTicks.size, size;
            if (options.shape === ARROW) {
                size = tickSize * .6;
            } else {
                size = tickSize * .3;
            }
            return round(size);
        },
        renderTrack: function(view) {
            var pointer = this, options = pointer.options, trackOptions = options.track, border = trackOptions.border || {}, trackBox = pointer.trackBox.clone().pad(border.width || 0);
            return view.createRect(trackBox, {
                fill: trackOptions.color,
                fillOpacity: trackOptions.opacity,
                stroke: border.width ? border.color || trackOptions.color : "",
                strokeWidth: border.width,
                dashType: border.dashType,
                align: false
            });
        },
        getViewElements: function(view) {
            var pointer = this, options = pointer.options, elements = [];
            pointer.element = pointer.renderPointer(view);
            elements.push(pointer.element);
            if (options.track.visible && (options.shape === BAR_INDICATOR || options.shape === "")) {
                elements.push(pointer.renderTrack(view));
            }
            pointer._view = view;
            append(elements, Pointer.fn.getViewElements.call(pointer, view));
            return elements;
        }
    });
    var LinearGaugePlotArea = ChartElement.extend({
        init: function(options) {
            ChartElement.fn.init.call(this, options);
            this.render();
        },
        options: {
            plotArea: {
                margin: {},
                background: "",
                border: {
                    color: BLACK,
                    width: 0
                }
            },
            pointer: {},
            scale: {}
        },
        reflow: function(box) {
            var plotArea = this, scale = plotArea.scale, pointer = plotArea.pointer;
            scale.reflow(box);
            pointer.reflow(box);
            plotArea.box = plotArea.getBox(box);
            plotArea.alignElements();
            plotArea.shrinkElements();
        },
        shrinkElements: function() {
            var plotArea = this, scale = plotArea.scale, pointer = plotArea.pointer, scaleBox = scale.box.clone(), pointerBox = pointer.box, pos = scale.options.vertical ? "y" : "x";
            scaleBox[pos + 1] += math.max(scaleBox[pos + 1] - pointerBox[pos + 1], 0);
            scaleBox[pos + 2] -= math.max(pointerBox[pos + 2] - scaleBox[pos + 2], 0);
            scale.reflow(scaleBox);
            pointer.reflow(plotArea.box);
        },
        getBox: function(box) {
            var plotArea = this, scale = plotArea.scale, pointer = plotArea.pointer, boxCenter = box.center(), plotAreaBox = pointer.box.clone().wrap(scale.box), size;
            if (scale.options.vertical) {
                size = plotAreaBox.width() / 2;
                plotAreaBox = new Box2D(boxCenter.x - size, box.y1, boxCenter.x + size, box.y2);
            } else {
                size = plotAreaBox.height() / 2;
                plotAreaBox = new Box2D(box.x1, boxCenter.y - size, box.x2, boxCenter.y + size);
            }
            return plotAreaBox;
        },
        alignElements: function() {
            var plotArea = this, scale = plotArea.scale, pointer = plotArea.pointer, scaleBox = scale.box, box = pointer.box.clone().wrap(scale.box), plotAreaBox = plotArea.box, diff;
            if (scale.options.vertical) {
                diff = plotAreaBox.center().x - box.center().x;
                scale.reflow(new Box2D(scaleBox.x1 + diff, plotAreaBox.y1, scaleBox.x2 + diff, plotAreaBox.y2));
            } else {
                diff = plotAreaBox.center().y - box.center().y;
                scale.reflow(new Box2D(plotAreaBox.x1, scaleBox.y1 + diff, plotAreaBox.x2, scaleBox.y2 + diff));
            }
            pointer.reflow(plotArea.box);
        },
        render: function() {
            var plotArea = this, options = plotArea.options, scale;
            scale = plotArea.scale = new LinearScale(options.scale);
            plotArea.append(plotArea.scale);
            plotArea.pointer = new LinearPointer(scale, deepExtend({}, options.pointer, {
                animation: {
                    transitions: options.transitions
                }
            }));
            plotArea.append(plotArea.pointer);
        },
        getViewElements: function(view) {
            var plotArea = this, options = plotArea.options.plotArea, childElements = ChartElement.fn.getViewElements.call(plotArea, view), border = options.border || {}, elements = [ view.createRect(plotArea.box, {
                fill: options.background,
                stroke: border.width ? border.color : "",
                strokeWidth: border.width,
                dashType: border.dashType
            }) ];
            append(elements, childElements);
            return elements;
        }
    });
    var Gauge = Widget.extend({
        init: function(element, userOptions) {
            var gauge = this, options, themeOptions, themeName, themes = dataviz.ui.themes || {}, theme;
            Widget.fn.init.call(gauge, element);
            gauge.wrapper = gauge.element;
            gauge._originalOptions = deepExtend({}, userOptions);
            options = deepExtend({}, gauge.options, userOptions);
            themeName = options.theme;
            theme = themes[themeName] || themes[themeName.toLowerCase()];
            themeOptions = themeName && theme ? theme.gauge : {};
            gauge.options = deepExtend({}, themeOptions, options);
            gauge.element.addClass("k-gauge");
            gauge.redraw();
        },
        options: {
            plotArea: {},
            theme: "default",
            pointer: {},
            scale: {},
            gaugeArea: {}
        },
        value: function(value) {
            if (arguments.length === 0) {
                return this._pointers[0].value();
            }
            this._pointers[0].value(value);
        },
        redraw: function() {
            var gauge = this, element = gauge.element, model = gauge._model = gauge._getModel(), viewType = dataviz.ui.defaultView(), view;
            gauge._plotArea = model._plotArea;
            if (viewType) {
                view = gauge._view = viewType.fromModel(model);
                element.css("position", "relative");
                gauge._viewElement = view.renderTo(element[0]);
            }
        },
        svg: function() {
            var model = this._getModel(), view = dataviz.SVGView.fromModel(model);
            return view.render();
        },
        _createModel: function() {
            var gauge = this, options = gauge.options, size = gauge._getSize();
            return new RootElement(deepExtend({
                width: size.width,
                height: size.height,
                transitions: options.transitions
            }, options.gaugeArea));
        },
        _getSize: function() {
            var gauge = this, element = gauge.element, width = element.width(), height = element.height();
            if (!width) {
                width = DEFAULT_WIDTH;
            }
            if (!height) {
                height = DEFAULT_HEIGHT;
            }
            return {
                width: width,
                height: height
            };
        }
    });
    var RadialGauge = Gauge.extend({
        init: function(element, options) {
            var radialGauge = this;
            Gauge.fn.init.call(radialGauge, element, options);
            kendo.notify(radialGauge, dataviz.ui);
        },
        options: {
            name: "RadialGauge",
            transitions: true,
            gaugeArea: {
                background: ""
            }
        },
        _getModel: function() {
            var gauge = this, options = gauge.options, model = gauge._createModel(), plotArea;
            plotArea = model._plotArea = new RadialGaugePlotArea(options);
            gauge._pointers = [ plotArea.pointer ];
            model.append(plotArea);
            model.reflow();
            return model;
        }
    });
    var LinearGauge = Gauge.extend({
        init: function(element, options) {
            var linearGauge = this;
            Gauge.fn.init.call(linearGauge, element, options);
            kendo.notify(linearGauge, dataviz.ui);
        },
        options: {
            name: "LinearGauge",
            transitions: true,
            gaugeArea: {
                background: ""
            },
            scale: {
                vertical: true
            }
        },
        _getModel: function() {
            var gauge = this, options = gauge.options, model = gauge._createModel(), plotArea;
            plotArea = model._plotArea = new LinearGaugePlotArea(options);
            gauge._pointers = [ plotArea.pointer ];
            model.append(plotArea);
            model.reflow();
            return model;
        },
        _getSize: function() {
            var gauge = this, element = gauge.element, width = element.width(), height = element.height(), vertical = gauge.options.scale.vertical;
            if (!width) {
                width = vertical ? DEFAULT_MIN_WIDTH : DEFAULT_WIDTH;
            }
            if (!height) {
                height = vertical ? DEFAULT_HEIGHT : DEFAULT_MIN_HEIGHT;
            }
            return {
                width: width,
                height: height
            };
        }
    });
    function getRange(range, min, max) {
        var from = defined(range.from) ? range.from : MIN_VALUE, to = defined(range.to) ? range.to : MAX_VALUE;
        range.from = math.max(math.min(to, from), min);
        range.to = math.min(math.max(to, from), max);
        return range;
    }
    var RadialPointerAnimationDecorator = animationDecorator(RADIAL_POINTER, RotationAnimation);
    var ArrowPointerAnimationDecorator = animationDecorator(ARROW_POINTER, ArrowAnimation);
    var BarIndicatorAnimationDecorator = animationDecorator(BAR_INDICATOR, BarIndicatorAnimatin);
    // Exports ================================================================
    dataviz.ui.plugin(RadialGauge);
    dataviz.ui.plugin(LinearGauge);
    deepExtend(dataviz, {
        Gauge: Gauge,
        RadialGaugePlotArea: RadialGaugePlotArea,
        LinearGaugePlotArea: LinearGaugePlotArea,
        RadialPointer: RadialPointer,
        LinearPointer: LinearPointer,
        LinearScale: LinearScale,
        RadialScale: RadialScale,
        RadialPointerAnimationDecorator: RadialPointerAnimationDecorator,
        ArrowPointerAnimationDecorator: ArrowPointerAnimationDecorator,
        BarIndicatorAnimationDecorator: BarIndicatorAnimationDecorator
    });
})(window.kendo.jQuery);

(function($, undefined) {
    // Imports ================================================================
    var kendo = window.kendo, Class = kendo.Class, Observable = kendo.Observable, deepExtend = kendo.deepExtend, math = Math, proxy = $.proxy, dataviz = kendo.dataviz, template = kendo.template, defined = dataviz.defined, Chart = dataviz.ui.Chart, Selection = dataviz.Selection, addDuration = dataviz.addDuration, duration = dataviz.duration, last = dataviz.last, renderTemplate = dataviz.renderTemplate, toDate = dataviz.toDate, toTime = dataviz.toTime;
    // Constants =============================================================
    var AUTO_CATEGORY_WIDTH = 28, CHANGE = "change", CSS_PREFIX = "k-", DRAG = "drag", DRAG_END = "dragEnd", NAVIGATOR_PANE = "_navigator", NAVIGATOR_AXIS = NAVIGATOR_PANE, ZOOM_ACCELERATION = 3, ZOOM = "zoom", ZOOM_END = "zoomEnd";
    // Stock chart ===========================================================
    var StockChart = Chart.extend({
        init: function(element, userOptions) {
            $(element).addClass(CSS_PREFIX + "chart");
            Chart.fn.init.call(this, element, userOptions);
        },
        _applyDefaults: function(options, themeOptions) {
            var chart = this, width = chart.element.width() || dataviz.DEFAULT_WIDTH;
            var stockDefaults = {
                axisDefaults: {
                    categoryAxis: {
                        name: "default",
                        field: options.dateField,
                        majorGridLines: {
                            visible: false
                        },
                        labels: {
                            step: 2
                        },
                        majorTicks: {
                            visible: false
                        },
                        maxDateGroups: math.floor(width / AUTO_CATEGORY_WIDTH)
                    }
                }
            };
            if (themeOptions) {
                themeOptions = deepExtend({}, themeOptions, stockDefaults);
            }
            if (!chart._navigator) {
                Navigator.setup(options, themeOptions);
            }
            Chart.fn._applyDefaults.call(chart, options, themeOptions);
        },
        _initDataSource: function(userOptions) {
            var options = userOptions || {}, dataSource = options.dataSource, hasServerFiltering = dataSource && dataSource.serverFiltering, mainAxis = [].concat(options.categoryAxis)[0], naviOptions = options.navigator || {}, select = naviOptions.select, hasSelect = select && select.from && select.to, filter, dummyAxis;
            if (hasServerFiltering && hasSelect) {
                filter = [].concat(dataSource.filter || []);
                dummyAxis = new dataviz.DateCategoryAxis(deepExtend({
                    baseUnit: "fit"
                }, mainAxis, {
                    categories: [ select.from, select.to ]
                }));
                dataSource.filter = Navigator.buildFilter(dummyAxis.options.min, select.to).concat(filter);
            }
            Chart.fn._initDataSource.call(this, userOptions);
        },
        options: {
            name: "StockChart",
            dateField: "date",
            axisDefaults: {
                categoryAxis: {
                    type: "date",
                    baseUnit: "fit",
                    justified: true
                },
                valueAxis: {
                    narrowRange: true,
                    labels: {
                        format: "C"
                    }
                }
            },
            navigator: {
                select: {},
                seriesDefaults: {
                    markers: {
                        visible: false
                    },
                    tooltip: {
                        visible: true,
                        template: "#= kendo.toString(category, 'd') #"
                    },
                    line: {
                        width: 2
                    }
                },
                hint: {},
                visible: true
            },
            tooltip: {
                visible: true
            },
            legend: {
                visible: false
            }
        },
        _redraw: function() {
            var chart = this, navigator = chart._navigator;
            if (navigator && navigator.dataSource) {
                navigator.redrawSlaves();
            } else {
                if (!navigator) {
                    navigator = chart._navigator = new Navigator(chart);
                }
                navigator.filterAxes();
                Chart.fn._redraw.call(chart);
                navigator.redraw();
            }
        },
        _onDataChanged: function() {
            var chart = this;
            Chart.fn._onDataChanged.call(chart);
            chart._dataBound = true;
        },
        _trackSharedTooltip: function(coords) {
            var chart = this, plotArea = chart._plotArea, pane = plotArea.paneByPoint(coords);
            if (pane && pane.options.name === NAVIGATOR_PANE) {
                chart._unsetActivePoint();
            } else {
                Chart.fn._trackSharedTooltip.call(chart, coords);
            }
        },
        destroy: function() {
            var chart = this;
            chart._navigator.destroy();
            Chart.fn.destroy.call(chart);
        }
    });
    var Navigator = Observable.extend({
        init: function(chart) {
            var navi = this;
            navi.chart = chart;
            navi.options = deepExtend({}, navi.options, chart.options.navigator);
            navi._initDataSource();
            if (!defined(navi.options.hint.visible)) {
                navi.options.hint.visible = navi.options.visible;
            }
            chart.bind(DRAG, proxy(navi._drag, navi));
            chart.bind(DRAG_END, proxy(navi._dragEnd, navi));
            chart.bind(ZOOM, proxy(navi._zoom, navi));
            chart.bind(ZOOM_END, proxy(navi._zoomEnd, navi));
        },
        options: {},
        _initDataSource: function() {
            var navi = this, options = navi.options, autoBind = options.autoBind, dsOptions = options.dataSource;
            if (!defined(autoBind)) {
                autoBind = navi.chart.options.autoBind;
            }
            navi._dataChangedHandler = proxy(navi._onDataChanged, navi);
            if (dsOptions) {
                navi.dataSource = kendo.data.DataSource.create(dsOptions).bind(CHANGE, navi._dataChangedHandler);
                if (autoBind) {
                    navi.dataSource.fetch();
                }
            }
        },
        _onDataChanged: function() {
            var navi = this, chart = navi.chart, series = chart.options.series, seriesIx, seriesLength = series.length, categoryAxes = chart.options.categoryAxis, axisIx, axesLength = categoryAxes.length, data = navi.dataSource.view(), currentSeries, currentAxis;
            for (seriesIx = 0; seriesIx < seriesLength; seriesIx++) {
                currentSeries = series[seriesIx];
                if (currentSeries.axis == NAVIGATOR_AXIS && chart.isBindable(currentSeries)) {
                    currentSeries.data = data;
                }
            }
            for (axisIx = 0; axisIx < axesLength; axisIx++) {
                currentAxis = categoryAxes[axisIx];
                if (currentAxis.pane == NAVIGATOR_PANE) {
                    chart._bindCategoryAxis(currentAxis, data);
                }
            }
            if (chart._model) {
                navi.redraw();
                navi.filterAxes();
                if (!chart.options.dataSource || chart.options.dataSource && chart._dataBound) {
                    navi.redrawSlaves();
                }
            }
        },
        destroy: function() {
            var navi = this, dataSource = navi.dataSource;
            if (dataSource) {
                dataSource.unbind(CHANGE, navi._dataChangeHandler);
            }
            if (navi.selection) {
                navi.selection.destroy();
            }
        },
        redraw: function() {
            this._redrawSelf();
            var navi = this, chart = navi.chart, options = navi.options, axis = navi.mainAxis(), axisClone = clone(axis), groups = axis.options.categories, select = navi.options.select || {}, selection = navi.selection, min = groups[0], max = last(groups), from = select.from || min, to = select.to || max;
            if (groups.length > 0) {
                if (selection) {
                    selection.destroy();
                    selection.wrapper.remove();
                }
                // "Freeze" the selection axis position until the next redraw
                axisClone.box = axis.box;
                // TODO: Move selection initialization to PlotArea.redraw
                selection = navi.selection = new Selection(chart, axisClone, {
                    min: min,
                    max: max,
                    from: from,
                    to: to,
                    selectStart: $.proxy(navi._selectStart, navi),
                    select: $.proxy(navi._select, navi),
                    selectEnd: $.proxy(navi._selectEnd, navi),
                    mousewheel: {
                        zoom: "left"
                    }
                });
                if (options.hint.visible) {
                    navi.hint = new NavigatorHint(chart.element, {
                        min: groups[0],
                        max: last(groups),
                        template: options.hint.template,
                        format: options.hint.format
                    });
                }
            }
        },
        _redrawSelf: function(silent) {
            var plotArea = this.chart._plotArea;
            if (plotArea) {
                plotArea.redraw(last(plotArea.panes), silent);
            }
        },
        redrawSlaves: function() {
            var navi = this, chart = navi.chart, plotArea = chart._plotArea, slavePanes = plotArea.panes.slice(0, -1);
            chart._plotArea.redraw(slavePanes);
        },
        _drag: function(e) {
            var navi = this, chart = navi.chart, coords = chart._eventCoordinates(e.originalEvent), navigatorAxis = navi.mainAxis(), inNavigator = navigatorAxis.pane.box.containsPoint(coords), groups = navigatorAxis.options.categories, axis = chart._plotArea.categoryAxis, baseUnit = axis.options.baseUnit, range = e.axisRanges[axis.options.name], selection = navi.selection, selectionDuration = duration(axis.options.min, axis.options.max, axis.options.baseUnit), from, to;
            if (!range || inNavigator) {
                return;
            }
            from = toDate(math.min(math.max(groups[0], range.min), addDuration(dataviz.last(groups), -selectionDuration, baseUnit)));
            to = toDate(math.min(addDuration(from, selectionDuration, baseUnit), dataviz.last(groups)));
            navi.options.select = {
                from: from,
                to: to
            };
            if (navi._liveDrag()) {
                navi.filterAxes();
                navi.redrawSlaves();
            }
            selection.set(from, addDuration(from, selectionDuration + 1, baseUnit));
            navi.showHint(from, to);
        },
        _dragEnd: function() {
            var navi = this;
            navi.filterAxes();
            navi.filterDataSource();
            navi.redrawSlaves();
            if (navi.hint) {
                navi.hint.hide();
            }
        },
        _liveDrag: function() {
            var support = kendo.support, isTouch = support.touch, browser = support.browser, isFirefox = browser.mozilla, isOldIE = browser.msie && browser.version < 9;
            return !isTouch && !isFirefox && !isOldIE;
        },
        readSelection: function() {
            var navi = this, selection = navi.selection, src = selection.options, dst = navi.options.select;
            dst.from = src.from;
            dst.to = src.to;
        },
        filterAxes: function() {
            var navi = this, categories, select = navi.options.select || {}, chart = navi.chart, allAxes = chart.options.categoryAxis, from = select.from, to = select.to, min, max, i, axis;
            for (i = 0; i < allAxes.length; i++) {
                axis = allAxes[i];
                if (axis.name === NAVIGATOR_AXIS) {
                    categories = axis.categories;
                    if (categories && categories.length > 0) {
                        min = toTime(categories[0]);
                        max = toTime(last(categories));
                        from = toTime(from);
                        if (from < min || from > max) {
                            from = min;
                        }
                        to = toTime(to);
                        if (to < min || to > max) {
                            to = max;
                        }
                        break;
                    }
                }
            }
            for (i = 0; i < allAxes.length; i++) {
                axis = allAxes[i];
                if (axis.pane !== NAVIGATOR_PANE) {
                    axis.min = toDate(from);
                    axis.max = toDate(to);
                }
            }
        },
        filterDataSource: function() {
            var navi = this, select = navi.options.select || {}, chart = navi.chart, chartDataSource = chart.dataSource, hasServerFiltering = chartDataSource && chartDataSource.options.serverFiltering, axisOptions;
            if (navi.dataSource && hasServerFiltering) {
                axisOptions = new dataviz.DateCategoryAxis(deepExtend({
                    baseUnit: "fit"
                }, chart.options.categoryAxis[0], {
                    categories: [ select.from, select.to ]
                })).options;
                chartDataSource.filter(Navigator.buildFilter(addDuration(axisOptions.min, -axisOptions.baseUnitStep, axisOptions.baseUnit), addDuration(axisOptions.max, axisOptions.baseUnitStep, axisOptions.baseUnit)));
            }
        },
        _zoom: function(e) {
            var navi = this, chart = navi.chart, delta = e.delta, axis = chart._plotArea.categoryAxis, select = navi.options.select, selection = navi.selection;
            e.originalEvent.preventDefault();
            if (math.abs(delta) > 1) {
                delta *= ZOOM_ACCELERATION;
            }
            if (selection.options.to - selection.options.from > 1) {
                selection.expand(delta);
                navi.readSelection();
            } else {
                axis.options.min = select.from;
                select.from = axis.scaleRange(-e.delta).min;
            }
            if (!kendo.support.touch) {
                navi.filterAxes();
                navi.redrawSlaves();
            }
            selection.set(select.from, select.to);
            navi.showHint(navi.options.select.from, navi.options.select.to);
        },
        _zoomEnd: function(e) {
            this._dragEnd(e);
        },
        showHint: function(from, to) {
            var navi = this, chart = navi.chart, plotArea = chart._plotArea;
            if (navi.hint) {
                navi.hint.show(from, to, plotArea.backgroundBox());
            }
        },
        _selectStart: function(e) {
            var chart = this.chart;
            chart._selectStart.call(chart, e);
        },
        _select: function(e) {
            var navi = this, chart = navi.chart;
            navi.showHint(e.from, e.to);
            chart._select.call(chart, e);
        },
        _selectEnd: function(e) {
            var navi = this, chart = navi.chart;
            if (navi.hint) {
                navi.hint.hide();
            }
            navi.readSelection();
            navi.filterAxes();
            navi.filterDataSource();
            navi.redrawSlaves();
            chart._selectEnd.call(chart, e);
        },
        mainAxis: function() {
            var plotArea = this.chart._plotArea;
            if (plotArea) {
                return plotArea.namedCategoryAxes[NAVIGATOR_AXIS];
            }
        }
    });
    Navigator.setup = function(options, themeOptions) {
        options = options || {};
        themeOptions = themeOptions || {};
        var naviOptions = deepExtend({}, themeOptions.navigator, options.navigator), panes = options.panes = [].concat(options.panes), paneOptions = deepExtend({}, naviOptions.pane, {
            name: NAVIGATOR_PANE
        });
        if (!naviOptions.visible) {
            paneOptions.visible = false;
            paneOptions.height = .1;
        }
        panes.push(paneOptions);
        Navigator.attachAxes(options, naviOptions);
        Navigator.attachSeries(options, naviOptions, themeOptions);
    };
    Navigator.attachAxes = function(options, naviOptions) {
        var categoryAxes, valueAxes;
        categoryAxes = options.categoryAxis = [].concat(options.categoryAxis);
        valueAxes = options.valueAxis = [].concat(options.valueAxis);
        var base = deepExtend({
            type: "date",
            pane: NAVIGATOR_PANE,
            field: naviOptions.dateField,
            roundToBaseUnit: false,
            justified: true,
            tooltip: {
                visible: false
            },
            labels: {
                step: 1
            },
            autoBind: !naviOptions.dataSource,
            autoBaseUnitSteps: {
                minutes: [ 1 ],
                hours: [ 1 ],
                days: [ 1 ],
                weeks: [],
                months: [ 1 ],
                years: [ 1 ]
            },
            _overlap: false
        }, naviOptions.categoryAxis);
        categoryAxes.push(deepExtend({}, base, {
            name: NAVIGATOR_AXIS,
            baseUnit: "fit",
            // TODO: Width based
            maxDateGroups: 200,
            baseUnitStep: "auto",
            labels: {
                visible: false
            },
            majorTicks: {
                visible: false
            }
        }), deepExtend({}, base, {
            name: NAVIGATOR_AXIS + "_labels",
            // TODO: Width based
            maxDateGroups: 20,
            baseUnitStep: "auto",
            autoBaseUnitSteps: {
                minutes: []
            },
            majorTicks: {
                visible: true
            }
        }), deepExtend({}, base, {
            name: NAVIGATOR_AXIS + "_ticks",
            // TODO: Width based
            maxDateGroups: 200,
            majorTicks: {
                visible: true,
                width: .5
            },
            labels: {
                visible: false,
                mirror: true
            }
        }));
        valueAxes.push({
            // TODO: Extend navigaor.valueAxis
            name: NAVIGATOR_AXIS,
            pane: NAVIGATOR_PANE,
            majorGridLines: {
                visible: false
            },
            visible: false
        });
    };
    Navigator.attachSeries = function(options, naviOptions, themeOptions) {
        var series = options.series = options.series || [], navigatorSeries = [].concat(naviOptions.series), seriesColors = themeOptions.seriesColors, defaults = naviOptions.seriesDefaults, i;
        for (i = 0; i < navigatorSeries.length; i++) {
            series.push(deepExtend({
                color: seriesColors[i % seriesColors.length],
                visibleInLegend: false,
                tooltip: {
                    visible: false
                }
            }, defaults, navigatorSeries[i], {
                axis: NAVIGATOR_AXIS,
                categoryAxis: NAVIGATOR_AXIS,
                autoBind: !naviOptions.dataSource
            }));
        }
    };
    Navigator.buildFilter = function(from, to) {
        return [ {
            field: "Date",
            operator: "gte",
            value: toDate(from)
        }, {
            field: "Date",
            operator: "lt",
            value: toDate(to)
        } ];
    };
    var NavigatorHint = Class.extend({
        init: function(container, options) {
            var hint = this;
            hint.options = deepExtend({}, hint.options, options);
            hint.container = container;
            hint.chartPadding = {
                top: parseInt(container.css("paddingTop"), 10),
                left: parseInt(container.css("paddingLeft"), 10)
            };
            hint.template = hint.template;
            if (!hint.template) {
                hint.template = hint.template = renderTemplate("<div class='" + CSS_PREFIX + "navigator-hint' " + "style='display: none; position: absolute; top: 1px; left: 1px;'>" + "<div class='" + CSS_PREFIX + "tooltip'>&nbsp;</div>" + "<div class='" + CSS_PREFIX + "scroll' />" + "</div>");
            }
            hint.element = $(hint.template()).appendTo(container);
        },
        options: {
            format: "{0:d} - {1:d}",
            hideDelay: 500
        },
        show: function(from, to, bbox) {
            var hint = this, middle = toDate(toTime(from) + toTime(to - from) / 2), options = hint.options, text = kendo.format(hint.options.format, from, to), tooltip = hint.element.find("." + CSS_PREFIX + "tooltip"), scroll = hint.element.find("." + CSS_PREFIX + "scroll"), scrollWidth = bbox.width() * .4, minPos = bbox.center().x - scrollWidth, maxPos = bbox.center().x, posRange = maxPos - minPos, range = options.max - options.min, scale = posRange / range, offset = middle - options.min, hintTemplate;
            if (hint._hideTimeout) {
                clearTimeout(hint._hideTimeout);
            }
            if (!hint._visible) {
                hint.element.stop(false, true).css("visibility", "hidden").show();
                hint._visible = true;
            }
            if (options.template) {
                hintTemplate = template(options.template);
                text = hintTemplate({
                    from: from,
                    to: to
                });
            }
            tooltip.text(text).css({
                left: bbox.center().x - tooltip.outerWidth() / 2,
                top: bbox.y1
            });
            scroll.css({
                width: scrollWidth,
                left: minPos + offset * scale,
                top: bbox.y1 + parseInt(tooltip.css("margin-top"), 10) + parseInt(tooltip.css("border-top-width"), 10) + tooltip.height() / 2
            });
            hint.element.css("visibility", "visible");
        },
        hide: function() {
            var hint = this;
            if (hint._hideTimeout) {
                clearTimeout(hint._hideTimeout);
            }
            hint._hideTimeout = setTimeout(function() {
                hint._visible = false;
                hint.element.fadeOut("slow");
            }, hint.options.hideDelay);
        }
    });
    function ClonedObject() {}
    function clone(obj) {
        ClonedObject.prototype = obj;
        return new ClonedObject();
    }
    // Exports ================================================================
    dataviz.ui.plugin(StockChart);
    deepExtend(dataviz, {
        Navigator: Navigator
    });
})(window.kendo.jQuery);

(function($, undefined) {
    // Imports ===============================================================
    var kendo = window.kendo, dataviz = kendo.dataviz, Chart = dataviz.ui.Chart, ObservableArray = kendo.data.ObservableArray, SharedTooltip = dataviz.SharedTooltip, deepExtend = kendo.deepExtend, isArray = $.isArray, inArray = dataviz.inArray, math = Math;
    // Constants =============================================================
    var CSS_PREFIX = "k-", DEAULT_BAR_WIDTH = 150, DEAULT_BULLET_WIDTH = 150, BAR = "bar", BULLET = "bullet", PIE = "pie", NO_CROSSHAIR = [ BAR, BULLET ];
    // Sparkline =============================================================
    var Sparkline = Chart.extend({
        init: function(element, userOptions) {
            var chart = this, stage = chart.stage = $("<span />"), options = userOptions || {};
            element = $(element).addClass(CSS_PREFIX + "sparkline").empty().append(stage);
            chart._initialWidth = math.floor(element.width());
            options = wrapNumber(options);
            if (isArray(options) || options instanceof ObservableArray) {
                options = {
                    seriesDefaults: {
                        data: options
                    }
                };
            }
            if (!options.series) {
                options.series = [ {
                    data: wrapNumber(options.data)
                } ];
            }
            deepExtend(options, {
                seriesDefaults: {
                    type: options.type
                }
            });
            if (inArray(options.series[0].type, NO_CROSSHAIR) || inArray(options.seriesDefaults.type, NO_CROSSHAIR)) {
                options = deepExtend({}, {
                    categoryAxis: {
                        crosshair: {
                            visible: false
                        }
                    }
                }, options);
            }
            Chart.fn.init.call(chart, element, options);
        },
        options: {
            name: "Sparkline",
            chartArea: {
                margin: 2
            },
            axisDefaults: {
                visible: false,
                majorGridLines: {
                    visible: false
                },
                valueAxis: {
                    narrowRange: true
                }
            },
            seriesDefaults: {
                type: "line",
                area: {
                    line: {
                        width: .5
                    }
                },
                bar: {
                    stack: true
                },
                width: .5,
                overlay: {
                    gradient: null
                },
                highlight: {
                    visible: false
                },
                border: {
                    width: 0
                },
                markers: {
                    size: 2,
                    visible: false
                }
            },
            tooltip: {
                visible: true,
                shared: true
            },
            categoryAxis: {
                crosshair: {
                    visible: true,
                    tooltip: {
                        visible: false
                    }
                }
            },
            legend: {
                visible: false
            },
            transitions: false,
            pointWidth: 5
        },
        _modelOptions: function() {
            var chart = this, chartOptions = chart.options, options, width = chart._initialWidth, stage = chart.stage;
            chart.stage[0].innerHTML = "&nbsp;";
            options = deepExtend({
                width: width ? width : chart._autoWidth(),
                height: stage.height(),
                transitions: chartOptions.transitions
            }, chartOptions.chartArea, {
                inline: true,
                align: false
            });
            stage.css({
                width: options.width,
                height: options.height
            });
            return options;
        },
        _createTooltip: function() {
            var chart = this, options = chart.options, element = chart.element, tooltip;
            if (chart._sharedTooltip()) {
                tooltip = new SparklineSharedTooltip(element, chart._plotArea, options.tooltip);
            } else {
                tooltip = Chart.fn._createTooltip.call(chart);
            }
            return tooltip;
        },
        _renderView: function() {
            var chart = this;
            chart.element.empty().append(chart.stage);
            return chart._view.renderTo(chart.stage[0]);
        },
        _autoWidth: function() {
            var chart = this, options = chart.options, margin = dataviz.getSpacing(options.chartArea.margin), series = options.series, dsTotal = chart.dataSource.total(), seriesTotal = 0, width, i, currentSeries;
            for (i = 0; i < series.length; i++) {
                currentSeries = series[i];
                if (currentSeries.type === BAR) {
                    return DEAULT_BAR_WIDTH;
                }
                if (currentSeries.type === BULLET) {
                    return DEAULT_BULLET_WIDTH;
                }
                if (currentSeries.type === PIE) {
                    return chart.stage.height();
                }
                if (currentSeries.data) {
                    seriesTotal = math.max(seriesTotal, currentSeries.data.length);
                }
            }
            width = math.max(dsTotal, seriesTotal) * options.pointWidth;
            if (width > 0) {
                width += margin.left + margin.right;
            }
            return width;
        }
    });
    var SparklineSharedTooltip = SharedTooltip.extend({
        options: {
            animation: {
                duration: 0
            }
        },
        _anchor: function(point, slot) {
            var anchor = SharedTooltip.fn._anchor.call(this, point, slot);
            anchor.y = -this.element.height() - this.options.offset;
            return anchor;
        },
        _hideElement: function() {
            this.element.hide();
        }
    });
    function wrapNumber(x) {
        return typeof x === "number" ? [ x ] : x;
    }
    // Exports ================================================================
    dataviz.ui.plugin(Sparkline);
    deepExtend(dataviz, {});
})(window.kendo.jQuery);

(function() {
    // Imports ================================================================
    var $ = jQuery, doc = document, math = Math, kendo = window.kendo, Class = kendo.Class, dataviz = kendo.dataviz, Box2D = dataviz.Box2D, ExpandAnimation = dataviz.ExpandAnimation, Point2D = dataviz.Point2D, ViewBase = dataviz.ViewBase, ViewElement = dataviz.ViewElement, deepExtend = kendo.deepExtend, defined = dataviz.defined, round = dataviz.round, renderTemplate = dataviz.renderTemplate, rotatePoint = dataviz.rotatePoint, uniqueId = dataviz.uniqueId;
    // Constants ==============================================================
    var BUTT = "butt", CLIP = dataviz.CLIP, COORD_PRECISION = dataviz.COORD_PRECISION, DEFAULT_WIDTH = dataviz.DEFAULT_WIDTH, DEFAULT_HEIGHT = dataviz.DEFAULT_HEIGHT, DEFAULT_FONT = dataviz.DEFAULT_FONT, NONE = "none", RADIAL = "radial", SOLID = "solid", SQUARE = "square", SVG_NS = "http://www.w3.org/2000/svg", SVG_DASH_TYPE = {
        dot: [ 1.5, 3.5 ],
        dash: [ 4, 3.5 ],
        longdash: [ 8, 3.5 ],
        dashdot: [ 3.5, 3.5, 1.5, 3.5 ],
        longdashdot: [ 8, 3.5, 1.5, 3.5 ],
        longdashdotdot: [ 8, 3.5, 1.5, 3.5, 1.5, 3.5 ]
    }, TRANSPARENT = "transparent", UNDEFINED = "undefined";
    // View ===================================================================
    var SVGView = ViewBase.extend({
        init: function(options) {
            var view = this;
            ViewBase.fn.init.call(view, options);
            view.decorators.push(new SVGOverlayDecorator(view), new SVGGradientDecorator(view));
            if (dataviz.ui.Chart) {
                view.decorators.push(new dataviz.BarAnimationDecorator(view), new dataviz.PieAnimationDecorator(view), new dataviz.BubbleAnimationDecorator(view));
            }
            view.decorators.push(new SVGClipAnimationDecorator(view), new dataviz.FadeAnimationDecorator(view));
            if (dataviz.Gauge) {
                view.decorators.push(new dataviz.RadialPointerAnimationDecorator(view), new dataviz.ArrowPointerAnimationDecorator(view), new dataviz.BarIndicatorAnimationDecorator(view));
            }
            view.defsId = uniqueId();
            view.template = SVGView.template;
            view.display = view.options.inline ? "inline" : "block";
            if (!view.template) {
                view.template = SVGView.template = renderTemplate("<?xml version='1.0' ?>" + "<svg xmlns='" + SVG_NS + "' version='1.1' " + "width='#= d.options.width #px' height='#= d.options.height #px' " + "style='position: relative; display: #= d.display #;'>" + "#= d.renderDefinitions() #" + "#= d.renderContent() #</svg>");
            }
        },
        options: {
            width: DEFAULT_WIDTH,
            height: DEFAULT_HEIGHT
        },
        renderTo: function(container) {
            var view = this, viewElement;
            view.setupAnimations();
            dataviz.renderSVG(container, view.render());
            viewElement = container.firstElementChild;
            view.alignToScreen(viewElement);
            view.playAnimations();
            return viewElement;
        },
        renderDefinitions: function() {
            var view = this, id = view.defsId, output = ViewBase.fn.renderDefinitions.call(view);
            return "<defs id='" + id + "'>" + output + "</defs>";
        },
        renderElement: function(element) {
            var view = this, container = doc.createElement("div"), defsCurrent = doc.getElementById(view.defsId), defsElement, domElement;
            dataviz.renderSVG(container, "<?xml version='1.0' ?>" + "<svg xmlns='" + SVG_NS + "' version='1.1'>" + view.renderDefinitions() + element.render() + "</svg>");
            defsElement = container.firstElementChild.firstChild;
            domElement = container.firstElementChild.lastChild;
            if (defsCurrent && defsCurrent.textContent !== defsElement.textContent) {
                defsCurrent.parentNode.replaceChild(defsElement, defsCurrent);
            }
            return domElement;
        },
        createGroup: function(options) {
            return this.decorate(new SVGGroup(options));
        },
        createText: function(content, options) {
            return this.decorate(new SVGText(content, options));
        },
        createRect: function(box, style) {
            return this.decorate(new SVGLine(box.points(), true, this.setDefaults(style)));
        },
        createLine: function(x1, y1, x2, y2, options) {
            return this.decorate(new SVGLine([ new Point2D(x1, y1), new Point2D(x2, y2) ], false, this.setDefaults(options)));
        },
        createPolyline: function(points, closed, options) {
            return this.decorate(new SVGLine(points, closed, this.setDefaults(options)));
        },
        createCircle: function(center, radius, options) {
            return this.decorate(new SVGCircle(center, radius, options));
        },
        createSector: function(sector, options) {
            return this.decorate(new SVGSector(sector, options));
        },
        createRing: function(ring, options) {
            return this.decorate(new SVGRing(ring, options));
        },
        createPin: function(pin, options) {
            return this.decorate(new SVGPin(pin, options));
        },
        createGradient: function(options) {
            if (options.type === RADIAL) {
                if (defined(options.ir)) {
                    return new SVGDonutGradient(options);
                } else {
                    return new SVGRadialGradient(options);
                }
            } else {
                return new SVGLinearGradient(options);
            }
        },
        alignToScreen: function(element) {
            var ctm;
            try {
                ctm = element.getScreenCTM ? element.getScreenCTM() : null;
            } catch (e) {}
            if (ctm) {
                var left = -ctm.e % 1, top = -ctm.f % 1, style = element.style;
                if (left !== 0 || top !== 0) {
                    style.left = left + "px";
                    style.top = top + "px";
                }
            }
        }
    });
    SVGView.fromModel = function(model) {
        var view = new SVGView(model.options);
        [].push.apply(view.children, model.getViewElements(view));
        return view;
    };
    SVGView.available = dataviz.supportsSVG;
    SVGView.preference = 100;
    dataviz.ui.registerView(SVGView);
    var SVGText = ViewElement.extend({
        init: function(content, options) {
            var text = this;
            ViewElement.fn.init.call(text, options);
            text.content = content;
            text.template = SVGText.template;
            if (!text.template) {
                text.template = SVGText.template = renderTemplate("<text #= d.renderId() # " + "#= d.renderDataAttributes() # " + "x='#= Math.round(d.options.x) #' " + "y='#= Math.round(d.options.y + d.options.baseline) #' " + "fill-opacity='#= d.options.fillOpacity #' " + "#= d.options.rotation ? d.renderRotation() : '' # " + "style='font: #= d.options.font #' fill='#= d.options.color #'>" + "#= d.content #</text>");
            }
        },
        options: {
            x: 0,
            y: 0,
            baseline: 0,
            font: DEFAULT_FONT,
            size: {
                width: 0,
                height: 0
            },
            fillOpacity: 1
        },
        refresh: function(domElement) {
            var options = this.options;
            $(domElement).attr({
                "fill-opacity": options.fillOpacity
            });
        },
        clone: function() {
            var text = this;
            return new SVGText(text.content, deepExtend({}, text.options));
        },
        renderRotation: function() {
            var text = this, options = text.options, size = options.size, cx = round(options.x + size.normalWidth / 2, COORD_PRECISION), cy = round(options.y + size.normalHeight / 2, COORD_PRECISION), rcx = round(options.x + size.width / 2, COORD_PRECISION), rcy = round(options.y + size.height / 2, COORD_PRECISION), offsetX = round(rcx - cx, COORD_PRECISION), offsetY = round(rcy - cy, COORD_PRECISION);
            return "transform='translate(" + offsetX + "," + offsetY + ") " + "rotate(" + options.rotation + "," + cx + "," + cy + ")'";
        }
    });
    var SVGPath = ViewElement.extend({
        init: function(options) {
            var path = this;
            ViewElement.fn.init.call(path, options);
            path.template = SVGPath.template;
            if (!path.template) {
                path.template = SVGPath.template = renderTemplate("<path #= d.renderId() #" + "style='display: #= d.renderDisplay() #' " + "#= d.renderDataAttributes() # " + "d='#= d.renderPoints() #' " + '#= d.renderAttr("stroke", d.options.stroke) # ' + '#= d.renderAttr("stroke-width", d.options.strokeWidth) #' + "#= d.renderDashType() # " + "stroke-linecap='#= d.renderLinecap() #' " + "stroke-linejoin='round' " + "fill-opacity='#= d.options.fillOpacity #' " + "stroke-opacity='#= d.options.strokeOpacity #' " + "fill='#= d.renderFill() #'></path>");
            }
        },
        options: {
            fill: "",
            fillOpacity: 1,
            strokeOpacity: 1,
            rotation: [ 0, 0, 0 ],
            strokeLineCap: SQUARE,
            visible: true
        },
        refresh: function(domElement) {
            var options = this.options;
            $(domElement).attr({
                d: this.renderPoints(),
                "fill-opacity": options.fillOpacity,
                "stroke-opacity": options.strokeOpacity
            }).css("display", this.renderDisplay());
        },
        clone: function() {
            return new SVGPath(deepExtend({}, this.options));
        },
        renderPoints: function() {},
        renderDashType: function() {
            var path = this, options = path.options;
            return renderSVGDash(options.dashType, options.strokeWidth);
        },
        renderLinecap: function() {
            var options = this.options, dashType = options.dashType, strokeLineCap = options.strokeLineCap;
            return dashType && dashType != SOLID ? BUTT : strokeLineCap;
        },
        renderFill: function() {
            var fill = this.options.fill;
            if (fill && fill !== TRANSPARENT) {
                return fill;
            }
            return NONE;
        },
        renderDisplay: function() {
            return this.options.visible ? "block" : "none";
        },
        destroy: function() {}
    });
    var SVGLine = SVGPath.extend({
        init: function(points, closed, options) {
            var line = this;
            SVGPath.fn.init.call(line, options);
            line.points = points;
            line.closed = closed;
        },
        renderPoints: function() {
            var line = this, points = line.points, i, count = points.length, rotate = function(point) {
                var rotation = line.options.rotation;
                return rotatePoint(point.x, point.y, rotation[1], rotation[2], -rotation[0]);
            }, result = "M" + line._print(rotate(points[0]));
            for (i = 1; i < count; i++) {
                result += " " + line._print(rotate(points[i]));
            }
            if (line.closed) {
                result += " z";
            }
            return result;
        },
        clone: function() {
            var line = this;
            return new SVGLine(deepExtend([], line.points), line.closed, deepExtend({}, line.options));
        },
        _print: function(point) {
            var line = this, options = line.options, strokeWidth = options.strokeWidth, shouldAlign = options.align !== false && strokeWidth && strokeWidth % 2 !== 0, align = shouldAlign ? alignToPixel : round;
            return align(point.x, COORD_PRECISION) + " " + align(point.y, COORD_PRECISION);
        }
    });
    var SVGRing = SVGPath.extend({
        init: function(config, options) {
            var ring = this;
            SVGPath.fn.init.call(ring, options);
            ring.pathTemplate = SVGRing.pathTemplate;
            if (!ring.pathTemplate) {
                ring.pathTemplate = SVGRing.pathTemplate = renderTemplate("M #= d.firstOuterPoint.x # #= d.firstOuterPoint.y # " + "A#= d.r # #= d.r # " + "0 #= d.isReflexAngle ? '1' : '0' #,1 " + "#= d.secondOuterPoint.x # #= d.secondOuterPoint.y # " + "L #= d.secondInnerPoint.x # #= d.secondInnerPoint.y # " + "A#= d.ir # #= d.ir # " + "0 #= d.isReflexAngle ? '1' : '0' #,0 " + "#= d.firstInnerPoint.x # #= d.firstInnerPoint.y # z");
            }
            ring.config = config || {};
        },
        renderPoints: function() {
            var ring = this, ringConfig = ring.config, startAngle = ringConfig.startAngle, endAngle = ringConfig.angle + startAngle, isReflexAngle = endAngle - startAngle > 180, r = math.max(ringConfig.r, 0), ir = math.max(ringConfig.ir, 0), center = ringConfig.c, firstOuterPoint = ringConfig.point(startAngle), firstInnerPoint = ringConfig.point(startAngle, true), secondOuterPoint, secondInnerPoint;
            endAngle = endAngle - startAngle === 360 ? endAngle - .002 : endAngle;
            secondOuterPoint = ringConfig.point(endAngle);
            secondInnerPoint = ringConfig.point(endAngle, true);
            return ring.pathTemplate({
                firstOuterPoint: firstOuterPoint,
                secondOuterPoint: secondOuterPoint,
                isReflexAngle: isReflexAngle,
                r: r,
                ir: ir,
                cx: center.x,
                cy: center.y,
                firstInnerPoint: firstInnerPoint,
                secondInnerPoint: secondInnerPoint
            });
        },
        clone: function() {
            var ring = this;
            return new SVGRing(deepExtend({}, ring.config), deepExtend({}, ring.options));
        }
    });
    var SVGPin = SVGPath.extend({
        init: function(config, options) {
            var pin = this;
            SVGPath.fn.init.call(pin, options);
            pin.pathTemplate = SVGPin.pathTemplate;
            if (!pin.pathTemplate) {
                pin.pathTemplate = SVGPin.pathTemplate = renderTemplate("M #= d.origin.x # #= d.origin.y # " + "#= d.as.x # #= d.as.y # " + "A#= d.r # #= d.r # " + "0 #= d.isReflexAngle ? '1' : '0' #,0 " + "#= d.ae.x # #= d.ae.y # " + "z");
            }
            pin.config = config || new dataviz.Pin();
        },
        renderPoints: function() {
            var pin = this, config = pin.config, r = config.radius, degrees = math.PI / 180, arcAngle = config.arcAngle, halfChordLength = r * math.sin(arcAngle * degrees / 2), height = config.height - r * (1 - math.cos(arcAngle * degrees / 2)), origin = config.origin, arcStart = {
                x: origin.x + halfChordLength,
                y: origin.y - height
            }, arcEnd = {
                x: origin.x - halfChordLength,
                y: origin.y - height
            }, rotate = function(point, inclinedPoint) {
                var rotation = pin.options.rotation, inclination = config.rotation;
                point = rotatePoint(point.x, point.y, rotation[1], rotation[2], -rotation[0]);
                if (inclinedPoint) {
                    point = rotatePoint(point.x, point.y, origin.x, origin.y, inclination);
                }
                return point;
            };
            origin = rotate(origin);
            return pin.pathTemplate({
                origin: origin,
                as: rotate(arcStart, true),
                ae: rotate(arcEnd, true),
                r: r,
                isReflexAngle: arcAngle > 180
            });
        }
    });
    var SVGSector = SVGRing.extend({
        init: function(config, options) {
            var sector = this;
            SVGRing.fn.init.call(sector, config, options);
            sector.pathTemplate = SVGSector.pathTemplate;
            if (!sector.pathTemplate) {
                sector.pathTemplate = SVGSector.pathTemplate = renderTemplate("M #= d.firstOuterPoint.x # #= d.firstOuterPoint.y # " + "A#= d.r # #= d.r # " + "0 #= d.isReflexAngle ? '1' : '0' #,1 " + "#= d.secondOuterPoint.x # #= d.secondOuterPoint.y # " + "L #= d.cx # #= d.cy # z");
            }
        },
        options: {
            fill: "",
            fillOpacity: 1,
            strokeOpacity: 1,
            strokeLineCap: SQUARE
        },
        clone: function() {
            var sector = this;
            return new SVGSector(deepExtend({}, sector.config), deepExtend({}, sector.options));
        }
    });
    var SVGCircle = ViewElement.extend({
        init: function(c, r, options) {
            var circle = this;
            ViewElement.fn.init.call(circle, options);
            circle.c = c;
            circle.r = r;
            circle.template = SVGCircle.template;
            if (!circle.template) {
                circle.template = SVGCircle.template = renderTemplate("<circle #= d.renderId() # " + "#= d.renderDataAttributes() #" + "cx='#= d.c.x #' cy='#= d.c.y #' " + "r='#= d.r #' " + '#= d.renderAttr("stroke", d.options.stroke) # ' + '#= d.renderAttr("stroke-width", d.options.strokeWidth) #' + "fill-opacity='#= d.options.fillOpacity #' " + "stroke-opacity='#= d.options.strokeOpacity #'  " + "fill='#= d.options.fill || \"none\" #'></circle>");
            }
        },
        options: {
            fill: "",
            fillOpacity: 1,
            strokeOpacity: 1
        },
        refresh: function(domElement) {
            $(domElement).attr({
                r: math.max(0, this.r),
                "fill-opacity": this.options.fillOpacity
            });
        },
        clone: function() {
            var circle = this;
            return new SVGCircle(deepExtend({}, circle.c), circle.r, deepExtend({}, circle.options));
        }
    });
    var SVGGroup = ViewElement.extend({
        init: function(options) {
            var group = this;
            ViewElement.fn.init.call(group, options);
            group.template = SVGGroup.template;
            if (!group.template) {
                group.template = SVGGroup.template = renderTemplate("<g#= d.renderId() #" + "#= d.renderDataAttributes() #" + '#= d.renderAttr("clip-path", d.options.clipPath) #>' + "#= d.renderContent() #</g>");
            }
        }
    });
    var SVGClipPath = ViewElement.extend({
        init: function(options) {
            var clip = this;
            ViewElement.fn.init.call(clip, options);
            clip.template = SVGClipPath.template;
            if (!clip.template) {
                clip.template = SVGClipPath.template = renderTemplate('<clipPath#= d.renderAttr("id", d.options.id) #>' + "#= d.renderContent() #</clipPath>");
            }
        }
    });
    var SVGGradient = ViewElement.extend({
        init: function(options) {
            var gradient = this;
            ViewElement.fn.init.call(gradient, options);
        },
        options: {
            id: ""
        },
        renderStops: function() {
            var gradient = this, stops = gradient.options.stops, stopTemplate = gradient.stopTemplate, i, length = stops.length, currentStop, output = "";
            for (i = 0; i < length; i++) {
                currentStop = stops[i];
                output += stopTemplate(currentStop);
            }
            return output;
        }
    });
    var SVGLinearGradient = SVGGradient.extend({
        init: function(options) {
            var gradient = this;
            SVGGradient.fn.init.call(gradient, options);
            gradient.template = SVGLinearGradient.template;
            gradient.stopTemplate = SVGLinearGradient.stopTemplate;
            if (!gradient.template) {
                gradient.template = SVGLinearGradient.template = renderTemplate("<linearGradient id='#= d.options.id #' " + "gradientTransform='rotate(#= d.options.rotation #)'> " + "#= d.renderStops() #" + "</linearGradient>");
                gradient.stopTemplate = SVGLinearGradient.stopTemplate = renderTemplate("<stop offset='#= Math.round(d.offset * 100) #%' " + "style='stop-color:#= d.color #;stop-opacity:#= d.opacity #' />");
            }
        },
        options: {
            rotation: 0
        }
    });
    var SVGRadialGradient = SVGGradient.extend({
        init: function(options) {
            var gradient = this;
            SVGGradient.fn.init.call(gradient, options);
            gradient.template = SVGRadialGradient.template;
            gradient.stopTemplate = SVGRadialGradient.stopTemplate;
            if (!gradient.template) {
                gradient.template = SVGRadialGradient.template = renderTemplate("<radialGradient id='#= d.options.id #' " + "cx='#= d.options.cx #' cy='#= d.options.cy #' " + "fx='#= d.options.cx #' fy='#= d.options.cy #' " + "r='#= d.options.r #' gradientUnits='userSpaceOnUse'>" + "#= d.renderStops() #" + "</radialGradient>");
                gradient.stopTemplate = SVGRadialGradient.stopTemplate = renderTemplate("<stop offset='#= Math.round(d.offset * 100) #%' " + "style='stop-color:#= d.color #;stop-opacity:#= d.opacity #' />");
            }
        }
    });
    var SVGDonutGradient = ViewElement.extend({
        init: function(options) {
            var gradient = this;
            ViewElement.fn.init.call(gradient, options);
            gradient.template = SVGDonutGradient.template;
            gradient.stopTemplate = SVGDonutGradient.stopTemplate;
            if (!gradient.template) {
                gradient.template = SVGDonutGradient.template = renderTemplate("<radialGradient id='#= d.options.id #' " + "cx='#= d.options.cx #' cy='#= d.options.cy #' " + "fx='#= d.options.cx #' fy='#= d.options.cy #' " + "r='#= d.options.r #' gradientUnits='userSpaceOnUse'>" + "#= d.renderStops() #" + "</radialGradient>");
                gradient.stopTemplate = SVGDonutGradient.stopTemplate = renderTemplate("<stop offset='#= d.offset #%' " + "style='stop-color:#= d.color #;stop-opacity:#= d.opacity #' />");
            }
        },
        options: {
            id: ""
        },
        renderStops: function() {
            var gradient = this, options = gradient.options, stops = options.stops, stopTemplate = gradient.stopTemplate, usedSpace = options.ir / options.r * 100, i, length = stops.length, currentStop, output = "";
            currentStop = deepExtend({}, stops[0]);
            currentStop.offset = usedSpace;
            output += stopTemplate(currentStop);
            for (i = 1; i < length; i++) {
                currentStop = deepExtend({}, stops[i]);
                currentStop.offset = currentStop.offset * (100 - usedSpace) + usedSpace;
                output += stopTemplate(currentStop);
            }
            return output;
        }
    });
    // Decorators =============================================================
    function SVGOverlayDecorator(view) {
        this.view = view;
    }
    SVGOverlayDecorator.prototype = {
        decorate: function(element) {
            var decorator = this, view = decorator.view, options = element.options, id = options.id, group, overlay;
            if (options.overlay) {
                element.options.id = uniqueId();
                group = view.createGroup();
                overlay = element.clone();
                group.children.push(element, overlay);
                overlay.options.id = id;
                overlay.options.fill = options.overlay;
                return group;
            } else {
                return element;
            }
        }
    };
    function SVGGradientDecorator(view) {
        this.view = view;
    }
    SVGGradientDecorator.prototype = {
        decorate: function(element) {
            var decorator = this, options = element.options;
            options.fill = decorator.getPaint(options.fill);
            return element;
        },
        getPaint: function(paint) {
            var decorator = this, view = decorator.view, definitions = view.definitions, overlay, overlayId, gradient;
            if (paint && defined(paint.gradient)) {
                overlay = view.buildGradient(paint);
                if (overlay) {
                    overlayId = overlay.id;
                    gradient = definitions[overlayId];
                    if (!gradient) {
                        gradient = view.createGradient(overlay);
                        definitions[overlayId] = gradient;
                    }
                    return "url(" + decorator.baseUrl() + "#" + gradient.options.id + ")";
                } else {
                    return NONE;
                }
            } else {
                return paint;
            }
        },
        baseUrl: function() {
            var base = doc.getElementsByTagName("base")[0], baseUrl = "", href = doc.location.href, hashIndex = href.indexOf("#");
            if (base && !kendo.support.browser.msie) {
                if (hashIndex !== -1) {
                    href = href.substring(0, hashIndex);
                }
                baseUrl = href;
            }
            return baseUrl;
        }
    };
    var SVGClipAnimationDecorator = Class.extend({
        init: function(view) {
            this.view = view;
        },
        decorate: function(element) {
            var decorator = this, view = decorator.view, clipId = decorator.clipId, options = view.options, animation = element.options.animation, definitions = view.definitions, clipPath, clipRect;
            if (animation && animation.type === CLIP && options.transitions) {
                if (!clipId) {
                    decorator.clipId = clipId = uniqueId();
                }
                clipPath = definitions[clipId];
                if (!clipPath) {
                    clipPath = new SVGClipPath({
                        id: clipId
                    });
                    clipRect = view.createRect(new Box2D(0, 0, options.width, options.height), {
                        id: uniqueId()
                    });
                    clipPath.children.push(clipRect);
                    definitions[clipId] = clipPath;
                    view.animations.push(new ExpandAnimation(clipRect, {
                        size: options.width
                    }));
                }
                element.options.clipPath = "url(#" + clipId + ")";
            }
            return element;
        }
    });
    // Helpers ================================================================
    function alignToPixel(coord) {
        return math.round(coord) + .5;
    }
    function renderSVGDash(dashType, strokeWidth) {
        var result = [], dashTypeArray, i;
        dashType = dashType ? dashType.toLowerCase() : null;
        if (dashType && dashType != SOLID) {
            dashTypeArray = SVG_DASH_TYPE[dashType];
            for (i = 0; i < dashTypeArray.length; i++) {
                result.push(dashTypeArray[i] * (strokeWidth || 1));
            }
            return "stroke-dasharray='" + result.join(" ") + "' ";
        }
        return "";
    }
    var renderSVG = function(container, svg) {
        container.innerHTML = svg;
    };
    (function() {
        var testFragment = "<svg xmlns='" + SVG_NS + "'></svg>", testContainer = doc.createElement("div"), hasParser = typeof DOMParser != UNDEFINED;
        testContainer.innerHTML = testFragment;
        if (hasParser && testContainer.firstChild.namespaceURI != SVG_NS) {
            renderSVG = function(container, svg) {
                var parser = new DOMParser(), chartDoc = parser.parseFromString(svg, "text/xml"), importedDoc = doc.adoptNode(chartDoc.documentElement);
                container.innerHTML = "";
                container.appendChild(importedDoc);
            };
        }
    })();
    // Exports ================================================================
    deepExtend(dataviz, {
        renderSVG: renderSVG,
        SVGCircle: SVGCircle,
        SVGClipAnimationDecorator: SVGClipAnimationDecorator,
        SVGClipPath: SVGClipPath,
        SVGGradientDecorator: SVGGradientDecorator,
        SVGGroup: SVGGroup,
        SVGLine: SVGLine,
        SVGLinearGradient: SVGLinearGradient,
        SVGOverlayDecorator: SVGOverlayDecorator,
        SVGPath: SVGPath,
        SVGRadialGradient: SVGRadialGradient,
        SVGDonutGradient: SVGDonutGradient,
        SVGRing: SVGRing,
        SVGSector: SVGSector,
        SVGText: SVGText,
        SVGView: SVGView
    });
})(window.kendo.jQuery);

(function() {
    // Imports ================================================================
    var $ = jQuery, doc = document, math = Math, kendo = window.kendo, Class = kendo.Class, deepExtend = kendo.deepExtend, dataviz = kendo.dataviz, Color = dataviz.Color, Box2D = dataviz.Box2D, Point2D = dataviz.Point2D, ExpandAnimation = dataviz.ExpandAnimation, ViewBase = dataviz.ViewBase, ViewElement = dataviz.ViewElement, defined = dataviz.defined, renderTemplate = dataviz.renderTemplate, uniqueId = dataviz.uniqueId, rotatePoint = dataviz.rotatePoint, round = dataviz.round, supportsSVG = dataviz.supportsSVG;
    // Constants ==============================================================
    var BLACK = "#000", CLIP = dataviz.CLIP, COORD_PRECISION = dataviz.COORD_PRECISION, DEFAULT_WIDTH = dataviz.DEFAULT_WIDTH, DEFAULT_HEIGHT = dataviz.DEFAULT_HEIGHT, DEFAULT_FONT = dataviz.DEFAULT_FONT, OBJECT = "object", LINEAR = "linear", RADIAL = "radial", TRANSPARENT = "transparent";
    // View ===================================================================
    var VMLView = ViewBase.extend({
        init: function(options) {
            var view = this;
            ViewBase.fn.init.call(view, options);
            view.decorators.push(new VMLOverlayDecorator(view), new VMLGradientDecorator(view));
            if (dataviz.ui.Chart) {
                view.decorators.push(new dataviz.BarAnimationDecorator(view), new dataviz.PieAnimationDecorator(view), new dataviz.BubbleAnimationDecorator(view));
            }
            view.decorators.push(new VMLClipAnimationDecorator(view));
            if (!isIE9CompatibilityView()) {
                // Setting opacity on VML elements is broken in
                // IE9 Compatibility View
                view.decorators.push(new dataviz.FadeAnimationDecorator(view));
            }
            if (dataviz.Gauge) {
                view.decorators.push(new dataviz.RadialPointerAnimationDecorator(view), new dataviz.ArrowPointerAnimationDecorator(view), new dataviz.BarIndicatorAnimationDecorator(view));
            }
            view.template = VMLView.template;
            view.tagName = view.options.inline ? "span" : "div";
            if (!view.template) {
                view.template = VMLView.template = renderTemplate("<#= d.tagName # style='width:#= d.options.width #px; " + "height:#= d.options.height #px; " + "position: relative;'>" + "#= d.renderContent() #</#= d.tagName #>");
            }
        },
        options: {
            width: DEFAULT_WIDTH,
            height: DEFAULT_HEIGHT
        },
        renderTo: function(container) {
            var view = this;
            if (doc.namespaces) {
                doc.namespaces.add("kvml", "urn:schemas-microsoft-com:vml", "#default#VML");
            }
            view.setupAnimations();
            container.innerHTML = view.render();
            view.playAnimations();
            return container.firstChild;
        },
        renderElement: function(element) {
            var container = doc.createElement("div"), domElement;
            container.style.display = "none";
            doc.body.appendChild(container);
            container.innerHTML = element.render();
            domElement = container.firstChild;
            doc.body.removeChild(container);
            return domElement;
        },
        createText: function(content, options) {
            return this.decorate(options && options.rotation ? new VMLRotatedText(content, options) : new VMLText(content, options));
        },
        createRect: function(box, style) {
            return this.decorate(new VMLLine(box.points(), true, this.setDefaults(style)));
        },
        createLine: function(x1, y1, x2, y2, options) {
            return this.decorate(new VMLLine([ new Point2D(x1, y1), new Point2D(x2, y2) ], false, this.setDefaults(options)));
        },
        createPolyline: function(points, closed, options) {
            return this.decorate(new VMLLine(points, closed, this.setDefaults(options)));
        },
        createCircle: function(center, radius, options) {
            return this.decorate(new VMLCircle(center, radius, options));
        },
        createSector: function(sector, options) {
            return this.decorate(new VMLSector(sector, options));
        },
        createRing: function(ring, options) {
            return this.decorate(new VMLRing(ring, this.setDefaults(options)));
        },
        createGroup: function(options) {
            return this.decorate(new VMLGroup(this.setDefaults(options)));
        },
        createGradient: function(options) {
            var validRadial = defined(options.cx) && defined(options.cy) && defined(options.bbox);
            if (options.type === RADIAL && validRadial) {
                return new VMLRadialGradient(options);
            } else if (options.type === LINEAR) {
                return new VMLLinearGradient(options);
            } else {
                return BLACK;
            }
        }
    });
    VMLView.fromModel = function(model) {
        var view = new VMLView(model.options);
        [].push.apply(view.children, model.getViewElements(view));
        return view;
    };
    VMLView.available = function() {
        return kendo.support.browser.msie;
    };
    VMLView.preference = 50;
    dataviz.ui.registerView(VMLView);
    // Primitives =============================================================
    var VMLText = ViewElement.extend({
        init: function(content, options) {
            var text = this;
            ViewElement.fn.init.call(text, options);
            text.content = content;
            text.template = VMLText.template;
            if (!text.template) {
                text.template = VMLText.template = renderTemplate("<kvml:textbox #= d.renderId() # " + "#= d.renderDataAttributes() #" + "style='position: absolute; " + "left: #= d.options.x #px; top: #= d.options.y #px; " + "font: #= d.options.font #; color: #= d.options.color #; " + "visibility: #= d.renderVisibility() #; white-space: nowrap;'>" + "#= d.content #</kvml:textbox>");
            }
        },
        options: {
            x: 0,
            y: 0,
            font: DEFAULT_FONT,
            color: BLACK,
            fillOpacity: 1
        },
        refresh: function(domElement) {
            $(domElement).css("visibility", this.renderVisibility());
        },
        clone: function() {
            var text = this;
            return new VMLText(text.content, deepExtend({}, text.options));
        },
        renderVisibility: function() {
            return this.options.fillOpacity > 0 ? "visible" : "hidden";
        }
    });
    var VMLRotatedText = ViewElement.extend({
        init: function(content, options) {
            var text = this;
            ViewElement.fn.init.call(text, options);
            text.content = content;
            text.template = VMLRotatedText.template;
            if (!text.template) {
                text.template = VMLRotatedText.template = renderTemplate("<kvml:shape #= d.renderId() # " + "#= d.renderDataAttributes() #" + "style='position: absolute; top: 0px; left: 0px; " + "width: 1px; height: 1px;' stroked='false' coordsize='1,1'>" + "#= d.renderPath() #" + "<kvml:fill color='#= d.options.color #' />" + "<kvml:textpath on='true' style='font: #= d.options.font #;' " + "fitpath='false' string='#= d.content #' /></kvml:shape>");
            }
        },
        options: {
            x: 0,
            y: 0,
            font: DEFAULT_FONT,
            color: BLACK,
            size: {
                width: 0,
                height: 0
            }
        },
        renderPath: function() {
            var text = this, options = text.options, width = options.size.width, height = options.size.height, cx = options.x + width / 2, cy = options.y + height / 2, angle = -options.rotation, r1 = rotatePoint(options.x, cy, cx, cy, angle), r2 = rotatePoint(options.x + width, cy, cx, cy, angle);
            return "<kvml:path textpathok='true' " + "v='m " + round(r1.x) + "," + round(r1.y) + " l " + round(r2.x) + "," + round(r2.y) + "' />";
        }
    });
    var VMLStroke = ViewElement.extend({
        init: function(options) {
            var stroke = this;
            ViewElement.fn.init.call(stroke, options);
            stroke.template = VMLStroke.template;
            if (!stroke.template) {
                stroke.template = VMLStroke.template = renderTemplate("<kvml:stroke on='#= !!d.options.stroke #' " + '#= d.renderAttr("color", d.options.stroke) #' + '#= d.renderAttr("weight", d.options.strokeWidth) #' + '#= d.renderAttr("dashstyle", d.options.dashType) #' + '#= d.renderAttr("opacity", d.options.strokeOpacity) # />');
            }
        },
        refresh: function(domElement) {
            try {
                domElement.opacity = this.options.strokeOpacity;
            } catch (e) {}
        }
    });
    var VMLFill = ViewElement.extend({
        init: function(options) {
            var stroke = this;
            ViewElement.fn.init.call(stroke, options);
            stroke.template = VMLFill.template;
            if (!stroke.template) {
                stroke.template = VMLFill.template = renderTemplate("<kvml:fill on='#= d.isEnabled() #' " + '#= d.renderAttr("color", d.options.fill) #' + '#= d.renderAttr("weight", d.options.fillWidth) #' + '#= d.renderAttr("opacity", d.options.fillOpacity) # />');
            }
        },
        isEnabled: function() {
            var fill = this.options.fill;
            return !!fill && fill.toLowerCase() !== TRANSPARENT;
        },
        refresh: function(domElement) {
            try {
                domElement.opacity = this.options.fillOpacity;
            } catch (e) {}
        }
    });
    var VMLPath = ViewElement.extend({
        init: function(options) {
            var path = this;
            ViewElement.fn.init.call(path, options);
            path.template = VMLPath.template;
            if (!path.template) {
                path.template = VMLPath.template = renderTemplate("<kvml:shape #= d.renderId() # " + "#= d.renderDataAttributes() #" + "style='position:absolute; #= d.renderSize() # display:#= d.renderDisplay() #;' " + "coordorigin='0 0' #= d.renderCoordsize() #>" + "<kvml:path v='#= d.renderPoints() # e' />" + "#= d.fill.render() + d.stroke.render() #" + "</kvml:shape>");
            }
            path.stroke = new VMLStroke(path.options);
            path.fill = new VMLFill(path.options);
        },
        options: {
            fill: "",
            fillOpacity: 1,
            strokeOpacity: 1,
            rotation: [ 0, 0, 0 ],
            visible: true
        },
        renderCoordsize: function() {
            var scale = this.options.align === false ? 1e4 : 1;
            return "coordsize='" + scale + " " + scale + "'";
        },
        renderSize: function() {
            var scale = this.options.align === false ? 100 : 1;
            return "width:" + scale + "px; height:" + scale + "px;";
        },
        render: function() {
            var path = this;
            path.fill.options.fillOpacity = path.options.fillOpacity;
            path.stroke.options.strokeOpacity = path.options.strokeOpacity;
            return ViewElement.fn.render.call(path);
        },
        renderDisplay: function() {
            return this.options.visible ? "block" : "none";
        },
        renderPoints: function() {},
        refresh: function(domElement) {
            if (!domElement) {
                return;
            }
            var path = this, element = $(domElement), parentNode = element[0].parentNode, fill = path.fill, stroke = path.stroke;
            if (parentNode) {
                element.find("path")[0].v = this.renderPoints();
                fill.options = stroke.options = path.options;
                fill.refresh(element.find("fill")[0]);
                stroke.refresh(element.find("stroke")[0]);
                element.css("display", path.renderDisplay());
                // Force redraw in order to remove artifacts in IE < 7
                parentNode.style.cssText = parentNode.style.cssText;
            }
        }
    });
    var VMLLine = VMLPath.extend({
        init: function(points, closed, options) {
            var line = this;
            VMLPath.fn.init.call(line, options);
            line.points = points;
            line.closed = closed;
        },
        renderPoints: function() {
            var line = this, points = line.points, i, count = points.length, rotate = function(point) {
                var rotation = line.options.rotation;
                return rotatePoint(point.x, point.y, rotation[1], rotation[2], -rotation[0]);
            }, result = "m " + line._print(rotate(points[0]));
            if (count > 1) {
                result += " l ";
                for (i = 1; i < count; i++) {
                    result += line._print(rotate(points[i]));
                    if (i < count - 1) {
                        result += ", ";
                    }
                }
            }
            if (line.closed) {
                result += " x";
            }
            return result;
        },
        clone: function() {
            var line = this;
            return new VMLLine(deepExtend([], line.points), line.closed, deepExtend({}, line.options));
        },
        _print: function(point) {
            var scale = this.options.align === false ? 100 : 1;
            return math.round(point.x * scale) + "," + math.round(point.y * scale);
        }
    });
    var VMLRing = VMLPath.extend({
        init: function(config, options) {
            var ring = this;
            VMLPath.fn.init.call(ring, options);
            ring.pathTemplate = VMLRing.pathTemplate;
            if (!ring.pathTemplate) {
                ring.pathTemplate = VMLRing.pathTemplate = renderTemplate("M #= d.osp.x #,#= d.osp.y # " + "WA #= d.obb.l #,#= d.obb.t # #= d.obb.r #,#= d.obb.b # " + "#= d.osp.x #,#= d.osp.y # #= d.oep.x #,#= d.oep.y # " + "L #= d.iep.x #,#= d.iep.y # " + "AT #= d.ibb.l #,#= d.ibb.t # #= d.ibb.r #,#= d.ibb.b # " + "#= d.iep.x #,#= d.iep.y # #= d.isp.x #,#= d.isp.y # " + "X E");
            }
            ring.config = config;
        },
        renderPoints: function() {
            var ring = this, config = ring.config, r = math.max(round(config.r), 0), ir = math.max(round(config.ir), 0), cx = round(config.c.x), cy = round(config.c.y), startAngle = config.startAngle, endAngle = config.angle + startAngle, angle = endAngle - startAngle, outerBBox = {
                l: cx - r,
                t: cy - r,
                r: cx + r,
                b: cy + r
            }, innerBBox = {
                l: cx - ir,
                t: cy - ir,
                r: cx + ir,
                b: cy + ir
            }, outerStartPoint, innerStartPoint, innerEndPoint, outerEndPoint;
            function roundPointCoordinates(point) {
                return new Point2D(round(point.x), round(point.y));
            }
            if (angle <= 1) {
                endAngle += 1 - angle;
            } else if (angle > 359) {
                endAngle -= 1 - angle;
            }
            outerStartPoint = roundPointCoordinates(config.point(startAngle)), innerStartPoint = roundPointCoordinates(config.point(startAngle, true)), 
            outerEndPoint = roundPointCoordinates(config.point(endAngle));
            innerEndPoint = roundPointCoordinates(config.point(endAngle, true));
            return ring.pathTemplate({
                obb: outerBBox,
                ibb: innerBBox,
                osp: outerStartPoint,
                isp: innerStartPoint,
                oep: outerEndPoint,
                iep: innerEndPoint,
                cx: cx,
                cy: cy
            });
        },
        clone: function() {
            var sector = this;
            return new VMLRing(deepExtend({}, sector.config), deepExtend({}, sector.options));
        }
    });
    var VMLSector = VMLRing.extend({
        init: function(config, options) {
            var sector = this;
            VMLRing.fn.init.call(sector, config, options);
            sector.pathTemplate = VMLSector.pathTemplate;
            if (!sector.pathTemplate) {
                sector.pathTemplate = VMLSector.pathTemplate = renderTemplate("M #= d.osp.x #,#= d.osp.y # " + "WA #= d.obb.l #,#= d.obb.t # #= d.obb.r #,#= d.obb.b # " + "#= d.osp.x #,#= d.osp.y # #= d.oep.x #,#= d.oep.y # " + "L #= d.cx #,#= d.cy # " + "X E");
            }
        },
        clone: function() {
            var sector = this;
            return new VMLSector(deepExtend({}, sector.config), deepExtend({}, sector.options));
        }
    });
    var VMLCircle = ViewElement.extend({
        init: function(c, r, options) {
            var circle = this;
            ViewElement.fn.init.call(circle, options);
            circle.c = c;
            circle.r = r;
            circle.template = VMLCircle.template;
            if (!circle.template) {
                circle.template = VMLCircle.template = renderTemplate("<kvml:oval #= d.renderId() # " + "#= d.renderDataAttributes() #" + "style='position:absolute; " + "width:#= d.r * 2 #px; height:#= d.r * 2 #px; " + "top:#= d.c.y - d.r #px; " + "left:#= d.c.x - d.r #px;'>" + "#= d.fill.render() + d.stroke.render() #" + "</kvml:oval>");
            }
            circle.stroke = new VMLStroke(circle.options);
            circle.fill = new VMLFill(circle.options);
        },
        options: {
            fill: "",
            fillOpacity: 1
        },
        refresh: function(domElement) {
            var circle = this, c = circle.c, r = math.max(0, circle.r), size = r * 2, element = $(domElement);
            element.css({
                width: size,
                height: size,
                top: c.y - r,
                left: c.x - r
            });
            circle.fill.options = circle.options;
            circle.fill.refresh(element.find("fill")[0]);
        },
        clone: function() {
            var circle = this;
            return new VMLCircle(deepExtend({}, circle.c), circle.r, deepExtend({}, circle.options));
        }
    });
    var VMLGroup = ViewElement.extend({
        init: function(options) {
            var group = this;
            ViewElement.fn.init.call(group, options);
            group.tagName = group.options.inline ? "span" : "div";
            group.template = VMLGroup.template;
            if (!group.template) {
                group.template = VMLGroup.template = renderTemplate("<#= d.tagName # #= d.renderId() #" + "#= d.renderDataAttributes() #" + "style='position: absolute; white-space: nowrap;'>" + "#= d.renderContent() #</#= d.tagName #>");
            }
        }
    });
    var VMLClipRect = ViewElement.extend({
        init: function(box, options) {
            var clipRect = this;
            ViewElement.fn.init.call(clipRect, options);
            clipRect.tagName = clipRect.options.inline ? "span" : "div";
            clipRect.template = VMLClipRect.template;
            clipRect.clipTemplate = VMLClipRect.clipTemplate;
            if (!clipRect.template) {
                clipRect.template = VMLClipRect.template = renderTemplate("<#= d.tagName # #= d.renderId() #" + "style='position:absolute; " + "width:#= d.box.width() #px; height:#= d.box.height() #px; " + "top:#= d.box.y1 #px; " + "left:#= d.box.x1 #px; " + "clip:#= d._renderClip() #;' >" + "#= d.renderContent() #</#= d.tagName #>");
                clipRect.clipTemplate = VMLClipRect.clipTemplate = renderTemplate("rect(#= d.points[0].y #px #= d.points[1].x #px " + "#= d.points[2].y #px #= d.points[0].x #px)");
            }
            clipRect.box = box;
            // Points defining the clipping rectangle
            clipRect.points = box.points();
        },
        clone: function() {
            var clipRect = this;
            return new VMLClipRect(clipRect.box, deepExtend({}, clipRect.options));
        },
        refresh: function(domElement) {
            if (domElement) {
                domElement.style.clip = this._renderClip();
            }
        },
        _renderClip: function() {
            return this.clipTemplate(this);
        },
        destroy: function() {
            $("#" + this.options.id + ">*").unwrap();
        }
    });
    var VMLGradient = ViewElement.extend({
        init: function(options) {
            var gradient = this;
            ViewElement.fn.init.call(gradient, options);
        },
        options: {
            opacity: 1
        },
        renderColors: function() {
            var gradient = this, options = gradient.options, stops = options.stops, currentStop, i, length = stops.length, output = [], round = math.round;
            for (i = 0; i < length; i++) {
                currentStop = stops[i];
                output.push(round(currentStop.offset * 100) + "% " + currentStop.color);
            }
            return output.join(",");
        }
    });
    var VMLLinearGradient = VMLGradient.extend({
        init: function(options) {
            var gradient = this;
            VMLGradient.fn.init.call(gradient, options);
            gradient.template = VMLLinearGradient.template;
            if (!gradient.template) {
                gradient.template = VMLLinearGradient.template = renderTemplate("<kvml:fill type='gradient' angle='#= 270 - d.options.rotation #' " + "colors='#= d.renderColors() #' opacity='#= d.options.opacity #' />");
            }
        },
        options: {
            rotation: 0
        }
    });
    var VMLRadialGradient = VMLGradient.extend({
        init: function(options) {
            var gradient = this;
            VMLGradient.fn.init.call(gradient, options);
            gradient.template = VMLRadialGradient.template;
            if (!gradient.template) {
                gradient.template = VMLRadialGradient.template = renderTemplate("<kvml:fill type='gradienttitle' focus='100%' focusposition='#= d.focusPosition() #'" + "colors='#= d.renderColors() #' color='#= d.firstColor() #' color2='#= d.lastColor() #' opacity='#= d.options.opacity #' />");
            }
        },
        focusPosition: function() {
            var options = this.options, bbox = options.bbox, cx = options.cx, cy = options.cy, focusx = Math.max(0, Math.min(1, (cx - bbox.x1) / bbox.width())), focusy = Math.max(0, Math.min(1, (cy - bbox.y1) / bbox.height()));
            return round(focusx, COORD_PRECISION) + " " + round(focusy, COORD_PRECISION);
        },
        firstColor: function() {
            var stops = this.options.stops;
            return stops[0].color;
        },
        lastColor: function() {
            var stops = this.options.stops;
            return stops[stops.length - 1].color;
        }
    });
    // Decorators =============================================================
    function VMLOverlayDecorator(view) {
        this.view = view;
    }
    VMLOverlayDecorator.prototype = {
        decorate: function(element) {
            var options = element.options, view = this.view, overlay, bbox;
            if (options.overlay) {
                bbox = options.overlay.bbox;
                overlay = view.buildGradient(deepExtend({}, options.overlay, {
                    // Make the gradient definition unique for this color
                    _overlayFill: options.fill,
                    // and for the radial gradient bounding box, if specified
                    _bboxHash: defined(bbox) ? bbox.getHash() : ""
                }));
            }
            if (!overlay) {
                return element;
            }
            delete options.overlay;
            options.fill = deepExtend(blendGradient(options.fill, overlay), {
                opacity: options.fillOpacity
            });
            return element;
        }
    };
    function VMLGradientDecorator(view) {
        this.view = view;
    }
    VMLGradientDecorator.prototype = {
        decorate: function(element) {
            var decorator = this, view = decorator.view, options = element.options, fill = options.fill;
            if (fill && fill.supportVML !== false) {
                if (fill.gradient) {
                    fill = view.buildGradient(fill);
                }
                if (typeof fill === OBJECT) {
                    element.fill = view.createGradient(fill);
                }
            }
            return element;
        }
    };
    var VMLClipAnimationDecorator = Class.extend({
        init: function(view) {
            this.view = view;
        },
        decorate: function(element) {
            var decorator = this, view = decorator.view, options = view.options, animation = element.options.animation, clipRect;
            if (animation && animation.type === CLIP && options.transitions) {
                clipRect = new VMLClipRect(new Box2D(0, 0, options.width, options.height), {
                    id: uniqueId(),
                    inline: options.inline
                });
                view.animations.push(new ExpandAnimation(clipRect, {
                    size: options.width
                }));
                clipRect.children.push(element);
                return clipRect;
            } else {
                return element;
            }
        }
    });
    // Helpers ================================================================
    function isIE9CompatibilityView() {
        return kendo.support.browser.msie && !supportsSVG() && typeof window.performance !== "undefined";
    }
    function blendColors(base, overlay, alpha) {
        var baseColor = new Color(base), overlayColor = new Color(overlay), r = blendChannel(baseColor.r, overlayColor.r, alpha), g = blendChannel(baseColor.g, overlayColor.g, alpha), b = blendChannel(baseColor.b, overlayColor.b, alpha);
        return new Color(r, g, b).toHex();
    }
    function blendChannel(a, b, alpha) {
        return math.round(alpha * b + (1 - alpha) * a);
    }
    function blendGradient(color, gradient) {
        var srcStops = gradient.stops, stopsLength = srcStops.length, result = deepExtend({}, gradient), i, stop, resultStop;
        result.stops = [];
        for (i = 0; i < stopsLength; i++) {
            stop = srcStops[i];
            resultStop = result.stops[i] = deepExtend({}, srcStops[i]);
            resultStop.color = blendColors(color, stop.color, stop.opacity);
            resultStop.opacity = 0;
        }
        return result;
    }
    // Exports ================================================================
    deepExtend(dataviz, {
        VMLCircle: VMLCircle,
        VMLClipAnimationDecorator: VMLClipAnimationDecorator,
        VMLClipRect: VMLClipRect,
        VMLFill: VMLFill,
        VMLGroup: VMLGroup,
        VMLLine: VMLLine,
        VMLLinearGradient: VMLLinearGradient,
        VMLOverlayDecorator: VMLOverlayDecorator,
        VMLPath: VMLPath,
        VMLRadialGradient: VMLRadialGradient,
        VMLRing: VMLRing,
        VMLRotatedText: VMLRotatedText,
        VMLSector: VMLSector,
        VMLStroke: VMLStroke,
        VMLText: VMLText,
        VMLView: VMLView,
        blendColors: blendColors,
        blendGradient: blendGradient
    });
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, Widget = kendo.ui.Widget, proxy = $.proxy, abs = Math.abs, MAX_DOUBLE_TAP_DISTANCE = 20;
    var Swipe = kendo.Class.extend({
        init: function(element, callback, options) {
            options = $.extend({
                minXDelta: 30,
                maxYDelta: 20,
                maxDuration: 1e3
            }, options);
            new kendo.UserEvents(element, {
                surface: options.surface,
                allowSelection: true,
                start: function(e) {
                    if (abs(e.x.velocity) * 2 >= abs(e.y.velocity)) {
                        e.sender.capture();
                    }
                },
                move: function(e) {
                    var touch = e.touch, duration = e.event.timeStamp - touch.startTime, direction = touch.x.initialDelta > 0 ? "right" : "left";
                    if (abs(touch.x.initialDelta) >= options.minXDelta && abs(touch.y.initialDelta) < options.maxYDelta && duration < options.maxDuration) {
                        callback({
                            direction: direction,
                            touch: touch,
                            target: touch.target
                        });
                        touch.cancel();
                    }
                }
            });
        }
    });
    var Touch = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            options = that.options;
            element = that.element;
            function eventProxy(name) {
                return function(e) {
                    that._triggerTouch(name, e);
                };
            }
            function gestureEventProxy(name) {
                return function(e) {
                    that.trigger(name, {
                        touches: e.touches,
                        distance: e.distance,
                        center: e.center,
                        event: e.event
                    });
                };
            }
            that.events = new kendo.UserEvents(element, {
                filter: options.filter,
                surface: options.surface,
                multiTouch: options.multiTouch,
                allowSelection: true,
                press: proxy(that, "_touchstart"),
                tap: proxy(that, "_tap"),
                gesturestart: gestureEventProxy("gesturestart"),
                gesturechange: gestureEventProxy("gesturechange"),
                gestureend: gestureEventProxy("gestureend")
            });
            if (options.enableSwipe) {
                that.events.bind("start", proxy(that, "_swipestart"));
                that.events.bind("move", proxy(that, "_swipemove"));
            } else {
                that.events.bind("start", proxy(that, "_dragstart"));
                that.events.bind("move", eventProxy("drag"));
                that.events.bind("end", eventProxy("dragend"));
            }
            kendo.notify(that);
        },
        events: [ "touchstart", "dragstart", "drag", "dragend", "tap", "doubletap", "hold", "swipe", "gesturestart", "gesturechange", "gestureend" ],
        options: {
            name: "Touch",
            surface: null,
            global: false,
            multiTouch: false,
            enableSwipe: false,
            minXDelta: 30,
            maxYDelta: 20,
            maxDuration: 1e3,
            minHold: 800,
            doubleTapTimeout: 800
        },
        _cancelHold: function() {
            clearTimeout(this.holdTimeout);
        },
        _triggerTouch: function(type, e) {
            this.trigger(type, {
                touch: e.touch,
                event: e.event
            });
        },
        _touchstart: function(e) {
            var that = this;
            that._triggerTouch("touchstart", e);
            that._cancelHold();
            that.holdTimeout = setTimeout(function() {
                that._triggerTouch("hold", e);
            }, that.options.minHold);
        },
        _tap: function(e) {
            var that = this, lastTap = that.lastTap, touch = e.touch;
            that._cancelHold();
            if (lastTap && touch.endTime - lastTap.endTime < that.options.doubleTapTimeout && kendo.touchDelta(touch, lastTap).distance < MAX_DOUBLE_TAP_DISTANCE) {
                that._triggerTouch("doubletap", e);
                that.lastTap = null;
            } else {
                that._triggerTouch("tap", e);
                that.lastTap = touch;
            }
        },
        _dragstart: function(e) {
            this._cancelHold();
            this._triggerTouch("dragstart", e);
        },
        _swipestart: function(e) {
            this._cancelHold();
            if (abs(e.x.velocity) * 2 >= abs(e.y.velocity)) {
                e.sender.capture();
            }
        },
        _swipemove: function(e) {
            var that = this, options = that.options, touch = e.touch, duration = e.event.timeStamp - touch.startTime, direction = touch.x.initialDelta > 0 ? "right" : "left";
            that._cancelHold();
            if (abs(touch.x.initialDelta) >= options.minXDelta && abs(touch.y.initialDelta) < options.maxYDelta && duration < options.maxDuration) {
                that.trigger("swipe", {
                    direction: direction,
                    touch: e.touch
                });
                touch.cancel();
            }
        }
    });
    window.jQuery.fn.kendoMobileSwipe = function(callback, options) {
        this.each(function() {
            new Swipe(this, callback, options);
        });
    };
    kendo.ui.plugin(Touch);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, mobile = kendo.mobile, ui = mobile.ui, SHOW = "show", HIDE = "hide", OPEN = "open", CLOSE = "close", WRAPPER = '<div class="km-popup-wrapper" />', ARROW = '<div class="km-popup-arrow" />', OVERLAY = '<div class="km-popup-overlay" />', DIRECTION_CLASSES = "km-up km-down km-left km-right", Widget = ui.Widget, DIRECTIONS = {
        down: {
            origin: "bottom center",
            position: "top center"
        },
        up: {
            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"
        }
    }, ANIMATION = {
        animation: {
            open: {
                effects: "fade:in",
                duration: 0
            },
            close: {
                effects: "fade:out",
                duration: 400
            }
        }
    }, DIMENSIONS = {
        horizontal: {
            offset: "top",
            size: "height"
        },
        vertical: {
            offset: "left",
            size: "width"
        }
    }, REVERSE = {
        up: "down",
        down: "up",
        left: "right",
        right: "left"
    };
    var Popup = Widget.extend({
        init: function(element, options) {
            var that = this, container = mobile.application.element, popupOptions = {
                viewport: kendo.mobile.application.element,
                open: function() {
                    that.overlay.show();
                },
                activate: $.proxy(that._activate, that),
                deactivate: function() {
                    that.overlay.hide();
                    that.trigger(HIDE);
                }
            }, axis;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            options = that.options;
            element.wrap(WRAPPER).addClass("km-popup").show();
            axis = that.options.direction.match(/left|right/) ? "horizontal" : "vertical";
            that.dimensions = DIMENSIONS[axis];
            that.wrapper = element.parent().css({
                width: options.width,
                height: options.height
            }).addClass("km-popup-wrapper km-" + options.direction).hide();
            that.arrow = $(ARROW).prependTo(that.wrapper).hide();
            that.overlay = $(OVERLAY).appendTo(container).hide();
            popupOptions.appendTo = that.overlay;
            that.popup = new kendo.ui.Popup(that.wrapper, $.extend(true, popupOptions, ANIMATION, DIRECTIONS[options.direction]));
        },
        options: {
            name: "Popup",
            width: 240,
            height: 320,
            direction: "down"
        },
        events: [ SHOW, HIDE ],
        show: function(target) {
            var that = this, popup = that.popup;
            popup.options.anchor = $(target);
            popup.open();
        },
        target: function() {
            return this.popup.options.anchor;
        },
        hide: function() {
            this.popup.close();
        },
        destroy: function() {
            Widget.fn.destroy.call(this);
            this.popup.destroy();
        },
        _activate: function() {
            var that = this, direction = that.options.direction, dimensions = that.dimensions, offset = dimensions.offset, popup = that.popup, anchor = popup.options.anchor, anchorOffset = $(anchor).offset(), elementOffset = $(popup.element).offset(), cssClass = popup.flipped ? REVERSE[direction] : direction, offsetAmount = anchorOffset[offset] - elementOffset[offset] + $(anchor)[dimensions.size]() / 2;
            that.wrapper.removeClass(DIRECTION_CLASSES).addClass("km-" + cssClass);
            that.arrow.css(offset, offsetAmount).show();
            that.trigger(SHOW);
        }
    });
    var PopOver = Widget.extend({
        init: function(element, options) {
            var that = this, popupOptions;
            that.initialOpen = false;
            Widget.fn.init.call(that, element, options);
            options = that.options;
            popupOptions = $.extend({
                show: function() {
                    that.trigger(OPEN, {
                        target: that.popup.target()
                    });
                },
                hide: function() {
                    that.trigger(CLOSE);
                }
            }, this.options.popup);
            that.popup = new Popup(that.element, popupOptions);
            that.pane = new ui.Pane(that.element, this.options.pane);
            kendo.notify(that, ui);
        },
        options: {
            name: "PopOver",
            popup: {},
            pane: {}
        },
        events: [ OPEN, CLOSE ],
        open: function(target) {
            this.openFor(target);
        },
        openFor: function(target) {
            this.popup.show(target);
            if (!this.initialOpen) {
                this.pane.navigate("");
                this.initialOpen = true;
            }
        },
        close: function() {
            this.popup.hide();
        },
        destroy: function() {
            Widget.fn.destroy.call(this);
            this.pane.destroy();
            this.popup.destroy();
            kendo.destroy(this.element);
        }
    });
    ui.plugin(Popup);
    ui.plugin(PopOver);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, CAPTURE_EVENTS = $.map(kendo.eventMap, function(value) {
        return value;
    }).join(" ").split(" ");
    var Loader = Widget.extend({
        init: function(container, options) {
            var that = this, element = $('<div class="km-loader"><span class="km-loading km-spin"></span></div>');
            Widget.fn.init.call(that, element, options);
            that.container = container;
            that._attachCapture();
            element.append(that.options.loading).hide().appendTo(container);
        },
        options: {
            name: "Loader",
            loading: "<h1>Loading...</h1>",
            timeout: 100
        },
        show: function() {
            var that = this;
            clearTimeout(that._loading);
            if (that.options.loading === false) {
                return;
            }
            that._loading = setTimeout(function() {
                that.element.show();
            }, that.options.timeout);
        },
        hide: function() {
            var that = this;
            clearTimeout(that._loading);
            that.element.hide();
        },
        transition: function() {
            this.transitioning = true;
            this.container.css("pointer-events", "none");
        },
        transitionDone: function() {
            this.transitioning = false;
            this.container.css("pointer-events", "");
        },
        _attachCapture: function() {
            var that = this;
            that.transitioning = false;
            function capture(e) {
                if (that.transitioning) {
                    e.stopPropagation();
                }
            }
            for (var i = 0; i < CAPTURE_EVENTS.length; i++) {
                that.container[0].addEventListener(CAPTURE_EVENTS[i], capture, true);
            }
        }
    });
    ui.plugin(Loader);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.mobile.ui, Popup = kendo.ui.Popup, SHIM = '<div class="km-shim"/>', Widget = ui.Widget;
    var Shim = Widget.extend({
        init: function(element, options) {
            var that = this, app = kendo.mobile.application, osname = app ? app.os.name : kendo.support.mobileOS.name, ioswp = osname === "ios" || osname === "wp", align = options.align || (ioswp ? "bottom center" : "center center"), position = options.position || (ioswp ? "bottom center" : "center center"), effect = options.effect || (ioswp ? "slideIn:up" : "fade:in"), shim = $(SHIM).handler(that).hide();
            Widget.fn.init.call(that, element, options);
            that.shim = shim;
            that.element = element;
            if (!that.options.modal) {
                that.shim.on("up", "hide");
            }
            (app ? app.element : $(document.body)).append(shim);
            that.popup = new Popup(that.element, {
                anchor: shim,
                appendTo: shim,
                origin: align,
                position: position,
                animation: {
                    open: {
                        effects: effect,
                        duration: that.options.duration
                    },
                    close: {
                        duration: that.options.duration
                    }
                },
                deactivate: function() {
                    shim.hide();
                },
                open: function() {
                    shim.show();
                }
            });
            kendo.notify(that);
        },
        options: {
            name: "Shim",
            modal: true,
            align: undefined,
            position: undefined,
            effect: undefined,
            duration: 200
        },
        show: function() {
            this.popup.open();
        },
        hide: function(e) {
            if (!e || !$.contains(this.shim[0], e.target)) {
                this.popup.close();
            }
        },
        destroy: function() {
            Widget.fn.destroy.call(this);
            this.shim.kendoDestroy();
            this.popup.destroy();
        }
    });
    ui.plugin(Shim);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, mobile = kendo.mobile, ui = mobile.ui, attr = kendo.attr, Class = kendo.Class, Widget = ui.Widget, INIT = "init", SHOW = "show", BEFORE_SHOW = "beforeShow", AFTER_SHOW = "afterShow", HIDE = "hide", Z_INDEX = "z-index", attrValue = kendo.attrValue, roleSelector = kendo.roleSelector;
    function initPopOvers(element) {
        var popovers = element.find(roleSelector("popover")), idx, length, roles = ui.roles;
        for (idx = 0, length = popovers.length; idx < length; idx++) {
            kendo.initWidget(popovers[idx], {}, roles);
        }
    }
    var View = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            that.params = {};
            that.lastParams = {};
            $.extend(that, options);
            that._layout();
            that._scroller();
            that._model();
        },
        events: [ INIT, BEFORE_SHOW, SHOW, AFTER_SHOW, HIDE ],
        options: {
            name: "View",
            title: "",
            reload: false,
            defaultTransition: "",
            useNativeScrolling: false,
            stretch: false,
            zoom: false,
            model: null
        },
        destroy: function() {
            Widget.fn.destroy.call(this);
            if (this.scroller) {
                this.scroller.destroy();
            }
            kendo.destroy(this.element);
        },
        purge: function() {
            this.destroy();
            this.element.remove();
        },
        showStart: function() {
            var that = this;
            that.element.css("display", "");
            if (!that.inited) {
                that.inited = true;
                that.trigger(INIT, {
                    view: that
                });
            }
            if (that.layout) {
                that.layout.attach(that);
            }
            that.trigger(SHOW, {
                view: that
            });
        },
        hideStart: function() {
            var that = this;
            if (that.layout) {
                that.layout.detach(that);
            }
        },
        hideComplete: function() {
            var that = this;
            that.element.hide();
            that.trigger(HIDE, {
                view: that
            });
        },
        updateParams: function(params) {
            var that = this;
            if (that.trigger(BEFORE_SHOW, {
                view: that
            })) {
                return;
            }
            that.lastParams = that.params;
            that.params = params;
            that.trigger(SHOW, {
                view: that
            });
        },
        switchWith: function(view, transition, params, callback) {
            var that = this, complete = function() {
                that.trigger(AFTER_SHOW, {
                    view: that
                });
                callback();
            };
            if (that.trigger(BEFORE_SHOW, {
                view: that
            })) {
                return;
            }
            that.lastParams = that.params;
            that.params = params;
            if (view) {
                // layout needs to be detached first, then reattached
                view.hideStart();
                that.showStart();
                new ViewTransition({
                    current: view,
                    next: that,
                    transition: transition,
                    defaultTransition: view.options.defaultTransition,
                    complete: complete
                });
            } else {
                that.showStart();
                complete();
            }
        },
        parallaxContents: function(other) {
            var that = this, contents = that.content;
            if (!other.header[0]) {
                contents = contents.add(that.header);
            }
            if (!other.footer[0]) {
                contents = contents.add(that.footer);
            }
            return contents;
        },
        contentElement: function() {
            var that = this;
            return that.options.stretch ? that.content : that.scrollerContent;
        },
        _scroller: function() {
            var that = this;
            if (that.options.stretch) {
                that.content.addClass("km-stretched-view");
            } else {
                that.content.kendoMobileScroller({
                    zoom: that.options.zoom,
                    useNative: that.options.useNativeScrolling
                });
                that.scroller = that.content.data("kendoMobileScroller");
                that.scrollerContent = that.scroller.scrollElement;
            }
            // prevent accidental address bar display when pulling the header
            if (kendo.support.kineticScrollNeeded) {
                $(that.element).on("touchmove", ".km-header", kendo.preventDefault);
            }
        },
        _model: function() {
            var that = this, element = that.element, model = that.options.model;
            if (typeof model === "string") {
                model = kendo.getter(model)(window);
            }
            that.model = model;
            initPopOvers(element);
            if (model) {
                kendo.bind(element.children(), model, ui, kendo.ui, kendo.dataviz.ui);
            } else {
                mobile.init(element.children());
            }
        },
        _layout: function() {
            var that = this, contentSelector = roleSelector("content"), element = that.element;
            element.data("kendoView", that).addClass("km-view");
            that.transition = attrValue(element, "transition");
            that.header = element.children(roleSelector("header")).addClass("km-header");
            that.footer = element.children(roleSelector("footer")).addClass("km-footer");
            if (!element.children(contentSelector)[0]) {
                element.wrapInner("<div " + attr("role") + '="content"></div>');
            }
            that.content = element.children(roleSelector("content")).addClass("km-content");
            that.element.prepend(that.header).append(that.footer);
            that.id = attrValue(element, "url") || "#" + element.attr("id");
            if (that.layout) {
                that.layout.setup(that);
            }
        }
    });
    function fade(source, destination) {
        if (source[0] && destination[0] && source[0] != destination[0]) {
            source.kendoAnimateTo(destination, {
                effects: "fade"
            });
        }
    }
    function initWidgets(collection) {
        collection.each(function() {
            kendo.initWidget($(this), {}, ui.roles);
        });
    }
    var ViewTransition = Class.extend({
        init: function(options) {
            $.extend(this, options);
            var that = this, current = that.current, next = that.next, currentContent = current.element, nextContent = next.element, upper = next, lower = current, transition = that._transition();
            if (transition.reverse && !transition.parallax) {
                upper = current;
                lower = next;
            }
            upper.element.css(Z_INDEX, 1);
            lower.element.css(Z_INDEX, 0);
            if (transition.parallax) {
                fade(current.footer, next.footer);
                fade(current.header, next.header);
                currentContent = current.parallaxContents(next);
                nextContent = next.parallaxContents(current);
            }
            currentContent.kendoAnimateTo(nextContent, transition);
            if (!that.back()) {
                current.nextViewID = next.id;
                current.backTransition = transition.transition;
            }
        },
        _transition: function() {
            var that = this, current = that.current, next = that.next, back = that.back(), complete = function() {
                current.hideComplete();
                that.complete();
            }, viewTransition = back ? next.backTransition : next.transition, transition = that.transition || viewTransition || that.defaultTransition, animationData = transition.split(" "), animationType = animationData[0], parallax = /^slide/.test(animationType), reverse = animationData[1] === "reverse";
            if (that.back() && !that.transition) {
                reverse = !reverse;
            }
            return {
                effects: animationType,
                reverse: reverse,
                parallax: parallax,
                complete: complete,
                transition: transition
            };
        },
        back: function() {
            var next = this.next, current = this.current;
            return next.nextViewID && next.nextViewID === current.id && JSON.stringify(next.params) === JSON.stringify(next.lastParams);
        }
    });
    var Layout = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            that.element = element.detach();
            that.header = element.children(roleSelector("header")).addClass("km-header");
            that.footer = element.children(roleSelector("footer")).addClass("km-footer");
            that.elements = that.header.add(that.footer);
            initPopOvers(element);
            kendo.mobile.init(that.element.children());
            that.trigger(INIT, {
                layout: that
            });
        },
        options: {
            name: "Layout"
        },
        events: [ INIT, SHOW, HIDE ],
        setup: function(view) {
            if (!view.header[0]) {
                view.header = this.header;
            }
            if (!view.footer[0]) {
                view.footer = this.footer;
            }
        },
        detach: function(view) {
            var that = this;
            if (view.header === that.header) {
                view.element.prepend(that.header.detach().clone(true));
            }
            if (view.footer === that.footer) {
                view.element.append(that.footer.detach().clone(true));
            }
            that.trigger(HIDE, {
                layout: that,
                view: view
            });
        },
        attach: function(view) {
            var that = this;
            if (view.header === that.header) {
                that.header.detach();
                view.element.children(roleSelector("header")).remove();
                view.element.prepend(that.header);
            }
            if (view.footer === that.footer) {
                that.footer.detach();
                view.element.children(roleSelector("footer")).remove();
                view.element.append(that.footer);
            }
            that.trigger(SHOW, {
                layout: that,
                view: view
            });
        }
    });
    var Observable = kendo.Observable, BODY_REGEX = /<body[^>]*>(([\u000a\u000d\u2028\u2029]|.)*)<\/body>/i, LOAD_START = "loadStart", LOAD_COMPLETE = "loadComplete", SHOW_START = "showStart", VIEW_SHOW = "viewShow";
    function urlParams(url) {
        var queryString = url.split("?")[1] || "", params = {}, paramParts = queryString.split(/&|=/), length = paramParts.length, idx = 0;
        for (;idx < length; idx += 2) {
            params[paramParts[idx]] = paramParts[idx + 1];
        }
        return params;
    }
    var ViewEngine = Observable.extend({
        init: function(options) {
            var that = this, views, container;
            Observable.fn.init.call(that);
            $.extend(that, options);
            that.sandbox = $("<div />");
            container = that.container;
            views = that._hideViews(container);
            that.rootView = views.first();
            if (!that.rootView[0]) {
                throw new Error('Your kendo mobile application element does not contain any direct child elements with data-role="view" attribute set. Make sure that you instantiate the mobile application using the correct container.');
            }
            that._view = null;
            that.layouts = {};
            that._setupLayouts(container);
            initWidgets(container.children(roleSelector("modalview")));
            if (that.loader) {
                that.bind(SHOW_START, function() {
                    that.loader.transition();
                });
                that.bind(LOAD_START, function() {
                    that.loader.show();
                });
                that.bind(LOAD_COMPLETE, function() {
                    that.loader.hide();
                });
                that.bind(VIEW_SHOW, function() {
                    that.loader.transitionDone();
                });
            }
        },
        view: function() {
            return this._view;
        },
        showView: function(url, transition) {
            if (url === this.url) {
                return;
            }
            this.trigger(SHOW_START);
            var that = this, showClosure = function(view) {
                that._show(view, transition, urlParams(url));
            }, element = that._findViewElement(url), view = element.data("kendoView");
            that.url = url;
            if (view && view.reload) {
                view.purge();
                element = [];
            }
            this.trigger("viewTypeDetermined", {
                remote: element.length === 0,
                url: url
            });
            if (element[0]) {
                if (!view) {
                    view = that._createView(element);
                }
                showClosure(view);
            } else {
                that._loadView(url, showClosure);
            }
        },
        _findViewElement: function(url) {
            var element, urlPath = url.split("?")[0];
            if (!url || url === "/") {
                return this.rootView;
            }
            element = this.container.children("[" + attr("url") + "='" + urlPath + "']");
            // do not try to search for "#/foo/bar" id, jQuery throws error
            if (!element[0] && urlPath.indexOf("/") === -1) {
                element = this.container.children(urlPath.charAt(0) === "#" ? urlPath : "#" + urlPath);
            }
            return element;
        },
        _createView: function(element) {
            var that = this, viewOptions, layout = attrValue(element, "layout");
            if (typeof layout === "undefined") {
                layout = that.layout;
            }
            if (layout) {
                layout = that.layouts[layout];
            }
            viewOptions = {
                defaultTransition: that.transition,
                loader: that.loader,
                container: that.container,
                layout: layout,
                reload: attrValue(element, "reload")
            };
            return kendo.initWidget(element, viewOptions, ui.roles);
        },
        _loadView: function(url, callback) {
            var that = this;
            if (this.serverNavigation) {
                location.href = url;
                return;
            }
            if (that._xhr) {
                that._xhr.abort();
            }
            that.trigger(LOAD_START);
            that._xhr = $.get(url, function(html) {
                that.trigger(LOAD_COMPLETE);
                callback(that._createRemoteView(url, html));
            }, "html").fail(function(request) {
                that.trigger(LOAD_COMPLETE);
                if (request.status === 0 && request.responseText) {
                    callback(that._createRemoteView(url, request.responseText));
                }
            });
        },
        _createRemoteView: function(url, html) {
            var that = this, sandbox = that.sandbox, urlPath = url.split("?")[0], container = that.container, views, modalViews, view;
            if (BODY_REGEX.test(html)) {
                html = RegExp.$1;
            }
            sandbox[0].innerHTML = html;
            container.append(sandbox.children("script, style"));
            views = that._hideViews(sandbox);
            view = views.first();
            // Generic HTML content found as remote view - no remote view markers
            if (!view.length) {
                views = view = sandbox.wrapInner("<div data-role=view />").children();
            }
            view.hide().attr(attr("url"), urlPath);
            that._setupLayouts(sandbox);
            modalViews = sandbox.children(roleSelector("modalview"));
            container.append(sandbox.children(roleSelector("layout modalview")).add(views));
            // Initialize the modalviews after they have been appended to the final container
            initWidgets(modalViews);
            return that._createView(view);
        },
        _show: function(view, transition, params) {
            var that = this;
            if (that._view !== view) {
                view.switchWith(that._view, transition, params, function() {
                    that._view = view;
                    that.trigger(VIEW_SHOW, {
                        view: view
                    });
                });
            } else {
                that._view.updateParams(params);
                that.trigger(VIEW_SHOW, {
                    view: view
                });
            }
        },
        _hideViews: function(container) {
            return container.children(roleSelector("view splitview")).hide();
        },
        _setupLayouts: function(element) {
            var that = this;
            element.children(roleSelector("layout")).each(function() {
                var layout = $(this), platform = attrValue(layout, "platform");
                if (platform === undefined || platform === mobile.application.os.name) {
                    that.layouts[kendo.attrValue(layout, "id")] = kendo.initWidget(layout, {}, ui.roles);
                }
            });
        }
    });
    kendo.mobile.ViewEngine = ViewEngine;
    ui.plugin(View);
    ui.plugin(Layout);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.mobile.ui, Shim = ui.Shim, Widget = ui.Widget, OPEN = "open", CLOSE = "close", INIT = "init", WRAP = '<div class="km-modalview-wrapper" />';
    var ModalView = ui.View.extend({
        init: function(element, options) {
            var that = this, width, height;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            options = that.options;
            width = element[0].style.width || element.css("width");
            height = element[0].style.height || element.css("height");
            element.addClass("km-modalview").wrap(WRAP);
            that.wrapper = element.parent().css({
                width: options.width || width || 300,
                height: options.height || height || 300
            });
            element.css({
                width: "",
                height: ""
            });
            that.shim = new Shim(that.wrapper, {
                modal: options.modal,
                position: "center center",
                align: "center center",
                effect: "fade:in"
            });
            that._layout();
            that._scroller();
            that._model();
            that.trigger(INIT);
            kendo.onResize(function() {
                var positionedElement = that.wrapper.parent(), viewPort = positionedElement.parent();
                positionedElement.css({
                    top: (viewPort.height() - positionedElement.height()) / 2 + "px",
                    left: (viewPort.width() - positionedElement.width()) / 2 + "px"
                });
            });
        },
        events: [ INIT, OPEN, CLOSE ],
        options: {
            name: "ModalView",
            modal: true,
            width: null,
            height: null
        },
        destroy: function() {
            Widget.fn.destroy.call(this);
            this.shim.destroy();
        },
        open: function(target) {
            var that = this;
            that.target = $(target);
            that.shim.show();
            that.trigger("show", {
                view: that
            });
        },
        // Interface implementation, called from the pane click handlers
        openFor: function(target) {
            this.open(target);
            this.trigger(OPEN, {
                target: target
            });
        },
        close: function() {
            this.shim.hide();
            this.trigger(CLOSE);
        }
    });
    ui.plugin(ModalView);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, View = ui.View;
    var SplitView = View.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            $.extend(that, options);
            that._layout();
            that._style();
            kendo.mobile.init(element.children(kendo.roleSelector("modalview")));
            that.panes = [];
            that.element.children(kendo.roleSelector("pane")).each(function() {
                that.panes.push(kendo.initWidget(this, {}, ui.roles));
            });
        },
        options: {
            name: "SplitView",
            style: "horizontal"
        },
        // Implement view interface
        _layout: function() {
            var that = this, element = that.element;
            element.data("kendoView", that).addClass("km-view km-splitview");
            that.transition = kendo.attrValue(element, "transition");
            $.extend(that, {
                header: [],
                footer: [],
                content: element
            });
        },
        _style: function() {
            var style = this.options.style, element = this.element, styles;
            if (style) {
                styles = style.split(" ");
                $.each(styles, function() {
                    element.addClass("km-split-" + this);
                });
            }
        },
        showStart: function() {
            var that = this;
            that.element.css("display", "");
            if (!that.inited) {
                that.inited = true;
                $.each(that.panes, function() {
                    this.navigate("");
                });
                that.trigger("init", {
                    view: that
                });
            }
            that.trigger("show", {
                view: that
            });
        }
    });
    ui.plugin(SplitView);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, mobile = kendo.mobile, roleSelector = kendo.roleSelector, ui = mobile.ui, Widget = ui.Widget, ViewEngine = mobile.ViewEngine, Loader = mobile.ui.Loader, EXTERNAL = "external", HREF = "href", DUMMY_HREF = "#!", NAVIGATE = "navigate", VIEW_SHOW = "viewShow", WIDGET_RELS = /popover|actionsheet|modalview/, BACK = "#:back", attrValue = kendo.attrValue, // navigation element roles
    buttonRoles = "button backbutton detailbutton listview-link", linkRoles = "tab";
    function appLinkClick(e) {
        if (attrValue($(e.currentTarget), "rel") != EXTERNAL) {
            e.preventDefault();
        }
    }
    var Pane = Widget.extend({
        init: function(element, options) {
            var that = this, initial;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            element.addClass("km-pane");
            that.history = [];
            that.loader = new Loader(element, {
                loading: that.options.loading
            });
            that.viewEngine = new ViewEngine({
                container: element,
                transition: that.options.transition,
                serverNavigation: that.options.serverNavigation,
                layout: that.options.layout,
                loader: that.loader
            });
            that.viewEngine.bind(VIEW_SHOW, function(e) {
                that.trigger(VIEW_SHOW, e);
            });
            that.viewEngine.bind("viewTypeDetermined", function(e) {
                if (!e.remote || !that.options.serverNavigation) {
                    that.trigger(NAVIGATE, {
                        url: e.url
                    });
                }
            });
            initial = that.options.initial;
            if (initial) {
                that.navigate(initial);
            }
            that._setupAppLinks();
        },
        options: {
            name: "Pane",
            transition: "",
            layout: "",
            initial: undefined,
            loading: undefined
        },
        events: [ NAVIGATE, VIEW_SHOW ],
        destroy: function() {
            Widget.fn.destroy.call(this);
            kendo.destroy(this.element);
        },
        navigate: function(url, transition) {
            var that = this, history = that.history;
            if (url === BACK) {
                history.pop();
                url = history[history.length - 1];
            } else {
                that.history.push(url);
            }
            that.viewEngine.showView(url, transition);
        },
        hideLoading: function() {
            this.loader.hide();
        },
        showLoading: function() {
            this.loader.show();
        },
        view: function() {
            return this.viewEngine.view();
        },
        _setupAppLinks: function() {
            this.element.handler(this).on("down", roleSelector(linkRoles), "_mouseup").on("up", roleSelector(buttonRoles), "_mouseup").on("click", roleSelector(linkRoles + " " + buttonRoles), appLinkClick);
        },
        _mouseup: function(e) {
            if (e.which > 1 || e.isDefaultPrevented()) {
                return;
            }
            var link = $(e.currentTarget), transition = attrValue(link, "transition"), rel = attrValue(link, "rel") || "", target = attrValue(link, "target"), pane = this, href = link.attr(HREF);
            if (rel === EXTERNAL || typeof href === "undefined" || href === DUMMY_HREF) {
                return;
            }
            // Prevent iOS address bar progress display for in app navigation
            link.attr(HREF, DUMMY_HREF);
            setTimeout(function() {
                link.attr(HREF, href);
            });
            if (rel.match(WIDGET_RELS)) {
                kendo.widgetInstance($(href), ui).openFor(link);
                e.stopPropagation();
            } else {
                if (target === "_top") {
                    pane = mobile.application.pane;
                } else if (target) {
                    pane = $("#" + target).data("kendoMobilePane");
                }
                pane.navigate(href, transition);
            }
            e.preventDefault();
        }
    });
    ui.plugin(Pane);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, mobile = kendo.mobile, support = kendo.support, Pane = mobile.ui.Pane, DEFAULT_OS = "ios", OS = support.mobileOS, BERRYPHONEGAP = OS.device == "blackberry" && OS.flatVersion >= 600 && OS.flatVersion < 1e3 && OS.appMode, VERTICAL = "km-vertical", HORIZONTAL = "km-horizontal", MOBILE_PLATFORMS = {
        ios: {
            ios: true,
            appMode: false,
            browser: "default",
            device: "iphone",
            flatVersion: "612",
            majorVersion: "6",
            minorVersion: "1.2",
            name: "ios",
            tablet: false
        },
        android: {
            android: true,
            appMode: false,
            browser: "default",
            device: "android",
            flatVersion: "233",
            majorVersion: "2",
            minorVersion: "3.3",
            name: "android",
            tablet: false
        },
        blackberry: {
            blackberry: true,
            appMode: false,
            browser: "default",
            device: "blackberry",
            flatVersion: "710",
            majorVersion: "7",
            minorVersion: "1.0",
            name: "blackberry",
            tablet: false
        },
        meego: {
            meego: true,
            appMode: false,
            browser: "default",
            device: "meego",
            flatVersion: "850",
            majorVersion: "8",
            minorVersion: "5.0",
            name: "meego",
            tablet: false
        },
        wp: {
            wp: true,
            appMode: false,
            browser: "default",
            device: "wp",
            flatVersion: "800",
            majorVersion: "8",
            minorVersion: "0.0",
            name: "wp",
            tablet: false
        }
    }, viewportTemplate = kendo.template('<meta content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no#=data.height#" name="viewport" />', {
        usedWithBlock: false
    }), systemMeta = '<meta name="apple-mobile-web-app-capable" content="yes" /> ' + '<meta name="apple-mobile-web-app-status-bar-style" content="black" /> ' + '<meta name="msapplication-tap-highlight" content="no" /> ', viewportMeta = viewportTemplate({
        height: ""
    }), iconMeta = kendo.template('<link rel="apple-touch-icon' + (OS.android ? "-precomposed" : "") + '" # if(data.size) { # sizes="#=data.size#" #}# href="#=data.icon#" />', {
        usedWithBlock: false
    }), HIDEBAR = (OS.device == "iphone" || OS.device == "ipod") && OS.browser == "mobilesafari", BARCOMPENSATION = 60, WINDOW = $(window), HEAD = $("head"), proxy = $.proxy;
    function osCssClass(os) {
        var classes = [];
        classes.push("km-" + os.name);
        if (OS) {
            classes.push("km-on-" + OS.name);
        }
        classes.push("km-" + os.name + os.majorVersion);
        classes.push("km-" + os.majorVersion);
        classes.push("km-m" + (os.minorVersion ? os.minorVersion[0] : 0));
        if (os.appMode) {
            classes.push("km-app");
        } else {
            classes.push("km-web");
        }
        return classes.join(" ");
    }
    function isOrientationHorizontal(element) {
        return OS.wp ? element.css("animation-name") == "-kendo-landscape" : Math.abs(window.orientation) / 90 == 1;
    }
    function getOrientationClass(element) {
        return isOrientationHorizontal(element) ? HORIZONTAL : VERTICAL;
    }
    function applyViewportHeight() {
        $("meta[name=viewport]").remove();
        HEAD.append(viewportTemplate({
            height: isOrientationHorizontal() ? ", height=" + window.innerHeight + "px" : support.mobileOS.flatVersion >= 600 && support.mobileOS.flatVersion < 700 ? ", height=" + window.innerWidth + "px" : ", height=device-height"
        }));
    }
    var Application = kendo.Observable.extend({
        init: function(element, options) {
            var that = this;
            mobile.application = that;
            // global reference to current application
            that.options = $.extend({
                hideAddressBar: true,
                transition: "",
                updateDocumentTitle: true
            }, options);
            kendo.Observable.fn.init.call(that, that.options);
            $(function() {
                that.element = $(element ? element : document.body);
                that._setupPlatform();
                that._setupElementClass();
                that._attachHideBarHandlers();
                that.pane = new Pane(that.element, that.options);
                that._attachMeta();
                if (that.options.updateDocumentTitle) {
                    that._setupDocumentTitle();
                }
                that._startHistory();
            });
        },
        navigate: function(url, transition) {
            this.pane.navigate(url, transition);
        },
        scroller: function() {
            return this.view().scroller;
        },
        hideLoading: function() {
            this.pane.hideLoading();
        },
        showLoading: function() {
            this.pane.showLoading();
        },
        view: function() {
            return this.pane.view();
        },
        _setupPlatform: function() {
            var that = this, wpThemeProxy, platform = that.options.platform, os = OS || MOBILE_PLATFORMS[DEFAULT_OS];
            if (platform) {
                if (typeof platform === "string") {
                    os = MOBILE_PLATFORMS[platform];
                } else {
                    os = platform;
                }
            }
            that.os = os;
            that.osCssClass = osCssClass(that.os);
            if (os.name == "wp") {
                wpThemeProxy = proxy(that._setupWP8Theme, that);
                $(window).on("focusin", wpThemeProxy);
                // Restore theme on browser focus (requires click).
                document.addEventListener("resume", wpThemeProxy);
                // PhoneGap fires resume.
                that._setupWP8Theme();
            }
        },
        _setupWP8Theme: function() {
            var that = this, element = $(that.element), bgColor;
            if (!that._bgColorDiv) {
                that._bgColorDiv = $("<div />").css({
                    background: "Background",
                    visibility: "hidden",
                    position: "absolute",
                    top: "-3333px"
                }).appendTo(document.body);
            }
            bgColor = parseInt(that._bgColorDiv.css("background-color").split(",")[1], 10);
            element.removeClass("km-wp-dark km-wp-light");
            if (bgColor === 0) {
                element.addClass("km-wp-dark");
            } else {
                element.addClass("km-wp-light");
            }
            element.parent().css("overflow", "hidden");
        },
        _startHistory: function() {
            var that = this, initial = that.options.initial, router = new kendo.Router({
                init: function(e) {
                    var url = e.url;
                    if (url === "/" && initial) {
                        router.navigate(initial, true);
                        e.preventDefault();
                    }
                },
                routeMissing: function(e) {
                    that.pane.navigate(e.url);
                }
            });
            that.pane.bind("navigate", function(e) {
                router.navigate(e.url, true);
            });
            router.start();
            that.router = router;
        },
        _setupElementClass: function() {
            var that = this, element = that.element;
            element.parent().addClass("km-root km-" + (that.os.tablet ? "tablet" : "phone"));
            element.addClass(that.osCssClass + " " + getOrientationClass(element));
            if (BERRYPHONEGAP) {
                applyViewportHeight();
            }
            kendo.onResize(function() {
                element.removeClass("km-horizontal km-vertical").addClass(getOrientationClass(element));
                if (BERRYPHONEGAP) {
                    applyViewportHeight();
                }
            });
        },
        _attachMeta: function() {
            var icon = this.options.icon, size;
            if (!BERRYPHONEGAP) {
                HEAD.prepend(viewportMeta);
            }
            HEAD.prepend(systemMeta);
            if (icon) {
                if (typeof icon === "string") {
                    icon = {
                        "": icon
                    };
                }
                for (size in icon) {
                    HEAD.prepend(iconMeta({
                        icon: icon[size],
                        size: size
                    }));
                }
            }
        },
        _attachHideBarHandlers: function() {
            var that = this, hideBar = proxy(that._hideBar, that);
            if (support.mobileOS.appMode || !that.options.hideAddressBar) {
                return;
            }
            that._initialHeight = {};
            if (HIDEBAR) {
                WINDOW.on("load", hideBar);
                kendo.onResize(hideBar);
                that.element[0].addEventListener("touchstart", hideBar, true);
            }
        },
        _setupDocumentTitle: function() {
            var that = this, defaultTitle = document.title;
            that.pane.bind("viewShow", function(e) {
                var title = e.view.title;
                document.title = title !== undefined ? title : defaultTitle;
            });
        },
        _hideBar: function() {
            var that = this, element = that.element, orientation = window.orientation + "", initialHeight = that._initialHeight, newHeight;
            if (!initialHeight[orientation]) {
                initialHeight[orientation] = WINDOW.height();
            }
            newHeight = initialHeight[orientation] + BARCOMPENSATION;
            if (newHeight != element.height()) {
                element.height(newHeight);
            }
            setTimeout(window.scrollTo, 0, 0, 1);
        }
    });
    kendo.mobile.Application = Application;
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.mobile.ui, Shim = ui.Shim, Popup = ui.Popup, Widget = ui.Widget, OPEN = "open", BUTTONS = "li>a", CONTEXT_DATA = "actionsheetContext", WRAP = '<div class="km-actionsheet-wrapper" />', cancelTemplate = kendo.template('<li class="km-actionsheet-cancel"><a href="\\#">#:cancel#</a></li>');
    var ActionSheet = Widget.extend({
        init: function(element, options) {
            var that = this, os = kendo.support.mobileOS, ShimClass = os.tablet ? Popup : Shim;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            element.addClass("km-actionsheet").append(cancelTemplate({
                cancel: that.options.cancel
            })).wrap(WRAP).on("up", BUTTONS, "_click").on("click", BUTTONS, kendo.preventDefault);
            that.wrapper = element.parent();
            that.shim = new ShimClass(that.wrapper, $.extend({
                modal: !(os.android || os.meego || os.wp)
            }, that.options.popup));
            kendo.notify(that, ui);
            kendo.onResize(function() {
                var positionedElement = that.wrapper.parent(), viewPort = positionedElement.parent();
                positionedElement.css({
                    top: viewPort.height() - positionedElement.height() + "px",
                    width: viewPort.width() + "px"
                });
            });
        },
        events: [ OPEN ],
        options: {
            name: "ActionSheet",
            cancel: "Cancel",
            popup: {
                height: "auto"
            }
        },
        open: function(target, context) {
            var that = this;
            that.target = $(target);
            that.context = context;
            that.shim.show(target);
        },
        close: function() {
            this.context = this.target = null;
            this.shim.hide();
        },
        openFor: function(target) {
            var that = this, context = target.data(CONTEXT_DATA);
            that.open(target, context);
            that.trigger(OPEN, {
                target: target,
                context: context
            });
        },
        destroy: function() {
            Widget.fn.destroy.call(this);
            this.shim.destroy();
        },
        _click: function(e) {
            if (e.isDefaultPrevented()) {
                return;
            }
            var action = $(e.currentTarget).data("action");
            if (action) {
                kendo.getter(action)(window)({
                    target: this.target,
                    context: this.context
                });
            }
            e.preventDefault();
            this.close();
        }
    });
    ui.plugin(ActionSheet);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, mobile = kendo.mobile, ui = mobile.ui, Widget = ui.Widget, support = kendo.support, os = support.mobileOS, ANDROID3UP = os.android && os.flatVersion >= 300, CLICK = "click";
    function highlightButton(widget, event, highlight) {
        $(event.target).closest(".km-button,.km-detail").toggleClass("km-state-active", highlight);
        if (ANDROID3UP && widget.deactivateTimeoutID) {
            clearTimeout(widget.deactivateTimeoutID);
            widget.deactivateTimeoutID = 0;
        }
    }
    var Button = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            that._wrap();
            that._style();
            that.element.on("up", "_release").on("down", "_activate").on("up cancel", "_deactivate");
            if (ANDROID3UP) {
                that.element.on("move", "_timeoutDeactivate");
            }
        },
        events: [ CLICK ],
        options: {
            name: "Button",
            icon: "",
            style: ""
        },
        _timeoutDeactivate: function(e) {
            if (!this.deactivateTimeoutID) {
                this.deactivateTimeoutID = setTimeout(highlightButton, 500, this, e, false);
            }
        },
        _activate: function(e) {
            var activeElement = document.activeElement, nodeName = activeElement ? activeElement.nodeName : "";
            highlightButton(this, e, true);
            if (nodeName == "INPUT" || nodeName == "TEXTAREA") {
                activeElement.blur();
            }
        },
        _deactivate: function(e) {
            highlightButton(this, e, false);
        },
        _release: function(e) {
            var that = this;
            if (e.which > 1) {
                return;
            }
            if (that.trigger(CLICK, {
                target: $(e.target),
                button: that.element
            })) {
                e.preventDefault();
            }
        },
        _style: function() {
            var style = this.options.style, element = this.element, styles;
            if (style) {
                styles = style.split(" ");
                $.each(styles, function() {
                    element.addClass("km-" + this);
                });
            }
        },
        _wrap: function() {
            var that = this, icon = that.options.icon, iconSpan = '<span class="km-icon km-' + icon, element = that.element.addClass("km-button"), span = element.children("span:not(.km-icon)").addClass("km-text"), image = element.find("img").addClass("km-image");
            if (!span[0] && element.html()) {
                span = element.wrapInner('<span class="km-text" />').children("span.km-text");
            }
            if (!image[0] && icon) {
                if (!span[0]) {
                    iconSpan += " km-notext";
                }
                element.prepend($(iconSpan + '" />'));
            }
        }
    });
    var BackButton = Button.extend({
        options: {
            name: "BackButton",
            style: "back"
        },
        init: function(element, options) {
            var that = this;
            Button.fn.init.call(that, element, options);
            if (typeof that.element.attr("href") === "undefined") {
                that.element.attr("href", "#:back");
            }
        }
    });
    var DetailButton = Button.extend({
        options: {
            name: "DetailButton",
            style: ""
        },
        init: function(element, options) {
            Button.fn.init.call(this, element, options);
        },
        _style: function() {
            var style = this.options.style + " detail", element = this.element;
            if (style) {
                var styles = style.split(" ");
                $.each(styles, function() {
                    element.addClass("km-" + this);
                });
            }
        },
        _wrap: function() {
            var that = this, icon = that.options.icon, iconSpan = '<span class="km-icon km-' + icon, element = that.element, span = element.children("span"), image = element.find("img").addClass("km-image");
            if (!image[0] && icon) {
                if (!span[0]) {
                    iconSpan += " km-notext";
                }
                element.prepend($(iconSpan + '" />'));
            }
        }
    });
    ui.plugin(Button);
    ui.plugin(BackButton);
    ui.plugin(DetailButton);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, ACTIVE = "km-state-active", SELECT = "select", SELECTOR = "li:not(." + ACTIVE + ")";
    var ButtonGroup = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            that.element.addClass("km-buttongroup").find("li").each(that._button);
            that.element.on(that.options.selectOn, SELECTOR, "_select");
            that.select(that.options.index);
        },
        events: [ SELECT ],
        options: {
            name: "ButtonGroup",
            selectOn: "down",
            index: -1
        },
        current: function() {
            return this.element.find("." + ACTIVE);
        },
        select: function(li) {
            var that = this, index = -1;
            if (li === undefined || li === -1) {
                return;
            }
            that.current().removeClass(ACTIVE);
            if (typeof li === "number") {
                index = li;
                li = $(that.element[0].children[li]);
            } else if (li.nodeType) {
                li = $(li);
                index = li.index();
            }
            li.addClass(ACTIVE);
            that.selectedIndex = index;
        },
        _button: function() {
            var button = $(this).addClass("km-button"), icon = kendo.attrValue(button, "icon"), span = button.children("span"), image = button.find("img").addClass("km-image");
            if (!span[0]) {
                span = button.wrapInner("<span/>").children("span");
            }
            span.addClass("km-text");
            if (!image[0] && icon) {
                button.prepend($('<span class="km-icon km-' + icon + '"/>'));
            }
        },
        _select: function(e) {
            if (e.which > 1 || e.isDefaultPrevented()) {
                return;
            }
            var that = this;
            that.select(e.currentTarget);
            that.trigger(SELECT);
        }
    });
    ui.plugin(ButtonGroup);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, Node = window.Node, mobile = kendo.mobile, ui = mobile.ui, DataSource = kendo.data.DataSource, Widget = ui.Widget, ITEM_SELECTOR = ".km-list > li, > li:not(.km-group-container)", HIGHLIGHT_SELECTOR = ".km-listview-link, .km-listview-label", proxy = $.proxy, attrValue = kendo.attrValue, GROUP_CLASS = "km-group-title", ACTIVE_CLASS = "km-state-active", GROUP_WRAPPER = '<div class="' + GROUP_CLASS + '"><div class="km-text"></div></div>', GROUP_TEMPLATE = kendo.template('<li><div class="' + GROUP_CLASS + '"><div class="km-text">#= this.headerTemplate(data) #</div></div><ul>#= kendo.render(this.template, data.items)#</ul></li>'), WRAPPER = '<div class="km-listview-wrapper" />', SEARCH_TEMPLATE = kendo.template('<form class="km-filter-form"><div class="km-filter-wrap"><input type="search" placeholder="#=placeholder#"/><a href="\\#" class="km-filter-reset" title="Clear"><span class="km-icon km-clear"></span><span class="km-text">Clear</span></a></div></form>'), NS = ".kendoMobileListView", LAST_PAGE_REACHED = "lastPageReached", CLICK = "click", CHANGE = "change", PROGRESS = "progress", FUNCTION = "function", whitespaceRegExp = /^\s+$/, buttonRegExp = /button/;
    function whitespace() {
        return this.nodeType === Node.TEXT_NODE && this.nodeValue.match(whitespaceRegExp);
    }
    function addIcon(item, icon) {
        if (icon) {
            item.prepend('<span class="km-icon km-' + icon + '"/>');
        }
    }
    function enhanceItem(item) {
        addIcon(item, attrValue(item, "icon"));
    }
    function enhanceLinkItem(item) {
        var parent = item.parent(), itemAndDetailButtons = item.add(parent.children(kendo.roleSelector("detailbutton"))), otherNodes = parent.contents().not(itemAndDetailButtons).not(whitespace);
        if (otherNodes.length) {
            return;
        }
        item.addClass("km-listview-link").attr(kendo.attr("role"), "listview-link");
        addIcon(item, attrValue(parent, "icon"));
    }
    function enhanceCheckBoxItem(label) {
        if (!label.children("input[type=checkbox],input[type=radio]").length) {
            return;
        }
        var item = label.parent();
        if (item.contents().not(label).not(function() {
            return this.nodeType == 3;
        })[0]) {
            return;
        }
        label.addClass("km-listview-label");
    }
    var ListView = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            options = that.options;
            if (options.scrollTreshold) {
                options.scrollThreshold = options.scrollTreshold;
            }
            that._dragged = false;
            element.on("down", HIGHLIGHT_SELECTOR, "_highlight").on("move up cancel", HIGHLIGHT_SELECTOR, "_dim").on("down", ITEM_SELECTOR, function() {
                that._dragged = false;
            }).on("move", ITEM_SELECTOR, function() {
                that._dragged = true;
            }).on("up", ITEM_SELECTOR, "_click");
            element.wrap(WRAPPER);
            that.wrapper = that.element.parent();
            that._footer();
            that._dataSource();
            that._bindScroller();
            that._fixHeaders();
            that._filterable();
            if (options.dataSource && that.options.autoBind) {
                that.dataSource.fetch();
            } else {
                that._style();
            }
            kendo.notify(that, ui);
        },
        events: [ CLICK, "dataBound", LAST_PAGE_REACHED ],
        options: {
            name: "ListView",
            style: "",
            type: "flat",
            autoBind: true,
            fixedHeaders: false,
            template: "#:data#",
            headerTemplate: '<span class="km-text">#:value#</span>',
            appendOnRefresh: false,
            loadMore: false,
            loadMoreText: "Press to load more",
            endlessScroll: false,
            scrollThreshold: 30,
            pullToRefresh: false,
            pullTemplate: "Pull to refresh",
            releaseTemplate: "Release to refresh",
            refreshTemplate: "Refreshing",
            pullOffset: 140,
            filterable: false
        },
        setOptions: function(options) {
            Widget.fn.setOptions.call(this, options);
        },
        setDataSource: function(dataSource) {
            this.options.dataSource = dataSource;
            this._dataSource();
            if (this.options.autoBind) {
                dataSource.fetch();
            }
        },
        destroy: function() {
            var that = this;
            Widget.fn.destroy.call(that);
            that._unbindDataSource();
            that.stopEndlessScrolling();
            that.stopLoadMore();
            kendo.destroy(that.element);
        },
        refresh: function(e) {
            e = e || {};
            var that = this, element = that.element, options = that.options, dataSource = that.dataSource, view = dataSource.view(), loading = that.loading, appendMethod = "html", action = e.action, items = e.items, idx = 0, contents, groups, length, data, item;
            if (action === "itemchange") {
                data = items[0];
                item = $(that.template(data));
                element.find("[data-" + kendo.ns + "uid=" + data.uid + "]").replaceWith(item);
                that.trigger("itemChange", {
                    item: item,
                    data: data,
                    ns: ui
                });
                that._style();
                return;
            } else if (action === "add") {
                length = items.length;
                for (;idx < length; idx++) {
                    element.append(that.template(items[idx]));
                }
                that._style();
                return;
            } else if (action === "remove") {
                length = items.length;
                for (;idx < length; idx++) {
                    element.find("[data-" + kendo.ns + "uid=" + items[idx].uid + "]").remove();
                }
                return;
            }
            if (!that.template) {
                that._templates();
            }
            that._cacheDataItems(view);
            that.trigger("dataBinding");
            groups = dataSource.group();
            if (groups && groups[0]) {
                options.type = "group";
                contents = kendo.render(that.groupTemplate, view);
            } else {
                contents = kendo.render(that.template, view);
            }
            if (loading) {
                appendMethod = "append";
            } else if (options.appendOnRefresh && !that._filter) {
                appendMethod = "prepend";
            }
            that._filter = false;
            contents = $(contents);
            element[appendMethod](contents);
            mobile.init(contents);
            if (loading) {
                that.loading = false;
                that._calcThreshold();
                that._toggleLoader(false);
            }
            if (options.pullToRefresh) {
                that._scroller().pullHandled();
            }
            that._hideLoading();
            that._shouldFixHeaders();
            that._style();
            that._invalidateLoadMore();
            that.trigger("dataBound", {
                ns: ui
            });
        },
        _invalidateLoadMore: function() {
            var that = this, options = that.options, dataSource = that.dataSource, shouldInit = that._stopLoadMore && (!dataSource.total() || dataSource.page() < dataSource.totalPages());
            if (shouldInit) {
                if (options.endlessScroll) {
                    that.initEndlessScrolling();
                }
                if (options.loadMore) {
                    that.initLoadMore();
                }
            }
        },
        _cacheDataItems: function(view) {
            var that = this, item;
            if (!that.element[0].firstChild) {
                that._firstOrigin = that._first = view[0];
                that._last = view[view.length - 1];
            }
            if (that._pulled) {
                item = view[0];
                that._pulled = false;
                if (item) {
                    that._first = item;
                }
            }
            if (that.loading) {
                item = view[view.length - 1];
                if (item) {
                    that._last = item;
                }
            }
        },
        items: function() {
            if (this.options.type === "group") {
                return this.element.find(".km-list").children();
            } else {
                return this.element.children();
            }
        },
        initEndlessScrolling: function() {
            this._stopLoadMore = false;
            this._scroller().setOptions({
                resize: this._scrollerResize,
                scroll: this._scrollerScroll
            });
        },
        stopEndlessScrolling: function() {
            var that = this, scroller = that._scroller();
            if (scroller && that._loadIcon) {
                that.loading = false;
                that._stopLoadMore = true;
                that._loadIcon.parent().hide();
                scroller.unbind("resize", that._scrollerResize).unbind("scroll", that._scrollerScroll);
                that.trigger(LAST_PAGE_REACHED);
            }
        },
        initLoadMore: function() {
            var that = this;
            that._stopLoadMore = false;
            that._loadButton.autoApplyNS().on("up", proxy(that._nextPage, that));
        },
        stopLoadMore: function() {
            var that = this;
            if (that._loadButton) {
                that._stopLoadMore = true;
                that.loading = false;
                that._loadButton.kendoDestroy().parent().hide();
                that.trigger(LAST_PAGE_REACHED);
            }
        },
        _dim: function(e) {
            this._toggle(e, false);
        },
        _highlight: function(e) {
            this._toggle(e, true);
        },
        _toggle: function(e, highlight) {
            if (e.which > 1) {
                return;
            }
            var clicked = $(e.currentTarget), item = clicked.parent(), role = attrValue(clicked, "role") || "", plainItem = !role.match(buttonRegExp), prevented = e.isDefaultPrevented();
            if (plainItem) {
                item.toggleClass(ACTIVE_CLASS, highlight && !prevented);
            }
        },
        _unbindDataSource: function() {
            var that = this;
            that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(PROGRESS, that._progressHandler);
        },
        _dataSource: function() {
            var that = this, options = that.options;
            if (that.dataSource && that._refreshHandler) {
                that._unbindDataSource();
            } else {
                that._refreshHandler = proxy(that.refresh, that);
                that._progressHandler = proxy(that._showLoading, that);
            }
            that.dataSource = DataSource.create(options.dataSource).bind(CHANGE, that._refreshHandler);
            if (!options.pullToRefresh && !options.loadMore && !options.endlessScroll) {
                that.dataSource.bind(PROGRESS, that._progressHandler);
            }
        },
        _fixHeader: function(e) {
            if (this.fixedHeaders) {
                var i = 0, that = this, scroller = that._scroller(), scrollTop = e.scrollTop, headers = that.headers, headerPair, offset, header;
                do {
                    headerPair = headers[i++];
                    if (!headerPair) {
                        header = $("<div />");
                        break;
                    }
                    offset = headerPair.offset;
                    header = headerPair.header;
                } while (offset > scrollTop);
                if (that.currentHeader != i) {
                    scroller.fixedContainer.html(header.clone());
                    that.currentHeader = i;
                }
            }
        },
        _shouldFixHeaders: function() {
            this.fixedHeaders = this.options.type === "group" && this.options.fixedHeaders;
        },
        _cacheHeaders: function() {
            var that = this, headers = [];
            if (that.fixedHeaders) {
                that.element.find("." + GROUP_CLASS).each(function(_, header) {
                    header = $(header);
                    headers.unshift({
                        offset: header.position().top,
                        header: header
                    });
                });
                that.headers = headers;
                that._fixHeader({
                    scrollTop: 0
                });
            }
        },
        _fixHeaders: function() {
            var that = this, scroller = that._scroller();
            that._shouldFixHeaders();
            if (scroller) {
                kendo.onResize(function() {
                    that._cacheHeaders();
                });
                scroller.bind("scroll", function(e) {
                    that._fixHeader(e);
                });
            }
        },
        _bindScroller: function() {
            var that = this, options = that.options, scroller = that._scroller();
            if (!scroller) {
                return;
            }
            if (options.pullToRefresh) {
                scroller.setOptions({
                    pullToRefresh: true,
                    pull: function() {
                        var callback = options.pullParameters, params = {
                            page: 1
                        };
                        if (callback) {
                            params = callback.call(that, that._first);
                        }
                        that._pulled = true;
                        that.dataSource.read(params);
                    },
                    pullTemplate: options.pullTemplate,
                    releaseTemplate: options.releaseTemplate,
                    refreshTemplate: options.refreshTemplate
                });
            }
            if (options.endlessScroll) {
                that._scrollHeight = scroller.element.height();
                that._scrollerResize = function() {
                    that._scrollHeight = scroller.element.height();
                    that._calcThreshold();
                }, that._scrollerScroll = function(e) {
                    if (!that.loading && e.scrollTop + that._scrollHeight > that._threshold) {
                        that._nextPage();
                    }
                };
                that.initEndlessScrolling();
            }
        },
        _calcThreshold: function() {
            var that = this, scroller = that._scroller();
            if (scroller) {
                that._threshold = scroller.scrollHeight() - that.options.scrollThreshold;
            }
        },
        _nextPage: function() {
            var that = this, options = that.options, callback = options.endlessScrollParameters || options.loadMoreParameters, params;
            that.loading = true;
            that._toggleLoader(true);
            if (callback) {
                params = callback.call(that, that._firstOrigin, that._last);
            }
            if (!that.dataSource.next(params)) {
                that.stopLoadMore();
                that.stopEndlessScrolling();
            }
        },
        _templates: function() {
            var that = this, template = that.options.template, headerTemplate = that.options.headerTemplate, dataIDAttribute = ' data-uid="#=data.uid || ""#"', templateProxy = {}, groupTemplateProxy = {};
            if (typeof template === FUNCTION) {
                templateProxy.template = template;
                template = "#=this.template(data)#";
            }
            groupTemplateProxy.template = that.template = proxy(kendo.template("<li" + dataIDAttribute + ">" + template + "</li>"), templateProxy);
            if (typeof headerTemplate === FUNCTION) {
                groupTemplateProxy._headerTemplate = headerTemplate;
                headerTemplate = "#=this._headerTemplate(data)#";
            }
            groupTemplateProxy.headerTemplate = kendo.template(headerTemplate);
            that.groupTemplate = proxy(GROUP_TEMPLATE, groupTemplateProxy);
        },
        _click: function(e) {
            if (e.which > 1 || e.isDefaultPrevented()) {
                return;
            }
            if (this._dragged) {
                e.preventDefault();
                return;
            }
            var that = this, dataItem, item = $(e.currentTarget), target = $(e.target), buttonElement = target.closest(kendo.roleSelector("button", "detailbutton", "backbutton")), button = kendo.widgetInstance(buttonElement, ui), id = item.attr(kendo.attr("uid"));
            if (id) {
                dataItem = that.dataSource.getByUid(id);
            }
            if (that.trigger(CLICK, {
                target: target,
                item: item,
                dataItem: dataItem,
                button: button
            })) {
                e.preventDefault();
            }
        },
        _style: function() {
            var that = this, options = that.options, grouped = options.type === "group", element = that.element, inset = options.style === "inset";
            element.addClass("km-listview").toggleClass("km-list", !grouped).toggleClass("km-listinset", !grouped && inset).toggleClass("km-listgroup", grouped && !inset).toggleClass("km-listgroupinset", grouped && inset);
            if (grouped) {
                element.children().children("ul").addClass("km-list");
                element.children("li").each(function() {
                    var li = $(this), groupHeader = li.contents().first();
                    li.addClass("km-group-container");
                    if (!groupHeader.is("ul") && !groupHeader.is("div." + GROUP_CLASS)) {
                        groupHeader.wrap(GROUP_WRAPPER);
                    }
                });
            }
            that._enhanceItems();
            if (!element.parents(".km-listview")[0]) {
                element.closest(".km-content").toggleClass("km-insetcontent", inset);
            }
            that._cacheHeaders();
        },
        _enhanceItems: function() {
            this.items().each(function() {
                var item = $(this), child, enhanced = false;
                item.children().each(function() {
                    child = $(this);
                    if (child.is("a")) {
                        enhanceLinkItem(child);
                        enhanced = true;
                    } else if (child.is("label")) {
                        enhanceCheckBoxItem(child);
                        enhanced = true;
                    }
                });
                if (!enhanced) {
                    enhanceItem(item);
                }
            });
        },
        _footer: function() {
            var that = this, options = that.options, loadMore = options.loadMore, loadWrapper;
            if (loadMore || options.endlessScroll) {
                that._loadIcon = $('<span style="display:none" class="km-icon"></span>');
                loadWrapper = $('<span class="km-load-more"></span>').append(that._loadIcon);
                if (loadMore) {
                    that._loadButton = $('<button class="km-load km-button">' + options.loadMoreText + "</button>");
                    that.initLoadMore();
                    loadWrapper.append(that._loadButton);
                }
                that.wrapper.append(loadWrapper);
            }
        },
        _toggleLoader: function(toggle) {
            var that = this, icon = that._loadIcon, button = that._loadButton;
            if (button) {
                button.toggle(!toggle);
            }
            if (toggle) {
                icon.parent().addClass("km-scroller-refresh");
                icon.css("display", "block");
            } else {
                icon.hide();
                icon.parent().removeClass("km-scroller-refresh");
            }
        },
        _scroller: function() {
            var that = this, view;
            if (!that._scrollerInstance) {
                view = that.view();
                that._scrollerInstance = view && view.scroller;
            }
            return that._scrollerInstance;
        },
        _showLoading: function() {
            var view = this.view();
            if (view && view.loader) {
                view.loader.show();
            }
        },
        _hideLoading: function() {
            var view = this.view();
            if (view && view.loader) {
                view.loader.hide();
            }
        },
        _filterable: function() {
            var that = this, filterable = that.options.filterable, events = "change paste";
            if (filterable) {
                that.element.before(SEARCH_TEMPLATE({
                    placeholder: filterable.placeholder || "Search..."
                }));
                if (filterable.autoFilter !== false) {
                    events += " keyup";
                }
                that.searchInput = that.wrapper.find("input[type=search]").closest("form").on("submit" + NS, function(e) {
                    e.preventDefault();
                }).end().on("focus" + NS, function() {
                    that._oldFilter = that.searchInput.val();
                }).on(events.split(" ").join(NS + " ") + NS, proxy(that._filterChange, that));
                that.clearButton = that.wrapper.find(".km-filter-reset").on(CLICK, proxy(that._clearFilter, that)).hide();
            }
        },
        _search: function(expr) {
            this._filter = true;
            this.clearButton[expr ? "show" : "hide"]();
            this.dataSource.filter(expr);
        },
        _filterChange: function(e) {
            var that = this;
            if (e.type == "paste" && that.options.filterable.autoFilter !== false) {
                setTimeout(function() {
                    that._applyFilter();
                }, 1);
            } else {
                that._applyFilter();
            }
        },
        _applyFilter: function() {
            var that = this, filterable = that.options.filterable, value = that.searchInput.val(), expr = value.length ? {
                field: filterable.field,
                operator: filterable.operator || "startsWith",
                ignoreCase: filterable.ignoreCase,
                value: value
            } : null;
            if (value === that._oldFilter) {
                return;
            }
            that._oldFilter = value;
            that._search(expr);
        },
        _clearFilter: function(e) {
            this.searchInput.val("");
            this._search(null);
            e.preventDefault();
        }
    });
    ui.plugin(ListView);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.mobile.ui, roleSelector = kendo.roleSelector, Widget = ui.Widget;
    function createContainer(align, element) {
        var items = element.find("[" + kendo.attr("align") + "=" + align + "]");
        if (items[0]) {
            element.prepend($('<div class="km-' + align + 'item" />').append(items));
        }
    }
    function toggleTitle(centerElement) {
        var siblings = centerElement.siblings();
        centerElement.toggleClass("km-show-title", !!siblings[0] && $.trim(centerElement.text()) === "");
        centerElement.toggleClass("km-no-title", !!centerElement.children("ul")[0]);
        centerElement.toggleClass("km-hide-title", centerElement.css("visibility") == "hidden" && !siblings.children().is(":visible"));
    }
    var NavBar = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            that.container().bind("show", $.proxy(this, "refresh"));
            element.addClass("km-navbar").wrapInner($('<div class="km-view-title" />'));
            createContainer("left", element);
            createContainer("right", element);
            that.centerElement = element.find(".km-view-title");
        },
        options: {
            name: "NavBar"
        },
        title: function(value) {
            this.element.find(roleSelector("view-title")).text(value);
            toggleTitle(this.centerElement);
        },
        refresh: function(e) {
            var view = e.view;
            if (view.options.title) {
                this.title(view.options.title);
            } else {
                toggleTitle(this.centerElement);
            }
        },
        destroy: function() {
            Widget.fn.destroy.call(this);
            kendo.destroy(this.element);
        }
    });
    ui.plugin(NavBar);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, mobile = kendo.mobile, ui = mobile.ui, proxy = $.proxy, Transition = kendo.fx.Transition, Pane = kendo.ui.Pane, PaneDimensions = kendo.ui.PaneDimensions, Widget = ui.Widget, // Math
    math = Math, abs = math.abs, ceil = math.ceil, round = math.round, max = math.max, min = math.min, floor = math.floor, CHANGE = "change", CURRENT_PAGE_CLASS = "km-current-page";
    var ScrollView = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            element = that.element;
            element.wrapInner("<div/>").addClass("km-scrollview").append('<ol class="km-pages"/>');
            that.inner = element.children().first();
            that.pager = element.children().last();
            that.page = 0;
            that.inner.css("height", that.options.contentHeight);
            that.container().bind("show", $.proxy(this, "viewShow")).bind("init", $.proxy(this, "viewInit"));
            var movable, transition, userEvents, dimensions, dimension, pane;
            movable = new kendo.ui.Movable(that.inner);
            transition = new Transition({
                axis: "x",
                movable: movable,
                onEnd: proxy(that._transitionEnd, that)
            });
            userEvents = new kendo.UserEvents(element, {
                start: function(e) {
                    if (abs(e.x.velocity) * 2 >= abs(e.y.velocity)) {
                        userEvents.capture();
                    } else {
                        userEvents.cancel();
                    }
                    transition.cancel();
                },
                allowSelection: true,
                end: proxy(that._dragEnd, that)
            });
            dimensions = new PaneDimensions({
                element: that.inner,
                container: that.element
            });
            dimension = dimensions.x;
            dimension.bind(CHANGE, proxy(that.refresh, that));
            pane = new Pane({
                dimensions: dimensions,
                userEvents: userEvents,
                movable: movable,
                elastic: true
            });
            $.extend(that, {
                movable: movable,
                transition: transition,
                userEvents: userEvents,
                dimensions: dimensions,
                dimension: dimension,
                pane: pane
            });
            that.page = that.options.page;
        },
        options: {
            name: "ScrollView",
            page: 0,
            duration: 300,
            velocityThreshold: .8,
            contentHeight: "auto",
            pageSize: 1,
            bounceVelocityThreshold: 1.6
        },
        events: [ CHANGE ],
        destroy: function() {
            Widget.fn.destroy.call(this);
            this.userEvents.destroy();
            kendo.destroy(this.element);
        },
        viewInit: function() {
            this.movable.moveAxis("x", -this.page * this.dimension.getSize());
        },
        viewShow: function() {
            this.dimensions.refresh();
        },
        refresh: function() {
            var that = this, pageHTML = "", dimension = that.dimension, width = dimension.getSize(), pages, pageElements = that.element.find("[data-role=page]");
            pageElements.width(width);
            dimension.update(true);
            // if no pages present, try to retain the current position
            if (!pageElements[0]) {
                that.page = Math.floor(-that.movable.x / width);
            }
            that.scrollTo(that.page, true);
            pages = that.pages = ceil(dimension.getTotal() / width);
            that.minSnap = -(pages - 1) * width;
            that.maxSnap = 0;
            for (var idx = 0; idx < pages; idx++) {
                pageHTML += "<li/>";
            }
            that.pager.html(pageHTML);
            that._updatePager();
        },
        content: function(html) {
            this.element.children().first().html(html);
            this.dimensions.refresh();
        },
        scrollTo: function(page, instant) {
            this.page = page;
            this._moveTo(-page * this.dimension.getSize(), Transition.easeOutExpo, instant);
        },
        _moveTo: function(location, ease, instant) {
            this.transition.moveTo({
                location: location,
                duration: instant ? 1 : this.options.duration,
                ease: ease
            });
        },
        _dragEnd: function(e) {
            var that = this, velocity = e.x.velocity, width = that.dimension.size * that.options.pageSize, options = that.options, velocityThreshold = options.velocityThreshold, snap, approx = round, ease = Transition.easeOutExpo;
            if (velocity > velocityThreshold) {
                approx = ceil;
            } else if (velocity < -velocityThreshold) {
                approx = floor;
            }
            if (abs(velocity) > options.bounceVelocityThreshold) {
                ease = Transition.easeOutBack;
            }
            snap = max(that.minSnap, min(approx(that.movable.x / width) * width, that.maxSnap));
            this._moveTo(snap, ease);
        },
        _transitionEnd: function() {
            var that = this, page = Math.round(-that.movable.x / that.dimension.size);
            if (page != that.page) {
                that.page = page;
                that.trigger(CHANGE, {
                    page: page
                });
                that._updatePager();
            }
        },
        _updatePager: function() {
            this.pager.children().removeClass(CURRENT_PAGE_CLASS).eq(this.page).addClass(CURRENT_PAGE_CLASS);
        }
    });
    ui.plugin(ScrollView);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, support = kendo.support, CHANGE = "change", SWITCHON = "km-switch-on", SWITCHOFF = "km-switch-off", MARGINLEFT = "margin-left", ACTIVE_STATE = "km-state-active", TRANSFORMSTYLE = support.transitions.css + "transform", proxy = $.proxy;
    function limitValue(value, minLimit, maxLimit) {
        return Math.max(minLimit, Math.min(maxLimit, value));
    }
    var Switch = Widget.extend({
        init: function(element, options) {
            var that = this, checked;
            Widget.fn.init.call(that, element, options);
            that._wrapper();
            that._drag();
            that._background();
            that.origin = parseInt(that.background.css(MARGINLEFT), 10);
            that._handle();
            that.constrain = 0;
            that.snapPoint = 0;
            that.container().bind("show", $.proxy(this, "refresh"));
            element = that.element[0];
            element.type = "checkbox";
            that._animateBackground = true;
            checked = that.options.checked;
            if (checked === null) {
                checked = element.checked;
            }
            that.check(checked);
            that.refresh();
            kendo.notify(that, kendo.mobile.ui);
        },
        refresh: function() {
            var that = this, width, handleWidth;
            width = that.wrapper.width();
            handleWidth = that.handle.outerWidth(true);
            that.constrain = width - handleWidth;
            that.snapPoint = width / 2 - handleWidth / 2;
            that.background.data("origin", that.origin);
            that.check(that.element[0].checked);
        },
        events: [ CHANGE ],
        options: {
            name: "Switch",
            onLabel: "ON",
            offLabel: "OFF",
            checked: null
        },
        check: function(check) {
            var that = this, element = that.element[0];
            if (check === undefined) {
                return element.checked;
            }
            that._position(check ? that.constrain : 0);
            element.checked = check;
            that.wrapper.toggleClass(SWITCHON, check).toggleClass(SWITCHOFF, !check);
        },
        destroy: function() {
            Widget.fn.destroy.call(this);
            this.userEvents.destroy();
        },
        toggle: function() {
            var that = this;
            that.check(!that.element[0].checked);
        },
        _move: function(e) {
            var that = this;
            e.preventDefault();
            that._position(limitValue(that.position + e.x.delta, 0, that.constrain));
        },
        _position: function(position) {
            var that = this;
            that.position = position;
            that.handle.css(TRANSFORMSTYLE, "translatex(" + position + "px)");
            if (that._animateBackground) {
                that.background.css(MARGINLEFT, that.origin + position);
            }
        },
        _start: function() {
            this.userEvents.capture();
            this.handle.addClass(ACTIVE_STATE);
        },
        _stop: function() {
            var that = this;
            that.handle.removeClass(ACTIVE_STATE);
            that._toggle(that.position > that.snapPoint);
        },
        _toggle: function(checked) {
            var that = this, handle = that.handle, element = that.element[0], value = element.checked, duration = kendo.mobile.application && kendo.mobile.application.os.wp ? 100 : 200, distance;
            that.wrapper.toggleClass(SWITCHON, checked).toggleClass(SWITCHOFF, !checked);
            that.position = distance = checked * that.constrain;
            if (that._animateBackground) {
                that.background.kendoStop(true, true).kendoAnimate({
                    effects: "slideMargin",
                    offset: distance,
                    reset: true,
                    reverse: !checked,
                    axis: "left",
                    duration: duration
                });
            }
            handle.kendoStop(true, true).kendoAnimate({
                effects: "slideTo",
                duration: duration,
                offset: distance + "px,0",
                reset: true,
                complete: function() {
                    if (value !== checked) {
                        element.checked = checked;
                        that.trigger(CHANGE, {
                            checked: checked
                        });
                    }
                }
            });
        },
        _background: function() {
            var that = this, background;
            background = $("<span class='km-switch-wrapper'><span class='km-switch-background'></span></span>").appendTo(that.wrapper).children(".km-switch-background");
            that.background = background;
        },
        _handle: function() {
            var that = this, options = that.options;
            that.handle = $("<span class='km-switch-container'><span class='km-switch-handle' /></span>").appendTo(that.wrapper).children(".km-switch-handle");
            that.handle.append('<span class="km-switch-label-on">' + options.onLabel + '</span><span class="km-switch-label-off">' + options.offLabel + "</span>");
        },
        _wrapper: function() {
            var that = this, element = that.element, wrapper = element.parent("span.km-switch");
            if (!wrapper[0]) {
                wrapper = element.wrap('<span class="km-switch"/>').parent();
            }
            that.wrapper = wrapper;
        },
        _drag: function() {
            var that = this;
            that.userEvents = new kendo.UserEvents(that.wrapper, {
                tap: function() {
                    that._toggle(!that.element[0].checked);
                },
                start: proxy(that._start, that),
                move: proxy(that._move, that),
                end: proxy(that._stop, that)
            });
        }
    });
    ui.plugin(Switch);
})(window.kendo.jQuery);

(function($, undefined) {
    var kendo = window.kendo, ui = kendo.mobile.ui, Widget = ui.Widget, ACTIVE_STATE_CLASS = "km-state-active", SELECT = "select";
    var TabStrip = Widget.extend({
        init: function(element, options) {
            var that = this;
            Widget.fn.init.call(that, element, options);
            that.container().bind("show", $.proxy(this, "refresh"));
            that.element.addClass("km-tabstrip").find("a").each(that._buildButton).eq(that.options.selectedIndex).addClass(ACTIVE_STATE_CLASS);
            that.element.on("down", "a", "_release");
        },
        events: [ SELECT ],
        switchTo: function(url) {
            var tabs = this.element.find("a"), tab, path, idx = 0, length = tabs.length;
            for (;idx < length; idx++) {
                tab = tabs[idx];
                path = tab.href.replace(/(\#.+)(\?.+)$/, "$1");
                // remove the fragment query string - http://www.foo.com?foo#bar**?baz=qux**
                if (path.indexOf(url, path.length - url.length) !== -1) {
                    this._setActiveItem($(tab));
                    return;
                }
            }
        },
        clear: function() {
            this.currentItem().removeClass(ACTIVE_STATE_CLASS);
        },
        currentItem: function() {
            return this.element.children("." + ACTIVE_STATE_CLASS);
        },
        _release: function(e) {
            if (e.which > 1) {
                return;
            }
            var that = this, item = $(e.currentTarget);
            if (item[0] === that.currentItem()[0]) {
                return;
            }
            if (that.trigger(SELECT, {
                item: item
            })) {
                e.preventDefault();
            } else {
                that._setActiveItem(item);
            }
        },
        _setActiveItem: function(item) {
            if (!item[0]) {
                return;
            }
            this.clear();
            item.addClass(ACTIVE_STATE_CLASS);
        },
        _buildButton: function() {
            var button = $(this), icon = kendo.attrValue(button, "icon"), image = button.find("img"), iconSpan = $('<span class="km-icon"/>');
            button.addClass("km-button").attr(kendo.attr("role"), "tab").contents().not(image).wrapAll('<span class="km-text"/>');
            if (image[0]) {
                image.addClass("km-image");
            } else {
                button.prepend(iconSpan);
                if (icon) {
                    iconSpan.addClass("km-" + icon);
                }
            }
        },
        refresh: function(e) {
            this.switchTo(e.view.id);
        },
        destroy: function() {
            Widget.fn.destroy.call(this);
        },
        options: {
            name: "TabStrip",
            selectedIndex: 0,
            enable: true
        }
    });
    ui.plugin(TabStrip);
})(window.kendo.jQuery);