<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
  <meta charset="utf-8">
  <title>ngAttr</title>

  <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.js"></script>

  <script src="ngAttr.js"></script>
</head>

<body ng-controller="AppCtrl">
  <script>
    var app = angular.module('myApp', []);

    app.directive('ngAttr', ngAttrDirective);

    app.controller('AppCtrl', function($scope) {
      $scope.alert = function() {
        alert('Click!');
      };
    });
  </script>

  <p>
    Classic ng-click (works):
    <button ng-click="alert()">Call alert()</button>
  </p>

  <p>
    Now ng-click generated by ng-attr (does not work):
    <button ng-attr="{'ng-click=alert()': true}">Call alert()</button>
  </p>

</body>
</html>
'use strict';

/**
 * See feat(ngAttr): implement conditional/interpolated attributes directive #4269 https://github.com/angular/angular.js/pull/4269
 * See feat(ngAttr): implement conditional/interpolated attributes directive https://github.com/caitp/angular.js/commit/4b79321fbd59b4fd7e97ff82257958993185ab01
 */
function attrDirective(name, selector) {
  name = 'ngAttr' + name;
  return function() {
    var ATTR_MATCH = /\s*([^=]+)(=\s*(\S+))?/;
    return {
      restrict: 'A',
      link: function(scope, element, attr) {
        var oldVal;

        scope.$watch(attr[name], function(value) {
          ngAttrWatchAction(scope.$eval(attr[name]));
        }, true);

        attr.$observe(name, function() {
          ngAttrWatchAction(scope.$eval(attr[name]));
        });

        function ngAttrWatchAction(newVal) {
          if (selector === true || scope.$index % 2 === selector) {
            if (oldVal && !angular.equals(newVal,oldVal)) {
              attrWorker(oldVal, removeAttr);
            }
            attrWorker(newVal, setAttr);
          }
          oldVal = angular.copy(newVal);
        }


        function splitAttr(value) {
          var m = ATTR_MATCH.exec(value);
          return m && [m[1].replace(/\s+$/, ''), m[3]];
        }


        function setAttr(value) {
          if (value) {
            if (value[0] === 'undefined' || value[0] === 'null') {
              return;
            }
            element.attr(value[0], angular.isDefined(value[1]) ? value[1] : '');
          }
        }

        function removeAttr(value) {
          if (value) {
            element.removeAttr(value[0]);
          }
        }

        function attrWorker(attrVal, action, compare) {
          if (angular.isString(attrVal)) {
            attrVal = attrVal.split(/\s+/);
          }
          if (angular.isArray(attrVal)) {
            angular.forEach(attrVal, function(v) {
              v = splitAttr(v);
              action(v);
            });
          } else if (angular.isObject(attrVal)) {
            var attrs = [];
            angular.forEach(attrVal, function(v, k) {
              k = splitAttr(k);
              if (v) {
                action(k);
              }
            });
          }
        }
      }
    };
  };
}

var ngAttrDirective = attrDirective('', true);