UNPKG

foundation-sites

Version:

The most advanced responsive front-end framework in the world.

339 lines (280 loc) 9.57 kB
'use strict'; import $ from 'jquery'; // Default set of media queries const defaultQueries = { 'default' : 'only screen', landscape : 'only screen and (orientation: landscape)', portrait : 'only screen and (orientation: portrait)', retina : 'only screen and (-webkit-min-device-pixel-ratio: 2),' + 'only screen and (min--moz-device-pixel-ratio: 2),' + 'only screen and (-o-min-device-pixel-ratio: 2/1),' + 'only screen and (min-device-pixel-ratio: 2),' + 'only screen and (min-resolution: 192dpi),' + 'only screen and (min-resolution: 2dppx)' }; // matchMedia() polyfill - Test a CSS media type/query in JS. // Authors & copyright © 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. MIT license /* eslint-disable */ window.matchMedia || (window.matchMedia = (function () { "use strict"; // For browsers that support matchMedium api such as IE 9 and webkit var styleMedia = (window.styleMedia || window.media); // For those that don't support matchMedium if (!styleMedia) { var style = document.createElement('style'), script = document.getElementsByTagName('script')[0], info = null; style.type = 'text/css'; style.id = 'matchmediajs-test'; if (!script) { document.head.appendChild(style); } else { script.parentNode.insertBefore(style, script); } // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle; styleMedia = { matchMedium: function (media) { var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }'; // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers if (style.styleSheet) { style.styleSheet.cssText = text; } else { style.textContent = text; } // Test if media query is true or false return info.width === '1px'; } }; } return function(media) { return { matches: styleMedia.matchMedium(media || 'all'), media: media || 'all' }; }; })()); /* eslint-enable */ var MediaQuery = { queries: [], current: '', /** * Initializes the media query helper, by extracting the breakpoint list from the CSS and activating the breakpoint watcher. * @function * @private */ _init() { // make sure the initialization is only done once when calling _init() several times if (this.isInitialized === true) { return; } else { this.isInitialized = true; } var self = this; var $meta = $('meta.foundation-mq'); if(!$meta.length){ $('<meta class="foundation-mq">').appendTo(document.head); } var extractedStyles = $('.foundation-mq').css('font-family'); var namedQueries; namedQueries = parseStyleToObject(extractedStyles); self.queries = []; // reset for (var key in namedQueries) { if(namedQueries.hasOwnProperty(key)) { self.queries.push({ name: key, value: `only screen and (min-width: ${namedQueries[key]})` }); } } this.current = this._getCurrentSize(); this._watcher(); }, /** * Reinitializes the media query helper. * Useful if your CSS breakpoint configuration has just been loaded or has changed since the initialization. * @function * @private */ _reInit() { this.isInitialized = false; this._init(); }, /** * Checks if the screen is at least as wide as a breakpoint. * @function * @param {String} size - Name of the breakpoint to check. * @returns {Boolean} `true` if the breakpoint matches, `false` if it's smaller. */ atLeast(size) { var query = this.get(size); if (query) { return window.matchMedia(query).matches; } return false; }, /** * Checks if the screen is within the given breakpoint. * If smaller than the breakpoint of larger than its upper limit it returns false. * @function * @param {String} size - Name of the breakpoint to check. * @returns {Boolean} `true` if the breakpoint matches, `false` otherwise. */ only(size) { return size === this._getCurrentSize(); }, /** * Checks if the screen is within a breakpoint or smaller. * @function * @param {String} size - Name of the breakpoint to check. * @returns {Boolean} `true` if the breakpoint matches, `false` if it's larger. */ upTo(size) { const nextSize = this.next(size); // If the next breakpoint does not match, the screen is smaller than // the upper limit of this breakpoint. if (nextSize) { return !this.atLeast(nextSize); } // If there is no next breakpoint, the "size" breakpoint does not have // an upper limit and the screen will always be within it or smaller. return true; }, /** * Checks if the screen matches to a breakpoint. * @function * @param {String} size - Name of the breakpoint to check, either 'small only' or 'small'. Omitting 'only' falls back to using atLeast() method. * @returns {Boolean} `true` if the breakpoint matches, `false` if it does not. */ is(size) { const parts = size.trim().split(' ').filter(p => !!p.length); const [bpSize, bpModifier = ''] = parts; // Only the breakpont if (bpModifier === 'only') { return this.only(bpSize); } // At least the breakpoint (included) if (!bpModifier || bpModifier === 'up') { return this.atLeast(bpSize); } // Up to the breakpoint (included) if (bpModifier === 'down') { return this.upTo(bpSize); } throw new Error(` Invalid breakpoint passed to MediaQuery.is(). Expected a breakpoint name formatted like "<size> <modifier>", got "${size}". `); }, /** * Gets the media query of a breakpoint. * @function * @param {String} size - Name of the breakpoint to get. * @returns {String|null} - The media query of the breakpoint, or `null` if the breakpoint doesn't exist. */ get(size) { for (var i in this.queries) { if(this.queries.hasOwnProperty(i)) { var query = this.queries[i]; if (size === query.name) return query.value; } } return null; }, /** * Get the breakpoint following the given breakpoint. * @function * @param {String} size - Name of the breakpoint. * @returns {String|null} - The name of the following breakpoint, or `null` if the passed breakpoint was the last one. */ next(size) { const queryIndex = this.queries.findIndex((q) => this._getQueryName(q) === size); if (queryIndex === -1) { throw new Error(` Unknown breakpoint "${size}" passed to MediaQuery.next(). Ensure it is present in your Sass "$breakpoints" setting. `); } const nextQuery = this.queries[queryIndex + 1]; return nextQuery ? nextQuery.name : null; }, /** * Returns the name of the breakpoint related to the given value. * @function * @private * @param {String|Object} value - Breakpoint name or query object. * @returns {String} Name of the breakpoint. */ _getQueryName(value) { if (typeof value === 'string') return value; if (typeof value === 'object') return value.name; throw new TypeError(` Invalid value passed to MediaQuery._getQueryName(). Expected a breakpoint name (String) or a breakpoint query (Object), got "${value}" (${typeof value}) `); }, /** * Gets the current breakpoint name by testing every breakpoint and returning the last one to match (the biggest one). * @function * @private * @returns {String} Name of the current breakpoint. */ _getCurrentSize() { var matched; for (var i = 0; i < this.queries.length; i++) { var query = this.queries[i]; if (window.matchMedia(query.value).matches) { matched = query; } } return matched && this._getQueryName(matched); }, /** * Activates the breakpoint watcher, which fires an event on the window whenever the breakpoint changes. * @function * @private */ _watcher() { $(window).off('resize.zf.mediaquery').on('resize.zf.mediaquery', () => { var newSize = this._getCurrentSize(), currentSize = this.current; if (newSize !== currentSize) { // Change the current media query this.current = newSize; // Broadcast the media query change on the window $(window).trigger('changed.zf.mediaquery', [newSize, currentSize]); } }); } }; // Thank you: https://github.com/sindresorhus/query-string function parseStyleToObject(str) { var styleObject = {}; if (typeof str !== 'string') { return styleObject; } str = str.trim().slice(1, -1); // browsers re-quote string style values if (!str) { return styleObject; } styleObject = str.split('&').reduce(function(ret, param) { var parts = param.replace(/\+/g, ' ').split('='); var key = parts[0]; var val = parts[1]; key = decodeURIComponent(key); // missing `=` should be `null`: // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters val = typeof val === 'undefined' ? null : decodeURIComponent(val); if (!ret.hasOwnProperty(key)) { ret[key] = val; } else if (Array.isArray(ret[key])) { ret[key].push(val); } else { ret[key] = [ret[key], val]; } return ret; }, {}); return styleObject; } export {MediaQuery};