Add an exponential backoff to rechecking the app list doodle.
[chromium-blink-merge.git] / third_party / polymer / components-chromium / core-dropdown / core-dropdown-extracted.js
blob4f68d5b68a5d185aaf6a6749c1dcdc24bbb75ea0
3 (function() {
5   function docElem(property) {
6     var t;
7     return ((t = document.documentElement) || (t = document.body.parentNode)) && (typeof t[property] === 'number') ? t : document.body;
8   }
10   // View width and height excluding any visible scrollbars
11   // http://www.highdots.com/forums/javascript/faq-topic-how-do-i-296669.html
12   //    1) document.client[Width|Height] always reliable when available, including Safari2
13   //    2) document.documentElement.client[Width|Height] reliable in standards mode DOCTYPE, except for Safari2, Opera<9.5
14   //    3) document.body.client[Width|Height] is gives correct result when #2 does not, except for Safari2
15   //    4) When document.documentElement.client[Width|Height] is unreliable, it will be size of <html> element either greater or less than desired view size
16   //       https://bugzilla.mozilla.org/show_bug.cgi?id=156388#c7
17   //    5) When document.body.client[Width|Height] is unreliable, it will be size of <body> element less than desired view size
18   function viewSize() {
19     // This algorithm avoids creating test page to determine if document.documentElement.client[Width|Height] is greater then view size,
20     // will succeed where such test page wouldn't detect dynamic unreliability,
21     // and will only fail in the case the right or bottom edge is within the width of a scrollbar from edge of the viewport that has visible scrollbar(s).
22     var doc = docElem('clientWidth');
23     var body = document.body;
24     var w, h;
25     return typeof document.clientWidth === 'number' ?
26       {w: document.clientWidth, h: document.clientHeight} :
27       doc === body || (w = Math.max( doc.clientWidth, body.clientWidth )) > self.innerWidth || (h = Math.max( doc.clientHeight, body.clientHeight )) > self.innerHeight ?
28         {w: body.clientWidth, h: body.clientHeight} : {w: w, h: h };
29   }
31   Polymer('core-dropdown',{
33     publish: {
35       /**
36        * The element associated with this dropdown, usually the element that triggers
37        * the menu. If unset, this property will default to the target's parent node
38        * or shadow host.
39        *
40        * @attribute relatedTarget
41        * @type Node
42        */
43       relatedTarget: null,
45       /**
46        * The horizontal alignment of the popup relative to `relatedTarget`. `left`
47        * means the left edges are aligned together. `right` means the right edges
48        * are aligned together.
49        *
50        * @attribute halign
51        * @type 'left' | 'right'
52        * @default 'left'
53        */
54       halign: 'left',
56       /**
57        * The vertical alignment of the popup relative to `relatedTarget`. `top` means
58        * the top edges are aligned together. `bottom` means the bottom edges are
59        * aligned together.
60        *
61        * @attribute valign
62        * @type 'top' | 'bottom'
63        * @default 'top'
64        */
65       valign: 'top',
67     },
69     measure: function() {
70       var target = this.target;
71       // remember position, because core-overlay may have set the property
72       var pos = target.style.position;
74       // get the size of the target as if it's positioned in the top left
75       // corner of the screen
76       target.style.position = 'fixed';
77       target.style.left = '0px';
78       target.style.top = '0px';
80       var rect = target.getBoundingClientRect();
82       target.style.position = pos;
83       target.style.left = null;
84       target.style.top = null;
86       return rect;
87     },
89     resetTargetDimensions: function() {
90       var dims = this.dimensions;
91       var style = this.target.style;
92       if (dims.position.h_by === this.localName) {
93         style[dims.position.h] = null;
94         dims.position.h_by = null;
95       }
96       if (dims.position.v_by === this.localName) {
97         style[dims.position.v] = null;
98         dims.position.v_by = null;
99       }
100       style.width = null;
101       style.height = null;
102       this.super();
103     },
105     positionTarget: function() {
106       if (!this.relatedTarget) {
107         this.relatedTarget = this.target.parentElement || (this.target.parentNode && this.target.parentNode.host);
108         if (!this.relatedTarget) {
109           this.super();
110           return;
111         }
112       }
114       // explicitly set width/height, because we don't want it constrained
115       // to the offsetParent
116       var target = this.sizingTarget;
117       var rect = this.measure();
118       target.style.width = Math.ceil(rect.width) + 'px';
119       target.style.height = Math.ceil(rect.height) + 'px';
121       if (this.layered) {
122         this.positionLayeredTarget();
123       } else {
124         this.positionNestedTarget();
125       }
126     },
128     positionLayeredTarget: function() {
129       var target = this.target;
130       var rect = this.relatedTarget.getBoundingClientRect();
132       var dims = this.dimensions;
133       var margin = dims.margin;
134       var vp = viewSize();
136       if (!dims.position.h) {
137         if (this.halign === 'right') {
138           target.style.right = vp.w - rect.right - margin.right + 'px';
139           dims.position.h = 'right';
140         } else {
141           target.style.left = rect.left - margin.left + 'px';
142           dims.position.h = 'left';
143         }
144         dims.position.h_by = this.localName;
145       }
147       if (!dims.position.v) {
148         if (this.valign === 'bottom') {
149           target.style.bottom = vp.h - rect.bottom - margin.bottom + 'px';
150           dims.position.v = 'bottom';
151         } else {
152           target.style.top = rect.top - margin.top + 'px';
153           dims.position.v = 'top';
154         }
155         dims.position.v_by = this.localName;
156       }
158       if (dims.position.h_by || dims.position.v_by) {
159         target.style.position = 'fixed';
160       }
161     },
163     positionNestedTarget: function() {
164       var target = this.target;
165       var related = this.relatedTarget;
167       var t_op = target.offsetParent;
168       var r_op = related.offsetParent;
169       if (window.ShadowDOMPolyfill) {
170         t_op = wrap(t_op);
171         r_op = wrap(r_op);
172       }
174       if (t_op !== r_op && t_op !== related) {
175         console.warn('core-dropdown-overlay: dropdown\'s offsetParent must be the relatedTarget or the relatedTarget\'s offsetParent!');
176       }
178       // Don't use CSS to handle halign/valign so we can use
179       // dimensions.position to detect custom positioning
181       var dims = this.dimensions;
182       var margin = dims.margin;
183       var inside = t_op === related;
185       if (!dims.position.h) {
186         if (this.halign === 'right') {
187           target.style.right = ((inside ? 0 : t_op.offsetWidth - related.offsetLeft - related.offsetWidth) - margin.right) + 'px';
188           dims.position.h = 'right';
189         } else {
190           target.style.left = ((inside ? 0 : related.offsetLeft) - margin.left) + 'px';
191           dims.position.h = 'left';
192         }
193         dims.position.h_by = this.localName;
194       }
196       if (!dims.position.v) {
197         if (this.valign === 'bottom') {
198           target.style.bottom = ((inside ? 0 : t_op.offsetHeight - related.offsetTop - related.offsetHeight) - margin.bottom) + 'px';
199           dims.position.v = 'bottom';
200         } else {
201           target.style.top = ((inside ? 0 : related.offsetTop) - margin.top) + 'px';
202           dims.position.v = 'top';
203         }
204         dims.position.v_by = this.localName;
205       }
206     }
208   });
210 })();