Created
March 30, 2012 03:19
-
-
Save jonathantneal/2246170 to your computer and use it in GitHub Desktop.
Match Media
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (window, screen, documentElement) { | |
/* ====================================================================== | |
Setup | |
====================================================================== */ | |
var hasEventListener = ('addEventListener' in window); | |
var hasOrientation = ('orientation' in window); | |
var hasGetComputedStyle = ('getComputedStyle' in window); | |
var regexpTrim = /^\s+|\s+$/g; | |
var regexpValue = /(\d+)(\w+)/; | |
var gap = ' '; | |
var landscape = 'landscape'; | |
var portrait = 'portrait'; | |
var fontSize = 16; | |
function documentElementHasClass(className) { | |
return (gap + documentElement.className + gap).indexOf(gap + className + gap) > -1; | |
} | |
function documentElementAddClass(className) { | |
if (!documentElementHasClass(className)) { | |
documentElement.className = (documentElement.className + gap + className).replace(regexpTrim, ''); | |
} | |
} | |
function documentElementCutClass(className) { | |
if (documentElementHasClass(className)) { | |
documentElement.className = (gap + documentElement.className + gap).replace(gap + className + gap, gap).replace(regexpTrim, ''); | |
} | |
} | |
function documentElementFontSize() { | |
return (hasGetComputedStyle ? getComputedStyle(documentElement, null) : documentElement.currentStyle).fontSize.toLowerCase().replace(regexpValue, parseDocumentElementFontSize) + 0; | |
} | |
function windowAddEvent(type, callback) { | |
window[hasEventListener ? 'addEventListener' : 'attachEvent']((hasEventListener ? '' : 'on') + type, callback); | |
} | |
function toCamelCase(string) { | |
return string.toLowerCase().replace(/-[a-z]/g, function (match) { | |
return match.substr(1).toUpperCase(); | |
}); | |
} | |
function parseDocumentElementFontSize(m, m1, m2) { | |
switch (m2) { | |
case 'em': | |
return m1 * 16; | |
case 'in': | |
return m1 * 96; | |
case 'pt': | |
return m1 * 96 / 72; | |
case '%': | |
return m1 / 100 * 16; | |
} | |
return m1; | |
} | |
function parseCSSValue(m, m1, m2) { | |
switch (m2) { | |
case 'em': | |
return m1 * matchMedia.current.fontSize; | |
case 'in': | |
return m1 * 96; | |
case 'pt': | |
return m1 * 96 / 72; | |
case '%': | |
return m1 / 100 * matchMedia.current.width; | |
} | |
return m1; | |
} | |
/* ====================================================================== | |
MediaQueryList | |
====================================================================== */ | |
function MediaQueryList(mediaQuery) { | |
var instance = this; | |
instance.__events__ = {}; | |
instance.__classNames__ = {}; | |
instance.validator = getMediaQueryValidator(mediaQuery); | |
instance.matches = instance.validator(matchMedia.current); | |
instance.query = mediaQuery; | |
matchMedia.instance.push(instance); | |
return instance; | |
} | |
MediaQueryList.prototype.addListener = function (fn) { | |
this.__events__[fn] = fn; | |
if (this.matches) { | |
fn.call(this, matchMedia.current); | |
} | |
}; | |
MediaQueryList.prototype.removeListener = function (fn) { | |
delete this.__events__[fn]; | |
}; | |
MediaQueryList.prototype.addClass = function (className) { | |
this.__classNames__[className] = true; | |
if (this.matches) { | |
documentElementAddClass(className); | |
} else { | |
documentElementCutClass(className); | |
} | |
}; | |
MediaQueryList.prototype.removeClass = function (className) { | |
delete this.__classNames__[className]; | |
}; | |
/* ====================================================================== | |
matchMedia | |
====================================================================== */ | |
function matchMedia(mediaQuery) { | |
return new MediaQueryList(mediaQuery); | |
} | |
matchMedia.current = {}; | |
matchMedia.instance = []; | |
matchMedia.update = function () { | |
var | |
width = window.innerWidth || documentElement.clientWidth, | |
height = window.innerHeight || documentElement.clientHeight, | |
orientation = width > height ? landscape : portrait, | |
deviceWidth = screen.width, | |
deviceHeight = screen.height, | |
deviceOrientation = hasOrientation ? window.orientation % 180 ? landscape : portrait : deviceWidth > deviceHeight ? landscape : portrait; | |
matchMedia.current = { | |
all: true, | |
screen: true, | |
print: false, | |
width: width, | |
height: height, | |
aspectRatio: width / height, | |
orientation: orientation, | |
fontSize: documentElementFontSize(), | |
deviceWidth: deviceWidth, | |
deviceHeight: deviceHeight, | |
deviceAspectRatio: deviceWidth / deviceHeight, | |
deviceOrientation: deviceOrientation | |
}; | |
var instanceIndex, callbackIndex, classNameIndex; | |
for (instanceIndex in matchMedia.instance) { | |
instance = matchMedia.instance[instanceIndex]; | |
if (instance.validator(matchMedia.current) != instance.matches) { | |
instance.matches = !instance.matches; | |
for (callbackIndex in instance.__events__) { | |
instance.__events__[callbackIndex].call(instance, matchMedia.current); | |
} | |
for (className in instance.__classNames__) { | |
if (instance.matches) { | |
documentElementAddClass(className); | |
} else { | |
documentElementCutClass(className); | |
} | |
} | |
} | |
} | |
} | |
function getMediaQueryValidator(mediaQuery) { | |
var | |
callback = [], | |
entries = mediaQuery.toLowerCase().split(/\s*,\s*/), | |
entriesIndex, entry, entryIndex, mq; | |
for (entriesIndex in entries) { | |
callback[entriesIndex] = []; | |
entry = entries[entriesIndex].replace(/\(|\)/g, '').split(/\s+and\s+/); | |
for (entryIndex in entry) { | |
callback[entriesIndex].push( | |
entry[entryIndex] | |
.replace(/min-(.*?):/g, '$1>=') | |
.replace(/max-(.*?):/g, '$1<=') | |
.replace(/:/g, '==') | |
.replace(/^([\W\w]+?)\s*([<>=]+)\s*([\W\w]+?)$|[\W\w]+/, function (m, m1, m2, m3) { | |
return m1 | |
? 'q.' + toCamelCase(m1) + m2 + m3.replace(regexpValue, parseCSSValue) | |
: '!!q.' + toCamelCase(m) | |
}) | |
); | |
} | |
callback[entriesIndex] = '(' + callback[entriesIndex].join('&&') + ')'; | |
} | |
return Function('q', 'return' + callback.join('||')); | |
} | |
matchMedia.update(); | |
windowAddEvent('resize', matchMedia.update); | |
window.matchMedia = matchMedia; | |
})(window, screen, document.documentElement); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment