UNPKG

thalassa-aqueduct

Version:

Dynamic haproxy load balancer and configuration. Part of Thalassa

346 lines (299 loc) 11.7 kB
/** * The following features are still outstanding: animation as a * function, placement as a function, inside, support for more triggers than * just mouse enter/leave, html tooltips, and selector delegation. */ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] ) /** * 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, popupDelay: 0 }; // Default hide triggers for each show trigger var triggerMap = { 'mouseenter': 'mouseleave', 'click': 'click', 'focus': 'blur' }; // The options specified to the provider globally. var globalOptions = {}; /** * `options({})` allows global configuration of all tooltips 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 ); }; /** * This allows you to extend the set of trigger mappings available. E.g.: * * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' ); */ this.setTriggers = function setTriggers ( triggers ) { angular.extend( triggerMap, triggers ); }; /** * This is a helper function for translating camel-case to snake-case. */ function snake_case(name){ var regexp = /[A-Z]/g; var separator = '-'; return name.replace(regexp, function(letter, pos) { return (pos ? separator : '') + letter.toLowerCase(); }); } /** * Returns the actual instance of the $tooltip service. * TODO support multiple triggers */ this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $parse, $document, $position, $interpolate ) { return function $tooltip ( type, prefix, defaultTriggerShow ) { var options = angular.extend( {}, defaultOptions, globalOptions ); /** * Returns an object of show and hide triggers. * * If a trigger is supplied, * it is used to show the tooltip; otherwise, it will use the `trigger` * option passed to the `$tooltipProvider.options` method; else it will * default to the trigger supplied to this directive factory. * * The hide trigger is based on the show trigger. If the `trigger` option * was passed to the `$tooltipProvider.options` method, it will use the * mapped trigger from `triggerMap` or the passed trigger if the map is * undefined; otherwise, it uses the `triggerMap` value of the show * trigger; else it will just use the show trigger. */ function setTriggers ( trigger ) { var show, hide; show = trigger || options.trigger || defaultTriggerShow; if ( angular.isDefined ( options.trigger ) ) { hide = triggerMap[options.trigger] || show; } else { hide = triggerMap[show] || show; } return { show: show, hide: hide }; } var directiveName = snake_case( type ); var triggers = setTriggers( undefined ); var startSym = $interpolate.startSymbol(); var endSym = $interpolate.endSymbol(); var template = '<'+ directiveName +'-popup '+ 'title="'+startSym+'tt_title'+endSym+'" '+ 'content="'+startSym+'tt_content'+endSym+'" '+ 'placement="'+startSym+'tt_placement'+endSym+'" '+ 'animation="tt_animation()" '+ 'is-open="tt_isOpen"'+ '>'+ '</'+ directiveName +'-popup>'; return { restrict: 'EA', scope: true, link: function link ( scope, element, attrs ) { var tooltip = $compile( template )( scope ); var transitionTimeout; var popupTimeout; var $body; var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false; // By default, the tooltip is not open. // TODO add ability to start tooltip opened scope.tt_isOpen = false; function toggleTooltipBind () { if ( ! scope.tt_isOpen ) { showTooltipBind(); } else { hideTooltipBind(); } } // Show the tooltip with delay if specified, otherwise show it immediately function showTooltipBind() { if ( scope.tt_popupDelay ) { popupTimeout = $timeout( show, scope.tt_popupDelay ); } else { scope.$apply( show ); } } function hideTooltipBind () { scope.$apply(function () { hide(); }); } // 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 // tooltip 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. if ( appendToBody ) { $body = $body || $document.find( 'body' ); $body.append( tooltip ); } else { element.after( tooltip ); } // Get the position of the directive element. position = options.appendToBody ? $position.offset( element ) : $position.position( 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 'mouse': var mousePos = $position.mouse(); ttPosition = { top: mousePos.y, left: mousePos.x }; break; case 'right': ttPosition = { top: position.top + position.height / 2 - ttHeight / 2, left: position.left + position.width }; break; case 'bottom': ttPosition = { top: position.top + position.height, left: position.left + position.width / 2 - ttWidth / 2 }; break; case 'left': ttPosition = { top: position.top + position.height / 2 - ttHeight / 2, left: position.left - ttWidth }; break; default: ttPosition = { top: position.top - ttHeight, left: position.left + position.width / 2 - ttWidth / 2 }; break; } ttPosition.top += 'px'; ttPosition.left += 'px'; // 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. scope.tt_isOpen = false; //if tooltip is going to be shown after delay, we must cancel this $timeout.cancel( popupTimeout ); // 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(); } } /** * Observe the relevant attributes. */ attrs.$observe( type, function ( val ) { scope.tt_content = val; }); attrs.$observe( prefix+'Title', function ( val ) { scope.tt_title = val; }); attrs.$observe( prefix+'Placement', function ( val ) { scope.tt_placement = angular.isDefined( val ) ? val : options.placement; }); attrs.$observe( prefix+'Animation', function ( val ) { scope.tt_animation = angular.isDefined( val ) ? $parse( val ) : function(){ return options.animation; }; }); attrs.$observe( prefix+'PopupDelay', function ( val ) { var delay = parseInt( val, 10 ); scope.tt_popupDelay = ! isNaN(delay) ? delay : options.popupDelay; }); attrs.$observe( prefix+'Trigger', function ( val ) { element.unbind( triggers.show ); element.unbind( triggers.hide ); triggers = setTriggers( val ); if ( triggers.show === triggers.hide ) { element.bind( triggers.show, toggleTooltipBind ); } else { element.bind( triggers.show, showTooltipBind ); element.bind( triggers.hide, hideTooltipBind ); } }); attrs.$observe( prefix+'AppendToBody', function ( val ) { appendToBody = angular.isDefined( val ) ? $parse( val )( scope ) : appendToBody; }); // if a tooltip is attached to <body> we need to remove it on // location change as its parent scope will probably not be destroyed // by the change. if ( appendToBody ) { scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () { if ( scope.tt_isOpen ) { hide(); } }); } // Make sure tooltip is destroyed and removed. scope.$on('$destroy', function onDestroyTooltip() { if ( scope.tt_isOpen ) { hide(); } else { tooltip.remove(); } }); } }; }; }]; }) .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', 'tooltip', 'mouseenter' ); }]) .directive( 'tooltipHtmlUnsafePopup', function () { return { restrict: 'E', replace: true, scope: { content: '@', placement: '@', animation: '&', isOpen: '&' }, templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html' }; }) .directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) { return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' ); }]);