Skip to content
This repository was archived by the owner on Jul 3, 2024. It is now read-only.

Commit 6ac3019

Browse files
committed
eCSStender::matchMedia method
1 parent 66de01a commit 6ac3019

File tree

2 files changed

+324
-3
lines changed

2 files changed

+324
-3
lines changed

src/eCSStender.js

Lines changed: 225 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,28 @@ License: MIT License (see homepage)
5656
HYPHEN = '-',
5757
OPEN_CURLY = '{',
5858
CLOSE_CURLY = '}',
59+
COMMA = ',',
5960
DIV = 'div',
6061
TYPE = 'type',
6162
COMPLETE = 'complete',
63+
BODY = 'body',
64+
ORIENTATION = 'orientation',
65+
PORTRAIT = 'portrait',
66+
LANDSCAPE = 'landscape',
67+
WIDTH = 'width',
68+
MAXWIDTH = 'max-width',
69+
MINWIDTH = 'min-width',
70+
DEVWIDTH = 'device-width',
71+
DEVMAXWIDTH = 'max-device-width',
72+
DEVMINWIDTH = 'min-device-width',
73+
HEIGHT = 'height',
74+
MAXHEIGHT = 'max-height',
75+
MINHEIGHT = 'min-height',
76+
DEVHEIGHT = 'device-height',
77+
DEVMAXHEIGHT = 'max-device-height',
78+
DEVMINHEIGHT = 'min-device-height',
79+
PX = 'px',
80+
PT = 'pt',
6281

6382
// Regex Bits
6483
ANYTHING = '.*?',
@@ -76,6 +95,7 @@ License: MIT License (see homepage)
7695
__s = 0, // index of current stylesheet
7796
__style_objects = {}, // style rules to track
7897
__media_groups = {},
98+
__media_queries = {}, // all styles where a CSS3-style media queries is in use
7999
__xhr = NULL,
80100
__initialized = FALSE,
81101
__ignored_css = [],
@@ -121,6 +141,8 @@ License: MIT License (see homepage)
121141
REGEXP_ATRULE = /@([\w-]+)(.*?)\{([^}]*)\}/ig,
122142
// for splitting properties from values
123143
REGEXP_P_V = /:(?!\/\/)/,
144+
// for detecting CSS3-style media queries
145+
REGEXP_MQ_PARENS = /\(.*:.*\)/,
124146

