UNPKG

foundation-apps

Version:

A responsive, Angular-powered framework for web apps from ZURB.

307 lines (245 loc) 8.77 kB
(function() { 'use strict'; angular.module('foundation.mediaquery', ['foundation.core']) .run(mqInitRun) .factory('FoundationMQInit', FoundationMQInit) .factory('mqHelpers', mqHelpers) .service('FoundationMQ', FoundationMQ) ; mqInitRun.$inject = ['FoundationMQInit']; function mqInitRun(mqInit) { mqInit.init(); } FoundationMQInit.$inject = ['mqHelpers', 'FoundationApi', 'Utils']; function FoundationMQInit(helpers, foundationApi, u){ var factory = {}; var namedQueries = { '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)' }; factory.init = init; return factory; function init() { var mediaQueries; var extractedMedia; var mediaQuerySizes; var mediaMap; var key; helpers.headerHelper(['foundation-mq']); extractedMedia = helpers.getStyle('.foundation-mq', 'font-family'); if (!extractedMedia.match(/([\w]+=[\d]+[a-z]*&?)+/)) { extractedMedia = 'small=0&medium=40rem&large=75rem&xlarge=90rem&xxlarge=120rem'; } mediaQueries = helpers.parseStyleToObject((extractedMedia)); mediaQuerySizes = []; for(key in mediaQueries) { mediaQuerySizes.push({ query: key, size: parseInt(mediaQueries[key].replace('rem', '')) }); mediaQueries[key] = 'only screen and (min-width: ' + mediaQueries[key].replace('rem', 'em') + ')'; } // sort by increasing size mediaQuerySizes.sort(function(a,b) { return a.size > b.size ? 1 : (a.size < b.size ? -1 : 0); }); mediaMap = {}; for (key = 0; key < mediaQuerySizes.length; key++) { mediaMap[mediaQuerySizes[key].query] = { up: null, down: null }; if (key+1 < mediaQuerySizes.length) { mediaMap[mediaQuerySizes[key].query].up = mediaQuerySizes[key+1].query; } if (key !== 0) { mediaMap[mediaQuerySizes[key].query].down = mediaQuerySizes[key-1].query; } } foundationApi.modifySettings({ mediaQueries: angular.extend(mediaQueries, namedQueries), mediaMap: mediaMap }); window.addEventListener('resize', u.throttle(function() { foundationApi.publish('resize', 'window resized'); }, 50)); } } function mqHelpers() { var factory = {}; factory.headerHelper = headerHelper; factory.getStyle = getStyle; factory.parseStyleToObject = parseStyleToObject; return factory; function headerHelper(classArray) { var i = classArray.length; var head = angular.element(document.querySelectorAll('head')); while(i--) { head.append('<meta class="' + classArray[i] + '" />'); } return; } function getStyle(selector, styleName) { var elem = document.querySelectorAll(selector)[0]; var style = window.getComputedStyle(elem, null); return style.getPropertyValue('font-family'); } // https://github.com/sindresorhus/query-string function parseStyleToObject(str) { var styleObject = {}; if (typeof str !== 'string') { return styleObject; } if ((str[0] === '"' && str[str.length - 1] === '"') || (str[0] === '\'' && str[str.length - 1] === '\'')) { str = str.trim().slice(1, -1); // some 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 = 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; } } FoundationMQ.$inject = ['FoundationApi']; function FoundationMQ(foundationApi) { var service = [], mediaQueryResultCache = {}, queryMinWidthCache = {}; foundationApi.subscribe('resize', function() { // any new resize event causes a clearing of the media cache mediaQueryResultCache = {}; }); service.getMediaQueries = getMediaQueries; service.match = match; service.matchesMedia = matchesMedia; service.matchesMediaOrSmaller = matchesMediaOrSmaller; service.matchesMediaOnly = matchesMediaOnly; service.collectScenariosFromElement = collectScenariosFromElement; return service; function getMediaQueries() { return foundationApi.getSettings().mediaQueries; } function getNextLargestMediaQuery(media) { var mediaMapEntry = foundationApi.getSettings().mediaMap[media]; if (mediaMapEntry) { return mediaMapEntry.up; } else { return null; } } function getNextSmallestMediaQuery(media) { var mediaMapEntry = foundationApi.getSettings().mediaMap[media]; if (mediaMapEntry) { return mediaMapEntry.down; } else { return null; } } function match(scenarios) { var count = scenarios.length; var queries = service.getMediaQueries(); var matches = []; if (count > 0) { while (count--) { var mq; var rule = scenarios[count].media; if (queries[rule]) { mq = matchMedia(queries[rule]); } else { mq = matchMedia(rule); } if (mq.matches) { matches.push({ ind: count}); } } } return matches; } function matchesMedia(query) { if (angular.isUndefined(mediaQueryResultCache[query])) { // cache miss, run media query mediaQueryResultCache[query] = match([{media: query}]).length > 0; } return mediaQueryResultCache[query]; } function matchesMediaOrSmaller(query) { // In order to match the named breakpoint or smaller, // the next largest named breakpoint cannot be matched var nextLargestMedia = getNextLargestMediaQuery(query); if (nextLargestMedia && matchesMedia(nextLargestMedia)) { return false; } // Check to see if any smaller named breakpoint is matched return matchesSmallerRecursive(query); function matchesSmallerRecursive(query) { var nextSmallestMedia; if (matchesMedia(query)) { // matches breakpoint return true; } else { // check if matches smaller media nextSmallestMedia = getNextSmallestMediaQuery(query); if (!nextSmallestMedia) { // no more smaller breakpoints return false; } else { return matchesSmallerRecursive(nextSmallestMedia); } } } } function matchesMediaOnly(query) { // Check that media ONLY matches named breakpoint and nothing else var nextLargestMedia = getNextLargestMediaQuery(query); if (!nextLargestMedia) { // reached max media size, run query for current media return matchesMedia(query); } else { // must match named breakpoint, but not next largest return matchesMedia(query) && !matchesMedia(nextLargestMedia); } } // Collects a scenario object and templates from element function collectScenariosFromElement(parentElement) { var scenarios = []; var templates = []; var elements = parentElement.children(); var i = 0; angular.forEach(elements, function(el) { var elem = angular.element(el); //if no source or no html, capture element itself if (!elem.attr('src') || !elem.attr('src').match(/.html$/)) { templates[i] = elem; scenarios[i] = { media: elem.attr('media'), templ: i }; } else { scenarios[i] = { media: elem.attr('media'), src: elem.attr('src') }; } i++; }); return { scenarios: scenarios, templates: templates }; } } })();