| @@ 4486-5026 (lines=541) @@ | ||
| 4483 | * The $tooltip service creates tooltip- and popover-like directives as well as |
|
| 4484 | * houses global options for them. |
|
| 4485 | */ |
|
| 4486 | .provider('$uibTooltip', function() { |
|
| 4487 | // The default options tooltip and popover. |
|
| 4488 | var defaultOptions = { |
|
| 4489 | placement: 'top', |
|
| 4490 | placementClassPrefix: '', |
|
| 4491 | animation: true, |
|
| 4492 | popupDelay: 0, |
|
| 4493 | popupCloseDelay: 0, |
|
| 4494 | useContentExp: false |
|
| 4495 | }; |
|
| 4496 | ||
| 4497 | // Default hide triggers for each show trigger |
|
| 4498 | var triggerMap = { |
|
| 4499 | 'mouseenter': 'mouseleave', |
|
| 4500 | 'click': 'click', |
|
| 4501 | 'outsideClick': 'outsideClick', |
|
| 4502 | 'focus': 'blur', |
|
| 4503 | 'none': '' |
|
| 4504 | }; |
|
| 4505 | ||
| 4506 | // The options specified to the provider globally. |
|
| 4507 | var globalOptions = {}; |
|
| 4508 | ||
| 4509 | /** |
|
| 4510 | * `options({})` allows global configuration of all tooltips in the |
|
| 4511 | * application. |
|
| 4512 | * |
|
| 4513 | * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { |
|
| 4514 | * // place tooltips left instead of top by default |
|
| 4515 | * $tooltipProvider.options( { placement: 'left' } ); |
|
| 4516 | * }); |
|
| 4517 | */ |
|
| 4518 | this.options = function(value) { |
|
| 4519 | angular.extend(globalOptions, value); |
|
| 4520 | }; |
|
| 4521 | ||
| 4522 | /** |
|
| 4523 | * This allows you to extend the set of trigger mappings available. E.g.: |
|
| 4524 | * |
|
| 4525 | * $tooltipProvider.setTriggers( { 'openTrigger': 'closeTrigger' } ); |
|
| 4526 | */ |
|
| 4527 | this.setTriggers = function setTriggers(triggers) { |
|
| 4528 | angular.extend(triggerMap, triggers); |
|
| 4529 | }; |
|
| 4530 | ||
| 4531 | /** |
|
| 4532 | * This is a helper function for translating camel-case to snake_case. |
|
| 4533 | */ |
|
| 4534 | function snake_case(name) { |
|
| 4535 | var regexp = /[A-Z]/g; |
|
| 4536 | var separator = '-'; |
|
| 4537 | return name.replace(regexp, function(letter, pos) { |
|
| 4538 | return (pos ? separator : '') + letter.toLowerCase(); |
|
| 4539 | }); |
|
| 4540 | } |
|
| 4541 | ||
| 4542 | /** |
|
| 4543 | * Returns the actual instance of the $tooltip service. |
|
| 4544 | * TODO support multiple triggers |
|
| 4545 | */ |
|
| 4546 | this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) { |
|
| 4547 | var openedTooltips = $$stackedMap.createNew(); |
|
| 4548 | $document.on('keypress', keypressListener); |
|
| 4549 | ||
| 4550 | $rootScope.$on('$destroy', function() { |
|
| 4551 | $document.off('keypress', keypressListener); |
|
| 4552 | }); |
|
| 4553 | ||
| 4554 | function keypressListener(e) { |
|
| 4555 | if (e.which === 27) { |
|
| 4556 | var last = openedTooltips.top(); |
|
| 4557 | if (last) { |
|
| 4558 | last.value.close(); |
|
| 4559 | openedTooltips.removeTop(); |
|
| 4560 | last = null; |
|
| 4561 | } |
|
| 4562 | } |
|
| 4563 | } |
|
| 4564 | ||
| 4565 | return function $tooltip(ttType, prefix, defaultTriggerShow, options) { |
|
| 4566 | options = angular.extend({}, defaultOptions, globalOptions, options); |
|
| 4567 | ||
| 4568 | /** |
|
| 4569 | * Returns an object of show and hide triggers. |
|
| 4570 | * |
|
| 4571 | * If a trigger is supplied, |
|
| 4572 | * it is used to show the tooltip; otherwise, it will use the `trigger` |
|
| 4573 | * option passed to the `$tooltipProvider.options` method; else it will |
|
| 4574 | * default to the trigger supplied to this directive factory. |
|
| 4575 | * |
|
| 4576 | * The hide trigger is based on the show trigger. If the `trigger` option |
|
| 4577 | * was passed to the `$tooltipProvider.options` method, it will use the |
|
| 4578 | * mapped trigger from `triggerMap` or the passed trigger if the map is |
|
| 4579 | * undefined; otherwise, it uses the `triggerMap` value of the show |
|
| 4580 | * trigger; else it will just use the show trigger. |
|
| 4581 | */ |
|
| 4582 | function getTriggers(trigger) { |
|
| 4583 | var show = (trigger || options.trigger || defaultTriggerShow).split(' '); |
|
| 4584 | var hide = show.map(function(trigger) { |
|
| 4585 | return triggerMap[trigger] || trigger; |
|
| 4586 | }); |
|
| 4587 | return { |
|
| 4588 | show: show, |
|
| 4589 | hide: hide |
|
| 4590 | }; |
|
| 4591 | } |
|
| 4592 | ||
| 4593 | var directiveName = snake_case(ttType); |
|
| 4594 | ||
| 4595 | var startSym = $interpolate.startSymbol(); |
|
| 4596 | var endSym = $interpolate.endSymbol(); |
|
| 4597 | var template = |
|
| 4598 | '<div '+ directiveName + '-popup ' + |
|
| 4599 | 'uib-title="' + startSym + 'title' + endSym + '" ' + |
|
| 4600 | (options.useContentExp ? |
|
| 4601 | 'content-exp="contentExp()" ' : |
|
| 4602 | 'content="' + startSym + 'content' + endSym + '" ') + |
|
| 4603 | 'placement="' + startSym + 'placement' + endSym + '" ' + |
|
| 4604 | 'popup-class="' + startSym + 'popupClass' + endSym + '" ' + |
|
| 4605 | 'animation="animation" ' + |
|
| 4606 | 'is-open="isOpen" ' + |
|
| 4607 | 'origin-scope="origScope" ' + |
|
| 4608 | 'class="uib-position-measure"' + |
|
| 4609 | '>' + |
|
| 4610 | '</div>'; |
|
| 4611 | ||
| 4612 | return { |
|
| 4613 | compile: function(tElem, tAttrs) { |
|
| 4614 | var tooltipLinker = $compile(template); |
|
| 4615 | ||
| 4616 | return function link(scope, element, attrs, tooltipCtrl) { |
|
| 4617 | var tooltip; |
|
| 4618 | var tooltipLinkedScope; |
|
| 4619 | var transitionTimeout; |
|
| 4620 | var showTimeout; |
|
| 4621 | var hideTimeout; |
|
| 4622 | var positionTimeout; |
|
| 4623 | var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false; |
|
| 4624 | var triggers = getTriggers(undefined); |
|
| 4625 | var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); |
|
| 4626 | var ttScope = scope.$new(true); |
|
| 4627 | var repositionScheduled = false; |
|
| 4628 | var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false; |
|
| 4629 | var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false; |
|
| 4630 | var observers = []; |
|
| 4631 | var lastPlacement; |
|
| 4632 | ||
| 4633 | var positionTooltip = function() { |
|
| 4634 | // check if tooltip exists and is not empty |
|
| 4635 | if (!tooltip || !tooltip.html()) { return; } |
|
| 4636 | ||
| 4637 | if (!positionTimeout) { |
|
| 4638 | positionTimeout = $timeout(function() { |
|
| 4639 | var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); |
|
| 4640 | tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' }); |
|
| 4641 | ||
| 4642 | if (!tooltip.hasClass(ttPosition.placement.split('-')[0])) { |
|
| 4643 | tooltip.removeClass(lastPlacement.split('-')[0]); |
|
| 4644 | tooltip.addClass(ttPosition.placement.split('-')[0]); |
|
| 4645 | } |
|
| 4646 | ||
| 4647 | if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) { |
|
| 4648 | tooltip.removeClass(options.placementClassPrefix + lastPlacement); |
|
| 4649 | tooltip.addClass(options.placementClassPrefix + ttPosition.placement); |
|
| 4650 | } |
|
| 4651 | ||
| 4652 | // first time through tt element will have the |
|
| 4653 | // uib-position-measure class or if the placement |
|
| 4654 | // has changed we need to position the arrow. |
|
| 4655 | if (tooltip.hasClass('uib-position-measure')) { |
|
| 4656 | $position.positionArrow(tooltip, ttPosition.placement); |
|
| 4657 | tooltip.removeClass('uib-position-measure'); |
|
| 4658 | } else if (lastPlacement !== ttPosition.placement) { |
|
| 4659 | $position.positionArrow(tooltip, ttPosition.placement); |
|
| 4660 | } |
|
| 4661 | lastPlacement = ttPosition.placement; |
|
| 4662 | ||
| 4663 | positionTimeout = null; |
|
| 4664 | }, 0, false); |
|
| 4665 | } |
|
| 4666 | }; |
|
| 4667 | ||
| 4668 | // Set up the correct scope to allow transclusion later |
|
| 4669 | ttScope.origScope = scope; |
|
| 4670 | ||
| 4671 | // By default, the tooltip is not open. |
|
| 4672 | // TODO add ability to start tooltip opened |
|
| 4673 | ttScope.isOpen = false; |
|
| 4674 | openedTooltips.add(ttScope, { |
|
| 4675 | close: hide |
|
| 4676 | }); |
|
| 4677 | ||
| 4678 | function toggleTooltipBind() { |
|
| 4679 | if (!ttScope.isOpen) { |
|
| 4680 | showTooltipBind(); |
|
| 4681 | } else { |
|
| 4682 | hideTooltipBind(); |
|
| 4683 | } |
|
| 4684 | } |
|
| 4685 | ||
| 4686 | // Show the tooltip with delay if specified, otherwise show it immediately |
|
| 4687 | function showTooltipBind() { |
|
| 4688 | if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { |
|
| 4689 | return; |
|
| 4690 | } |
|
| 4691 | ||
| 4692 | cancelHide(); |
|
| 4693 | prepareTooltip(); |
|
| 4694 | ||
| 4695 | if (ttScope.popupDelay) { |
|
| 4696 | // Do nothing if the tooltip was already scheduled to pop-up. |
|
| 4697 | // This happens if show is triggered multiple times before any hide is triggered. |
|
| 4698 | if (!showTimeout) { |
|
| 4699 | showTimeout = $timeout(show, ttScope.popupDelay, false); |
|
| 4700 | } |
|
| 4701 | } else { |
|
| 4702 | show(); |
|
| 4703 | } |
|
| 4704 | } |
|
| 4705 | ||
| 4706 | function hideTooltipBind() { |
|
| 4707 | cancelShow(); |
|
| 4708 | ||
| 4709 | if (ttScope.popupCloseDelay) { |
|
| 4710 | if (!hideTimeout) { |
|
| 4711 | hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false); |
|
| 4712 | } |
|
| 4713 | } else { |
|
| 4714 | hide(); |
|
| 4715 | } |
|
| 4716 | } |
|
| 4717 | ||
| 4718 | // Show the tooltip popup element. |
|
| 4719 | function show() { |
|
| 4720 | cancelShow(); |
|
| 4721 | cancelHide(); |
|
| 4722 | ||
| 4723 | // Don't show empty tooltips. |
|
| 4724 | if (!ttScope.content) { |
|
| 4725 | return angular.noop; |
|
| 4726 | } |
|
| 4727 | ||
| 4728 | createTooltip(); |
|
| 4729 | ||
| 4730 | // And show the tooltip. |
|
| 4731 | ttScope.$evalAsync(function() { |
|
| 4732 | ttScope.isOpen = true; |
|
| 4733 | assignIsOpen(true); |
|
| 4734 | positionTooltip(); |
|
| 4735 | }); |
|
| 4736 | } |
|
| 4737 | ||
| 4738 | function cancelShow() { |
|
| 4739 | if (showTimeout) { |
|
| 4740 | $timeout.cancel(showTimeout); |
|
| 4741 | showTimeout = null; |
|
| 4742 | } |
|
| 4743 | ||
| 4744 | if (positionTimeout) { |
|
| 4745 | $timeout.cancel(positionTimeout); |
|
| 4746 | positionTimeout = null; |
|
| 4747 | } |
|
| 4748 | } |
|
| 4749 | ||
| 4750 | // Hide the tooltip popup element. |
|
| 4751 | function hide() { |
|
| 4752 | if (!ttScope) { |
|
| 4753 | return; |
|
| 4754 | } |
|
| 4755 | ||
| 4756 | // First things first: we don't show it anymore. |
|
| 4757 | ttScope.$evalAsync(function() { |
|
| 4758 | if (ttScope) { |
|
| 4759 | ttScope.isOpen = false; |
|
| 4760 | assignIsOpen(false); |
|
| 4761 | // And now we remove it from the DOM. However, if we have animation, we |
|
| 4762 | // need to wait for it to expire beforehand. |
|
| 4763 | // FIXME: this is a placeholder for a port of the transitions library. |
|
| 4764 | // The fade transition in TWBS is 150ms. |
|
| 4765 | if (ttScope.animation) { |
|
| 4766 | if (!transitionTimeout) { |
|
| 4767 | transitionTimeout = $timeout(removeTooltip, 150, false); |
|
| 4768 | } |
|
| 4769 | } else { |
|
| 4770 | removeTooltip(); |
|
| 4771 | } |
|
| 4772 | } |
|
| 4773 | }); |
|
| 4774 | } |
|
| 4775 | ||
| 4776 | function cancelHide() { |
|
| 4777 | if (hideTimeout) { |
|
| 4778 | $timeout.cancel(hideTimeout); |
|
| 4779 | hideTimeout = null; |
|
| 4780 | } |
|
| 4781 | ||
| 4782 | if (transitionTimeout) { |
|
| 4783 | $timeout.cancel(transitionTimeout); |
|
| 4784 | transitionTimeout = null; |
|
| 4785 | } |
|
| 4786 | } |
|
| 4787 | ||
| 4788 | function createTooltip() { |
|
| 4789 | // There can only be one tooltip element per directive shown at once. |
|
| 4790 | if (tooltip) { |
|
| 4791 | return; |
|
| 4792 | } |
|
| 4793 | ||
| 4794 | tooltipLinkedScope = ttScope.$new(); |
|
| 4795 | tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { |
|
| 4796 | if (appendToBody) { |
|
| 4797 | $document.find('body').append(tooltip); |
|
| 4798 | } else { |
|
| 4799 | element.after(tooltip); |
|
| 4800 | } |
|
| 4801 | }); |
|
| 4802 | ||
| 4803 | prepObservers(); |
|
| 4804 | } |
|
| 4805 | ||
| 4806 | function removeTooltip() { |
|
| 4807 | cancelShow(); |
|
| 4808 | cancelHide(); |
|
| 4809 | unregisterObservers(); |
|
| 4810 | ||
| 4811 | if (tooltip) { |
|
| 4812 | tooltip.remove(); |
|
| 4813 | tooltip = null; |
|
| 4814 | } |
|
| 4815 | if (tooltipLinkedScope) { |
|
| 4816 | tooltipLinkedScope.$destroy(); |
|
| 4817 | tooltipLinkedScope = null; |
|
| 4818 | } |
|
| 4819 | } |
|
| 4820 | ||
| 4821 | /** |
|
| 4822 | * Set the initial scope values. Once |
|
| 4823 | * the tooltip is created, the observers |
|
| 4824 | * will be added to keep things in sync. |
|
| 4825 | */ |
|
| 4826 | function prepareTooltip() { |
|
| 4827 | ttScope.title = attrs[prefix + 'Title']; |
|
| 4828 | if (contentParse) { |
|
| 4829 | ttScope.content = contentParse(scope); |
|
| 4830 | } else { |
|
| 4831 | ttScope.content = attrs[ttType]; |
|
| 4832 | } |
|
| 4833 | ||
| 4834 | ttScope.popupClass = attrs[prefix + 'Class']; |
|
| 4835 | ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement; |
|
| 4836 | var placement = $position.parsePlacement(ttScope.placement); |
|
| 4837 | lastPlacement = placement[1] ? placement[0] + '-' + placement[1] : placement[0]; |
|
| 4838 | ||
| 4839 | var delay = parseInt(attrs[prefix + 'PopupDelay'], 10); |
|
| 4840 | var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10); |
|
| 4841 | ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; |
|
| 4842 | ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay; |
|
| 4843 | } |
|
| 4844 | ||
| 4845 | function assignIsOpen(isOpen) { |
|
| 4846 | if (isOpenParse && angular.isFunction(isOpenParse.assign)) { |
|
| 4847 | isOpenParse.assign(scope, isOpen); |
|
| 4848 | } |
|
| 4849 | } |
|
| 4850 | ||
| 4851 | ttScope.contentExp = function() { |
|
| 4852 | return ttScope.content; |
|
| 4853 | }; |
|
| 4854 | ||
| 4855 | /** |
|
| 4856 | * Observe the relevant attributes. |
|
| 4857 | */ |
|
| 4858 | attrs.$observe('disabled', function(val) { |
|
| 4859 | if (val) { |
|
| 4860 | cancelShow(); |
|
| 4861 | } |
|
| 4862 | ||
| 4863 | if (val && ttScope.isOpen) { |
|
| 4864 | hide(); |
|
| 4865 | } |
|
| 4866 | }); |
|
| 4867 | ||
| 4868 | if (isOpenParse) { |
|
| 4869 | scope.$watch(isOpenParse, function(val) { |
|
| 4870 | if (ttScope && !val === ttScope.isOpen) { |
|
| 4871 | toggleTooltipBind(); |
|
| 4872 | } |
|
| 4873 | }); |
|
| 4874 | } |
|
| 4875 | ||
| 4876 | function prepObservers() { |
|
| 4877 | observers.length = 0; |
|
| 4878 | ||
| 4879 | if (contentParse) { |
|
| 4880 | observers.push( |
|
| 4881 | scope.$watch(contentParse, function(val) { |
|
| 4882 | ttScope.content = val; |
|
| 4883 | if (!val && ttScope.isOpen) { |
|
| 4884 | hide(); |
|
| 4885 | } |
|
| 4886 | }) |
|
| 4887 | ); |
|
| 4888 | ||
| 4889 | observers.push( |
|
| 4890 | tooltipLinkedScope.$watch(function() { |
|
| 4891 | if (!repositionScheduled) { |
|
| 4892 | repositionScheduled = true; |
|
| 4893 | tooltipLinkedScope.$$postDigest(function() { |
|
| 4894 | repositionScheduled = false; |
|
| 4895 | if (ttScope && ttScope.isOpen) { |
|
| 4896 | positionTooltip(); |
|
| 4897 | } |
|
| 4898 | }); |
|
| 4899 | } |
|
| 4900 | }) |
|
| 4901 | ); |
|
| 4902 | } else { |
|
| 4903 | observers.push( |
|
| 4904 | attrs.$observe(ttType, function(val) { |
|
| 4905 | ttScope.content = val; |
|
| 4906 | if (!val && ttScope.isOpen) { |
|
| 4907 | hide(); |
|
| 4908 | } else { |
|
| 4909 | positionTooltip(); |
|
| 4910 | } |
|
| 4911 | }) |
|
| 4912 | ); |
|
| 4913 | } |
|
| 4914 | ||
| 4915 | observers.push( |
|
| 4916 | attrs.$observe(prefix + 'Title', function(val) { |
|
| 4917 | ttScope.title = val; |
|
| 4918 | if (ttScope.isOpen) { |
|
| 4919 | positionTooltip(); |
|
| 4920 | } |
|
| 4921 | }) |
|
| 4922 | ); |
|
| 4923 | ||
| 4924 | observers.push( |
|
| 4925 | attrs.$observe(prefix + 'Placement', function(val) { |
|
| 4926 | ttScope.placement = val ? val : options.placement; |
|
| 4927 | if (ttScope.isOpen) { |
|
| 4928 | positionTooltip(); |
|
| 4929 | } |
|
| 4930 | }) |
|
| 4931 | ); |
|
| 4932 | } |
|
| 4933 | ||
| 4934 | function unregisterObservers() { |
|
| 4935 | if (observers.length) { |
|
| 4936 | angular.forEach(observers, function(observer) { |
|
| 4937 | observer(); |
|
| 4938 | }); |
|
| 4939 | observers.length = 0; |
|
| 4940 | } |
|
| 4941 | } |
|
| 4942 | ||
| 4943 | // hide tooltips/popovers for outsideClick trigger |
|
| 4944 | function bodyHideTooltipBind(e) { |
|
| 4945 | if (!ttScope || !ttScope.isOpen || !tooltip) { |
|
| 4946 | return; |
|
| 4947 | } |
|
| 4948 | // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked |
|
| 4949 | if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) { |
|
| 4950 | hideTooltipBind(); |
|
| 4951 | } |
|
| 4952 | } |
|
| 4953 | ||
| 4954 | var unregisterTriggers = function() { |
|
| 4955 | triggers.show.forEach(function(trigger) { |
|
| 4956 | if (trigger === 'outsideClick') { |
|
| 4957 | element.off('click', toggleTooltipBind); |
|
| 4958 | } else { |
|
| 4959 | element.off(trigger, showTooltipBind); |
|
| 4960 | element.off(trigger, toggleTooltipBind); |
|
| 4961 | } |
|
| 4962 | }); |
|
| 4963 | triggers.hide.forEach(function(trigger) { |
|
| 4964 | if (trigger === 'outsideClick') { |
|
| 4965 | $document.off('click', bodyHideTooltipBind); |
|
| 4966 | } else { |
|
| 4967 | element.off(trigger, hideTooltipBind); |
|
| 4968 | } |
|
| 4969 | }); |
|
| 4970 | }; |
|
| 4971 | ||
| 4972 | function prepTriggers() { |
|
| 4973 | var val = attrs[prefix + 'Trigger']; |
|
| 4974 | unregisterTriggers(); |
|
| 4975 | ||
| 4976 | triggers = getTriggers(val); |
|
| 4977 | ||
| 4978 | if (triggers.show !== 'none') { |
|
| 4979 | triggers.show.forEach(function(trigger, idx) { |
|
| 4980 | if (trigger === 'outsideClick') { |
|
| 4981 | element.on('click', toggleTooltipBind); |
|
| 4982 | $document.on('click', bodyHideTooltipBind); |
|
| 4983 | } else if (trigger === triggers.hide[idx]) { |
|
| 4984 | element.on(trigger, toggleTooltipBind); |
|
| 4985 | } else if (trigger) { |
|
| 4986 | element.on(trigger, showTooltipBind); |
|
| 4987 | element.on(triggers.hide[idx], hideTooltipBind); |
|
| 4988 | } |
|
| 4989 | ||
| 4990 | element.on('keypress', function(e) { |
|
| 4991 | if (e.which === 27) { |
|
| 4992 | hideTooltipBind(); |
|
| 4993 | } |
|
| 4994 | }); |
|
| 4995 | }); |
|
| 4996 | } |
|
| 4997 | } |
|
| 4998 | ||
| 4999 | prepTriggers(); |
|
| 5000 | ||
| 5001 | var animation = scope.$eval(attrs[prefix + 'Animation']); |
|
| 5002 | ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; |
|
| 5003 | ||
| 5004 | var appendToBodyVal; |
|
| 5005 | var appendKey = prefix + 'AppendToBody'; |
|
| 5006 | if (appendKey in attrs && attrs[appendKey] === undefined) { |
|
| 5007 | appendToBodyVal = true; |
|
| 5008 | } else { |
|
| 5009 | appendToBodyVal = scope.$eval(attrs[appendKey]); |
|
| 5010 | } |
|
| 5011 | ||
| 5012 | appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; |
|
| 5013 | ||
| 5014 | // Make sure tooltip is destroyed and removed. |
|
| 5015 | scope.$on('$destroy', function onDestroyTooltip() { |
|
| 5016 | unregisterTriggers(); |
|
| 5017 | removeTooltip(); |
|
| 5018 | openedTooltips.remove(ttScope); |
|
| 5019 | ttScope = null; |
|
| 5020 | }); |
|
| 5021 | }; |
|
| 5022 | } |
|
| 5023 | }; |
|
| 5024 | }; |
|
| 5025 | }]; |
|
| 5026 | }) |
|
| 5027 | ||
| 5028 | // This is mostly ngInclude code but with a custom scope |
|
| 5029 | .directive('uibTooltipTemplateTransclude', [ |
|
| @@ 4485-5025 (lines=541) @@ | ||
| 4482 | * The $tooltip service creates tooltip- and popover-like directives as well as |
|
| 4483 | * houses global options for them. |
|
| 4484 | */ |
|
| 4485 | .provider('$uibTooltip', function() { |
|
| 4486 | // The default options tooltip and popover. |
|
| 4487 | var defaultOptions = { |
|
| 4488 | placement: 'top', |
|
| 4489 | placementClassPrefix: '', |
|
| 4490 | animation: true, |
|
| 4491 | popupDelay: 0, |
|
| 4492 | popupCloseDelay: 0, |
|
| 4493 | useContentExp: false |
|
| 4494 | }; |
|
| 4495 | ||
| 4496 | // Default hide triggers for each show trigger |
|
| 4497 | var triggerMap = { |
|
| 4498 | 'mouseenter': 'mouseleave', |
|
| 4499 | 'click': 'click', |
|
| 4500 | 'outsideClick': 'outsideClick', |
|
| 4501 | 'focus': 'blur', |
|
| 4502 | 'none': '' |
|
| 4503 | }; |
|
| 4504 | ||
| 4505 | // The options specified to the provider globally. |
|
| 4506 | var globalOptions = {}; |
|
| 4507 | ||
| 4508 | /** |
|
| 4509 | * `options({})` allows global configuration of all tooltips in the |
|
| 4510 | * application. |
|
| 4511 | * |
|
| 4512 | * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { |
|
| 4513 | * // place tooltips left instead of top by default |
|
| 4514 | * $tooltipProvider.options( { placement: 'left' } ); |
|
| 4515 | * }); |
|
| 4516 | */ |
|
| 4517 | this.options = function(value) { |
|
| 4518 | angular.extend(globalOptions, value); |
|
| 4519 | }; |
|
| 4520 | ||
| 4521 | /** |
|
| 4522 | * This allows you to extend the set of trigger mappings available. E.g.: |
|
| 4523 | * |
|
| 4524 | * $tooltipProvider.setTriggers( { 'openTrigger': 'closeTrigger' } ); |
|
| 4525 | */ |
|
| 4526 | this.setTriggers = function setTriggers(triggers) { |
|
| 4527 | angular.extend(triggerMap, triggers); |
|
| 4528 | }; |
|
| 4529 | ||
| 4530 | /** |
|
| 4531 | * This is a helper function for translating camel-case to snake_case. |
|
| 4532 | */ |
|
| 4533 | function snake_case(name) { |
|
| 4534 | var regexp = /[A-Z]/g; |
|
| 4535 | var separator = '-'; |
|
| 4536 | return name.replace(regexp, function(letter, pos) { |
|
| 4537 | return (pos ? separator : '') + letter.toLowerCase(); |
|
| 4538 | }); |
|
| 4539 | } |
|
| 4540 | ||
| 4541 | /** |
|
| 4542 | * Returns the actual instance of the $tooltip service. |
|
| 4543 | * TODO support multiple triggers |
|
| 4544 | */ |
|
| 4545 | this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) { |
|
| 4546 | var openedTooltips = $$stackedMap.createNew(); |
|
| 4547 | $document.on('keypress', keypressListener); |
|
| 4548 | ||
| 4549 | $rootScope.$on('$destroy', function() { |
|
| 4550 | $document.off('keypress', keypressListener); |
|
| 4551 | }); |
|
| 4552 | ||
| 4553 | function keypressListener(e) { |
|
| 4554 | if (e.which === 27) { |
|
| 4555 | var last = openedTooltips.top(); |
|
| 4556 | if (last) { |
|
| 4557 | last.value.close(); |
|
| 4558 | openedTooltips.removeTop(); |
|
| 4559 | last = null; |
|
| 4560 | } |
|
| 4561 | } |
|
| 4562 | } |
|
| 4563 | ||
| 4564 | return function $tooltip(ttType, prefix, defaultTriggerShow, options) { |
|
| 4565 | options = angular.extend({}, defaultOptions, globalOptions, options); |
|
| 4566 | ||
| 4567 | /** |
|
| 4568 | * Returns an object of show and hide triggers. |
|
| 4569 | * |
|
| 4570 | * If a trigger is supplied, |
|
| 4571 | * it is used to show the tooltip; otherwise, it will use the `trigger` |
|
| 4572 | * option passed to the `$tooltipProvider.options` method; else it will |
|
| 4573 | * default to the trigger supplied to this directive factory. |
|
| 4574 | * |
|
| 4575 | * The hide trigger is based on the show trigger. If the `trigger` option |
|
| 4576 | * was passed to the `$tooltipProvider.options` method, it will use the |
|
| 4577 | * mapped trigger from `triggerMap` or the passed trigger if the map is |
|
| 4578 | * undefined; otherwise, it uses the `triggerMap` value of the show |
|
| 4579 | * trigger; else it will just use the show trigger. |
|
| 4580 | */ |
|
| 4581 | function getTriggers(trigger) { |
|
| 4582 | var show = (trigger || options.trigger || defaultTriggerShow).split(' '); |
|
| 4583 | var hide = show.map(function(trigger) { |
|
| 4584 | return triggerMap[trigger] || trigger; |
|
| 4585 | }); |
|
| 4586 | return { |
|
| 4587 | show: show, |
|
| 4588 | hide: hide |
|
| 4589 | }; |
|
| 4590 | } |
|
| 4591 | ||
| 4592 | var directiveName = snake_case(ttType); |
|
| 4593 | ||
| 4594 | var startSym = $interpolate.startSymbol(); |
|
| 4595 | var endSym = $interpolate.endSymbol(); |
|
| 4596 | var template = |
|
| 4597 | '<div '+ directiveName + '-popup ' + |
|
| 4598 | 'uib-title="' + startSym + 'title' + endSym + '" ' + |
|
| 4599 | (options.useContentExp ? |
|
| 4600 | 'content-exp="contentExp()" ' : |
|
| 4601 | 'content="' + startSym + 'content' + endSym + '" ') + |
|
| 4602 | 'placement="' + startSym + 'placement' + endSym + '" ' + |
|
| 4603 | 'popup-class="' + startSym + 'popupClass' + endSym + '" ' + |
|
| 4604 | 'animation="animation" ' + |
|
| 4605 | 'is-open="isOpen" ' + |
|
| 4606 | 'origin-scope="origScope" ' + |
|
| 4607 | 'class="uib-position-measure"' + |
|
| 4608 | '>' + |
|
| 4609 | '</div>'; |
|
| 4610 | ||
| 4611 | return { |
|
| 4612 | compile: function(tElem, tAttrs) { |
|
| 4613 | var tooltipLinker = $compile(template); |
|
| 4614 | ||
| 4615 | return function link(scope, element, attrs, tooltipCtrl) { |
|
| 4616 | var tooltip; |
|
| 4617 | var tooltipLinkedScope; |
|
| 4618 | var transitionTimeout; |
|
| 4619 | var showTimeout; |
|
| 4620 | var hideTimeout; |
|
| 4621 | var positionTimeout; |
|
| 4622 | var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false; |
|
| 4623 | var triggers = getTriggers(undefined); |
|
| 4624 | var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); |
|
| 4625 | var ttScope = scope.$new(true); |
|
| 4626 | var repositionScheduled = false; |
|
| 4627 | var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false; |
|
| 4628 | var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false; |
|
| 4629 | var observers = []; |
|
| 4630 | var lastPlacement; |
|
| 4631 | ||
| 4632 | var positionTooltip = function() { |
|
| 4633 | // check if tooltip exists and is not empty |
|
| 4634 | if (!tooltip || !tooltip.html()) { return; } |
|
| 4635 | ||
| 4636 | if (!positionTimeout) { |
|
| 4637 | positionTimeout = $timeout(function() { |
|
| 4638 | var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); |
|
| 4639 | tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' }); |
|
| 4640 | ||
| 4641 | if (!tooltip.hasClass(ttPosition.placement.split('-')[0])) { |
|
| 4642 | tooltip.removeClass(lastPlacement.split('-')[0]); |
|
| 4643 | tooltip.addClass(ttPosition.placement.split('-')[0]); |
|
| 4644 | } |
|
| 4645 | ||
| 4646 | if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) { |
|
| 4647 | tooltip.removeClass(options.placementClassPrefix + lastPlacement); |
|
| 4648 | tooltip.addClass(options.placementClassPrefix + ttPosition.placement); |
|
| 4649 | } |
|
| 4650 | ||
| 4651 | // first time through tt element will have the |
|
| 4652 | // uib-position-measure class or if the placement |
|
| 4653 | // has changed we need to position the arrow. |
|
| 4654 | if (tooltip.hasClass('uib-position-measure')) { |
|
| 4655 | $position.positionArrow(tooltip, ttPosition.placement); |
|
| 4656 | tooltip.removeClass('uib-position-measure'); |
|
| 4657 | } else if (lastPlacement !== ttPosition.placement) { |
|
| 4658 | $position.positionArrow(tooltip, ttPosition.placement); |
|
| 4659 | } |
|
| 4660 | lastPlacement = ttPosition.placement; |
|
| 4661 | ||
| 4662 | positionTimeout = null; |
|
| 4663 | }, 0, false); |
|
| 4664 | } |
|
| 4665 | }; |
|
| 4666 | ||
| 4667 | // Set up the correct scope to allow transclusion later |
|
| 4668 | ttScope.origScope = scope; |
|
| 4669 | ||
| 4670 | // By default, the tooltip is not open. |
|
| 4671 | // TODO add ability to start tooltip opened |
|
| 4672 | ttScope.isOpen = false; |
|
| 4673 | openedTooltips.add(ttScope, { |
|
| 4674 | close: hide |
|
| 4675 | }); |
|
| 4676 | ||
| 4677 | function toggleTooltipBind() { |
|
| 4678 | if (!ttScope.isOpen) { |
|
| 4679 | showTooltipBind(); |
|
| 4680 | } else { |
|
| 4681 | hideTooltipBind(); |
|
| 4682 | } |
|
| 4683 | } |
|
| 4684 | ||
| 4685 | // Show the tooltip with delay if specified, otherwise show it immediately |
|
| 4686 | function showTooltipBind() { |
|
| 4687 | if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { |
|
| 4688 | return; |
|
| 4689 | } |
|
| 4690 | ||
| 4691 | cancelHide(); |
|
| 4692 | prepareTooltip(); |
|
| 4693 | ||
| 4694 | if (ttScope.popupDelay) { |
|
| 4695 | // Do nothing if the tooltip was already scheduled to pop-up. |
|
| 4696 | // This happens if show is triggered multiple times before any hide is triggered. |
|
| 4697 | if (!showTimeout) { |
|
| 4698 | showTimeout = $timeout(show, ttScope.popupDelay, false); |
|
| 4699 | } |
|
| 4700 | } else { |
|
| 4701 | show(); |
|
| 4702 | } |
|
| 4703 | } |
|
| 4704 | ||
| 4705 | function hideTooltipBind() { |
|
| 4706 | cancelShow(); |
|
| 4707 | ||
| 4708 | if (ttScope.popupCloseDelay) { |
|
| 4709 | if (!hideTimeout) { |
|
| 4710 | hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false); |
|
| 4711 | } |
|
| 4712 | } else { |
|
| 4713 | hide(); |
|
| 4714 | } |
|
| 4715 | } |
|
| 4716 | ||
| 4717 | // Show the tooltip popup element. |
|
| 4718 | function show() { |
|
| 4719 | cancelShow(); |
|
| 4720 | cancelHide(); |
|
| 4721 | ||
| 4722 | // Don't show empty tooltips. |
|
| 4723 | if (!ttScope.content) { |
|
| 4724 | return angular.noop; |
|
| 4725 | } |
|
| 4726 | ||
| 4727 | createTooltip(); |
|
| 4728 | ||
| 4729 | // And show the tooltip. |
|
| 4730 | ttScope.$evalAsync(function() { |
|
| 4731 | ttScope.isOpen = true; |
|
| 4732 | assignIsOpen(true); |
|
| 4733 | positionTooltip(); |
|
| 4734 | }); |
|
| 4735 | } |
|
| 4736 | ||
| 4737 | function cancelShow() { |
|
| 4738 | if (showTimeout) { |
|
| 4739 | $timeout.cancel(showTimeout); |
|
| 4740 | showTimeout = null; |
|
| 4741 | } |
|
| 4742 | ||
| 4743 | if (positionTimeout) { |
|
| 4744 | $timeout.cancel(positionTimeout); |
|
| 4745 | positionTimeout = null; |
|
| 4746 | } |
|
| 4747 | } |
|
| 4748 | ||
| 4749 | // Hide the tooltip popup element. |
|
| 4750 | function hide() { |
|
| 4751 | if (!ttScope) { |
|
| 4752 | return; |
|
| 4753 | } |
|
| 4754 | ||
| 4755 | // First things first: we don't show it anymore. |
|
| 4756 | ttScope.$evalAsync(function() { |
|
| 4757 | if (ttScope) { |
|
| 4758 | ttScope.isOpen = false; |
|
| 4759 | assignIsOpen(false); |
|
| 4760 | // And now we remove it from the DOM. However, if we have animation, we |
|
| 4761 | // need to wait for it to expire beforehand. |
|
| 4762 | // FIXME: this is a placeholder for a port of the transitions library. |
|
| 4763 | // The fade transition in TWBS is 150ms. |
|
| 4764 | if (ttScope.animation) { |
|
| 4765 | if (!transitionTimeout) { |
|
| 4766 | transitionTimeout = $timeout(removeTooltip, 150, false); |
|
| 4767 | } |
|
| 4768 | } else { |
|
| 4769 | removeTooltip(); |
|
| 4770 | } |
|
| 4771 | } |
|
| 4772 | }); |
|
| 4773 | } |
|
| 4774 | ||
| 4775 | function cancelHide() { |
|
| 4776 | if (hideTimeout) { |
|
| 4777 | $timeout.cancel(hideTimeout); |
|
| 4778 | hideTimeout = null; |
|
| 4779 | } |
|
| 4780 | ||
| 4781 | if (transitionTimeout) { |
|
| 4782 | $timeout.cancel(transitionTimeout); |
|
| 4783 | transitionTimeout = null; |
|
| 4784 | } |
|
| 4785 | } |
|
| 4786 | ||
| 4787 | function createTooltip() { |
|
| 4788 | // There can only be one tooltip element per directive shown at once. |
|
| 4789 | if (tooltip) { |
|
| 4790 | return; |
|
| 4791 | } |
|
| 4792 | ||
| 4793 | tooltipLinkedScope = ttScope.$new(); |
|
| 4794 | tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { |
|
| 4795 | if (appendToBody) { |
|
| 4796 | $document.find('body').append(tooltip); |
|
| 4797 | } else { |
|
| 4798 | element.after(tooltip); |
|
| 4799 | } |
|
| 4800 | }); |
|
| 4801 | ||
| 4802 | prepObservers(); |
|
| 4803 | } |
|
| 4804 | ||
| 4805 | function removeTooltip() { |
|
| 4806 | cancelShow(); |
|
| 4807 | cancelHide(); |
|
| 4808 | unregisterObservers(); |
|
| 4809 | ||
| 4810 | if (tooltip) { |
|
| 4811 | tooltip.remove(); |
|
| 4812 | tooltip = null; |
|
| 4813 | } |
|
| 4814 | if (tooltipLinkedScope) { |
|
| 4815 | tooltipLinkedScope.$destroy(); |
|
| 4816 | tooltipLinkedScope = null; |
|
| 4817 | } |
|
| 4818 | } |
|
| 4819 | ||
| 4820 | /** |
|
| 4821 | * Set the initial scope values. Once |
|
| 4822 | * the tooltip is created, the observers |
|
| 4823 | * will be added to keep things in sync. |
|
| 4824 | */ |
|
| 4825 | function prepareTooltip() { |
|
| 4826 | ttScope.title = attrs[prefix + 'Title']; |
|
| 4827 | if (contentParse) { |
|
| 4828 | ttScope.content = contentParse(scope); |
|
| 4829 | } else { |
|
| 4830 | ttScope.content = attrs[ttType]; |
|
| 4831 | } |
|
| 4832 | ||
| 4833 | ttScope.popupClass = attrs[prefix + 'Class']; |
|
| 4834 | ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement; |
|
| 4835 | var placement = $position.parsePlacement(ttScope.placement); |
|
| 4836 | lastPlacement = placement[1] ? placement[0] + '-' + placement[1] : placement[0]; |
|
| 4837 | ||
| 4838 | var delay = parseInt(attrs[prefix + 'PopupDelay'], 10); |
|
| 4839 | var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10); |
|
| 4840 | ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; |
|
| 4841 | ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay; |
|
| 4842 | } |
|
| 4843 | ||
| 4844 | function assignIsOpen(isOpen) { |
|
| 4845 | if (isOpenParse && angular.isFunction(isOpenParse.assign)) { |
|
| 4846 | isOpenParse.assign(scope, isOpen); |
|
| 4847 | } |
|
| 4848 | } |
|
| 4849 | ||
| 4850 | ttScope.contentExp = function() { |
|
| 4851 | return ttScope.content; |
|
| 4852 | }; |
|
| 4853 | ||
| 4854 | /** |
|
| 4855 | * Observe the relevant attributes. |
|
| 4856 | */ |
|
| 4857 | attrs.$observe('disabled', function(val) { |
|
| 4858 | if (val) { |
|
| 4859 | cancelShow(); |
|
| 4860 | } |
|
| 4861 | ||
| 4862 | if (val && ttScope.isOpen) { |
|
| 4863 | hide(); |
|
| 4864 | } |
|
| 4865 | }); |
|
| 4866 | ||
| 4867 | if (isOpenParse) { |
|
| 4868 | scope.$watch(isOpenParse, function(val) { |
|
| 4869 | if (ttScope && !val === ttScope.isOpen) { |
|
| 4870 | toggleTooltipBind(); |
|
| 4871 | } |
|
| 4872 | }); |
|
| 4873 | } |
|
| 4874 | ||
| 4875 | function prepObservers() { |
|
| 4876 | observers.length = 0; |
|
| 4877 | ||
| 4878 | if (contentParse) { |
|
| 4879 | observers.push( |
|
| 4880 | scope.$watch(contentParse, function(val) { |
|
| 4881 | ttScope.content = val; |
|
| 4882 | if (!val && ttScope.isOpen) { |
|
| 4883 | hide(); |
|
| 4884 | } |
|
| 4885 | }) |
|
| 4886 | ); |
|
| 4887 | ||
| 4888 | observers.push( |
|
| 4889 | tooltipLinkedScope.$watch(function() { |
|
| 4890 | if (!repositionScheduled) { |
|
| 4891 | repositionScheduled = true; |
|
| 4892 | tooltipLinkedScope.$$postDigest(function() { |
|
| 4893 | repositionScheduled = false; |
|
| 4894 | if (ttScope && ttScope.isOpen) { |
|
| 4895 | positionTooltip(); |
|
| 4896 | } |
|
| 4897 | }); |
|
| 4898 | } |
|
| 4899 | }) |
|
| 4900 | ); |
|
| 4901 | } else { |
|
| 4902 | observers.push( |
|
| 4903 | attrs.$observe(ttType, function(val) { |
|
| 4904 | ttScope.content = val; |
|
| 4905 | if (!val && ttScope.isOpen) { |
|
| 4906 | hide(); |
|
| 4907 | } else { |
|
| 4908 | positionTooltip(); |
|
| 4909 | } |
|
| 4910 | }) |
|
| 4911 | ); |
|
| 4912 | } |
|
| 4913 | ||
| 4914 | observers.push( |
|
| 4915 | attrs.$observe(prefix + 'Title', function(val) { |
|
| 4916 | ttScope.title = val; |
|
| 4917 | if (ttScope.isOpen) { |
|
| 4918 | positionTooltip(); |
|
| 4919 | } |
|
| 4920 | }) |
|
| 4921 | ); |
|
| 4922 | ||
| 4923 | observers.push( |
|
| 4924 | attrs.$observe(prefix + 'Placement', function(val) { |
|
| 4925 | ttScope.placement = val ? val : options.placement; |
|
| 4926 | if (ttScope.isOpen) { |
|
| 4927 | positionTooltip(); |
|
| 4928 | } |
|
| 4929 | }) |
|
| 4930 | ); |
|
| 4931 | } |
|
| 4932 | ||
| 4933 | function unregisterObservers() { |
|
| 4934 | if (observers.length) { |
|
| 4935 | angular.forEach(observers, function(observer) { |
|
| 4936 | observer(); |
|
| 4937 | }); |
|
| 4938 | observers.length = 0; |
|
| 4939 | } |
|
| 4940 | } |
|
| 4941 | ||
| 4942 | // hide tooltips/popovers for outsideClick trigger |
|
| 4943 | function bodyHideTooltipBind(e) { |
|
| 4944 | if (!ttScope || !ttScope.isOpen || !tooltip) { |
|
| 4945 | return; |
|
| 4946 | } |
|
| 4947 | // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked |
|
| 4948 | if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) { |
|
| 4949 | hideTooltipBind(); |
|
| 4950 | } |
|
| 4951 | } |
|
| 4952 | ||
| 4953 | var unregisterTriggers = function() { |
|
| 4954 | triggers.show.forEach(function(trigger) { |
|
| 4955 | if (trigger === 'outsideClick') { |
|
| 4956 | element.off('click', toggleTooltipBind); |
|
| 4957 | } else { |
|
| 4958 | element.off(trigger, showTooltipBind); |
|
| 4959 | element.off(trigger, toggleTooltipBind); |
|
| 4960 | } |
|
| 4961 | }); |
|
| 4962 | triggers.hide.forEach(function(trigger) { |
|
| 4963 | if (trigger === 'outsideClick') { |
|
| 4964 | $document.off('click', bodyHideTooltipBind); |
|
| 4965 | } else { |
|
| 4966 | element.off(trigger, hideTooltipBind); |
|
| 4967 | } |
|
| 4968 | }); |
|
| 4969 | }; |
|
| 4970 | ||
| 4971 | function prepTriggers() { |
|
| 4972 | var val = attrs[prefix + 'Trigger']; |
|
| 4973 | unregisterTriggers(); |
|
| 4974 | ||
| 4975 | triggers = getTriggers(val); |
|
| 4976 | ||
| 4977 | if (triggers.show !== 'none') { |
|
| 4978 | triggers.show.forEach(function(trigger, idx) { |
|
| 4979 | if (trigger === 'outsideClick') { |
|
| 4980 | element.on('click', toggleTooltipBind); |
|
| 4981 | $document.on('click', bodyHideTooltipBind); |
|
| 4982 | } else if (trigger === triggers.hide[idx]) { |
|
| 4983 | element.on(trigger, toggleTooltipBind); |
|
| 4984 | } else if (trigger) { |
|
| 4985 | element.on(trigger, showTooltipBind); |
|
| 4986 | element.on(triggers.hide[idx], hideTooltipBind); |
|
| 4987 | } |
|
| 4988 | ||
| 4989 | element.on('keypress', function(e) { |
|
| 4990 | if (e.which === 27) { |
|
| 4991 | hideTooltipBind(); |
|
| 4992 | } |
|
| 4993 | }); |
|
| 4994 | }); |
|
| 4995 | } |
|
| 4996 | } |
|
| 4997 | ||
| 4998 | prepTriggers(); |
|
| 4999 | ||
| 5000 | var animation = scope.$eval(attrs[prefix + 'Animation']); |
|
| 5001 | ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; |
|
| 5002 | ||
| 5003 | var appendToBodyVal; |
|
| 5004 | var appendKey = prefix + 'AppendToBody'; |
|
| 5005 | if (appendKey in attrs && attrs[appendKey] === undefined) { |
|
| 5006 | appendToBodyVal = true; |
|
| 5007 | } else { |
|
| 5008 | appendToBodyVal = scope.$eval(attrs[appendKey]); |
|
| 5009 | } |
|
| 5010 | ||
| 5011 | appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; |
|
| 5012 | ||
| 5013 | // Make sure tooltip is destroyed and removed. |
|
| 5014 | scope.$on('$destroy', function onDestroyTooltip() { |
|
| 5015 | unregisterTriggers(); |
|
| 5016 | removeTooltip(); |
|
| 5017 | openedTooltips.remove(ttScope); |
|
| 5018 | ttScope = null; |
|
| 5019 | }); |
|
| 5020 | }; |
|
| 5021 | } |
|
| 5022 | }; |
|
| 5023 | }; |
|
| 5024 | }]; |
|
| 5025 | }) |
|
| 5026 | ||
| 5027 | // This is mostly ngInclude code but with a custom scope |
|
| 5028 | .directive('uibTooltipTemplateTransclude', [ |
|