1/** 2 * Breakpoints 3 * 4 * Library which automatically detects breakpoints from SiteVision's responsive grids stylesheet and exposes them in an api. 5 * 6 * @author Hampus Nordin <[email protected]> 7 * @copyright Limepark AB, 2017 8 */ 9(function (window, document, exports, $) { 10 var breakpoints = exports.breakpoints = exports.breakpoints || {}; 11 12 // Holds all MediaQueryList objects for the breakpoints. 13 breakpoints.mediaQueryLists = []; 14 // Holds callbacks for each breakpoint group. 15 breakpoints.callbacks = {}; 16 // Default options 17 breakpoints.defaults = { 18 "styleSheetNamePart": "sitevision-responsive-grids.css", 19 "mediaQueryRuleRegex": /\.sv-visible-([A-Za-z0-9\-\_]+)/ 20 }; 21 22 /** 23 * Extracts the Media Query condition from a CSSRule object. 24 * `conditionText` is currently only supported/implemented by Chrome. 25 * 26 * @private 27 * @param {CSSRule} cssRule 28 * @return {String} 29 */ 30 function extractMediaQueryFromCSSRule (cssRule) { 31 if (cssRule.conditionText) { 32 return cssRule.conditionText; 33 } else { 34 var mediaQueryMatches = cssRule.cssText.match(/^[\t]*@media(.+){/); 35 if (mediaQueryMatches && mediaQueryMatches[1]) { 36 return mediaQueryMatches[1].trim(); 37 } 38 } 39 return ''; 40 } 41 42 /** 43 * Returns an array of StyleSheet objects which 44 * matches the passed string on the chosen attribute. 45 * 46 * @private 47 * @param {StyleSheetList} styleSheets A List with stylesheets. 48 * @param {String} name The name of the attribute to filter on. 49 * @param {String} stringPart A string which should exist in the attribute value. 50 * @return {Array} An array of StyleSheet objects. 51 */ 52 function filterStyleSheetsOnAttributeByStringPart (styleSheets, name, stringPart) { 53 return [].slice.call(styleSheets).filter(function (sheet) { 54 return sheet[name] && sheet[name].indexOf(stringPart) !== -1; 55 }); 56 } 57 58 /** 59 * Returns an array of CSSRule objects which 60 * has a conditionRule. 61 * 62 * @private 63 * @param {CSSRuleList} cssRules A list of css rules. 64 * @return {[type]} An array of CSSRule objects. 65 */ 66 function filterRulesOnAndPopulateConditionText (cssRules) { 67 var cssRulesArray = [].slice.call(cssRules); 68 69 for (var i = cssRulesArray.length - 1; i >= 0; i--) { 70 cssRulesArray[i].conditionText = extractMediaQueryFromCSSRule(cssRulesArray[i]); 71 } 72 73 return cssRulesArray.filter(function (rule) { 74 return !!rule.conditionText; 75 }); 76 } 77 78 /** 79 * Parses all media query rules form document.styleSheets. 80 * 81 * @private 82 * @param {String} styleSheetNamePart String which will be used to filter the styleSheets on. 83 * @param {RegExp} mediaQueryRuleRegex [description] 84 * @return {Array} [description] 85 */ 86 function findBreakpointsFromStylesheets (styleSheetNamePart, mediaQueryRuleRegex) { 87 var bpoints = []; 88 var _added = []; 89 var mq; 90 var styleSheets; 91 var styleSheet; 92 var sheetMediaQueryRules; 93 var i; 94 95 styleSheets = filterStyleSheetsOnAttributeByStringPart(document.styleSheets, 'href', styleSheetNamePart); 96 97 if (styleSheets.length === 1) { 98 styleSheet = styleSheets[0]; 99 sheetMediaQueryRules = filterRulesOnAndPopulateConditionText(styleSheet.cssRules); 100 101 for (i = sheetMediaQueryRules.length - 1; i >= 0; i--) { 102 mq = sheetMediaQueryRules[i].cssText.match(mediaQueryRuleRegex); 103 104 if (mq.length === 2) { 105 mq = mq[1]; 106 if (mq[1] && _added.indexOf(mq) === -1) { 107 bpoints.push({ 108 "conditionText": sheetMediaQueryRules[i].conditionText, 109 "cssClass": mq 110 }); 111 _added.push(mq); 112 } 113 } 114 } 115 } 116 117 return bpoints; 118 } 119 120 function breakpointListenerCallback (groupName, options, event) { 121 var callbacks; 122 123 if (typeof breakpoints.callbacks[groupName] !== 'undefined') { 124 callbacks = breakpoints.callbacks[groupName]; 125 for (var i = 0; i < callbacks.length; i++) { 126 callbacks[i](event); 127 } 128 } 129 } 130 131 132 breakpoints.addBreakpoint = function addBreakpoint (mediaQueryString, groupName, options) { 133 var opts = $.extend(true, {}, breakpoints.defaults, options); 134 var mql = window.matchMedia(mediaQueryString); 135 // ensure callbacks array exist so we can pass array by referene. 136 breakpoints.callbacks[groupName] = breakpoints.callbacks[groupName] || []; 137 138 mql.addListener(breakpointListenerCallback.bind(mql, groupName, opts)); 139 breakpoints.mediaQueryLists.push({ 140 "mql": mql, 141 "groupName": groupName, 142 "options": opts, 143 "aliases": [], 144 "callbacks": breakpoints.callbacks[groupName] 145 }); 146 147 return breakpoints; 148 }; 149 150 151 breakpoints.addCallback = function addCallback (groupName, callback) { 152 var bp = breakpoints.get(groupName); 153 154 if (bp) { 155 // ensure true name since groupName could be an alias. 156 groupName = bp.groupName; 157 // ensure callbacks array exist. 158 if (typeof breakpoints.callbacks[groupName] === 'undefined') { 159 breakpoints.callbacks[groupName] = []; 160 } 161 breakpoints.callbacks[groupName].push(callback); 162 } 163 return breakpoints; 164 }; 165 166 /** 167 * Gets a breakpoint by it's group name. 168 * 169 * @param {[type]} groupName [description] 170 * @return {[type]} [description] 171 */ 172 breakpoints.get = function get (groupName) { 173 for (var i = breakpoints.mediaQueryLists.length - 1; i >= 0; i--) { 174 if (breakpoints.mediaQueryLists[i].groupName === groupName || breakpoints.mediaQueryLists[i].aliases.indexOf(groupName) !== -1) { 175 return breakpoints.mediaQueryLists[i]; 176 } 177 } 178 return null; 179 }; 180 181 182 breakpoints.matches = function matches (groupName) { 183 var bp = breakpoints.get(groupName); 184 return bp && bp.mql.matches; 185 }; 186 187 188 /** 189 * Triggers all callbacks for a breakpoint. 190 * 191 * @param {String} groupName The group name to trigger. 192 * @return {Void} 193 */ 194 breakpoints.trigger = function trigger (groupName) { 195 var bp = breakpoints.get(groupName); 196 if (bp && bp.callbacks.length > 0) { 197 for (var i = 0; i < bp.callbacks.length; i++) { 198 if (typeof bp.callbacks[i] === 'function') { 199 bp.callbacks[i](bp.mql); 200 } 201 } 202 } 203 return breakpoints; 204 }; 205 206 207 breakpoints.addAlias = function addAlias (groupName, alias) { 208 var bp = breakpoints.get(groupName); 209 if (bp) { 210 bp.aliases.push(alias.trim()); 211 } 212 return breakpoints; 213 }; 214 215 breakpoints.removeAlias = function removeAlias (groupName, alias) { 216 var bp = breakpoints.get(groupName); 217 if (bp) { 218 // Use Array.prototype.splice() instead of Array.prototype.filter() 219 // to preserve variable reference. 220 for (var i = bp.aliases.length - 1; i >= 0; i--) { 221 if (bp.aliases[i] === alias) { 222 bp.aliases.splice(i, 1); 223 } 224 } 225 } 226 return breakpoints; 227 }; 228 229 230 breakpoints.names = function names () { 231 return breakpoints.mediaQueryLists.map(function (mql) { 232 return mql.groupName; 233 }); 234 }; 235 236 237 breakpoints.initialize = function initialize (options) { 238 var opts = $.extend(true, {}, breakpoints.defaults, options); 239 var bpoints = findBreakpointsFromStylesheets(opts.styleSheetNamePart, opts.mediaQueryRuleRegex); 240 241 for (var i = bpoints.length - 1; i >= 0; i--) { 242 breakpoints.addBreakpoint(bpoints[i].conditionText, bpoints[i].cssClass, opts); 243 } 244 }; 245 246 247 // Init on load so the api is ready when other modules tries to use it. 248 breakpoints.initialize({}); 249 250})(this, this.document, lp || {}, $svjq || jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.