5 function docElem(property) {
7 return ((t = document.documentElement) || (t = document.body.parentNode)) && (typeof t[property] === 'number') ? t : document.body;
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
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;
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 };
31 Polymer('core-dropdown',{
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
40 * @attribute relatedTarget
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.
51 * @type 'left' | 'right'
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
62 * @type 'top' | 'bottom'
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;
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;
96 if (dims.position.v_by === this.localName) {
97 style[dims.position.v] = null;
98 dims.position.v_by = null;
105 positionTarget: function() {
106 if (!this.relatedTarget) {
107 this.relatedTarget = this.target.parentElement || (this.target.parentNode && this.target.parentNode.host);
108 if (!this.relatedTarget) {
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';
122 this.positionLayeredTarget();
124 this.positionNestedTarget();
128 positionLayeredTarget: function() {
129 var target = this.target;
130 var rect = this.relatedTarget.getBoundingClientRect();
132 var dims = this.dimensions;
133 var margin = dims.margin;
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';
141 target.style.left = rect.left - margin.left + 'px';
142 dims.position.h = 'left';
144 dims.position.h_by = this.localName;
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';
152 target.style.top = rect.top - margin.top + 'px';
153 dims.position.v = 'top';
155 dims.position.v_by = this.localName;
158 if (dims.position.h_by || dims.position.v_by) {
159 target.style.position = 'fixed';
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) {
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!');
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';
190 target.style.left = ((inside ? 0 : related.offsetLeft) - margin.left) + 'px';
191 dims.position.h = 'left';
193 dims.position.h_by = this.localName;
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';
201 target.style.top = ((inside ? 0 : related.offsetTop) - margin.top) + 'px';
202 dims.position.v = 'top';
204 dims.position.v_by = this.localName;