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

app.controller('MainCtrl', function($scope, $compile) {

  $scope.gridDataSource = [ { name: "Jane Doe" }, { name: "John Doe" } ];
  $scope.isChecked = function(dataItem) {
    console.log("isChecked called");
    return dataItem.name == "Jane Doe";
  }
     
});
<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8">
    <title>Checkbox Column Editor</title>
    <link href="http://cdn.kendostatic.com/2013.3.1119/styles/kendo.common.min.css" rel="stylesheet">
    <link href="http://cdn.kendostatic.com/2013.3.1119/styles/kendo.bootstrap.min.css" rel="stylesheet">
    <style>.ng-cloak { display: none; }</style>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="//code.angularjs.org/1.2.11/angular.js"></script>
    <script src="http://cdn.kendostatic.com/2013.3.1119/js/kendo.all.min.js"></script>
    
    <!-- defect in 0.6.0 
    <script src="angular-kendo-0.6.0.js"></script>
    -->

    <script src="https://rawgithub.com/kendo-labs/angular-kendo/5ce9b23ff874fb4a174c7e70d3ef217e21208991/angular-kendo.js"></script>

    <script>
      document.write('<base href="' + document.location + '" />');
    </script>
    <script src="app.js"></script>
  </head>

<body ng-controller="MainCtrl">

  <div kendo-grid 
    k-columns='[
      { template: "<input type=\"checkbox\" ng-checked=\"isChecked(dataItem)\" />" },
      { field: "name" }
  	]' 
  	k-editable="true"
  	k-sortable="true"
    k-data-source="gridDataSource">
  </div>

</body>

