foundation.util.mediaQuery.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. 'use strict';
  2. import $ from 'jquery';
  3. // Default set of media queries
  4. const defaultQueries = {
  5. 'default' : 'only screen',
  6. landscape : 'only screen and (orientation: landscape)',
  7. portrait : 'only screen and (orientation: portrait)',
  8. retina : 'only screen and (-webkit-min-device-pixel-ratio: 2),' +
  9. 'only screen and (min--moz-device-pixel-ratio: 2),' +
  10. 'only screen and (-o-min-device-pixel-ratio: 2/1),' +
  11. 'only screen and (min-device-pixel-ratio: 2),' +
  12. 'only screen and (min-resolution: 192dpi),' +
  13. 'only screen and (min-resolution: 2dppx)'
  14. };
  15. // matchMedia() polyfill - Test a CSS media type/query in JS.
  16. // Authors & copyright(c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. MIT license
  17. /* eslint-disable */
  18. window.matchMedia || (window.matchMedia = (function () {
  19. "use strict";
  20. // For browsers that support matchMedium api such as IE 9 and webkit
  21. var styleMedia = (window.styleMedia || window.media);
  22. // For those that don't support matchMedium
  23. if (!styleMedia) {
  24. var style = document.createElement('style'),
  25. script = document.getElementsByTagName('script')[0],
  26. info = null;
  27. style.type = 'text/css';
  28. style.id = 'matchmediajs-test';
  29. if (!script) {
  30. document.head.appendChild(style);
  31. } else {
  32. script.parentNode.insertBefore(style, script);
  33. }
  34. // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers
  35. info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle;
  36. styleMedia = {
  37. matchMedium: function (media) {
  38. var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }';
  39. // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers
  40. if (style.styleSheet) {
  41. style.styleSheet.cssText = text;
  42. } else {
  43. style.textContent = text;
  44. }
  45. // Test if media query is true or false
  46. return info.width === '1px';
  47. }
  48. };
  49. }
  50. return function(media) {
  51. return {
  52. matches: styleMedia.matchMedium(media || 'all'),
  53. media: media || 'all'
  54. };
  55. };
  56. })());
  57. /* eslint-enable */
  58. var MediaQuery = {
  59. queries: [],
  60. current: '',
  61. /**
  62. * Initializes the media query helper, by extracting the breakpoint list from the CSS and activating the breakpoint watcher.
  63. * @function
  64. * @private
  65. */
  66. _init() {
  67. var self = this;
  68. var $meta = $('meta.foundation-mq');
  69. if(!$meta.length){
  70. $('<meta class="foundation-mq">').appendTo(document.head);
  71. }
  72. var extractedStyles = $('.foundation-mq').css('font-family');
  73. var namedQueries;
  74. namedQueries = parseStyleToObject(extractedStyles);
  75. for (var key in namedQueries) {
  76. if(namedQueries.hasOwnProperty(key)) {
  77. self.queries.push({
  78. name: key,
  79. value: `only screen and (min-width: ${namedQueries[key]})`
  80. });
  81. }
  82. }
  83. this.current = this._getCurrentSize();
  84. this._watcher();
  85. },
  86. /**
  87. * Checks if the screen is at least as wide as a breakpoint.
  88. * @function
  89. * @param {String} size - Name of the breakpoint to check.
  90. * @returns {Boolean} `true` if the breakpoint matches, `false` if it's smaller.
  91. */
  92. atLeast(size) {
  93. var query = this.get(size);
  94. if (query) {
  95. return window.matchMedia(query).matches;
  96. }
  97. return false;
  98. },
  99. /**
  100. * Checks if the screen matches to a breakpoint.
  101. * @function
  102. * @param {String} size - Name of the breakpoint to check, either 'small only' or 'small'. Omitting 'only' falls back to using atLeast() method.
  103. * @returns {Boolean} `true` if the breakpoint matches, `false` if it does not.
  104. */
  105. is(size) {
  106. size = size.trim().split(' ');
  107. if(size.length > 1 && size[1] === 'only') {
  108. if(size[0] === this._getCurrentSize()) return true;
  109. } else {
  110. return this.atLeast(size[0]);
  111. }
  112. return false;
  113. },
  114. /**
  115. * Gets the media query of a breakpoint.
  116. * @function
  117. * @param {String} size - Name of the breakpoint to get.
  118. * @returns {String|null} - The media query of the breakpoint, or `null` if the breakpoint doesn't exist.
  119. */
  120. get(size) {
  121. for (var i in this.queries) {
  122. if(this.queries.hasOwnProperty(i)) {
  123. var query = this.queries[i];
  124. if (size === query.name) return query.value;
  125. }
  126. }
  127. return null;
  128. },
  129. /**
  130. * Gets the current breakpoint name by testing every breakpoint and returning the last one to match (the biggest one).
  131. * @function
  132. * @private
  133. * @returns {String} Name of the current breakpoint.
  134. */
  135. _getCurrentSize() {
  136. var matched;
  137. for (var i = 0; i < this.queries.length; i++) {
  138. var query = this.queries[i];
  139. if (window.matchMedia(query.value).matches) {
  140. matched = query;
  141. }
  142. }
  143. if (typeof matched === 'object') {
  144. return matched.name;
  145. } else {
  146. return matched;
  147. }
  148. },
  149. /**
  150. * Activates the breakpoint watcher, which fires an event on the window whenever the breakpoint changes.
  151. * @function
  152. * @private
  153. */
  154. _watcher() {
  155. $(window).off('resize.zf.mediaquery').on('resize.zf.mediaquery', () => {
  156. var newSize = this._getCurrentSize(), currentSize = this.current;
  157. if (newSize !== currentSize) {
  158. // Change the current media query
  159. this.current = newSize;
  160. // Broadcast the media query change on the window
  161. $(window).trigger('changed.zf.mediaquery', [newSize, currentSize]);
  162. }
  163. });
  164. }
  165. };
  166. // Thank you: https://github.com/sindresorhus/query-string
  167. function parseStyleToObject(str) {
  168. var styleObject = {};
  169. if (typeof str !== 'string') {
  170. return styleObject;
  171. }
  172. str = str.trim().slice(1, -1); // browsers re-quote string style values
  173. if (!str) {
  174. return styleObject;
  175. }
  176. styleObject = str.split('&').reduce(function(ret, param) {
  177. var parts = param.replace(/\+/g, ' ').split('=');
  178. var key = parts[0];
  179. var val = parts[1];
  180. key = decodeURIComponent(key);
  181. // missing `=` should be `null`:
  182. // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
  183. val = typeof val === 'undefined' ? null : decodeURIComponent(val);
  184. if (!ret.hasOwnProperty(key)) {
  185. ret[key] = val;
  186. } else if (Array.isArray(ret[key])) {
  187. ret[key].push(val);
  188. } else {
  189. ret[key] = [ret[key], val];
  190. }
  191. return ret;
  192. }, {});
  193. return styleObject;
  194. }
  195. export {MediaQuery};