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