</html>
(function(f, define){
  define([ "jquery", "angular", "kendo" ], f);
})(function($, angular, kendo) {

  "use strict";

  var _UID_ = kendo.attr("uid");

  var module = angular.module('kendo.directives', []);
  var parse, timeout, compile = function compile(){ return compile }, log;

  function immediately(f) {
    var save_timeout = timeout;
    timeout = function(f) { return f() };
    try {
      return f();
    } finally {
      timeout = save_timeout;
    }
  }

  // The following disables AngularJS built-in directives for <input> fields
  // when a Kendo widget is defined.  The reason we have to do this is:
  //
  // 1. user updates field.
  //
  // 2. widget triggers "change" event on the Widget object => angular-kendo
  //    gets notified, updates the model with the correct value!
  //
  // 3. widget triggers "change" event on the <input> field => AngularJS's
  //    built-in directive validates the *content* of the input field and
  //    updates the model again WITH THE WRONG VALUE.
  //
  // https://github.com/kendo-labs/angular-kendo/issues/135
  // https://github.com/kendo-labs/angular-kendo/issues/152
  module.config([ "$provide", function($provide){
    function dismissAngular($delegate) {
      var orig_compile = $delegate[0].compile;
      $delegate[0].compile = function(element, attrs) {
        for (var i in attrs) {
          if (attrs.hasOwnProperty(i)) {
            if (/^kendo/.test(i) && typeof $.fn[i] == "function") {
              return;           // HA!
            }
          }
        }
        return orig_compile.apply(this, arguments);
      };
      return $delegate;
    }
    $provide.decorator("inputDirective", [ "$delegate", dismissAngular ]);
    $provide.decorator("selectDirective", [ "$delegate", dismissAngular ]);
  }]);

  var OPTIONS_NOW;

  var factories = {
    dataSource: (function() {
      var types = {
        TreeView: 'HierarchicalDataSource',
        Scheduler: 'SchedulerDataSource'
      };
      var toDataSource = function(dataSource, type) {
        return kendo.data[type].create(dataSource);
      };
      return function(scope, element, attrs, role) {
        var type = types[role] || 'DataSource';
        var ds = toDataSource(scope.$eval(attrs.kDataSource), type);

        // 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', ds);

        // not recursive -- this triggers when the whole data source changed
        scope.$watch(attrs.kDataSource, function(mew, old){
          if (mew !== old) {
            var ds = toDataSource(mew, type);
            element.data('$kendoDataSource', ds);
            var widget = kendoWidgetInstance(element);
            if (widget && typeof widget.setDataSource == "function") {
              widget.setDataSource(ds);
            }
          }
        });
        return ds;
      };
    }()),

    widget: (function() {
      var ignoredAttributes = {
        kDataSource: true,
        kOptions: true,
        kRebind: true
      };
      return function(scope, element, attrs, widget) {
        var role = widget.replace(/^kendo/, '');
        var options = angular.extend({}, scope.$eval(attrs.kOptions));
        $.each(attrs, function(name, value) {
          if (!ignoredAttributes[name]) {
            var match = name.match(/^k(On)?([A-Z].*)/);
            if (match) {
              var optionName = match[2].charAt(0).toLowerCase() + match[2].slice(1);
              if (match[1]) {
                options[optionName] = value;
              } else {
                options[optionName] = angular.copy(scope.$eval(value));
                if (options[optionName] === undefined && value.match(/^\w*$/)) {
                  log.warn(widget + '\'s ' + name + ' attribute resolved to undefined. Maybe you meant to use a string literal like: \'' + value + '\'?');
                }
              }
            }
          }
        });
        options.dataSource = element.inheritedData('$kendoDataSource') || options.dataSource;

        // parse the datasource attribute
        if (attrs.kDataSource) {
          options.dataSource = factories.dataSource(scope, element, attrs, role);
        }

        options.$angular = true;
        var object = $(element)[widget](OPTIONS_NOW = options).data(widget);
        exposeWidget(object, scope, attrs, widget);
        scope.$emit("kendoWidgetCreated", object);
        return object;
      };
    }())
  };

  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]);
      }
    }
  }

  module.factory('directiveFactory', ['$timeout', '$parse', '$compile', '$log', function($timeout, $parse, $compile, $log) {

    timeout = $timeout;
    parse = $parse;
    compile = $compile;
    log = $log;

    var KENDO_COUNT = 0;

    var create = function(role) {

      return {
        // Parse the directive for attributes and classes
        restrict: "ACE",
        require: [ "?ngModel", "^?form" ],
        scope: false,

        // // XXX: Is this transclusion needed?  We seem to do better without it.
        // //      https://github.com/kendo-labs/angular-kendo/issues/90
        //
        // transclude: true,
        // 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, controllers) {

          var ngModel = controllers[0];
          var ngForm = controllers[1];

          // we must remove data-kendo-widget-name attribute because
          // it breaks kendo.widgetInstance; can generate all kinds
          // of funny issues like
          // https://github.com/kendo-labs/angular-kendo/issues/167

          // $(element).removeData(role);
          // console.log($(element).data(role)); // --> not undefined.  now I'm pissed.
          $(element)[0].removeAttribute("data-" + role.replace(/([A-Z])/g, "-$1"));

          var originalElement = $(element)[0].cloneNode(true);

          ++KENDO_COUNT;

          timeout(function() {
            var widget = factories.widget(scope, element, attrs, role);

            // 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.
                  try {
                    /****************************************************************
                     // XXX: this is a gross hack that might not even work with all
                     // widgets.  we need to destroy the current widget and get its
                     // wrapper element out of the DOM, then make the original element
                     // visible so we can initialize a new widget on it.
                     //
                     // kRebind is probably impossible to get right at the moment.
                     ****************************************************************/
                    var _wrapper = $(widget.wrapper)[0];
                    var _element = $(widget.element)[0];
                    widget.destroy();
                    if (_wrapper && _element) {
                      _wrapper.parentNode.replaceChild(_element, _wrapper);
                      var clone = originalElement.cloneNode(true);
                      $(element).replaceWith(clone);
                      element = $(clone);
                    }
                    widget = factories.widget(scope, element, attrs, role);
                    setupBindings();
                  } catch(ex) {
                    console.error(ex);
                    console.error(ex.stack);
                  }
                }
              }, true); // watch for object equality. Use native or simple values.
            }

            setupBindings();

            var prev_destroy = null;
            function setupBindings() {

              // Cleanup after ourselves
              if (prev_destroy) {
                prev_destroy();
              }
              prev_destroy = scope.$on("$destroy", function() {
                widget.destroy();
              });

              // 2 way binding: ngModel <-> widget.value()
              if (ngModel) {
                if (!widget.value) {
                  throw new Error('ng-model used but ' + role + ' 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.$modelValue);
                };

                // 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.
                var onChange = function(pristine){
                  function doit() {
                    if (pristine && ngForm) {
                      var formPristine = ngForm.$pristine;
                    }
                    ngModel.$setViewValue(widget.value());
                    if (pristine) {
                      ngModel.$setPristine();
                      if (formPristine) {
                        ngForm.$setPristine();
                      }
                    }
                  }
                  return function(e) {
                    if (scope.$root.$$phase === '$apply' || scope.$root.$$phase === '$digest') {
                      doit();
                    } else {
                      scope.$apply(doit);
                    }
                  };
                };
                bindBefore(widget, "change", onChange(false));
                bindBefore(widget, "dataBound", onChange(true));

                // if the model value is undefined, then we set the widget value to match ( == null/undefined )
                if (widget.value() != ngModel.$modelValue) {
                  if (!ngModel.$isEmpty(ngModel.$modelValue)) {
                    widget.value(ngModel.$modelValue);
                  }
                  if (widget.value() != null && widget.value() != "" && widget.value() != ngModel.$modelValue) {
                    ngModel.$setViewValue(widget.value());
                  }
                }

                ngModel.$setPristine();
              }
            }

            // mutation observers — propagate the original
            // element's class to the widget wrapper.
            (function(){

              if (!(window.MutationObserver
                    && widget.wrapper
                    && $(widget.wrapper)[0] !== $(element)[0])) {
                return;
              }

              var prevClassList = [].slice.call($(element)[0].classList);

              var mo = new MutationObserver(function(changes, mo){
                suspend();    // make sure we don't trigger a loop

                changes.forEach(function(chg){
                  var w = $(widget.wrapper)[0];
                  switch (chg.attributeName) {

                   case "class":
                    // sync classes to the wrapper element
                    var currClassList = [].slice.call(chg.target.classList);
                    currClassList.forEach(function(cls){
                      if (prevClassList.indexOf(cls) < 0) {
                        w.classList.add(cls);
                      }
                    });
                    prevClassList.forEach(function(cls){
                      if (currClassList.indexOf(cls) < 0) {
                        w.classList.remove(cls);
                      }
                    });
                    prevClassList = currClassList;
                    break;

                   case "disabled":
                    if (typeof widget.enable == "function") {
                      widget.enable(!$(chg.target).attr("disabled"));
                    }
                    break;

                   case "readonly":
                    if (typeof widget.readonly == "function") {
                      widget.readonly(!!$(chg.target).attr("readonly"));
                    }
                    break;
                  }
                });

                resume();
              });

              function suspend() {
                mo.disconnect();
              }
              function resume() {
                mo.observe($(element)[0], { attributes: true });
              }
              resume();
              bindBefore(widget, "destroy", suspend);
            })();

            --KENDO_COUNT;
            if (KENDO_COUNT == 0) {
              scope.$emit("kendoRendered");
            }

          });
        }
      };
    };

    return {
      create: create
    };
  }]);

  // create directives for every widget.
  angular.forEach([ kendo.ui, kendo.dataviz && kendo.dataviz.ui ], function(namespace) {
    angular.forEach(namespace, function(value, key) {
      if (key.match(/^[A-Z]/) && key !== 'Widget') {
        var widget = "kendo" + key;
        module.directive(widget, [
          "directiveFactory",
          function(directiveFactory) {
            return directiveFactory.create(widget);
          }
        ]);
      }
    });
  });

  /* -----[ utils ]----- */

  function kendoWidgetInstance(el) {
    el = $(el);
    return kendo.widgetInstance(el, kendo.ui) ||
      kendo.widgetInstance(el, kendo.mobile.ui) ||
      kendo.widgetInstance(el, kendo.dataviz.ui);
  }

  // XXX: using internal API (Widget::_events).  Seems to be no way in Kendo to
  // insert a handler to be executed before any existing ones, hence this hack.
  // Use for a single event/handler combination.
  function bindBefore(widget, name, handler, one) {
    widget.bind.call(widget, name, handler, one);
    var a = widget._events[name];
    a.unshift(a.pop());
  }

  function digest(scope) {
    if (!/^\$(digest|apply)$/.test(scope.$root.$$phase)) {
      scope.$digest();
    }
  }

  // defadvice will patch a class' method with another function.  That
  // function will be called in a context containing `next` (to call
  // the next method) and `object` (a reference to the original
  // object).
  function defadvice(klass, methodName, func) {
    if ($.isArray(klass)) {
      return angular.forEach(klass, function(klass){
        defadvice(klass, methodName, func);
      });
    }
    var origMethod = klass.prototype[methodName];
    klass.prototype[methodName] = function() {
      var self = this, args = arguments;
      return func.apply({
        self: self,
        next: function() {
          return origMethod.apply(self, arguments.length > 0 ? arguments : args);
        }
      }, args);
    };
  }

  var BEFORE = "$angular_beforeCreate";
  var AFTER = "$angular_afterCreate";

  /* -----[ Customize widgets for Angular ]----- */

  // XXX: notice we can't override `init` in general for any widget,
  // because kendo.ui.Widget === kendo.ui.Widget.prototype.init.
  // Hence we resort to the beforeCreate/afterCreate hack.
  defadvice(kendo.ui.Widget, "init", function(element, options){
    if (!options && OPTIONS_NOW) options = OPTIONS_NOW;
    OPTIONS_NOW = null;
    var self = this.self;
    if (options && options.$angular) {
      // call before/after hooks only for widgets instantiated by angular-kendo
      self.$angular_beforeCreate(element, options);
      this.next();
      self.$angular_afterCreate();
    } else {
      this.next();
    }
  });

  // All event handlers that are strings are compiled the Angular way.
  defadvice(kendo.ui.Widget, BEFORE, function(element, options) {
    var self = this.self;
    if (options && !$.isArray(options)) {
      var scope = angular.element(element).scope();
      for (var i = self.events.length; --i >= 0;) {
        var event = self.events[i];
        var handler = options[event];
        if (handler && typeof handler == "string")
          options[event] = self.$angular_makeEventHandler(event, scope, handler);
      }
    }
  });

  defadvice(kendo.ui.Widget, AFTER, function(){});

  // most handers will only contain a kendoEvent in the scope.
  defadvice(kendo.ui.Widget, "$angular_makeEventHandler", function(event, scope, handler){
    handler = parse(handler);
    return function(e) {
      if (/^\$(apply|digest)$/.test(scope.$root.$$phase)) {
        handler(scope, { kendoEvent: e });
      } else {
        scope.$apply(function() { handler(scope, { kendoEvent: e }) });
      }
    };
  });

  // for the Grid and ListView we add `data` and `selected` too.
  defadvice([ kendo.ui.Grid, kendo.ui.ListView ], "$angular_makeEventHandler", function(event, scope, handler){
    if (event != "change") return this.next();
    handler = parse(handler);
    return function(ev) {
      var widget = ev.sender;
      var options = widget.options;
      var dataSource = widget.dataSource;
      var cell, multiple, locals = { kendoEvent: ev }, 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 item = cell ? elems[i].parentNode : elems[i];
        var itemUid = $(item).attr(_UID_);
        var dataItem = dataSource.getByUid(itemUid);
        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];
      }

      scope.$apply(function() { handler(scope, locals) });
    };
  });

  // for PanelBar, TabStrip and Splitter, hook on `contentLoad` to
  // compile Angular templates.
  defadvice([ kendo.ui.PanelBar, kendo.ui.TabStrip, kendo.ui.Splitter ], AFTER, function() {
    this.next();
    var self = this.self;
    var scope = angular.element(self.element).scope();
    if (scope) bindBefore(self, "contentLoad", function(ev){
      //                   tabstrip/panelbar    splitter
      var contentElement = ev.contentElement || ev.pane;
      compile(ev.contentElement)(scope);
      digest(scope);
    });
  });

  // on Draggable::_start compile the content as Angular template, if
  // an $angular_scope method is provided.
  defadvice(kendo.ui.Draggable, "_start", function(){
    this.next();
    var self = this.self;
    if (self.hint) {
      var scope = angular.element(self.currentTarget).scope();
      if (scope) {
        compile(self.hint)(scope);
        digest(scope);
      }
    }
  });

  // If no `template` is supplied for Grid columns, provide an Angular
  // template.  The reason is that in this way AngularJS will take
  // care to update the view as the data in scope changes.
  defadvice(kendo.ui.Grid, BEFORE, function(element, options){
    this.next();
    if (options.columns) angular.forEach(options.columns, function(col){
      if (col.field && !col.template && !col.format) {
        col.template = "<span ng-bind='dataItem." + col.field + "'>#: " + col.field + "#</span>";
      }
    });
  });

  // for Grid, ListView and TreeView, provide a dataBound handler that
  // recompiles Angular templates.  We need to do this before the
  // widget is initialized so that we catch the first dataBound event.
  defadvice([ kendo.ui.Grid, kendo.ui.ListView, kendo.ui.TreeView ], BEFORE, function(element, options){
    this.next();
    var scope = angular.element(element).scope();
    if (!scope) return;
    var self = this.self;
    var role = self.options.name;
    var prev_dataBound = options.dataBound;
    options.dataBound = function(ev) {
      var widget = ev.sender;
      var dataSource = widget.dataSource;
      var dirty = false;
      widget.items().each(function(){
        // XXX HACK: the tree view will call dataBound multiple
        // times, sometimes for LI-s containing nested items that
        // may have been already compiled.  Therefore in this
        // situation we compile the ".k-in" element, which contains
        // only the template for a single item.
        var elementToCompile = role == "TreeView"
          ? $(this).find(".k-in").first()
          : $(this);
        if (!elementToCompile.hasClass("ng-scope")) {
          var itemUid = $(this).attr(_UID_);
          var item = dataSource.getByUid(itemUid);
          var itemScope = scope.$new();
          itemScope.dataItem = item;
          compile(elementToCompile)(itemScope);
          dirty = true;
        }
      });
      try {
        if (prev_dataBound) return prev_dataBound.apply(this, arguments);
      } finally {
        if (dirty) digest(scope);
      }
    };
  });

  // templates for autocomplete and combo box
  defadvice([ kendo.ui.AutoComplete, kendo.ui.ComboBox ], BEFORE, function(element, options){
    this.next();
    var scope = angular.element(element).scope();
    if (!scope) return;
    var self = this.self;
    var prev_dataBound = options.dataBound;
    options.dataBound = function(ev) {
      var widget = ev.sender;
      var dataSource = widget.dataSource;
      var dirty = false;
      $(widget.items()).each(function(){
        var el = $(this);
        if (!el.hasClass("ng-scope")) {
          var item = widget.dataItem(el.index());
          var itemScope = scope.$new();
          itemScope.dataItem = item;
          compile(el)(itemScope);
          dirty = true;
        }
      });
      try {
        if (prev_dataBound) return prev_dataBound.apply(this, arguments);
      } finally {
        if (dirty) digest(scope);
      }
    };
  });

  defadvice([ kendo.ui.AutoComplete, kendo.ui.ComboBox ], AFTER, function(){
    this.next();
    this.self.bind("dataBinding", function(ev){
      $(ev.sender.items()).each(function(){
        var scope = angular.element(this).scope();
        if (scope) {
          scope.$destroy();
        }
      });
    });
  });

  defadvice([ kendo.ui.Grid, kendo.ui.ListView ], AFTER, function(){
    this.next();
    var self = this.self;
    var scope = angular.element(self.element).scope();
    if (!scope) return;

    // itemChange triggers when a single item is changed through our
    // DataSource mechanism.
    self.bind("itemChange", function(ev) {
      var dataSource = ev.sender.dataSource;
      var itemElement = ev.item[0];
      var itemScope = scope.$new();
      itemScope.dataItem = dataSource.getByUid(ev.item.attr(_UID_));
      compile(itemElement)(itemScope);
      digest(itemScope);
    });

    // dataBinding triggers when new data is loaded.  We use this to
    // destroy() each item's scope.
    //
    // BUG with this code on: create a simple grid with popup editing.
    // Click edit in some row (the window opens).  Click Cancel (the
    // window disappears).  Click Edit again (the window opens).
    // Click Update (the window disappears, without animation).  The
    // grid is now dead (does not respond to any more events).  I
    // cannot figure out why this happens, but until it's fixed, gonna
    // leave this note here and this code commented out.
    //
    // self.bind("dataBinding", function(ev) {
    //   ev.sender.items().each(function(){
    //     if ($(this).attr(_UID_)) {
    //       var rowScope = angular.element(this).scope();
    //       if (rowScope) rowScope.$destroy();
    //     }
    //   });
    // });
  });

  defadvice(kendo.ui.Grid, "_toolbar", function(){
    this.next();
    var self = this.self;
    var scope = angular.element(self.element).scope();
    if (scope) {
      compile(self.wrapper.find(".k-grid-toolbar").first())(scope);
      digest(scope);
    }
  });

  defadvice(kendo.ui.Grid, "_thead", function(){
    this.next();
    var self = this.self;
    var scope = angular.element(self.element).scope();
    if (scope) {
      compile(self.thead)(scope);
      digest(scope);
    }
  });

  defadvice(kendo.ui.editor.Toolbar, "render", function(){
    this.next();
    var self = this.self;
    var scope = angular.element(self.element).scope();
    if (scope) {
      compile(self.element)(scope);
      digest(scope);
    }
  });

  defadvice(kendo.ui.Grid, AFTER, function(){
    this.next();
    var self = this.self;
    var scope = angular.element(self.element).scope();
    if (scope) {
      if (self.options.detailTemplate) bindBefore(self, "detailInit", function(ev){
        var detailScope = scope.$new();
        detailScope.dataItem = ev.data;
        compile(ev.detailCell)(detailScope);
        digest(detailScope);
      });
    }
  });

  defadvice(kendo.ui.Editable, "refresh", function(){
    this.next();
    var self = this.self;
    var model = self.options.model;
    var scope = angular.element(self.element).scope();
    if (!scope || !model) return;
    scope = self.$angular_scope = scope.$new();
    scope.dataItem = model;

    // XXX: we need to disable the timeout here, or else the widget is
    // created but immediately destroyed (focus lost).
    immediately(function(){
      compile(self.element)(scope);
      digest(scope);
    });

    // and we still need to focus it.
    self.element.find(":kendoFocusable").eq(0).focus();
  });

  defadvice(kendo.ui.Editable, "destroy", function(){
    var self = this.self;
    if (self.$angular_scope) {
      self.$angular_scope.$destroy();
      self.$angular_scope = null;
    }
    this.next();
    timeout(function(){
      var scope = angular.element(self.element).scope();
      if (scope) {
        compile(self.element)(scope);
        digest(scope);
      }
    });
  });

}, typeof define == 'function' && define.amd ? define : function(_, f){ f(jQuery, angular, kendo); });

