/* * angular-ui-bootstrap * http://angular-ui.github.io/bootstrap/ * Version: 0.11.0-SNAPSHOT - 2014-03-17 * License: MIT */ angular.module('ui.bootstrap.dropdown', []) .constant('dropdownConfig', { openClass: 'open' }) .service('dropdownService', ['$document', function($document) { var self = this, openScope = null; this.open = function( dropdownScope ) { if ( !openScope ) { $document.bind('click', closeDropdown); $document.bind('keydown', escapeKeyBind); } if ( openScope && openScope !== dropdownScope ) { openScope.isOpen = false; } openScope = dropdownScope; }; this.close = function( dropdownScope ) { if ( openScope === dropdownScope ) { openScope = null; $document.unbind('click', closeDropdown); $document.unbind('keydown', escapeKeyBind); } }; var closeDropdown = function() { openScope.$apply(function() { openScope.isOpen = false; }); }; var escapeKeyBind = function( evt ) { if ( evt.which === 27 ) { openScope.focusToggleElement(); closeDropdown(); } }; }]) .controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate) { var self = this, scope = $scope.$new(), // create a child scope so we are not polluting original one openClass = dropdownConfig.openClass, getIsOpen, setIsOpen = angular.noop, toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop; this.init = function( element ) { self.$element = element; if ( $attrs.isOpen ) { getIsOpen = $parse($attrs.isOpen); setIsOpen = getIsOpen.assign; $scope.$watch(getIsOpen, function(value) { scope.isOpen = !!value; }); } }; this.toggle = function( open ) { return scope.isOpen = arguments.length ? !!open : !scope.isOpen; }; // Allow other directives to watch status this.isOpen = function() { return scope.isOpen; }; scope.focusToggleElement = function() { if ( self.toggleElement ) { self.toggleElement[0].focus(); } }; scope.$watch('isOpen', function( isOpen ) { $animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass); if ( isOpen ) { scope.focusToggleElement(); dropdownService.open( scope ); } else { dropdownService.close( scope ); } setIsOpen($scope, isOpen); toggleInvoker($scope, { open: !!isOpen }); }); $scope.$on('$locationChangeSuccess', function() { scope.isOpen = false; }); $scope.$on('$destroy', function() { scope.$destroy(); }); }]) .directive('dropdown', function() { return { restrict: 'CA', controller: 'DropdownController', link: function(scope, element, attrs, dropdownCtrl) { dropdownCtrl.init( element ); } }; }) .directive('dropdownToggle', function() { return { restrict: 'CA', require: '?^dropdown', link: function(scope, element, attrs, dropdownCtrl) { if ( !dropdownCtrl ) { return; } dropdownCtrl.toggleElement = element; var toggleDropdown = function(event) { event.preventDefault(); event.stopPropagation(); if ( !element.hasClass('disabled') && !attrs.disabled ) { scope.$apply(function() { dropdownCtrl.toggle(); }); } }; element.bind('click', toggleDropdown); // WAI-ARIA element.attr({ 'aria-haspopup': true, 'aria-expanded': false }); scope.$watch(dropdownCtrl.isOpen, function( isOpen ) { element.attr('aria-expanded', !!isOpen); }); scope.$on('$destroy', function() { element.unbind('click', toggleDropdown); }); } }; });