nexus site path corrected
[portal.git] / ecomp-portal-FE / client / bower_components / angular-material / modules / closure / tooltip / tooltip.js
1 /*!
2  * Angular Material Design
3  * https://github.com/angular/material
4  * @license MIT
5  * v0.9.8
6  */
7 goog.provide('ng.material.components.tooltip');
8 goog.require('ng.material.core');
9 /**
10  * @ngdoc module
11  * @name material.components.tooltip
12  */
13 angular
14     .module('material.components.tooltip', [ 'material.core' ])
15     .directive('mdTooltip', MdTooltipDirective);
16
17 /**
18  * @ngdoc directive
19  * @name mdTooltip
20  * @module material.components.tooltip
21  * @description
22  * Tooltips are used to describe elements that are interactive and primarily graphical (not textual).
23  *
24  * Place a `<md-tooltip>` as a child of the element it describes.
25  *
26  * A tooltip will activate when the user focuses, hovers over, or touches the parent.
27  *
28  * @usage
29  * <hljs lang="html">
30  * <md-button class="md-fab md-accent" aria-label="Play">
31  *   <md-tooltip>
32  *     Play Music
33  *   </md-tooltip>
34  *   <md-icon icon="img/icons/ic_play_arrow_24px.svg"></md-icon>
35  * </md-button>
36  * </hljs>
37  *
38  * @param {expression=} md-visible Boolean bound to whether the tooltip is
39  * currently visible.
40  * @param {number=} md-delay How many milliseconds to wait to show the tooltip after the user focuses, hovers, or touches the parent. Defaults to 400ms.
41  * @param {string=} md-direction Which direction would you like the tooltip to go?  Supports left, right, top, and bottom.  Defaults to bottom.
42  * @param {boolean=} md-autohide If present or provided with a boolean value, the tooltip will hide on mouse leave, regardless of focus
43  */
44 function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdTheming, $rootElement,
45                             $animate, $q) {
46
47   var TOOLTIP_SHOW_DELAY = 300;
48   var TOOLTIP_WINDOW_EDGE_SPACE = 8;
49
50   return {
51     restrict: 'E',
52     transclude: true,
53     priority:210, // Before ngAria
54     template: '\
55         <div class="md-background"></div>\
56         <div class="md-content" ng-transclude></div>',
57     scope: {
58       visible: '=?mdVisible',
59       delay: '=?mdDelay',
60       autohide: '=?mdAutohide'
61     },
62     link: postLink
63   };
64
65   function postLink(scope, element, attr) {
66
67     $mdTheming(element);
68
69     var parent        = getParentWithPointerEvents(),
70         background    = angular.element(element[0].getElementsByClassName('md-background')[0]),
71         content       = angular.element(element[0].getElementsByClassName('md-content')[0]),
72         direction     = attr.mdDirection,
73         current       = getNearestContentElement(),
74         tooltipParent = angular.element(current || document.body),
75         debouncedOnResize = $$rAF.throttle(function () { if (scope.visible) positionTooltip(); });
76
77     return init();
78
79     function init () {
80       setDefaults();
81       manipulateElement();
82       bindEvents();
83       configureWatchers();
84       addAriaLabel();
85     }
86
87     function setDefaults () {
88       if (!angular.isDefined(attr.mdDelay)) scope.delay = TOOLTIP_SHOW_DELAY;
89     }
90
91     function configureWatchers () {
92       scope.$on('$destroy', function() {
93         scope.visible = false;
94         element.remove();
95         angular.element($window).off('resize', debouncedOnResize);
96       });
97       scope.$watch('visible', function (isVisible) {
98         if (isVisible) showTooltip();
99         else hideTooltip();
100       });
101     }
102
103     function addAriaLabel () {
104       if (!parent.attr('aria-label') && !parent.text().trim()) {
105         parent.attr('aria-label', element.text().trim());
106       }
107     }
108
109     function manipulateElement () {
110       element.detach();
111       element.attr('role', 'tooltip');
112     }
113
114     function getParentWithPointerEvents () {
115       var parent = element.parent();
116       while (parent && $window.getComputedStyle(parent[0])['pointer-events'] == 'none') {
117         parent = parent.parent();
118       }
119       return parent;
120     }
121
122      function getNearestContentElement () {
123        var current = element.parent()[0];
124        // Look for the nearest parent md-content, stopping at the rootElement.
125        while (current && current !== $rootElement[0] && current !== document.body) {
126          current = current.parentNode;
127        }
128        return current;
129      }
130
131     function hasComputedStyleValue(key, value) {
132         // Check if we should show it or not...
133         var computedStyles = $window.getComputedStyle(element[0]);
134         return angular.isDefined(computedStyles[key]) && (computedStyles[key] == value);
135     }
136
137     function bindEvents () {
138       var mouseActive = false;
139       var enterHandler = function() {
140         if (!hasComputedStyleValue('pointer-events','none')) {
141           setVisible(true);
142         }
143       };
144       var leaveHandler = function () {
145         var autohide = scope.hasOwnProperty('autohide') ? scope.autohide : attr.hasOwnProperty('mdAutohide');
146         if (autohide || mouseActive || ($document[0].activeElement !== parent[0]) ) {
147           setVisible(false);
148         }
149         mouseActive = false;
150       };
151
152       // to avoid `synthetic clicks` we listen to mousedown instead of `click`
153       parent.on('mousedown', function() { mouseActive = true; });
154       parent.on('focus mouseenter touchstart', enterHandler );
155       parent.on('blur mouseleave touchend touchcancel', leaveHandler );
156
157
158       angular.element($window).on('resize', debouncedOnResize);
159     }
160
161     function setVisible (value) {
162       setVisible.value = !!value;
163       if (!setVisible.queued) {
164         if (value) {
165           setVisible.queued = true;
166           $timeout(function() {
167             scope.visible = setVisible.value;
168             setVisible.queued = false;
169           }, scope.delay);
170         } else {
171           $timeout(function() { scope.visible = false; });
172         }
173       }
174     }
175
176     function showTooltip() {
177       // Insert the element before positioning it, so we can get the position
178       // and check if we should display it
179       tooltipParent.append(element);
180
181       // Check if we should display it or not.
182       // This handles hide-* and show-* along with any user defined css
183       if ( hasComputedStyleValue('display','none') ) {
184         scope.visible = false;
185         element.detach();
186         return;
187       }
188
189       positionTooltip();
190       angular.forEach([element, background, content], function (element) {
191         $animate.addClass(element, 'md-show');
192       });
193     }
194
195     function hideTooltip() {
196       $q.all([
197         $animate.removeClass(content, 'md-show'),
198         $animate.removeClass(background, 'md-show'),
199         $animate.removeClass(element, 'md-show')
200       ]).then(function () {
201         if (!scope.visible) element.detach();
202       });
203     }
204
205     function positionTooltip() {
206       var tipRect = $mdUtil.offsetRect(element, tooltipParent);
207       var parentRect = $mdUtil.offsetRect(parent, tooltipParent);
208       var newPosition = getPosition(direction);
209
210       // If the user provided a direction, just nudge the tooltip onto the screen
211       // Otherwise, recalculate based on 'top' since default is 'bottom'
212       if (direction) {
213         newPosition = fitInParent(newPosition);
214       } else if (newPosition.top > element.prop('offsetParent').scrollHeight - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE) {
215         newPosition = fitInParent(getPosition('top'));
216       }
217
218       element.css({top: newPosition.top + 'px', left: newPosition.left + 'px'});
219
220       positionBackground();
221
222       function positionBackground () {
223         var size = direction === 'left' || direction === 'right'
224               ? Math.sqrt(Math.pow(tipRect.width, 2) + Math.pow(tipRect.height / 2, 2)) * 2
225               : Math.sqrt(Math.pow(tipRect.width / 2, 2) + Math.pow(tipRect.height, 2)) * 2,
226             position = direction === 'left' ? { left: 100, top: 50 }
227               : direction === 'right' ? { left: 0, top: 50 }
228               : direction === 'top' ? { left: 50, top: 100 }
229               : { left: 50, top: 0 };
230         background.css({
231           width: size + 'px',
232           height: size + 'px',
233           left: position.left + '%',
234           top: position.top + '%'
235         });
236       }
237
238       function fitInParent (pos) {
239         var newPosition = { left: pos.left, top: pos.top };
240         newPosition.left = Math.min( newPosition.left, tooltipParent.prop('scrollWidth') - tipRect.width - TOOLTIP_WINDOW_EDGE_SPACE );
241         newPosition.left = Math.max( newPosition.left, TOOLTIP_WINDOW_EDGE_SPACE );
242         newPosition.top  = Math.min( newPosition.top,  tooltipParent.prop('scrollHeight') - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE );
243         newPosition.top  = Math.max( newPosition.top,  TOOLTIP_WINDOW_EDGE_SPACE );
244         return newPosition;
245       }
246
247       function getPosition (dir) {
248         return dir === 'left'
249           ? { left: parentRect.left - tipRect.width - TOOLTIP_WINDOW_EDGE_SPACE,
250               top: parentRect.top + parentRect.height / 2 - tipRect.height / 2 }
251           : dir === 'right'
252           ? { left: parentRect.left + parentRect.width + TOOLTIP_WINDOW_EDGE_SPACE,
253               top: parentRect.top + parentRect.height / 2 - tipRect.height / 2 }
254           : dir === 'top'
255           ? { left: parentRect.left + parentRect.width / 2 - tipRect.width / 2,
256               top: parentRect.top - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE }
257           : { left: parentRect.left + parentRect.width / 2 - tipRect.width / 2,
258               top: parentRect.top + parentRect.height + TOOLTIP_WINDOW_EDGE_SPACE };
259       }
260     }
261
262   }
263
264 }
265 MdTooltipDirective.$inject = ["$timeout", "$window", "$$rAF", "$document", "$mdUtil", "$mdTheming", "$rootElement", "$animate", "$q"];
266
267 ng.material.components.tooltip = angular.module("material.components.tooltip");