Angular Modal Tabbing Test -2

POC for Angular Modal Tabbing support

by Keyur Patel

HTML

<link rel="stylesheet" href="http://getbootstrap.com/dist/css/bootstrap.min.css">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular-touch.min.js"></script>
<script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.0.min.js"></script>
<div ng-app="myApp">
    <parent>
        <a href="#">Launch modal</a>
    </parent>
    <script type="text/ng-template" id="myModalContent.html">
        <div class="modal-header">
            <h3 class="modal-title">I'm a modal!</h3>
        </div>
        <div class="modal-body">
            <ul>
                <li ng-repeat="item in items">
                    <a ng-click="selected.item = item">{{ item }}</a>
                </li>
            </ul>
            <span tabindex="0">Tabbable: <b>{{ name }}</b></span>
        </div>
        <div class="modal-footer">
            <button class="btn btn-primary" ng-click="ok()">OK</button>
            <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
        </div>
    </script>
</div>

JavaScript

// POC using jQuery/UI for 
// WAI-ARIA 1.0 Authoring Practices, 3.3.1 Trapping Focus - http://www.w3.org/TR/wai-aria-practices/

angular.module('myApp', ['ui.bootstrap']).
directive('parent', function($modal) {
    return {
        restrict: 'E',
        link:function(scope, elm) {
            scope.name = 'This item is tabbable';
            elm.on('click', function() {
                $modal.open({
                  scope: scope,
                  templateUrl: 'myModalContent.html',
                  controller: ['$scope', '$modalInstance',
                  function($scope, $modalInstance) {
                    $scope.ok = function(result) {
                      $modalInstance.close(result);
                    };
                    $scope.cancel = function() {
                      $modalInstance.dismiss('cancel');
                    };
                  }]
                });
            });
            
        }
    }
}).
directive('modalContent', function() {
  return {
    restrict: 'C',
    scope: true,
    link: function(scope, elm) {
      elm.on('keyup', function(evt) {
        if (evt.which === 9) { // tab

          var children = $('.modal :tabbable');
          var first = children[0];
          var last = children[children.length - 1];
          var onFirstWithShift = evt.target === first && evt.shiftKey;
          var onLastNoShift = evt.target === last && !evt.shiftKey;

          if (onFirstWithShift || onLastNoShift) {
            evt.preventDefault();
          }
        }
      });

      elm.on('keydown', function(evt) {
        if (evt.which === 9) { // tab

          var children = $('.modal :tabbable');
          var first = children[0];
          var last = children[children.length - 1];
          if (evt.target === first && evt.shiftKey) {
            // user is going from first to last
            evt.preventDefault();
            last.focus();
          } else if (evt.target === last && !evt.shiftKey) {
            // uesr is...