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