diff --git a/src/popover/docs/readme.md b/src/popover/docs/readme.md
index eb5f53428a..87316ddeee 100644
--- a/src/popover/docs/readme.md
+++ b/src/popover/docs/readme.md
@@ -1,2 +1,5 @@
A lightweight, extensible directive for fancy popover creation. The popover
directive supports multiple placements, optional transition animation, and more.
+
+Like the Twitter Bootstrap jQuery plugin, the popover **requires** the tooltip
+module.
diff --git a/src/popover/popover.js b/src/popover/popover.js
index d64e729eff..1cee58f851 100644
--- a/src/popover/popover.js
+++ b/src/popover/popover.js
@@ -3,152 +3,16 @@
* function, placement as a function, inside, support for more triggers than
* just mouse enter/leave, html popovers, and selector delegatation.
*/
-angular.module( 'ui.bootstrap.popover', [] )
+angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
.directive( 'popoverPopup', function () {
return {
restrict: 'EA',
replace: true,
- scope: { popoverTitle: '@', popoverContent: '@', placement: '@', animation: '&', isOpen: '&' },
+ scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },
templateUrl: 'template/popover/popover.html'
};
})
-.directive( 'popover', [ '$compile', '$timeout', '$parse', '$window', function ( $compile, $timeout, $parse, $window ) {
-
- var template =
- ''+
- '';
-
- return {
- scope: true,
- link: function ( scope, element, attr ) {
- var popover = $compile( template )( scope ),
- transitionTimeout;
-
- attr.$observe( 'popover', function ( val ) {
- scope.tt_popover = val;
- });
-
- attr.$observe( 'popoverTitle', function ( val ) {
- scope.tt_title = val;
- });
-
- attr.$observe( 'popoverPlacement', function ( val ) {
- // If no placement was provided, default to 'top'.
- scope.tt_placement = val || 'top';
- });
-
- attr.$observe( 'popoverAnimation', function ( val ) {
- scope.tt_animation = $parse( val );
- });
-
- // By default, the popover is not open.
- scope.tt_isOpen = false;
-
- // Calculate the current position and size of the directive element.
- function getPosition() {
- var boundingClientRect = element[0].getBoundingClientRect();
- return {
- width: element.prop( 'offsetWidth' ),
- height: element.prop( 'offsetHeight' ),
- top: boundingClientRect.top + $window.pageYOffset,
- left: boundingClientRect.left + $window.pageXOffset
- };
- }
-
- function show() {
- var position,
- ttWidth,
- ttHeight,
- ttPosition;
-
- // If there is a pending remove transition, we must cancel it, lest the
- // toolip be mysteriously removed.
- if ( transitionTimeout ) {
- $timeout.cancel( transitionTimeout );
- }
-
- // Set the initial positioning.
- popover.css({ top: 0, left: 0, display: 'block' });
-
- // Now we add it to the DOM because need some info about it. But it's not
- // visible yet anyway.
- element.after( popover );
-
- // Get the position of the directive element.
- position = getPosition();
-
- // Get the height and width of the popover so we can center it.
- ttWidth = popover.prop( 'offsetWidth' );
- ttHeight = popover.prop( 'offsetHeight' );
-
- // Calculate the popover's top and left coordinates to center it with
- // this directive.
- switch ( scope.tt_placement ) {
- case 'right':
- ttPosition = {
- top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
- left: (position.left + position.width) + 'px'
- };
- break;
- case 'bottom':
- ttPosition = {
- top: (position.top + position.height) + 'px',
- left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
- };
- break;
- case 'left':
- ttPosition = {
- top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
- left: (position.left - ttWidth) + 'px'
- };
- break;
- default:
- ttPosition = {
- top: (position.top - ttHeight) + 'px',
- left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
- };
- break;
- }
-
- // Now set the calculated positioning.
- popover.css( ttPosition );
-
- // And show the popover.
- scope.tt_isOpen = true;
- }
-
- // Hide the popover popup element.
- function hide() {
- // First things first: we don't show it anymore.
- //popover.removeClass( 'in' );
- scope.tt_isOpen = false;
-
- // And now we remove it from the DOM. However, if we have animation, we
- // need to wait for it to expire beforehand.
- // FIXME: this is a placeholder for a port of the transitions library.
- if ( angular.isDefined( scope.tt_animation ) && scope.tt_animation() ) {
- transitionTimeout = $timeout( function () { popover.remove(); }, 500 );
- } else {
- popover.remove();
- }
- }
-
- // Register the event listeners.
- element.bind( 'click', function() {
- if(scope.tt_isOpen){
- scope.$apply( hide );
- } else {
- scope.$apply( show );
- }
-
- });
- }
- };
+.directive( 'popover', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) {
+ return $tooltip( 'popover', 'click' );
}]);
diff --git a/src/popover/test/popoverSpec.js b/src/popover/test/popoverSpec.js
index f98d292b21..633aeb8b2c 100644
--- a/src/popover/test/popoverSpec.js
+++ b/src/popover/test/popoverSpec.js
@@ -81,7 +81,7 @@ describe('popover', function() {
tt.trigger( 'click' );
expect( tt.text() ).toBe( scope.items[0].name );
- expect( tt.scope().tt_popover ).toBe( scope.items[0].popover );
+ expect( tt.scope().tt_content ).toBe( scope.items[0].popover );
tt.trigger( 'click' );
}));
@@ -89,12 +89,12 @@ describe('popover', function() {
it('should only have an isolate scope on the popup', inject( function ( $compile ) {
var ttScope;
- scope.popoverMsg = "popover Text";
- scope.popoverTitle = "popover Text";
+ scope.popoverContent = "Popover Content";
+ scope.popoverTitle = "Popover Title";
scope.alt = "Alt Message";
elmBody = $compile( angular.element(
- '
Selector Text
'
+ 'Selector Text
'
) )( scope );
$compile( elmBody )( scope );
@@ -107,8 +107,8 @@ describe('popover', function() {
ttScope = angular.element( elmBody.children()[1] ).scope();
expect( ttScope.placement ).toBe( 'top' );
- expect( ttScope.popoverTitle ).toBe( scope.popoverTitle );
- expect( ttScope.popoverContent ).toBe( scope.popoverMsg );
+ expect( ttScope.title ).toBe( scope.popoverTitle );
+ expect( ttScope.content ).toBe( scope.popoverContent );
elm.trigger( 'click' );
}));
diff --git a/src/tooltip/docs/demo.html b/src/tooltip/docs/demo.html
index 815c540e2e..954c70e57e 100644
--- a/src/tooltip/docs/demo.html
+++ b/src/tooltip/docs/demo.html
@@ -12,7 +12,7 @@
nunc sed velit dignissim sodales ut eu sem integer vitae. Turpis egestas
bottom
pharetra convallis posuere morbi leo urna,
- fading
+ fading
at elementum eu, facilisis sed odio morbi quis commodo odio. In cursus
turpis massa tincidunt dui ut.
diff --git a/src/tooltip/test/tooltip.spec.js b/src/tooltip/test/tooltip.spec.js
index 1eb537a7e8..b9849df338 100644
--- a/src/tooltip/test/tooltip.spec.js
+++ b/src/tooltip/test/tooltip.spec.js
@@ -81,7 +81,7 @@ describe('tooltip', function() {
tt.trigger( 'mouseenter' );
expect( tt.text() ).toBe( scope.items[0].name );
- expect( tt.scope().tt_tooltip ).toBe( scope.items[0].tooltip );
+ expect( tt.scope().tt_content ).toBe( scope.items[0].tooltip );
tt.trigger( 'mouseleave' );
}));
@@ -106,7 +106,7 @@ describe('tooltip', function() {
ttScope = angular.element( elmBody.children()[1] ).scope();
expect( ttScope.placement ).toBe( 'top' );
- expect( ttScope.tooltipTitle ).toBe( scope.tooltipMsg );
+ expect( ttScope.content ).toBe( scope.tooltipMsg );
elm.trigger( 'mouseleave' );
}));
diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js
index 7af22a45b3..15617274a8 100644
--- a/src/tooltip/tooltip.js
+++ b/src/tooltip/tooltip.js
@@ -1,52 +1,57 @@
/**
* The following features are still outstanding: popup delay, animation as a
* function, placement as a function, inside, support for more triggers than
- * just mouse enter/leave, html tooltips, and selector delegatation.
+ * just mouse enter/leave, html tooltips, and selector delegation.
*/
angular.module( 'ui.bootstrap.tooltip', [] )
-.directive( 'tooltipPopup', function () {
- return {
- restrict: 'EA',
- replace: true,
- scope: { tooltipTitle: '@', placement: '@', animation: '&', isOpen: '&' },
- templateUrl: 'template/tooltip/tooltip-popup.html'
+
+/**
+ * The $tooltip service creates tooltip- and popover-like directives as well as
+ * houses global options for them.
+ */
+.provider( '$tooltip', function () {
+ // The default options tooltip and popover.
+ var defaultOptions = {
+ placement: 'top',
+ animation: true
};
-})
-.directive( 'tooltip', [ '$compile', '$timeout', '$parse', '$window', function ( $compile, $timeout, $parse, $window) {
-
- var template =
- ''+
- '';
+
+ // The options specified to the provider globally.
+ var globalOptions = {};
- return {
- scope: true,
- link: function ( scope, element, attr ) {
- var tooltip = $compile( template )( scope ),
- transitionTimeout;
-
- attr.$observe( 'tooltip', function ( val ) {
- scope.tt_tooltip = val;
- });
-
- attr.$observe( 'tooltipPlacement', function ( val ) {
- // If no placement was provided, default to 'top'.
- scope.tt_placement = val || 'top';
- });
-
- attr.$observe( 'tooltipAnimation', function ( val ) {
- scope.tt_animation = $parse( val );
- });
-
- // By default, the tooltip is not open.
- scope.tt_isOpen = false;
-
+ /**
+ * The `options({})` allows global configuration of all dialogs in the
+ * application.
+ *
+ * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
+ * // place tooltips left instead of top by default
+ * $tooltipProvider.options( { placement: 'left' } );
+ * });
+ */
+ this.options = function( value ) {
+ angular.extend( globalOptions, value );
+ };
+
+ /**
+ * Returns the actual instance of the $tooltip service.
+ * TODO support multiple triggers
+ */
+ this.$get = [ '$window', '$compile', '$timeout', '$parse', function ( $window, $compile, $timeout, $parse ) {
+ return function $tooltip ( type, defaultTriggerShow, defaultTriggerHide ) {
+ var options = angular.extend( {}, defaultOptions, globalOptions );
+
+ var template =
+ '<'+ type +'-popup '+
+ 'title="{{tt_title}}" '+
+ 'content="{{tt_content}}" '+
+ 'placement="{{tt_placement}}" '+
+ 'animation="tt_animation()" '+
+ 'is-open="tt_isOpen"'+
+ '>'+
+ ''+ type +'-popup>';
+
// Calculate the current position and size of the directive element.
- function getPosition() {
+ function getPosition( element ) {
var boundingClientRect = element[0].getBoundingClientRect();
return {
width: element.prop( 'offsetWidth' ),
@@ -55,99 +60,153 @@ angular.module( 'ui.bootstrap.tooltip', [] )
left: boundingClientRect.left + $window.pageXOffset
};
}
-
- // Show the tooltip popup element.
- function show() {
- var position,
- ttWidth,
- ttHeight,
- ttPosition;
-
- //don't show empty tooltips
- if (!scope.tt_tooltip) {
- return;
- }
+
+ return {
+ restrict: 'EA',
+ scope: true,
+ link: function link ( scope, element, attrs ) {
+ var tooltip = $compile( template )( scope ),
+ transitionTimeout;
- // If there is a pending remove transition, we must cancel it, lest the
- // toolip be mysteriously removed.
- if ( transitionTimeout ) {
- $timeout.cancel( transitionTimeout );
- }
-
- // Set the initial positioning.
- tooltip.css({ top: 0, left: 0, display: 'block' });
-
- // Now we add it to the DOM because need some info about it. But it's not
- // visible yet anyway.
- element.after( tooltip );
-
- // Get the position of the directive element.
- position = getPosition();
-
- // Get the height and width of the tooltip so we can center it.
- ttWidth = tooltip.prop( 'offsetWidth' );
- ttHeight = tooltip.prop( 'offsetHeight' );
-
- // Calculate the tooltip's top and left coordinates to center it with
- // this directive.
- switch ( scope.tt_placement ) {
- case 'right':
- ttPosition = {
- top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
- left: (position.left + position.width) + 'px'
- };
- break;
- case 'bottom':
- ttPosition = {
- top: (position.top + position.height) + 'px',
- left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
- };
- break;
- case 'left':
- ttPosition = {
- top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
- left: (position.left - ttWidth) + 'px'
- };
- break;
- default:
- ttPosition = {
- top: (position.top - ttHeight) + 'px',
- left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
- };
- break;
- }
-
- // Now set the calculated positioning.
- tooltip.css( ttPosition );
+ attrs.$observe( type, function ( val ) {
+ scope.tt_content = val;
+ });
+
+ attrs.$observe( type+'Title', function ( val ) {
+ scope.tt_title = val;
+ });
+
+ attrs.$observe( type+'Placement', function ( val ) {
+ scope.tt_placement = angular.isDefined( val ) ? val : options.placement;
+ });
+
+ attrs.$observe( type+'Animation', function ( val ) {
+ scope.tt_animation = angular.isDefined( val ) ? $parse( val ) : function(){ return options.animation; };
+ });
+
+ // By default, the tooltip is not open.
+ // TODO add ability to start tooltip opened
+ scope.tt_isOpen = false;
- // And show the tooltip.
- scope.tt_isOpen = true;
- }
-
- // Hide the tooltip popup element.
- function hide() {
- // First things first: we don't show it anymore.
- //tooltip.removeClass( 'in' );
- scope.tt_isOpen = false;
-
- // And now we remove it from the DOM. However, if we have animation, we
- // need to wait for it to expire beforehand.
- // FIXME: this is a placeholder for a port of the transitions library.
- if ( angular.isDefined( scope.tt_animation ) && scope.tt_animation() ) {
- transitionTimeout = $timeout( function () { tooltip.remove(); }, 500 );
- } else {
- tooltip.remove();
+ // Show the tooltip popup element.
+ function show() {
+ var position,
+ ttWidth,
+ ttHeight,
+ ttPosition;
+
+ // Don't show empty tooltips.
+ if ( ! scope.tt_content ) {
+ return;
+ }
+
+ // If there is a pending remove transition, we must cancel it, lest the
+ // toolip be mysteriously removed.
+ if ( transitionTimeout ) {
+ $timeout.cancel( transitionTimeout );
+ }
+
+ // Set the initial positioning.
+ tooltip.css({ top: 0, left: 0, display: 'block' });
+
+ // Now we add it to the DOM because need some info about it. But it's not
+ // visible yet anyway.
+ element.after( tooltip );
+
+ // Get the position of the directive element.
+ position = getPosition( element );
+
+ // Get the height and width of the tooltip so we can center it.
+ ttWidth = tooltip.prop( 'offsetWidth' );
+ ttHeight = tooltip.prop( 'offsetHeight' );
+
+ // Calculate the tooltip's top and left coordinates to center it with
+ // this directive.
+ switch ( scope.tt_placement ) {
+ case 'right':
+ ttPosition = {
+ top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
+ left: (position.left + position.width) + 'px'
+ };
+ break;
+ case 'bottom':
+ ttPosition = {
+ top: (position.top + position.height) + 'px',
+ left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
+ };
+ break;
+ case 'left':
+ ttPosition = {
+ top: (position.top + position.height / 2 - ttHeight / 2) + 'px',
+ left: (position.left - ttWidth) + 'px'
+ };
+ break;
+ default:
+ ttPosition = {
+ top: (position.top - ttHeight) + 'px',
+ left: (position.left + position.width / 2 - ttWidth / 2) + 'px'
+ };
+ break;
+ }
+
+ // Now set the calculated positioning.
+ tooltip.css( ttPosition );
+
+ // And show the tooltip.
+ scope.tt_isOpen = true;
+ }
+
+ // Hide the tooltip popup element.
+ function hide() {
+ // First things first: we don't show it anymore.
+ //tooltip.removeClass( 'in' );
+ scope.tt_isOpen = false;
+
+ // And now we remove it from the DOM. However, if we have animation, we
+ // need to wait for it to expire beforehand.
+ // FIXME: this is a placeholder for a port of the transitions library.
+ if ( angular.isDefined( scope.tt_animation ) && scope.tt_animation() ) {
+ transitionTimeout = $timeout( function () { tooltip.remove(); }, 500 );
+ } else {
+ tooltip.remove();
+ }
+ }
+
+ // Register the event listeners. If only one event listener was
+ // supplied, we use the same event listener for showing and hiding.
+ // TODO add ability to customize event triggers
+ if ( ! angular.isDefined( defaultTriggerHide ) ) {
+ element.bind( defaultTriggerShow, function toggleTooltipBind () {
+ if ( ! scope.tt_isOpen ) {
+ scope.$apply( show );
+ } else {
+ scope.$apply( hide );
+ }
+ });
+ } else {
+ element.bind( defaultTriggerShow, function showTooltipBind() {
+ scope.$apply( show );
+ });
+ element.bind( defaultTriggerHide, function hideTooltipBind() {
+ scope.$apply( hide );
+ });
+ }
}
- }
-
- // Register the event listeners.
- element.bind( 'mouseenter', function() {
- scope.$apply( show );
- });
- element.bind( 'mouseleave', function() {
- scope.$apply( hide );
- });
- }
+ };
+ };
+ }];
+})
+
+.directive( 'tooltipPopup', function () {
+ return {
+ restrict: 'E',
+ replace: true,
+ scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
+ templateUrl: 'template/tooltip/tooltip-popup.html'
};
+})
+
+.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
+ return $tooltip( 'tooltip', 'mouseenter', 'mouseleave' );
}]);
diff --git a/template/popover/popover.html b/template/popover/popover.html
index 1d1104dd2b..5929ee6e6a 100644
--- a/template/popover/popover.html
+++ b/template/popover/popover.html
@@ -2,7 +2,7 @@
diff --git a/template/tooltip/tooltip-popup.html b/template/tooltip/tooltip-popup.html
index 77d35de8e2..fd51120774 100644
--- a/template/tooltip/tooltip-popup.html
+++ b/template/tooltip/tooltip-popup.html
@@ -1,4 +1,4 @@