diff --git a/README.md b/README.md index 2cdc7c29..cb5254d0 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,15 @@ Usage Instructions 3. Crack open Internet Explorer and pump fists in delight +Options +====== + +- Set `window.RESPOND_REPLACE_STYLES = true` before referencing this library to avoid altering the order of media queries in the CSS. NOTE: this is not as fast as the default, but still quick. See the notes below and #325. +```html + + +``` + CDN/X-Domain Setup ====== @@ -69,6 +78,8 @@ Some notes to keep in mind: - As you might guess, this implementation is quite dumb in regards to CSS parsing rules. This is a good thing, because that allows it to run really fast, but its looseness may also cause unexpected behavior. For example: if you enclose a whole media query in a comment intending to disable its rules, you'll probably find that those rules will end up enabled in non-media-query-supporting browsers. +- Respond.js by default will change the order of media queries by moving them after the stylesheet they came from. This is the best performing method but does have a drawback: see #325. To fix that, but at a slight cost to performance, set `window.RESPOND_REPLACE_STYLES = true` before this script runs: each stylesheet is removed and replaced with a parsed `',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";if(a.matchMedia&&a.matchMedia("all").addListener)return!1;var b=a.matchMedia,c=b("only all").matches,d=!1,e=0,f=[],g=function(){a.clearTimeout(e),e=a.setTimeout(function(){for(var c=0,d=f.length;d>c;c++){var e=f[c].mql,g=f[c].listeners||[],h=b(e.media).matches;if(h!==e.matches){e.matches=h;for(var i=0,j=g.length;j>i;i++)g[i].call(a,e)}}},30)};a.matchMedia=function(e){var h=b(e),i=[],j=0;return h.addListener=function(b){c&&(d||(d=!0,a.addEventListener("resize",g,!0)),0===j&&(j=f.push({mql:h,listeners:i})),i.push(b))},h.removeListener=function(a){for(var b=0,c=i.length;c>b;b++)i[b]===a&&i.splice(b,1)},h}}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b #mq-test-1 { width: 42px; }',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(a.document)}(this),function(a){"use strict";if(a.matchMedia&&a.matchMedia("all").addListener)return!1;var b=a.matchMedia,c=b("only all").matches,d=!1,e=0,f=[],g=function(c){a.clearTimeout(e),e=a.setTimeout(function(){for(var c=0,d=f.length;d>c;c++){var e=f[c].mql,g=f[c].listeners||[],h=b(e.media).matches;if(h!==e.matches){e.matches=h;for(var i=0,j=g.length;j>i;i++)g[i].call(a,e)}}},30)};a.matchMedia=function(e){var h=b(e),i=[],j=0;return h.addListener=function(b){c&&(d||(d=!0,a.addEventListener("resize",g,!0)),0===j&&(j=f.push({mql:h,listeners:i})),i.push(b))},h.removeListener=function(a){for(var b=0,c=i.length;c>b;b++)i[b]===a&&i.splice(b,1)},h}}(this),function(a){"use strict";function b(){x(!0)}a.RESPOND_REPLACE_STYLES=a.RESPOND_REPLACE_STYLES||!1;var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{(([^\{\}]*\{[^\}\{]*\})+)[^\}]*\}/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)\}$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p={},q=30,r=j.getElementsByTagName("head")[0]||k,s=j.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},v=function(a,b,c){var d="clientWidth",e=k[d],f="CSS1Compat"===j.compatMode&&e||j.body[d]||e,g=null===b,h=null===c,l="em";return b&&(b=parseFloat(b)*(b.indexOf(l)>-1?i||u():1)),c&&(c=parseFloat(c)*(c.indexOf(l)>-1?i||u():1)),!a||(!g||!h)&&(g||f>=b)&&(h||c>=f)},w=function(a,b,c,d){return a.substring(0,c)+b+a.substring(d)},x=function(b){var c,d=(new Date).getTime();return c=a.RESPOND_REPLACE_STYLES?z:A,b&&g&&q>d-g?(a.clearTimeout(h),void(h=a.setTimeout(c,q))):(g=d,void c())},y=function(){for(var a in n)n.hasOwnProperty(a)&&n[a]&&n[a].parentNode===r&&r.removeChild(n[a]);n.length=0},z=function(){t[t.length-1];y();for(var a in o)if(o.hasOwnProperty(a)){var b,c,d,e=o[a],f=e.styles,g=e.sheet,h=f,i={},j=[],k={};for(var l in e.mediastyles)e.mediastyles.hasOwnProperty(l)&&(b=e.mediastyles[l],c=b.minw,d=b.maxw,"all"===b.media?j.push(b):v(b.hasquery,c,d)&&(i[b.media]||(i[b.media]=[]),i[b.media].push(m[b.rules])));for(var n=j.length-1;n>=0;n--){b=j[n],c=b.minw,d=b.maxw;var p=m[b.rules],q=b.replaceIndexStart,s=b.replaceIndexEnd,u="";k[p]||(v(b.hasquery,c,d)&&(u=p),h=w(h,u,q,s),k[p]=!0)}B(h,"all",e.insertBefore),null!==g.parentElement&&r.removeChild(g);for(var x in i)i.hasOwnProperty(x)&&B(i[x].join("\n"),"all",e.insertBefore)}},A=function(){var a={},b=t[t.length-1];for(var c in l)if(l.hasOwnProperty(c)){var d=l[c],e=d.minw,f=d.maxw;v(d.hasquery,e,f)&&(a[d.media]||(a[d.media]=[]),a[d.media].push(m[d.rules]))}y();for(var g in a)a.hasOwnProperty(g)&&B(a[g].join("\n"),g,b.nextSibling)},B=function(a,b,c){var d=j.createElement("style");d.type="text/css",d.media=b,r.insertBefore(d,c),d.styleSheet?d.styleSheet.cssText=a:d.appendChild(j.createTextNode(a)),n.push(d)},C=function(a,b){return b=b.substring(0,b.lastIndexOf("/")),b.length&&(b+="/"),a.replace(c.regex.urls,"$1"+b+"$2$3")},D=function(b,d,e){a.RESPOND_REPLACE_STYLES&&(b=C(b,d),o[d].styles=b,o[d].mediastyles=[]);var f=b.replace(c.regex.keyframes,"").match(c.regex.media),g=f&&f.length||0,h=!g&&e;h&&(g=1);for(var i=0;g>i;i++){var j,k,n,p,q;h?(j=e,q=b):(j=f[i].match(c.regex.findStyles)&&RegExp.$1,q=RegExp.$2),a.RESPOND_REPLACE_STYLES?m.push(q):m.push(q&&C(q,d)),n=j.split(","),p=n.length;for(var r=0;p>r;r++){k=n[r];var s={media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")};a.RESPOND_REPLACE_STYLES?(s.replaceIndexStart=o[d].styles.indexOf(f[i]),s.replaceIndexEnd=s.replaceIndexStart+f[i].length,o[d].mediastyles.push(s)):l.push(s)}}x()},E=function(){if(d.length){var b=d.shift();f(b.href,function(c){a.RESPOND_REPLACE_STYLES&&(o[b.href]={sheet:b.sheet,insertBefore:b.sheet.nextSibling,styles:c}),D(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){E()},0)})}},F=function(){for(var b=0;b -1 ? eminpx || getEmValue() : 1); + } + if (!!max) { + max = parseFloat(max) * (max.indexOf(em) > -1 ? eminpx || getEmValue() : 1); + } + return !hasquery || (!minnull || !maxnull) && (minnull || currWidth >= min) && (maxnull || currWidth <= max); + }, replaceStringBetween = function(source, replacement, start, end) { + return source.substring(0, start) + replacement + source.substring(end); }, applyMedia = function(fromResize) { - var name = "clientWidth", docElemProp = docElem[name], currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[name] || docElemProp, styleBlocks = {}, lastLink = links[links.length - 1], now = new Date().getTime(); + var method, now = new Date().getTime(); + if (w.RESPOND_REPLACE_STYLES) { + method = applyMediaReplace; + } else { + method = applyMediaAppend; + } if (fromResize && lastCall && now - lastCall < resizeThrottle) { w.clearTimeout(resizeDefer); - resizeDefer = w.setTimeout(applyMedia, resizeThrottle); + resizeDefer = w.setTimeout(method, resizeThrottle); return; } else { lastCall = now; } - for (var i in mediastyles) { - if (mediastyles.hasOwnProperty(i)) { - var thisstyle = mediastyles[i], min = thisstyle.minw, max = thisstyle.maxw, minnull = min === null, maxnull = max === null, em = "em"; - if (!!min) { - min = parseFloat(min) * (min.indexOf(em) > -1 ? eminpx || getEmValue() : 1); + method(); + }, removeAppendEls = function() { + for (var j in appendedEls) { + if (appendedEls.hasOwnProperty(j)) { + if (appendedEls[j] && appendedEls[j].parentNode === head) { + head.removeChild(appendedEls[j]); + } + } + } + appendedEls.length = 0; + }, applyMediaReplace = function() { + var lastLink = links[links.length - 1]; + removeAppendEls(); + for (var l in storedSheets) { + if (storedSheets.hasOwnProperty(l)) { + var stored = storedSheets[l], styles = stored.styles, sheet = stored.sheet, css = styles, styleBlocks = {}, styleBlocksAll = [], alreadyReplaced = {}, thisstyle, min, max; + for (var m in stored.mediastyles) { + if (stored.mediastyles.hasOwnProperty(m)) { + thisstyle = stored.mediastyles[m]; + min = thisstyle.minw; + max = thisstyle.maxw; + if (thisstyle.media === "all") { + styleBlocksAll.push(thisstyle); + } else if (isRuleActive(thisstyle.hasquery, min, max)) { + if (!styleBlocks[thisstyle.media]) { + styleBlocks[thisstyle.media] = []; + } + styleBlocks[thisstyle.media].push(rules[thisstyle.rules]); + } + } + } + for (var n = styleBlocksAll.length - 1; n >= 0; n--) { + thisstyle = styleBlocksAll[n]; + min = thisstyle.minw; + max = thisstyle.maxw; + var rule = rules[thisstyle.rules], start = thisstyle.replaceIndexStart, end = thisstyle.replaceIndexEnd, replacement = ""; + if (alreadyReplaced[rule]) { + continue; + } + if (isRuleActive(thisstyle.hasquery, min, max)) { + replacement = rule; + } + css = replaceStringBetween(css, replacement, start, end); + alreadyReplaced[rule] = true; } - if (!!max) { - max = parseFloat(max) * (max.indexOf(em) > -1 ? eminpx || getEmValue() : 1); + insertCss(css, "all", stored.insertBefore); + if (sheet.parentElement !== null) { + head.removeChild(sheet); } - if (!thisstyle.hasquery || (!minnull || !maxnull) && (minnull || currWidth >= min) && (maxnull || currWidth <= max)) { - if (!styleBlocks[thisstyle.media]) { - styleBlocks[thisstyle.media] = []; + for (var o in styleBlocks) { + if (styleBlocks.hasOwnProperty(o)) { + insertCss(styleBlocks[o].join("\n"), "all", stored.insertBefore); } - styleBlocks[thisstyle.media].push(rules[thisstyle.rules]); } } } - for (var j in appendedEls) { - if (appendedEls.hasOwnProperty(j)) { - if (appendedEls[j] && appendedEls[j].parentNode === head) { - head.removeChild(appendedEls[j]); + }, applyMediaAppend = function() { + var styleBlocks = {}, lastLink = links[links.length - 1]; + for (var i in mediastyles) { + if (mediastyles.hasOwnProperty(i)) { + var thisstyle = mediastyles[i], min = thisstyle.minw, max = thisstyle.maxw; + if (isRuleActive(thisstyle.hasquery, min, max)) { + if (!styleBlocks[thisstyle.media]) { + styleBlocks[thisstyle.media] = []; + } + styleBlocks[thisstyle.media].push(rules[thisstyle.rules]); } } } - appendedEls.length = 0; + removeAppendEls(); for (var k in styleBlocks) { if (styleBlocks.hasOwnProperty(k)) { - var ss = doc.createElement("style"), css = styleBlocks[k].join("\n"); - ss.type = "text/css"; - ss.media = k; - head.insertBefore(ss, lastLink.nextSibling); - if (ss.styleSheet) { - ss.styleSheet.cssText = css; - } else { - ss.appendChild(doc.createTextNode(css)); - } - appendedEls.push(ss); + insertCss(styleBlocks[k].join("\n"), k, lastLink.nextSibling); } } - }, translate = function(styles, href, media) { - var qs = styles.replace(respond.regex.keyframes, "").match(respond.regex.media), ql = qs && qs.length || 0; + }, insertCss = function(css, media, insertBefore) { + var ss = doc.createElement("style"); + ss.type = "text/css"; + ss.media = media; + head.insertBefore(ss, insertBefore); + if (ss.styleSheet) { + ss.styleSheet.cssText = css; + } else { + ss.appendChild(doc.createTextNode(css)); + } + appendedEls.push(ss); + }, replaceUrls = function(styles, href) { href = href.substring(0, href.lastIndexOf("/")); - var repUrls = function(css) { - return css.replace(respond.regex.urls, "$1" + href + "$2$3"); - }, useMedia = !ql && media; if (href.length) { href += "/"; } + return styles.replace(respond.regex.urls, "$1" + href + "$2$3"); + }, translate = function(styles, href, media) { + if (w.RESPOND_REPLACE_STYLES) { + styles = replaceUrls(styles, href); + storedSheets[href].styles = styles; + storedSheets[href].mediastyles = []; + } + var qs = styles.replace(respond.regex.keyframes, "").match(respond.regex.media), ql = qs && qs.length || 0, useMedia = !ql && media; if (useMedia) { ql = 1; } for (var i = 0; i < ql; i++) { - var fullq, thisq, eachq, eql; + var fullq, thisq, eachq, eql, rule; if (useMedia) { fullq = media; - rules.push(repUrls(styles)); + rule = styles; } else { fullq = qs[i].match(respond.regex.findStyles) && RegExp.$1; - rules.push(RegExp.$2 && repUrls(RegExp.$2)); + rule = RegExp.$2; + } + if (!w.RESPOND_REPLACE_STYLES) { + rules.push(rule && replaceUrls(rule, href)); + } else { + rules.push(rule); } eachq = fullq.split(","); eql = eachq.length; for (var j = 0; j < eql; j++) { thisq = eachq[j]; - mediastyles.push({ + var thisstyle = { media: thisq.split("(")[0].match(respond.regex.only) && RegExp.$2 || "all", rules: rules.length - 1, hasquery: thisq.indexOf("(") > -1, minw: thisq.match(respond.regex.minw) && parseFloat(RegExp.$1) + (RegExp.$2 || ""), maxw: thisq.match(respond.regex.maxw) && parseFloat(RegExp.$1) + (RegExp.$2 || "") - }); + }; + if (w.RESPOND_REPLACE_STYLES) { + thisstyle.replaceIndexStart = storedSheets[href].styles.indexOf(qs[i]); + thisstyle.replaceIndexEnd = thisstyle.replaceIndexStart + qs[i].length; + storedSheets[href].mediastyles.push(thisstyle); + } else { + mediastyles.push(thisstyle); + } } } applyMedia(); @@ -230,6 +310,13 @@ if (requestQueue.length) { var thisRequest = requestQueue.shift(); ajax(thisRequest.href, function(styles) { + if (w.RESPOND_REPLACE_STYLES) { + storedSheets[thisRequest.href] = { + sheet: thisRequest.sheet, + insertBefore: thisRequest.sheet.nextSibling, + styles: styles + }; + } translate(styles, thisRequest.href, thisRequest.media); parsedSheets[thisRequest.href] = true; w.setTimeout(function() { @@ -242,6 +329,13 @@ var sheet = links[i], href = sheet.href, media = sheet.media, isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; if (!!href && isCSS && !parsedSheets[href]) { if (sheet.styleSheet && sheet.styleSheet.rawCssText) { + if (w.RESPOND_REPLACE_STYLES) { + storedSheets[href] = { + sheet: sheet, + insertBefore: sheet.nextSibling, + styles: sheet.styleSheet.rawCssText + }; + } translate(sheet.styleSheet.rawCssText, href, media); parsedSheets[href] = true; } else { @@ -250,6 +344,7 @@ href = w.location.protocol + href; } requestQueue.push({ + sheet: sheet, href: href, media: media }); diff --git a/dest/respond.min.js b/dest/respond.min.js index 80a7b69d..b8f848d5 100644 --- a/dest/respond.min.js +++ b/dest/respond.min.js @@ -1,5 +1,5 @@ -/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl +/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2015 Scott Jehl * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT * */ -!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b #mq-test-1 { width: 42px; }',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(a.document)}(this),function(a){"use strict";function b(){x(!0)}a.RESPOND_REPLACE_STYLES=a.RESPOND_REPLACE_STYLES||!1;var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{(([^\{\}]*\{[^\}\{]*\})+)[^\}]*\}/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)\}$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p={},q=30,r=j.getElementsByTagName("head")[0]||k,s=j.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},v=function(a,b,c){var d="clientWidth",e=k[d],f="CSS1Compat"===j.compatMode&&e||j.body[d]||e,g=null===b,h=null===c,l="em";return b&&(b=parseFloat(b)*(b.indexOf(l)>-1?i||u():1)),c&&(c=parseFloat(c)*(c.indexOf(l)>-1?i||u():1)),!a||(!g||!h)&&(g||f>=b)&&(h||c>=f)},w=function(a,b,c,d){return a.substring(0,c)+b+a.substring(d)},x=function(b){var c,d=(new Date).getTime();return c=a.RESPOND_REPLACE_STYLES?z:A,b&&g&&q>d-g?(a.clearTimeout(h),void(h=a.setTimeout(c,q))):(g=d,void c())},y=function(){for(var a in n)n.hasOwnProperty(a)&&n[a]&&n[a].parentNode===r&&r.removeChild(n[a]);n.length=0},z=function(){t[t.length-1];y();for(var a in o)if(o.hasOwnProperty(a)){var b,c,d,e=o[a],f=e.styles,g=e.sheet,h=f,i={},j=[],k={};for(var l in e.mediastyles)e.mediastyles.hasOwnProperty(l)&&(b=e.mediastyles[l],c=b.minw,d=b.maxw,"all"===b.media?j.push(b):v(b.hasquery,c,d)&&(i[b.media]||(i[b.media]=[]),i[b.media].push(m[b.rules])));for(var n=j.length-1;n>=0;n--){b=j[n],c=b.minw,d=b.maxw;var p=m[b.rules],q=b.replaceIndexStart,s=b.replaceIndexEnd,u="";k[p]||(v(b.hasquery,c,d)&&(u=p),h=w(h,u,q,s),k[p]=!0)}B(h,"all",e.insertBefore),null!==g.parentElement&&r.removeChild(g);for(var x in i)i.hasOwnProperty(x)&&B(i[x].join("\n"),"all",e.insertBefore)}},A=function(){var a={},b=t[t.length-1];for(var c in l)if(l.hasOwnProperty(c)){var d=l[c],e=d.minw,f=d.maxw;v(d.hasquery,e,f)&&(a[d.media]||(a[d.media]=[]),a[d.media].push(m[d.rules]))}y();for(var g in a)a.hasOwnProperty(g)&&B(a[g].join("\n"),g,b.nextSibling)},B=function(a,b,c){var d=j.createElement("style");d.type="text/css",d.media=b,r.insertBefore(d,c),d.styleSheet?d.styleSheet.cssText=a:d.appendChild(j.createTextNode(a)),n.push(d)},C=function(a,b){return b=b.substring(0,b.lastIndexOf("/")),b.length&&(b+="/"),a.replace(c.regex.urls,"$1"+b+"$2$3")},D=function(b,d,e){a.RESPOND_REPLACE_STYLES&&(b=C(b,d),o[d].styles=b,o[d].mediastyles=[]);var f=b.replace(c.regex.keyframes,"").match(c.regex.media),g=f&&f.length||0,h=!g&&e;h&&(g=1);for(var i=0;g>i;i++){var j,k,n,p,q;h?(j=e,q=b):(j=f[i].match(c.regex.findStyles)&&RegExp.$1,q=RegExp.$2),a.RESPOND_REPLACE_STYLES?m.push(q):m.push(q&&C(q,d)),n=j.split(","),p=n.length;for(var r=0;p>r;r++){k=n[r];var s={media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")};a.RESPOND_REPLACE_STYLES?(s.replaceIndexStart=o[d].styles.indexOf(f[i]),s.replaceIndexEnd=s.replaceIndexStart+f[i].length,o[d].mediastyles.push(s)):l.push(s)}}x()},E=function(){if(d.length){var b=d.shift();f(b.href,function(c){a.RESPOND_REPLACE_STYLES&&(o[b.href]={sheet:b.sheet,insertBefore:b.sheet.nextSibling,styles:c}),D(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){E()},0)})}},F=function(){for(var b=0;b -1 ? eminpx || getEmValue() : 1); + } + if (!!max) { + max = parseFloat(max) * (max.indexOf(em) > -1 ? eminpx || getEmValue() : 1); + } + return !hasquery || (!minnull || !maxnull) && (minnull || currWidth >= min) && (maxnull || currWidth <= max); + }, replaceStringBetween = function(source, replacement, start, end) { + return source.substring(0, start) + replacement + source.substring(end); }, applyMedia = function(fromResize) { - var name = "clientWidth", docElemProp = docElem[name], currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[name] || docElemProp, styleBlocks = {}, lastLink = links[links.length - 1], now = new Date().getTime(); + var method, now = new Date().getTime(); + if (w.RESPOND_REPLACE_STYLES) { + method = applyMediaReplace; + } else { + method = applyMediaAppend; + } if (fromResize && lastCall && now - lastCall < resizeThrottle) { w.clearTimeout(resizeDefer); - resizeDefer = w.setTimeout(applyMedia, resizeThrottle); + resizeDefer = w.setTimeout(method, resizeThrottle); return; } else { lastCall = now; } - for (var i in mediastyles) { - if (mediastyles.hasOwnProperty(i)) { - var thisstyle = mediastyles[i], min = thisstyle.minw, max = thisstyle.maxw, minnull = min === null, maxnull = max === null, em = "em"; - if (!!min) { - min = parseFloat(min) * (min.indexOf(em) > -1 ? eminpx || getEmValue() : 1); + method(); + }, removeAppendEls = function() { + for (var j in appendedEls) { + if (appendedEls.hasOwnProperty(j)) { + if (appendedEls[j] && appendedEls[j].parentNode === head) { + head.removeChild(appendedEls[j]); } - if (!!max) { - max = parseFloat(max) * (max.indexOf(em) > -1 ? eminpx || getEmValue() : 1); + } + } + appendedEls.length = 0; + }, applyMediaReplace = function() { + var lastLink = links[links.length - 1]; + removeAppendEls(); + for (var l in storedSheets) { + if (storedSheets.hasOwnProperty(l)) { + var stored = storedSheets[l], styles = stored.styles, sheet = stored.sheet, css = styles, styleBlocks = {}, styleBlocksAll = [], alreadyReplaced = {}, thisstyle, min, max; + for (var m in stored.mediastyles) { + if (stored.mediastyles.hasOwnProperty(m)) { + thisstyle = stored.mediastyles[m]; + min = thisstyle.minw; + max = thisstyle.maxw; + if (thisstyle.media === "all") { + styleBlocksAll.push(thisstyle); + } else if (isRuleActive(thisstyle.hasquery, min, max)) { + if (!styleBlocks[thisstyle.media]) { + styleBlocks[thisstyle.media] = []; + } + styleBlocks[thisstyle.media].push(rules[thisstyle.rules]); + } + } } - if (!thisstyle.hasquery || (!minnull || !maxnull) && (minnull || currWidth >= min) && (maxnull || currWidth <= max)) { - if (!styleBlocks[thisstyle.media]) { - styleBlocks[thisstyle.media] = []; + for (var n = styleBlocksAll.length - 1; n >= 0; n--) { + thisstyle = styleBlocksAll[n]; + min = thisstyle.minw; + max = thisstyle.maxw; + var rule = rules[thisstyle.rules], start = thisstyle.replaceIndexStart, end = thisstyle.replaceIndexEnd, replacement = ""; + if (alreadyReplaced[rule]) { + continue; + } + if (isRuleActive(thisstyle.hasquery, min, max)) { + replacement = rule; + } + css = replaceStringBetween(css, replacement, start, end); + alreadyReplaced[rule] = true; + } + insertCss(css, "all", stored.insertBefore); + if (sheet.parentElement !== null) { + head.removeChild(sheet); + } + for (var o in styleBlocks) { + if (styleBlocks.hasOwnProperty(o)) { + insertCss(styleBlocks[o].join("\n"), "all", stored.insertBefore); } - styleBlocks[thisstyle.media].push(rules[thisstyle.rules]); } } } - for (var j in appendedEls) { - if (appendedEls.hasOwnProperty(j)) { - if (appendedEls[j] && appendedEls[j].parentNode === head) { - head.removeChild(appendedEls[j]); + }, applyMediaAppend = function() { + var styleBlocks = {}, lastLink = links[links.length - 1]; + for (var i in mediastyles) { + if (mediastyles.hasOwnProperty(i)) { + var thisstyle = mediastyles[i], min = thisstyle.minw, max = thisstyle.maxw; + if (isRuleActive(thisstyle.hasquery, min, max)) { + if (!styleBlocks[thisstyle.media]) { + styleBlocks[thisstyle.media] = []; + } + styleBlocks[thisstyle.media].push(rules[thisstyle.rules]); } } } - appendedEls.length = 0; + removeAppendEls(); for (var k in styleBlocks) { if (styleBlocks.hasOwnProperty(k)) { - var ss = doc.createElement("style"), css = styleBlocks[k].join("\n"); - ss.type = "text/css"; - ss.media = k; - head.insertBefore(ss, lastLink.nextSibling); - if (ss.styleSheet) { - ss.styleSheet.cssText = css; - } else { - ss.appendChild(doc.createTextNode(css)); - } - appendedEls.push(ss); + insertCss(styleBlocks[k].join("\n"), k, lastLink.nextSibling); } } - }, translate = function(styles, href, media) { - var qs = styles.replace(respond.regex.keyframes, "").match(respond.regex.media), ql = qs && qs.length || 0; + }, insertCss = function(css, media, insertBefore) { + var ss = doc.createElement("style"); + ss.type = "text/css"; + ss.media = media; + head.insertBefore(ss, insertBefore); + if (ss.styleSheet) { + ss.styleSheet.cssText = css; + } else { + ss.appendChild(doc.createTextNode(css)); + } + appendedEls.push(ss); + }, replaceUrls = function(styles, href) { href = href.substring(0, href.lastIndexOf("/")); - var repUrls = function(css) { - return css.replace(respond.regex.urls, "$1" + href + "$2$3"); - }, useMedia = !ql && media; if (href.length) { href += "/"; } + return styles.replace(respond.regex.urls, "$1" + href + "$2$3"); + }, translate = function(styles, href, media) { + if (w.RESPOND_REPLACE_STYLES) { + styles = replaceUrls(styles, href); + storedSheets[href].styles = styles; + storedSheets[href].mediastyles = []; + } + var qs = styles.replace(respond.regex.keyframes, "").match(respond.regex.media), ql = qs && qs.length || 0, useMedia = !ql && media; if (useMedia) { ql = 1; } for (var i = 0; i < ql; i++) { - var fullq, thisq, eachq, eql; + var fullq, thisq, eachq, eql, rule; if (useMedia) { fullq = media; - rules.push(repUrls(styles)); + rule = styles; } else { fullq = qs[i].match(respond.regex.findStyles) && RegExp.$1; - rules.push(RegExp.$2 && repUrls(RegExp.$2)); + rule = RegExp.$2; + } + if (!w.RESPOND_REPLACE_STYLES) { + rules.push(rule && replaceUrls(rule, href)); + } else { + rules.push(rule); } eachq = fullq.split(","); eql = eachq.length; for (var j = 0; j < eql; j++) { thisq = eachq[j]; - mediastyles.push({ + var thisstyle = { media: thisq.split("(")[0].match(respond.regex.only) && RegExp.$2 || "all", rules: rules.length - 1, hasquery: thisq.indexOf("(") > -1, minw: thisq.match(respond.regex.minw) && parseFloat(RegExp.$1) + (RegExp.$2 || ""), maxw: thisq.match(respond.regex.maxw) && parseFloat(RegExp.$1) + (RegExp.$2 || "") - }); + }; + if (w.RESPOND_REPLACE_STYLES) { + thisstyle.replaceIndexStart = storedSheets[href].styles.indexOf(qs[i]); + thisstyle.replaceIndexEnd = thisstyle.replaceIndexStart + qs[i].length; + storedSheets[href].mediastyles.push(thisstyle); + } else { + mediastyles.push(thisstyle); + } } } applyMedia(); @@ -181,6 +261,13 @@ if (requestQueue.length) { var thisRequest = requestQueue.shift(); ajax(thisRequest.href, function(styles) { + if (w.RESPOND_REPLACE_STYLES) { + storedSheets[thisRequest.href] = { + sheet: thisRequest.sheet, + insertBefore: thisRequest.sheet.nextSibling, + styles: styles + }; + } translate(styles, thisRequest.href, thisRequest.media); parsedSheets[thisRequest.href] = true; w.setTimeout(function() { @@ -193,6 +280,13 @@ var sheet = links[i], href = sheet.href, media = sheet.media, isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; if (!!href && isCSS && !parsedSheets[href]) { if (sheet.styleSheet && sheet.styleSheet.rawCssText) { + if (w.RESPOND_REPLACE_STYLES) { + storedSheets[href] = { + sheet: sheet, + insertBefore: sheet.nextSibling, + styles: sheet.styleSheet.rawCssText + }; + } translate(sheet.styleSheet.rawCssText, href, media); parsedSheets[href] = true; } else { @@ -201,6 +295,7 @@ href = w.location.protocol + href; } requestQueue.push({ + sheet: sheet, href: href, media: media }); diff --git a/src/respond.js b/src/respond.js index 7b77134f..6e7f4cfd 100644 --- a/src/respond.js +++ b/src/respond.js @@ -3,6 +3,9 @@ "use strict"; + //when set to true, linked stylesheets will be replaced entirely with style elements for "all" media whilst other media are appended in individual styles, to ensure the cascade is not altered + w.RESPOND_REPLACE_STYLES = w.RESPOND_REPLACE_STYLES || false; + //exposed namespace var respond = {}; w.respond = respond; @@ -50,10 +53,10 @@ // expose for testing respond.regex = { - media: /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi, + media: /@media[^\{]+\{(([^\{\}]*\{[^\}\{]*\})+)[^\}]*\}/gi, keyframes: /@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi, urls: /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, - findStyles: /@media *([^\{]+)\{([\S\s]+?)$/, + findStyles: /@media *([^\{]+)\{([\S\s]+?)\}$/, only: /(only\s+)?([a-zA-Z]+)\s?/, minw: /\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/, maxw: /\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ @@ -73,6 +76,7 @@ mediastyles = [], rules = [], appendedEls = [], + storedSheets = {}, parsedSheets = {}, resizeThrottle = 30, head = doc.getElementsByTagName( "head" )[0] || docElem, @@ -134,43 +138,154 @@ return ret; }, - //enable/disable styles - applyMedia = function( fromResize ){ + isRuleActive = function( hasquery, min, max ){ var name = "clientWidth", docElemProp = docElem[ name ], currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp, - styleBlocks = {}, - lastLink = links[ links.length-1 ], + minnull = min === null, + maxnull = max === null, + em = "em"; + + if( !!min ){ + min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); + } + if( !!max ){ + max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); + } + + // if there's no media query at all (the () part), or min or max is not null, and if either is present, they're true + return !hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ); + }, + + replaceStringBetween = function( source, replacement, start, end ){ + //replace the content between a start and end index + return source.substring( 0, start ) + replacement + source.substring( end ); + }, + + applyMedia = function( fromResize ){ + var method, now = (new Date()).getTime(); + if( w.RESPOND_REPLACE_STYLES ){ + method = applyMediaReplace; + } + else{ + method = applyMediaAppend; + } + //throttle resize calls if( fromResize && lastCall && now - lastCall < resizeThrottle ){ w.clearTimeout( resizeDefer ); - resizeDefer = w.setTimeout( applyMedia, resizeThrottle ); + resizeDefer = w.setTimeout( method, resizeThrottle ); return; } else { lastCall = now; } + method(); + }, + + removeAppendEls = function(){ + //remove any existing respond style element(s) + for( var j in appendedEls ){ + if( appendedEls.hasOwnProperty( j ) ){ + if( appendedEls[ j ] && appendedEls[ j ].parentNode === head ){ + head.removeChild( appendedEls[ j ] ); + } + } + } + appendedEls.length = 0; + }, + + applyMediaReplace = function(){ + var lastLink = links[ links.length-1 ]; + + removeAppendEls(); + + //inject active styles, replacing current stylesheet + for( var l in storedSheets ){ + if( storedSheets.hasOwnProperty( l ) ){ + var stored = storedSheets[ l ], + styles = stored.styles, + sheet = stored.sheet, + css = styles, + styleBlocks = {}, + styleBlocksAll = [], + alreadyReplaced = {}, + thisstyle, min, max; + + for( var m in stored.mediastyles ){ + if( stored.mediastyles.hasOwnProperty( m ) ){ + thisstyle = stored.mediastyles[ m ]; + min = thisstyle.minw; + max = thisstyle.maxw; + + if( thisstyle.media === "all" ){ + styleBlocksAll.push(thisstyle); + } + else if( isRuleActive( thisstyle.hasquery, min, max ) ){ + //group by media type + if( !styleBlocks[ thisstyle.media ] ){ + styleBlocks[ thisstyle.media ] = []; + } + styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] ); + } + } + } + + //replace active rules with @media stripped and remove inactive rules, in reverse order so replace index won't change + for( var n = styleBlocksAll.length - 1; n >= 0; n-- ){ + thisstyle = styleBlocksAll[ n ]; + min = thisstyle.minw; + max = thisstyle.maxw; + var rule = rules[ thisstyle.rules ], + start = thisstyle.replaceIndexStart, + end = thisstyle.replaceIndexEnd, + replacement = ''; + + if( alreadyReplaced[ rule ] ){ + //this rule has already been applied for "all" media, we don't need to add it again for the other media queries it is under + continue; + } + + if( isRuleActive( thisstyle.hasquery, min, max ) ){ + replacement = rule; + } + + css = replaceStringBetween( css, replacement, start, end ); + alreadyReplaced[ rule ] = true; + } + + insertCss( css, "all", stored.insertBefore ); + + //remove original stylesheet + if( sheet.parentElement !== null ){ + head.removeChild( sheet ); + } + + //inject active styles, grouped by media type + for( var o in styleBlocks ){ + if( styleBlocks.hasOwnProperty( o ) ){ + insertCss( styleBlocks[ o ].join( "\n" ), "all", stored.insertBefore ); + } + } + } + } + }, + + //enable/disable styles + applyMediaAppend = function(){ + var styleBlocks = {}, + lastLink = links[ links.length-1 ]; + for( var i in mediastyles ){ if( mediastyles.hasOwnProperty( i ) ){ var thisstyle = mediastyles[ i ], min = thisstyle.minw, - max = thisstyle.maxw, - minnull = min === null, - maxnull = max === null, - em = "em"; - - if( !!min ){ - min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); - } - if( !!max ){ - max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); - } + max = thisstyle.maxw; - // if there's no media query at all (the () part), or min or max is not null, and if either is present, they're true - if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){ + if( isRuleActive( thisstyle.hasquery, min, max ) ){ if( !styleBlocks[ thisstyle.media ] ){ styleBlocks[ thisstyle.media ] = []; } @@ -179,57 +294,59 @@ } } - //remove any existing respond style element(s) - for( var j in appendedEls ){ - if( appendedEls.hasOwnProperty( j ) ){ - if( appendedEls[ j ] && appendedEls[ j ].parentNode === head ){ - head.removeChild( appendedEls[ j ] ); - } - } - } - appendedEls.length = 0; + removeAppendEls(); //inject active styles, grouped by media type for( var k in styleBlocks ){ if( styleBlocks.hasOwnProperty( k ) ){ - var ss = doc.createElement( "style" ), - css = styleBlocks[ k ].join( "\n" ); + insertCss( styleBlocks[ k ].join( "\n" ), k, lastLink.nextSibling ); + } + } + }, - ss.type = "text/css"; - ss.media = k; + insertCss = function( css, media, insertBefore ){ + var ss = doc.createElement( "style" ); - //originally, ss was appended to a documentFragment and sheets were appended in bulk. - //this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set, so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one! - head.insertBefore( ss, lastLink.nextSibling ); + ss.type = "text/css"; + ss.media = media; - if ( ss.styleSheet ){ - ss.styleSheet.cssText = css; - } - else { - ss.appendChild( doc.createTextNode( css ) ); - } + //originally, ss was appended to a documentFragment and sheets were appended in bulk. + //this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set, so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one! + head.insertBefore( ss, insertBefore ); - //push to appendedEls to track for later removal - appendedEls.push( ss ); - } + if ( ss.styleSheet ){ + ss.styleSheet.cssText = css; + } + else { + ss.appendChild( doc.createTextNode( css ) ); } + + //push to appendedEls to track for later removal + appendedEls.push( ss ); }, - //find media blocks in css text, convert to style blocks - translate = function( styles, href, media ){ - var qs = styles.replace( respond.regex.keyframes, '' ).match( respond.regex.media ), - ql = qs && qs.length || 0; + replaceUrls = function( styles, href ){ //try to get CSS path href = href.substring( 0, href.lastIndexOf( "/" ) ); - var repUrls = function( css ){ - return css.replace( respond.regex.urls, "$1" + href + "$2$3" ); - }, - useMedia = !ql && media; - //if path exists, tack on trailing slash if( href.length ){ href += "/"; } + return styles.replace( respond.regex.urls, "$1" + href + "$2$3" ); + }, + + //find media blocks in css text, convert to style blocks + translate = function( styles, href, media ){ + if( w.RESPOND_REPLACE_STYLES ){ + styles = replaceUrls( styles, href ); + storedSheets[ href ].styles = styles; + storedSheets[ href ].mediastyles = []; + } + + var qs = styles.replace( respond.regex.keyframes, '' ).match( respond.regex.media ), + ql = qs && qs.length || 0, + useMedia = !ql && media; + //if no internal queries exist, but media attr does, use that //note: this currently lacks support for situations where a media attr is specified on a link AND //its associated stylesheet has internal CSS media queries. @@ -239,17 +356,24 @@ } for( var i = 0; i < ql; i++ ){ - var fullq, thisq, eachq, eql; + var fullq, thisq, eachq, eql, rule; //media attr if( useMedia ){ fullq = media; - rules.push( repUrls( styles ) ); + rule = styles; } //parse for styles else{ fullq = qs[ i ].match( respond.regex.findStyles ) && RegExp.$1; - rules.push( RegExp.$2 && repUrls( RegExp.$2 ) ); + rule = RegExp.$2; + } + + if( !w.RESPOND_REPLACE_STYLES ){ + rules.push( rule && replaceUrls( rule, href ) ); + } + else { + rules.push( rule ); } eachq = fullq.split( "," ); @@ -257,13 +381,21 @@ for( var j = 0; j < eql; j++ ){ thisq = eachq[ j ]; - mediastyles.push( { + var thisstyle = { media : thisq.split( "(" )[ 0 ].match( respond.regex.only ) && RegExp.$2 || "all", rules : rules.length - 1, hasquery : thisq.indexOf("(") > -1, minw : thisq.match( respond.regex.minw ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ), maxw : thisq.match( respond.regex.maxw ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ) - } ); + }; + if( w.RESPOND_REPLACE_STYLES ){ + thisstyle.replaceIndexStart = storedSheets[ href ].styles.indexOf( qs[ i ] ); + thisstyle.replaceIndexEnd = thisstyle.replaceIndexStart + qs[ i ].length; + storedSheets[ href ].mediastyles.push( thisstyle ); + } + else { + mediastyles.push( thisstyle ); + } } } @@ -276,6 +408,13 @@ var thisRequest = requestQueue.shift(); ajax( thisRequest.href, function( styles ){ + if( w.RESPOND_REPLACE_STYLES ){ + storedSheets[ thisRequest.href ] = { + sheet: thisRequest.sheet, + insertBefore: thisRequest.sheet.nextSibling, + styles: styles + }; + } translate( styles, thisRequest.href, thisRequest.media ); parsedSheets[ thisRequest.href ] = true; @@ -299,6 +438,13 @@ if( !!href && isCSS && !parsedSheets[ href ] ){ // selectivizr exposes css through the rawCssText expando if (sheet.styleSheet && sheet.styleSheet.rawCssText) { + if( w.RESPOND_REPLACE_STYLES ){ + storedSheets[ href ] = { + sheet: sheet, + insertBefore: sheet.nextSibling, + styles: sheet.styleSheet.rawCssText + }; + } translate( sheet.styleSheet.rawCssText, href, media ); parsedSheets[ href ] = true; } else { @@ -308,6 +454,7 @@ // manually add in the protocol if ( href.substring(0,2) === "//" ) { href = w.location.protocol + href; } requestQueue.push( { + sheet: sheet, href: href, media: media } );