// Local Variables:
// js-indent-level: 2
// js2-basic-offset: 2
// End:
(function(angular) {'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').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
      };
  }];

}]);

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]);
        }
      }
    }
	
	// $timeout tracking
	var $timeoutPromise = null;
	var unsetTimeoutPromise = function() { $timeoutPromise = null };

    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;
		  
		  // Instead of having angular digest each component that needs to be setup
		  // Use the same timeout until the timeout has been executed, this will cause all
		  //   directives to be evaluated in the next cycle, instead of over multiple cycles.
		  if (!$timeoutPromise)
		    $timeoutPromise = $timeout(unsetTimeoutPromise);

          // 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.
          $timeoutPromise.then( 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(){
  return {
    // This is an attribute directive
    restrict: 'A',
    controller: ['$scope', '$attrs', '$element', function($scope, $attrs, $element){
      var widgetType = getWidgetType($attrs);
      var dataSourceType = getDataSourceType(widgetType);

      // 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($scope.$eval($attrs.kDataSource), dataSourceType));

      // Keep the element's data up-to-date with changes.
      $scope.$watch($attrs.kDataSource, function(newDataSource, oldDataSource){
        if(newDataSource !== oldDataSource){
          $element.data('$kendoDataSource', toDataSource(newDataSource, dataSourceType));
        }
      });
    }]
  };

  // Returns the DataSource type based on the widgetType
  function getDataSourceType(widgetType){
    var hierarchicalDataSourceWidgets = ['TreeView'];
    var schedulerDataSourceWidgets = ['Scheduler'];
        if(jQuery.inArray(widgetType, hierarchicalDataSourceWidgets) !== -1){
      return 'HierarchicalDataSource';
    }
    else if(jQuery.inArray(widgetType, schedulerDataSourceWidgets) !== -1){
      return 'SchedulerDataSource';
    }
    else{
      return 'DataSource';
    }
  }

  // Returns the widgetType, eg: 'TreeView'
  function getWidgetType(attributes){
    for(var attribute in attributes){
      if(attributes.hasOwnProperty(attribute) && attribute.match(/kendo/)){
        return attribute.replace('kendo', '');
      }
    }
  }

  // Transforms the object into a Kendo UI DataSource.
  function toDataSource(dataSource, dataSourceType){
    // TODO: if ds is a $resource, wrap it in a kendo dataSource using an injected service
    return kendo.data[dataSourceType].create(dataSource);
  }
}]);
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');

    // 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) {
    // 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));