1 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ |
---|
2 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ |
---|
3 | |
---|
4 | window.matchMedia = window.matchMedia || (function( doc, undefined ) { |
---|
5 | |
---|
6 | "use strict"; |
---|
7 | |
---|
8 | var bool, |
---|
9 | docElem = doc.documentElement, |
---|
10 | refNode = docElem.firstElementChild || docElem.firstChild, |
---|
11 | // fakeBody required for <FF4 when executed in <head> |
---|
12 | fakeBody = doc.createElement( "body" ), |
---|
13 | div = doc.createElement( "div" ); |
---|
14 | |
---|
15 | div.id = "mq-test-1"; |
---|
16 | div.style.cssText = "position:absolute;top:-100em"; |
---|
17 | fakeBody.style.background = "none"; |
---|
18 | fakeBody.appendChild(div); |
---|
19 | |
---|
20 | return function(q){ |
---|
21 | |
---|
22 | div.innerHTML = "­<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>"; |
---|
23 | |
---|
24 | docElem.insertBefore( fakeBody, refNode ); |
---|
25 | bool = div.offsetWidth === 42; |
---|
26 | docElem.removeChild( fakeBody ); |
---|
27 | |
---|
28 | return { |
---|
29 | matches: bool, |
---|
30 | media: q |
---|
31 | }; |
---|
32 | |
---|
33 | }; |
---|
34 | |
---|
35 | }( document )); |
---|
36 | |
---|
37 | |
---|
38 | |
---|
39 | |
---|
40 | |
---|
41 | /*! Respond.js v1.3.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ |
---|
42 | (function( win ){ |
---|
43 | |
---|
44 | "use strict"; |
---|
45 | |
---|
46 | //exposed namespace |
---|
47 | var respond = {}; |
---|
48 | win.respond = respond; |
---|
49 | |
---|
50 | //define update even in native-mq-supporting browsers, to avoid errors |
---|
51 | respond.update = function(){}; |
---|
52 | |
---|
53 | //expose media query support flag for external use |
---|
54 | respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches; |
---|
55 | |
---|
56 | //if media queries are supported, exit here |
---|
57 | if( respond.mediaQueriesSupported ){ |
---|
58 | return; |
---|
59 | } |
---|
60 | |
---|
61 | //define vars |
---|
62 | var doc = win.document, |
---|
63 | docElem = doc.documentElement, |
---|
64 | mediastyles = [], |
---|
65 | rules = [], |
---|
66 | appendedEls = [], |
---|
67 | parsedSheets = {}, |
---|
68 | resizeThrottle = 30, |
---|
69 | head = doc.getElementsByTagName( "head" )[0] || docElem, |
---|
70 | base = doc.getElementsByTagName( "base" )[0], |
---|
71 | links = head.getElementsByTagName( "link" ), |
---|
72 | requestQueue = [], |
---|
73 | |
---|
74 | //loop stylesheets, send text content to translate |
---|
75 | ripCSS = function(){ |
---|
76 | |
---|
77 | for( var i = 0; i < links.length; i++ ){ |
---|
78 | var sheet = links[ i ], |
---|
79 | href = sheet.href, |
---|
80 | media = sheet.media, |
---|
81 | isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet"; |
---|
82 | |
---|
83 | //only links plz and prevent re-parsing |
---|
84 | if( !!href && isCSS && !parsedSheets[ href ] ){ |
---|
85 | // selectivizr exposes css through the rawCssText expando |
---|
86 | if (sheet.styleSheet && sheet.styleSheet.rawCssText) { |
---|
87 | translate( sheet.styleSheet.rawCssText, href, media ); |
---|
88 | parsedSheets[ href ] = true; |
---|
89 | } else { |
---|
90 | if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) || |
---|
91 | href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){ |
---|
92 | requestQueue.push( { |
---|
93 | href: href, |
---|
94 | media: media |
---|
95 | } ); |
---|
96 | } |
---|
97 | } |
---|
98 | } |
---|
99 | } |
---|
100 | makeRequests(); |
---|
101 | }, |
---|
102 | |
---|
103 | //recurse through request queue, get css text |
---|
104 | makeRequests = function(){ |
---|
105 | if( requestQueue.length ){ |
---|
106 | var thisRequest = requestQueue.shift(); |
---|
107 | |
---|
108 | ajax( thisRequest.href, function( styles ){ |
---|
109 | translate( styles, thisRequest.href, thisRequest.media ); |
---|
110 | parsedSheets[ thisRequest.href ] = true; |
---|
111 | |
---|
112 | // by wrapping recursive function call in setTimeout |
---|
113 | // we prevent "Stack overflow" error in IE7 |
---|
114 | win.setTimeout(function(){ makeRequests(); },0); |
---|
115 | } ); |
---|
116 | } |
---|
117 | }, |
---|
118 | |
---|
119 | //find media blocks in css text, convert to style blocks |
---|
120 | translate = function( styles, href, media ){ |
---|
121 | var qs = styles.match( /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ), |
---|
122 | ql = qs && qs.length || 0; |
---|
123 | |
---|
124 | //try to get CSS path |
---|
125 | href = href.substring( 0, href.lastIndexOf( "/" ) ); |
---|
126 | |
---|
127 | var repUrls = function( css ){ |
---|
128 | return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" ); |
---|
129 | }, |
---|
130 | useMedia = !ql && media; |
---|
131 | |
---|
132 | //if path exists, tack on trailing slash |
---|
133 | if( href.length ){ href += "/"; } |
---|
134 | |
---|
135 | //if no internal queries exist, but media attr does, use that |
---|
136 | //note: this currently lacks support for situations where a media attr is specified on a link AND |
---|
137 | //its associated stylesheet has internal CSS media queries. |
---|
138 | //In those cases, the media attribute will currently be ignored. |
---|
139 | if( useMedia ){ |
---|
140 | ql = 1; |
---|
141 | } |
---|
142 | |
---|
143 | for( var i = 0; i < ql; i++ ){ |
---|
144 | var fullq, thisq, eachq, eql; |
---|
145 | |
---|
146 | //media attr |
---|
147 | if( useMedia ){ |
---|
148 | fullq = media; |
---|
149 | rules.push( repUrls( styles ) ); |
---|
150 | } |
---|
151 | //parse for styles |
---|
152 | else{ |
---|
153 | fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1; |
---|
154 | rules.push( RegExp.$2 && repUrls( RegExp.$2 ) ); |
---|
155 | } |
---|
156 | |
---|
157 | eachq = fullq.split( "," ); |
---|
158 | eql = eachq.length; |
---|
159 | |
---|
160 | for( var j = 0; j < eql; j++ ){ |
---|
161 | thisq = eachq[ j ]; |
---|
162 | mediastyles.push( { |
---|
163 | media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all", |
---|
164 | rules : rules.length - 1, |
---|
165 | hasquery : thisq.indexOf("(") > -1, |
---|
166 | minw : thisq.match( /\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ), |
---|
167 | maxw : thisq.match( /\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ) |
---|
168 | } ); |
---|
169 | } |
---|
170 | } |
---|
171 | |
---|
172 | applyMedia(); |
---|
173 | }, |
---|
174 | |
---|
175 | lastCall, |
---|
176 | |
---|
177 | resizeDefer, |
---|
178 | |
---|
179 | // returns the value of 1em in pixels |
---|
180 | getEmValue = function() { |
---|
181 | var ret, |
---|
182 | div = doc.createElement('div'), |
---|
183 | body = doc.body, |
---|
184 | fakeUsed = false; |
---|
185 | |
---|
186 | div.style.cssText = "position:absolute;font-size:1em;width:1em"; |
---|
187 | |
---|
188 | if( !body ){ |
---|
189 | body = fakeUsed = doc.createElement( "body" ); |
---|
190 | body.style.background = "none"; |
---|
191 | } |
---|
192 | |
---|
193 | body.appendChild( div ); |
---|
194 | |
---|
195 | docElem.insertBefore( body, docElem.firstChild ); |
---|
196 | |
---|
197 | ret = div.offsetWidth; |
---|
198 | |
---|
199 | if( fakeUsed ){ |
---|
200 | docElem.removeChild( body ); |
---|
201 | } |
---|
202 | else { |
---|
203 | body.removeChild( div ); |
---|
204 | } |
---|
205 | |
---|
206 | //also update eminpx before returning |
---|
207 | ret = eminpx = parseFloat(ret); |
---|
208 | |
---|
209 | return ret; |
---|
210 | }, |
---|
211 | |
---|
212 | //cached container for 1em value, populated the first time it's needed |
---|
213 | eminpx, |
---|
214 | |
---|
215 | //enable/disable styles |
---|
216 | applyMedia = function( fromResize ){ |
---|
217 | var name = "clientWidth", |
---|
218 | docElemProp = docElem[ name ], |
---|
219 | currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp, |
---|
220 | styleBlocks = {}, |
---|
221 | lastLink = links[ links.length-1 ], |
---|
222 | now = (new Date()).getTime(); |
---|
223 | |
---|
224 | //throttle resize calls |
---|
225 | if( fromResize && lastCall && now - lastCall < resizeThrottle ){ |
---|
226 | win.clearTimeout( resizeDefer ); |
---|
227 | resizeDefer = win.setTimeout( applyMedia, resizeThrottle ); |
---|
228 | return; |
---|
229 | } |
---|
230 | else { |
---|
231 | lastCall = now; |
---|
232 | } |
---|
233 | |
---|
234 | for( var i in mediastyles ){ |
---|
235 | if( mediastyles.hasOwnProperty( i ) ){ |
---|
236 | var thisstyle = mediastyles[ i ], |
---|
237 | min = thisstyle.minw, |
---|
238 | max = thisstyle.maxw, |
---|
239 | minnull = min === null, |
---|
240 | maxnull = max === null, |
---|
241 | em = "em"; |
---|
242 | |
---|
243 | if( !!min ){ |
---|
244 | min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); |
---|
245 | } |
---|
246 | if( !!max ){ |
---|
247 | max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 ); |
---|
248 | } |
---|
249 | |
---|
250 | // 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 |
---|
251 | if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){ |
---|
252 | if( !styleBlocks[ thisstyle.media ] ){ |
---|
253 | styleBlocks[ thisstyle.media ] = []; |
---|
254 | } |
---|
255 | styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] ); |
---|
256 | } |
---|
257 | } |
---|
258 | } |
---|
259 | |
---|
260 | //remove any existing respond style element(s) |
---|
261 | for( var j in appendedEls ){ |
---|
262 | if( appendedEls.hasOwnProperty( j ) ){ |
---|
263 | if( appendedEls[ j ] && appendedEls[ j ].parentNode === head ){ |
---|
264 | head.removeChild( appendedEls[ j ] ); |
---|
265 | } |
---|
266 | } |
---|
267 | } |
---|
268 | |
---|
269 | //inject active styles, grouped by media type |
---|
270 | for( var k in styleBlocks ){ |
---|
271 | if( styleBlocks.hasOwnProperty( k ) ){ |
---|
272 | var ss = doc.createElement( "style" ), |
---|
273 | css = styleBlocks[ k ].join( "\n" ); |
---|
274 | |
---|
275 | ss.type = "text/css"; |
---|
276 | ss.media = k; |
---|
277 | |
---|
278 | //originally, ss was appended to a documentFragment and sheets were appended in bulk. |
---|
279 | //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! |
---|
280 | head.insertBefore( ss, lastLink.nextSibling ); |
---|
281 | |
---|
282 | if ( ss.styleSheet ){ |
---|
283 | ss.styleSheet.cssText = css; |
---|
284 | } |
---|
285 | else { |
---|
286 | ss.appendChild( doc.createTextNode( css ) ); |
---|
287 | } |
---|
288 | |
---|
289 | //push to appendedEls to track for later removal |
---|
290 | appendedEls.push( ss ); |
---|
291 | } |
---|
292 | } |
---|
293 | }, |
---|
294 | //tweaked Ajax functions from Quirksmode |
---|
295 | ajax = function( url, callback ) { |
---|
296 | var req = xmlHttp(); |
---|
297 | if (!req){ |
---|
298 | return; |
---|
299 | } |
---|
300 | req.open( "GET", url, true ); |
---|
301 | req.onreadystatechange = function () { |
---|
302 | if ( req.readyState !== 4 || req.status !== 200 && req.status !== 304 ){ |
---|
303 | return; |
---|
304 | } |
---|
305 | callback( req.responseText ); |
---|
306 | }; |
---|
307 | if ( req.readyState === 4 ){ |
---|
308 | return; |
---|
309 | } |
---|
310 | req.send( null ); |
---|
311 | }, |
---|
312 | //define ajax obj |
---|
313 | xmlHttp = (function() { |
---|
314 | var xmlhttpmethod = false; |
---|
315 | try { |
---|
316 | xmlhttpmethod = new win.XMLHttpRequest(); |
---|
317 | } |
---|
318 | catch( e ){ |
---|
319 | xmlhttpmethod = new win.ActiveXObject( "Microsoft.XMLHTTP" ); |
---|
320 | } |
---|
321 | return function(){ |
---|
322 | return xmlhttpmethod; |
---|
323 | }; |
---|
324 | })(); |
---|
325 | |
---|
326 | //translate CSS |
---|
327 | ripCSS(); |
---|
328 | |
---|
329 | //expose update for re-running respond later on |
---|
330 | respond.update = ripCSS; |
---|
331 | |
---|
332 | //adjust on resize |
---|
333 | function callMedia(){ |
---|
334 | applyMedia( true ); |
---|
335 | } |
---|
336 | if( win.addEventListener ){ |
---|
337 | win.addEventListener( "resize", callMedia, false ); |
---|
338 | } |
---|
339 | else if( win.attachEvent ){ |
---|
340 | win.attachEvent( "onresize", callMedia ); |
---|
341 | } |
---|
342 | })(this); |
---|