125147
// eCSStender Object
126148
eCSStender = {
@@ -158,6 +180,7 @@ License: MIT License (see homepage)
158180
{
159181
validateCache();
160182
runTests();
183+
getMediaQueryStyles();
161184
eCSStend();
162185
triggerCallbacks();
163186
writeBrowserCache();
@@ -181,6 +204,15 @@ License: MIT License (see homepage)
181204
__xhr = TRUE;
182205
}
183206
}
207+
function getMediaQueryStyles() {
208+
var mediaQueryRegex = newRegExp(REGEXP_MQ_PARENS);
209+
for( var styleObject in __style_objects ) {
210+
if( mediaQueryRegex.test(styleObject) ) {
211+
__media_queries[ styleObject ] = __style_objects[ styleObject ];
212+
}
213+
}
214+
eCSStender.mediaQueryStyles = __media_queries;
215+
}
184216
function parseStyles()
185217
{
186218
var s=0, sLen=__stylesheets.length, media, m, mLen;
@@ -2428,7 +2460,198 @@ License: MIT License (see homepage)
24282460
}
24292461
};
24302462
eCSStender.toggleClass = toggleClass;
2431-
2463+
/**
2464+
* eCSStender.matchMedia()
2465+
* returns true if the media query matches the state of rendered document
2466+
* and false if it does not (does not take into account things like "screen",
2467+
* "print" or "handheld", though native support does)
2468+
*
2469+
* based on http://dev.w3.org/csswg/cssom-view/
2470+
* uses native matchMedia if available
2471+
*
2472+
* @param str query - the media query to test
2473+
*/
2474+
function matchMedia( query ) {
2475+
if( WINDOW.matchMedia ) {
2476+
matchMedia = function( query ) {
2477+
return WINDOW.matchMedia( query ).matches;
2478+
}
2479+
} else {
2480+
var
2481+
getWidth,
2482+
getHeight,
2483+
if( defined( window.innerWidth ) ) {
2484+
getWidth = function () { return window.innerWidth };
2485+
} else if ( defined( document.documentElement ) && defined( document.documentElement.clientWidth ) && document.documentElement.clientWidth ) {
2486+
getWidth = function() { return document.documentElement.clientWidth };
2487+
} else {
2488+
getWidth = function() { return document.getElementsByTagName(BODY)[0].clientWidth; }
2489+
}
2490+
if( defined( window.innerHeight ) ) {
2491+
getHeight = function() { return window.innerHeight; }
2492+
} else if ( defined( document.documentElement ) && defined( document.documentElement.clientHeight ) && document.documentElement.clientHeight ) {
2493+
getHeight = function() { return document.documentElement.clientHeight; }
2494+
} else {
2495+
getHeight = function() { return document.getElementsByTagName(BODY)[0].clientHeight; }
2496+
}
2497+
function convertToPixels( val ) {
2498+
var
2499+
number = parseInt(val.replace(/[^\d]+/g, ''), 10),
2500+
unit = val.replace(number, '');
2501+
switch(unit) {
2502+
case PX:
2503+
break;
2504+
case PT:
2505+
number = number * 96 / 72;
2506+
break;
2507+
default:
2508+
break;
2509+
}
2510+
return number;
2511+
}
2512+
2513+
matchMedia = function( query ) {
2514+
if(query.indexOf(COMMA) > -1) { // handle OR conditions
2515+
var
2516+
queries = query.split(COMMA),
2517+
i = queries.length;
2518+
while( i ) {
2519+
var q = queries[i - 1];
2520+
q = trim(q);
2521+
if( matchMedia(q) ) { // if any of the conditions match, we can return true and bail
2522+
return TRUE;
2523+
}
2524+
i--;
2525+
}
2526+
}
2527+
var
2528+
queries = query.split(' and '), // split the query into each condition
2529+
matches = TRUE, // optimism
2530+
mediaQueryRegex = newRegExp(REGEXP_MQ_PARENS),
2531+
W = getWidth(),
2532+
DW = screen.width,
2533+
H = getHeight(),
2534+
DH = screen.height;
2535+
i = queries.length;
2536+
while ( i ) {
2537+
var q = queries[i - 1];
2538+
if(mediaQueryRegex.test(q)) { // we only test query parts in the style of (property:value)
2539+
var
2540+
q = q.split(COLON),
2541+
prop = q[0].toLowerCase(),
2542+
val = q[1];
2543+
2544+
prop = prop.replace(/^\(/, EMPTY);
2545+
val = val.replace(/\)$/, EMPTY);
2546+
2547+
if( prop != ORIENTATION ) {
2548+
val = convertToPixels(val);
2549+
}
2550+
switch( prop ) {
2551+
case ORIENTATION:
2552+
switch( val ) {
2553+
case LANDSCAPE:
2554+
if( W < H )
2555+
{
2556+
matches = FALSE;
2557+
}
2558+
break;
2559+
case PORTRAIT:
2560+
if( W > H )
2561+
{
2562+
matches = FALSE;
2563+
}
2564+
break;
2565+
default:
2566+
// we only support landscape and portrait
2567+
break;
2568+
}
2569+
break;
2570+
case WIDTH:
2571+
if( W != val )
2572+
{
2573+
matches = FALSE;
2574+
}
2575+
break;
2576+
case MAXWIDTH:
2577+
if( W > val )
2578+
{
2579+
matches = FALSE;
2580+
}
2581+
break;
2582+
case MINWIDTH:
2583+
if( W < val )
2584+
{
2585+
matches = FALSE;
2586+
}
2587+
break;
2588+
case DEVWIDTH:
2589+
if( DW != val )
2590+
{
2591+
matches = FALSE;
2592+
}
2593+
break;
2594+
case DEVMAXWIDTH:
2595+
if( DW > val )
2596+
{
2597+
matches = FALSE;
2598+
}
2599+
break;
2600+
case DEVMINWIDTH:
2601+
if( DW < val )
2602+
{
2603+
matches = FALSE;
2604+
}
2605+
break;
2606+
case HEIGHT:
2607+
if( H != val )
2608+
{
2609+
matches = FALSE;
2610+
}
2611+
break;
2612+
case MAXHEIGHT:
2613+
if( H > val )
2614+
{
2615+
matches = FALSE;
2616+
}
2617+
break;
2618+
case MINHEIGHT:
2619+
if( H < val )
2620+
{
2621+
matches = FALSE;
2622+
}
2623+
break;
2624+
case DEVHEIGHT:
2625+
if( DH != val )
2626+
{
2627+
matches = FALSE;
2628+
}
2629+
break;
2630+
case DEVMAXHEIGHT:
2631+
if( DH > val )
2632+
{
2633+
matches = FALSE;
2634+
}
2635+
break;
2636+
case DEVMINHEIGHT:
2637+
if( DH < val )
2638+
{
2639+
matches = FALSE;
2640+
}
2641+
break;
2642+
default:
2643+
break;
2644+
}
2645+
}
2646+
i--;
2647+
};
2648+
2649+
return matches;
2650+
}
2651+
return matchMedia( query );
2652+
}
2653+
}
2654+
eCSStender.matchMedia = matchMedia;
24322655
/*-------------------------------------*
24332656
* DOM Loaded Trigger *
24342657
* Based on jQuery's *
@@ -2480,5 +2703,5 @@ License: MIT License (see homepage)
24802703
}
24812704
}
24822705
})();
2483-
2706+
24842707
})();

tests/js/core.js

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,5 +178,103 @@ eCSStender.onComplete(function(){
178178
matches = eCSStender.lookup({'selector':'#bar'},'*');
179179
ok( matches.length===0, 'array of css files ignored' );
180180
});
181-
181+
module('Media Queries');
182+
test( 'eCSStender::matchMedia', function(){
183+
ok( typeof(eCSStender.matchMedia) == 'function', 'eCSStender::matchMedia() exists' );
184+
var
185+
getWidth,
186+
getHeight,
187+
if( typeof( window.innerWidth ) != 'undefined' ) {
188+
getWidth = function () { return window.innerWidth };
189+
} else if ( typeof( document.documentElement ) != 'undefined' && typeof( document.documentElement.clientWidth ) != 'undefined' && document.documentElement.clientWidth ) {
190+
getWidth = function() { return document.documentElement.clientWidth };
191+
} else {
192+
getWidth = function() { return document.getElementsByTagName('body')[0].clientWidth; }
193+
}
194+
if( typeof( window.innerHeight ) != 'undefined' ) {
195+
getHeight = function() { return window.innerHeight; }
196+
} else if ( typeof( document.documentElement ) != 'undefined' && typeof( document.documentElement.clientHeight ) != 'undefined' && document.documentElement.clientHeight ) {
197+
getHeight = function() { return document.documentElement.clientHeight; }
198+
} else {
199+
getHeight = function() { return document.getElementsByTagName('body')[0].clientHeight; }
200+
}
201+
var
202+
W = getWidth(),
203+
H = getHeight(),
204+
DW = screen.width,
205+
DH = screen.height,
206+
O = W > H ? 'landscape' : 'portrait';
207+
208+
// orientation
209+
ok( eCSStender.matchMedia('screen and (orientation:portrait)') == (O == 'portrait'), '(orientation:portrait) ' + (O == 'portrait' ? 'matched' : 'does not match') );
210+
ok( eCSStender.matchMedia('screen and (orientation:landscape)') == (O == 'landscape'), '(orientation:landscape) ' + (O == 'landscape' ? 'matched' : 'does not match') );
211+
212+
// width
213+
ok( eCSStender.matchMedia('screen and (width:' + W + 'px)'), 'width set to window width matches' );
214+
ok( !eCSStender.matchMedia('screen and (width:' + (W - 100) + 'px)'), 'width set to window width - 100 does not match' );
215+
ok( !eCSStender.matchMedia('screen and (width:' + (W + 100) + 'px)'), 'width set to window width + 100 does not match' );
216+
217+
// max-width
218+
ok( eCSStender.matchMedia('screen and (max-width:' + W + 'px)'), 'max-width set to window width matches' );
219+
ok( !eCSStender.matchMedia('screen and (max-width:' + (W - 100) + 'px)'), 'max-width set to window width - 100 does not match' );
220+
ok( eCSStender.matchMedia('screen and (max-width:' + (W + 100) + 'px)'), 'max-width set to window width + 100 matches' );
221+
222+
// min-width
223+
ok( eCSStender.matchMedia('screen and (min-width:' + W + 'px)'), 'min-width set to window width matches' );
224+
ok( eCSStender.matchMedia('screen and (min-width:' + (W - 100) + 'px)'), 'min-width set to window width - 100 matches' );
225+
ok( !eCSStender.matchMedia('screen and (min-width:' + (W + 100) + 'px)'), 'min-width set to window width + 100 does not match' );
226+
227+
// device-width
228+
ok( eCSStender.matchMedia('screen and (device-width:' + DW + 'px)'), 'device-width set to device-width matches' );
229+
ok( !eCSStender.matchMedia('screen and (device-width:' + (DW - 100) + 'px)'), 'device-width set to device-width - 100 does not match' );
230+
ok( !eCSStender.matchMedia('screen and (device-width:' + (DW + 100) + 'px)'), 'device-width set to device-width + 100 does not match' );
231+
232+
// max-device-width
233+
ok( eCSStender.matchMedia('screen and (max-device-width:' + DW + 'px)'), 'max-device-width set to device-width matches' );
234+
ok( !eCSStender.matchMedia('screen and (max-device-width:' + (DW - 100) + 'px)'), 'max-device-width set to device-width - 100 does not match' );
235+
ok( eCSStender.matchMedia('screen and (max-device-width:' + (DW + 100) + 'px)'), 'max-device-width set to device-width + 100 matches' );
236+
237+
// min-device-width
238+
ok( eCSStender.matchMedia('screen and (min-device-width:' + DW + 'px)'), 'min-device-width set to device-width matches' );
239+
ok( eCSStender.matchMedia('screen and (min-device-width:' + (DW - 100) + 'px)'), 'min-device-width set to device-width - 100 matches' );
240+
ok( !eCSStender.matchMedia('screen and (min-device-width:' + (DW + 100) + 'px)'), 'min-device-width set to device-width + 100 does not match' );
241+
242+
// height
243+
ok( eCSStender.matchMedia('screen and (height:' + H + 'px)'), 'height set to window height matches' );
244+
ok( !eCSStender.matchMedia('screen and (height:' + (H - 100) + 'px)'), 'height set to window height - 100 does not match' );
245+
ok( !eCSStender.matchMedia('screen and (height:' + (H + 100) + 'px)'), 'height set to window height + 100 does not match' );
246+
247+
// max-height
248+
ok( eCSStender.matchMedia('screen and (max-height:' + H + 'px)'), 'max-height set to window height matches' );
249+
ok( !eCSStender.matchMedia('screen and (max-height:' + (H - 100) + 'px)'), 'max-height set to window height - 100 does not match' );
250+
ok( eCSStender.matchMedia('screen and (max-height:' + (H + 100) + 'px)'), 'max-height set to window height + 100 matches' );
251+
252+
// min-height
253+
ok( eCSStender.matchMedia('screen and (min-height:' + H + 'px)'), 'min-height set to window height matches' );
254+
ok( eCSStender.matchMedia('screen and (min-height:' + (H - 100) + 'px)'), 'min-height set to window height - 100 matches' );
255+
ok( !eCSStender.matchMedia('screen and (min-height:' + (H + 100) + 'px)'), 'min-height set to window height + 100 does not match' );
256+
257+
// device-height
258+
ok( eCSStender.matchMedia('screen and (device-height:' + DH + 'px)'), 'device-height set to device-height matches' );
259+
ok( !eCSStender.matchMedia('screen and (device-height:' + (DH - 100) + 'px)'), 'device-height set to device-height - 100 does not match' );
260+
ok( !eCSStender.matchMedia('screen and (device-height:' + (DH + 100) + 'px)'), 'device-height set to device-height + 100 does not match' );
261+
262+
// max-device-height
263+
ok( eCSStender.matchMedia('screen and (max-device-height:' + DH + 'px)'), 'max-device-height set to device-height matches' );
264+
ok( !eCSStender.matchMedia('screen and (max-device-height:' + (DH - 100) + 'px)'), 'max-device-height set to device-height - 100 does not match' );
265+
ok( eCSStender.matchMedia('screen and (max-device-height:' + (DH + 100) + 'px)'), 'max-device-height set to device-height + 100 matches' );
266+
267+
// min-device-height
268+
ok( eCSStender.matchMedia('screen and (min-device-height:' + DH + 'px)'), 'min-device-height set to device-height matches' );
269+
ok( eCSStender.matchMedia('screen and (min-device-height:' + (DH - 100) + 'px)'), 'min-device-height set to device-height - 100 matches' );
270+
ok( !eCSStender.matchMedia('screen and (min-device-height:' + (DH + 100) + 'px)'), 'min-device-height set to device-height + 100 does not match' );
271+
272+
// complex rules
273+
ok( eCSStender.matchMedia('screen and (min-width:' + (W - 100) + 'px) and (max-width:' + (W + 100) + 'px)'), '' + 'screen and (min-width:' + (W - 100) + 'px) and (max-width:' + (W + 100) + 'px) matches');
274+
ok( eCSStender.matchMedia('screen and (min-height:' + (H - 100) + 'px) and (max-height:' + (H + 100) + 'px)'), '' + 'screen and (min-height:' + (H - 100) + 'px) and (max-height:' + (H + 100) + 'px) matches');
275+
ok( eCSStender.matchMedia('screen and (min-width:' + (W - 100) + 'px), screen and (max-width:' + (W + 100) + 'px)'), '' + 'screen and (min-width:' + (W - 100) + 'px), screen and (max-width:' + (W + 100) + 'px) matches');
276+
ok( eCSStender.matchMedia('screen and (min-width:' + (W - 100) + 'px) and (orientation:' + O + ')'), '' + 'screen and (min-width:' + (W - 100) + 'px) and (orientation:' + O + ') matches');
277+
ok( !eCSStender.matchMedia('screen and (min-width:' + (W - 100) + 'px) and (orientation:' + ( O == 'portrait' ? 'landscape' : 'portrait' ) + ')'), '' + 'screen and (min-width:' + (W - 100) + 'px) and (orientation:' + ( O == 'portrait' ? 'landscape' : 'portrait' ) + ') does not match');
278+
ok( !eCSStender.matchMedia('screen and (max-width:' + (W - 100) + 'px), screen and (orientation:' + ( O == 'portrait' ? 'landscape' : 'portrait' ) + ')'), '' + 'screen and (max-width:' + (W - 100) + 'px), screen and (orientation:' + ( O == 'portrait' ? 'landscape' : 'portrait' ) + ') does not match');
279+
});
182280
});

0 commit comments

Comments
 (0)