Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / polymer / v1_0 / components-chromium / paper-tooltip / paper-tooltip-extracted.js
blob06535a32d5f194f8683d72dbf94ec5b03a4446a4
2     Polymer({
3       is: 'paper-tooltip',
5       hostAttributes: {
6         role: 'tooltip',
7         tabindex: -1
8       },
10       behaviors: [
11         Polymer.NeonAnimationRunnerBehavior
12       ],
14       properties: {
15         /**
16          * The id of the element that the tooltip is anchored to. This element
17          * must be a sibling of the tooltip.
18          */
19         for: {
20           type: String,
21           observer: '_forChanged'
22         },
24         /**
25          * Positions the tooltip to the top, right, bottom, left of its content.
26          */
27         position: {
28           type: String,
29           value: 'bottom'
30         },
32         /**
33          * If true, no parts of the tooltip will ever be shown offscreen.
34          */
35         fitToVisibleBounds: {
36           type: Boolean,
37           value: false
38         },
40         /**
41          * The spacing between the top of the tooltip and the element it is
42          * anchored to.
43          */
44         offset: {
45           type: Number,
46           value: 14
47         },
49         /**
50          * This property is deprecated, but left over so that it doesn't
51          * break exiting code. Please use `offset` instead. If both `offset` and
52          * `marginTop` are provided, `marginTop` will be ignored.
53          * @deprecated since version 1.0.3
54          */
55         marginTop: {
56           type: Number,
57           value: 14
58         },
60         animationConfig: {
61           type: Object,
62           value: function() {
63             return {
64               'entry': [{
65                 name: 'fade-in-animation',
66                 node: this,
67                 timing: {delay: 500}
68               }],
69               'exit': [{
70                 name: 'fade-out-animation',
71                 node: this
72               }]
73             }
74           }
75         },
77         _showing: {
78           type: Boolean,
79           value: false
80         }
81       },
83       listeners: {
84         'neon-animation-finish': '_onAnimationFinish'
85       },
87       /**
88        * Returns the target element that this tooltip is anchored to. It is
89        * either the element given by the `for` attribute, or the immediate parent
90        * of the tooltip.
91        */
92       get target () {
93         var parentNode = Polymer.dom(this).parentNode;
94         // If the parentNode is a document fragment, then we need to use the host.
95         var ownerRoot = Polymer.dom(this).getOwnerRoot();
97         var target;
98         if (this.for) {
99           target = Polymer.dom(ownerRoot).querySelector('#' + this.for);
100         } else {
101           target = parentNode.nodeType == Node.DOCUMENT_FRAGMENT_NODE ?
102               ownerRoot.host : parentNode;
103         }
105         return target;
106       },
108       attached: function() {
109         this._target = this.target;
111         this.listen(this._target, 'mouseenter', 'show');
112         this.listen(this._target, 'focus', 'show');
113         this.listen(this._target, 'mouseleave', 'hide');
114         this.listen(this._target, 'blur', 'hide');
115       },
117       detached: function() {
118         if (this._target) {
119           this.unlisten(this._target, 'mouseenter', 'show');
120           this.unlisten(this._target, 'focus', 'show');
121           this.unlisten(this._target, 'mouseleave', 'hide');
122           this.unlisten(this._target, 'blur', 'hide');
123         }
124       },
126       show: function() {
127         if (this._showing)
128           return;
130         if (Polymer.dom(this).textContent.trim() === '')
131           return;
133         this.cancelAnimation();
135         this.toggleClass('hidden', false, this.$.tooltip);
136         this.updatePosition();
137         this._showing = true;
139         this.playAnimation('entry');
140       },
142       hide: function() {
143         if (!this._showing)
144           return;
146         this._showing = false;
147         this.playAnimation('exit');
148       },
150       _forChanged: function() {
151         this._target = this.target;
152       },
154       updatePosition: function() {
155         if (!this._target)
156           return;
158         var offset = this.offset;
159         // If a marginTop has been provided by the user (pre 1.0.3), use it.
160         if (this.marginTop != 14 && this.offset == 14)
161           offset = this.marginTop;
163         var parentRect = this.offsetParent.getBoundingClientRect();
164         var targetRect = this._target.getBoundingClientRect();
165         var thisRect = this.getBoundingClientRect();
167         var horizontalCenterOffset = (targetRect.width - thisRect.width) / 2;
168         var verticalCenterOffset = (targetRect.height - thisRect.height) / 2;
170         var targetLeft = targetRect.left - parentRect.left;
171         var targetTop = targetRect.top - parentRect.top;
173         var tooltipLeft, tooltipTop;
175         switch (this.position) {
176           case 'top':
177             tooltipLeft = targetLeft + horizontalCenterOffset;
178             tooltipTop = targetTop - thisRect.height - offset;
179             break;
180           case 'bottom':
181             tooltipLeft = targetLeft + horizontalCenterOffset;
182             tooltipTop = targetTop + targetRect.height + offset;
183             break;
184           case 'left':
185             tooltipLeft = targetLeft - thisRect.width - offset;
186             tooltipTop = targetTop + verticalCenterOffset;
187             break;
188           case 'right':
189             tooltipLeft = targetLeft + targetRect.width + offset;
190             tooltipTop = targetTop + verticalCenterOffset;
191             break;
192         }
194         // TODO(noms): This should use IronFitBehavior if possible.
195         if (this.fitToVisibleBounds) {
196           // Clip the left/right side.
197           if (tooltipLeft + thisRect.width > window.innerWidth) {
198             this.style.right = '0px';
199             this.style.left = 'auto';
200           } else {
201             this.style.left = Math.max(0, tooltipLeft) + 'px';
202             this.style.right = 'auto';
203           }
205           // Clip the top/bottom side.
206           if (tooltipTop + thisRect.height > window.innerHeight) {
207             this.style.bottom = '0px';
208             this.style.top = 'auto';
209           } else {
210             this.style.top = Math.max(0, tooltipTop) + 'px';
211             this.style.bottom = 'auto';
212           }
213         } else {
214           this.style.left = tooltipLeft + 'px';
215           this.style.top = tooltipTop + 'px';
216         }
218       },
220       _onAnimationFinish: function() {
221         if (!this._showing) {
222           this.toggleClass('hidden', true, this.$.tooltip);
223         }
224       },
225     });
226