verge.js
162 lines
| 1 | /*! |
| 2 | * verge 1.10.2+201705300050 |
| 3 | * http://npm.im/verge |
| 4 | * MIT Ryan Van Etten |
| 5 | */ |
| 6 | |
| 7 | !function(root, name, make) { |
| 8 | if (typeof module != 'undefined' && module['exports']) module['exports'] = make(); |
| 9 | else root[name] = make(); |
| 10 | }(this, 'verge', function() { |
| 11 | |
| 12 | var xports = {} |
| 13 | , win = typeof window != 'undefined' && window |
| 14 | , doc = typeof document != 'undefined' && document |
| 15 | , docElem = doc && doc.documentElement |
| 16 | , matchMedia = win['matchMedia'] || win['msMatchMedia'] |
| 17 | , mq = matchMedia ? function(q) { |
| 18 | return !!matchMedia.call(win, q).matches; |
| 19 | } : function() { |
| 20 | return false; |
| 21 | } |
| 22 | , viewportW = xports['viewportW'] = function() { |
| 23 | var a = docElem['clientWidth'], b = win['innerWidth']; |
| 24 | return a < b ? b : a; |
| 25 | } |
| 26 | , viewportH = xports['viewportH'] = function() { |
| 27 | var a = docElem['clientHeight'], b = win['innerHeight']; |
| 28 | return a < b ? b : a; |
| 29 | }; |
| 30 | |
| 31 | /** |
| 32 | * Test if a media query is active. Like Modernizr.mq |
| 33 | * @since 1.6.0 |
| 34 | * @return {boolean} |
| 35 | */ |
| 36 | xports['mq'] = mq; |
| 37 | |
| 38 | /** |
| 39 | * Normalized matchMedia |
| 40 | * @since 1.6.0 |
| 41 | * @return {MediaQueryList|Object} |
| 42 | */ |
| 43 | xports['matchMedia'] = matchMedia ? function() { |
| 44 | // matchMedia must be binded to window |
| 45 | return matchMedia.apply(win, arguments); |
| 46 | } : function() { |
| 47 | // Gracefully degrade to plain object |
| 48 | return {}; |
| 49 | }; |
| 50 | |
| 51 | /** |
| 52 | * @since 1.8.0 |
| 53 | * @return {{width:number, height:number}} |
| 54 | */ |
| 55 | function viewport() { |
| 56 | return {'width':viewportW(), 'height':viewportH()}; |
| 57 | } |
| 58 | xports['viewport'] = viewport; |
| 59 | |
| 60 | /** |
| 61 | * Cross-browser window.scrollX |
| 62 | * @since 1.0.0 |
| 63 | * @return {number} |
| 64 | */ |
| 65 | xports['scrollX'] = function() { |
| 66 | return win.pageXOffset || docElem.scrollLeft; |
| 67 | }; |
| 68 | |
| 69 | /** |
| 70 | * Cross-browser window.scrollY |
| 71 | * @since 1.0.0 |
| 72 | * @return {number} |
| 73 | */ |
| 74 | xports['scrollY'] = function() { |
| 75 | return win.pageYOffset || docElem.scrollTop; |
| 76 | }; |
| 77 | |
| 78 | /** |
| 79 | * @param {{top:number, right:number, bottom:number, left:number}} coords |
| 80 | * @param {number=} cushion adjustment |
| 81 | * @return {Object} |
| 82 | */ |
| 83 | function calibrate(coords, cushion) { |
| 84 | var o = {}; |
| 85 | cushion = +cushion || 0; |
| 86 | o['width'] = (o['right'] = coords['right'] + cushion) - (o['left'] = coords['left'] - cushion); |
| 87 | o['height'] = (o['bottom'] = coords['bottom'] + cushion) - (o['top'] = coords['top'] - cushion); |
| 88 | return o; |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Cross-browser element.getBoundingClientRect plus optional cushion. |
| 93 | * Coords are relative to the top-left corner of the viewport. |
| 94 | * @since 1.0.0 |
| 95 | * @param {Element|Object} el element or stack (uses first item) |
| 96 | * @param {number=} cushion +/- pixel adjustment amount |
| 97 | * @return {Object|boolean} |
| 98 | */ |
| 99 | function rectangle(el, cushion) { |
| 100 | el = el && !el.nodeType ? el[0] : el; |
| 101 | if (!el || 1 !== el.nodeType) return false; |
| 102 | return calibrate(el.getBoundingClientRect(), cushion); |
| 103 | } |
| 104 | xports['rectangle'] = rectangle; |
| 105 | |
| 106 | /** |
| 107 | * Get the viewport aspect ratio (or the aspect ratio of an object or element) |
| 108 | * @since 1.7.0 |
| 109 | * @param {(Element|Object)=} o optional object with width/height props or methods |
| 110 | * @return {number} |
| 111 | * @link http://w3.org/TR/css3-mediaqueries/#orientation |
| 112 | */ |
| 113 | function aspect(o) { |
| 114 | o = null == o ? viewport() : 1 === o.nodeType ? rectangle(o) : o; |
| 115 | var h = o['height'], w = o['width']; |
| 116 | h = typeof h == 'function' ? h.call(o) : h; |
| 117 | w = typeof w == 'function' ? w.call(o) : w; |
| 118 | return w/h; |
| 119 | } |
| 120 | xports['aspect'] = aspect; |
| 121 | |
| 122 | /** |
| 123 | * Test if an element is in the same x-axis section as the viewport. |
| 124 | * @since 1.0.0 |
| 125 | * @param {Element|Object} el |
| 126 | * @param {number=} cushion |
| 127 | * @return {boolean} |
| 128 | */ |
| 129 | xports['inX'] = function(el, cushion) { |
| 130 | var r = rectangle(el, cushion); |
| 131 | return !!r && r.right >= 0 && r.left <= viewportW(); |
| 132 | }; |
| 133 | |
| 134 | /** |
| 135 | * Test if an element is in the same y-axis section as the viewport. |
| 136 | * @since 1.0.0 |
| 137 | * @param {Element|Object} el |
| 138 | * @param {number=} cushion |
| 139 | * @return {boolean} |
| 140 | */ |
| 141 | xports['inY'] = function(el, cushion) { |
| 142 | var r = rectangle(el, cushion); |
| 143 | return !!r && r.bottom >= 0 && r.top <= viewportH(); |
| 144 | }; |
| 145 | |
| 146 | /** |
| 147 | * Test if an element is in the viewport. |
| 148 | * @since 1.0.0 |
| 149 | * @param {Element|Object} el |
| 150 | * @param {number=} cushion |
| 151 | * @return {boolean} |
| 152 | */ |
| 153 | xports['inViewport'] = function(el, cushion) { |
| 154 | // Equiv to `inX(el, cushion) && inY(el, cushion)` but just manually do both |
| 155 | // to avoid calling rectangle() twice. It gzips just as small like this. |
| 156 | var r = rectangle(el, cushion); |
| 157 | return !!r && r.bottom >= 0 && r.right >= 0 && r.top <= viewportH() && r.left <= viewportW(); |
| 158 | }; |
| 159 | |
| 160 | return xports; |
| 161 | }); |
| 162 |