| OLD | NEW |
| (Empty) |
| 1 | |
| 2 | |
| 3 (function() { | |
| 4 | |
| 5 function docElem(property) { | |
| 6 var t; | |
| 7 return ((t = document.documentElement) || (t = document.body.parentNode)) &&
(typeof t[property] === 'number') ? t : document.body; | |
| 8 } | |
| 9 | |
| 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, includi
ng Safari2 | |
| 13 // 2) document.documentElement.client[Width|Height] reliable in standards m
ode DOCTYPE, except for Safari2, Opera<9.5 | |
| 14 // 3) document.body.client[Width|Height] is gives correct result when #2 do
es 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 siz
e of <body> element less than desired view size | |
| 18 function viewSize() { | |
| 19 // This algorithm avoids creating test page to determine if document.documen
tElement.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 wid
th 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.inne
rHeight ? | |
| 28 {w: body.clientWidth, h: body.clientHeight} : {w: w, h: h }; | |
| 29 } | |
| 30 | |
| 31 Polymer('core-dropdown',{ | |
| 32 | |
| 33 publish: { | |
| 34 | |
| 35 /** | |
| 36 * The element associated with this dropdown, usually the element that tri
ggers | |
| 37 * the menu. If unset, this property will default to the target's parent n
ode | |
| 38 * or shadow host. | |
| 39 * | |
| 40 * @attribute relatedTarget | |
| 41 * @type Node | |
| 42 */ | |
| 43 relatedTarget: null, | |
| 44 | |
| 45 /** | |
| 46 * The horizontal alignment of the popup relative to `relatedTarget`. `lef
t` | |
| 47 * means the left edges are aligned together. `right` means the right edge
s | |
| 48 * are aligned together. | |
| 49 * | |
| 50 * @attribute halign | |
| 51 * @type 'left' | 'right' | |
| 52 * @default 'left' | |
| 53 */ | |
| 54 halign: 'left', | |
| 55 | |
| 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', | |
| 66 | |
| 67 }, | |
| 68 | |
| 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; | |
| 73 | |
| 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'; | |
| 79 | |
| 80 var rect = target.getBoundingClientRect(); | |
| 81 | |
| 82 target.style.position = pos; | |
| 83 target.style.left = null; | |
| 84 target.style.top = null; | |
| 85 | |
| 86 return rect; | |
| 87 }, | |
| 88 | |
| 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 }, | |
| 104 | |
| 105 positionTarget: function() { | |
| 106 if (!this.relatedTarget) { | |
| 107 this.relatedTarget = this.target.parentElement || (this.target.parentNod
e && this.target.parentNode.host); | |
| 108 if (!this.relatedTarget) { | |
| 109 this.super(); | |
| 110 return; | |
| 111 } | |
| 112 } | |
| 113 | |
| 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'; | |
| 120 | |
| 121 if (this.layered) { | |
| 122 this.positionLayeredTarget(); | |
| 123 } else { | |
| 124 this.positionNestedTarget(); | |
| 125 } | |
| 126 }, | |
| 127 | |
| 128 positionLayeredTarget: function() { | |
| 129 var target = this.target; | |
| 130 var rect = this.relatedTarget.getBoundingClientRect(); | |
| 131 | |
| 132 var dims = this.dimensions; | |
| 133 var margin = dims.margin; | |
| 134 var vp = viewSize(); | |
| 135 | |
| 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 } | |
| 146 | |
| 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 } | |
| 157 | |
| 158 if (dims.position.h_by || dims.position.v_by) { | |
| 159 target.style.position = 'fixed'; | |
| 160 } | |
| 161 }, | |
| 162 | |
| 163 positionNestedTarget: function() { | |
| 164 var target = this.target; | |
| 165 var related = this.relatedTarget; | |
| 166 | |
| 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 } | |
| 173 | |
| 174 if (t_op !== r_op && t_op !== related) { | |
| 175 console.warn('core-dropdown-overlay: dropdown\'s offsetParent must be th
e relatedTarget or the relatedTarget\'s offsetParent!'); | |
| 176 } | |
| 177 | |
| 178 // Don't use CSS to handle halign/valign so we can use | |
| 179 // dimensions.position to detect custom positioning | |
| 180 | |
| 181 var dims = this.dimensions; | |
| 182 var margin = dims.margin; | |
| 183 var inside = t_op === related; | |
| 184 | |
| 185 if (!dims.position.h) { | |
| 186 if (this.halign === 'right') { | |
| 187 target.style.right = ((inside ? 0 : t_op.offsetWidth - related.offsetL
eft - 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 } | |
| 195 | |
| 196 if (!dims.position.v) { | |
| 197 if (this.valign === 'bottom') { | |
| 198 target.style.bottom = ((inside ? 0 : t_op.offsetHeight - related.offse
tTop - 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 } | |
| 207 | |
| 208 }); | |
| 209 | |
| 210 })(); | |
| 211 | |
| OLD | NEW |