diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js index 96403d7f04..8f5014b977 100644 --- a/src/tooltip/tooltip.js +++ b/src/tooltip/tooltip.js @@ -14,9 +14,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] ) var defaultOptions = { placement: 'top', animation: true, - popupDelay: 0 + popupDelay: 0, + popoverMode: 'multi' }; - + // Default hide triggers for each show trigger var triggerMap = { 'mouseenter': 'mouseleave', @@ -98,6 +99,102 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] ) }; } + /** + * Hides all popovers except for the current one. + * If the current element is not a popover or its child, + * this function will hide all popovers (example on body.click()) + * Invoked from show() and from body.click() + */ + function hideOthers(current) { + var isDomEvent = false; + if (current.target !== undefined) { + current = current.target; + isDomEvent = true; + } + /** + * Finds out recursively if an element is a child of another element + */ + function findChildRecursively(parent, child) { + if (parent === undefined) { + return false; + } + + var cs = parent.childNodes; + if (cs === undefined) { + return false; + } + + for (var i = 0, len = cs.length; i < len; i++) { + if (cs[i] == child) { + return true; + } + else { + // Find it in this tree + if (findChildRecursively(cs[i], child)) { + return true; + } + } + } + return false; + } + + //Find all elements with the popover attribute + var popups = document.querySelectorAll('*[popover]'); + if (popups) { + //Go through all of them + for (var i = 0; i < popups.length; i++) { + //The following is the popover DOM element + var popup = popups[i]; + //The following is the same jQuery lite element + var popupElement = angular.element(popup); + //The following is the tooltips jQuery lite element + var tooltipElement = popupElement.scope().tooltip; + //The following is the toltip DOM element + var tooltip; + if (tooltipElement !== undefined && tooltipElement != null && tooltipElement.length > 0) { + tooltip = tooltipElement[0]; + } + //If the element clicked is a child of the current popover's tooltip + var isTooltipChild = findChildRecursively(tooltip, current); + //If the element clicked is a child of the current popover + var isPopoverChild = findChildRecursively(popup, current); + + //If the following condition is met, then the click does not correspond + //to a click on the current popover in the loop or one of its children or its content. + //So, we can safely hide the current popover + if ( + popup != current && //The click was not on this popup + !isPopoverChild && //And it was not on any child of this popup + tooltip != current && //And it was not on this popups tooltip + !isTooltipChild //And it was not on any child of this popups tooltip + ) { + //If we have a tooltip for this popup + if (tooltip !== undefined) { + //And its width/height != 0 (i.e. it is displayed) + var w = tooltip.offsetWidth, h = tooltip.offsetHeight; + + if (w !== 0 && h !== 0) { + //Toggle its visibility + if (isDomEvent) { + popupElement.scope().hideTooltipBind(); + } + else { + popupElement.scope().hide(); + } + } + } + } + } + } + } + + //If mode is 'single' then bind to body click + var popoverMode = angular.isDefined(options.popoverMode) ? options.popoverMode : 'multi'; + //If this is a popover and mode in options is set to single, then bind to body elements + if (type == 'popover' && popoverMode == 'single') { + angular.element(document.body).bind('click', hideOthers); + } + var directiveName = snake_case( type ); var triggers = setTriggers( undefined ); @@ -109,10 +206,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] ) 'content="'+startSym+'tt_content'+endSym+'" '+ 'placement="'+startSym+'tt_placement'+endSym+'" '+ 'animation="tt_animation()" '+ - 'is-open="tt_isOpen"'+ + 'is-open="tt_isOpen" '+ + 'style="z-index:9999"' + //Changed z-index so that popover is visible over modal when appendToBody is true '>'+ ''; - return { restrict: 'EA', scope: true, @@ -126,6 +223,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] ) // By default, the tooltip is not open. // TODO add ability to start tooltip opened scope.tt_isOpen = false; + + //We need access to this if we want to hide + //etc. on body click + scope.tooltip = tooltip; function toggleTooltipBind () { if ( ! scope.tt_isOpen ) { @@ -134,7 +235,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] ) hideTooltipBind(); } } - + // Show the tooltip with delay if specified, otherwise show it immediately function showTooltipBind() { if ( scope.tt_popupDelay ) { @@ -149,9 +250,19 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] ) hide(); }); } + + //Need to expose above function so that we can globally hide tooltips + scope.hideTooltipBind = hideTooltipBind; + scope.hide = hide; // Show the tooltip popup element. function show() { + var popoverMode = angular.isDefined(options.popoverMode) ? options.popoverMode : 'multi'; + //If this is a popover and mode in options is set to single, then bind to body elements + if (type == 'popover' && popoverMode == 'single') { + hideOthers(element[0]); + } + var position, ttWidth, ttHeight,