/** @license CSS.supports polyfill | @version 0.4 | MIT License | github.com/termi/CSS.supports */ // ==ClosureCompiler== // @compilation_level ADVANCED_OPTIMIZATIONS // @warning_level VERBOSE // @jscomp_warning missingProperties // @output_file_name CSS.supports.js // @check_types // ==/ClosureCompiler== /* TODO:: 1. element.style.webkitProperty == element.style.WebkitProperty in Webkit (Chrome at least), so CSS.supporst("webkit-animation", "name") is true. Think this is wrong. */ ;(function() { "use strict"; var global = window , _CSS_supports , msie , testElement , prevResultsCache , _CSS = global["CSS"] ; if( !_CSS ) { _CSS = global["CSS"] = {}; } // ---=== HAS CSS.supports support ===--- _CSS_supports = _CSS.supports; // ---=== HAS supportsCSS support ===--- if( !_CSS_supports && global["supportsCSS"] ) {// Opera 12.10 impl _CSS_supports = _CSS.supports = global["supportsCSS"].bind(global); if( global.__proto__ ) { delete global.__proto__["supportsCSS"]; } } if(typeof _CSS_supports === "function") { if( (function() { // Test for support [supports condition](http://www.w3.org/TR/css3-conditional/#supportscondition) try { _CSS_supports.call(_CSS, "(a:a)"); // SUCCESS return !(global = _CSS_supports = null);//return true } catch(e) {//FAIL //_CSS_supports = _CSS_supports.bind(global); } })() ) { // EXIT return;// Do not need anything to do. Exit from polyfill } } else { // ---=== NO CSS.supports support ===--- msie = "runtimeStyle" in document.documentElement; testElement = global["document"].createElement("_"); prevResultsCache = {}; _CSS_supports = function(ToCamel_replacer, testStyle, testElement, propertyName, propertyValue) { var name_and_value = propertyName + "\\/" + propertyValue; if( name_and_value in prevResultsCache ) { return prevResultsCache[name_and_value]; } /* TODO:: for IE < 9: _ = document.documentElement.appendChild(document.createElement("_")) _.currentStyle[propertyName] == propertyValue */ var __bind__RE_FIRST_LETTER = this , propertyName_CC = (propertyName + "").replace(__bind__RE_FIRST_LETTER, ToCamel_replacer) ; var result = propertyName && propertyValue && (propertyName_CC in testStyle); if( result ) { /*if( msie ) { try { testElement.style[propertyName] = propertyValue;// IE throw here, if unsupported this syntax testElement.style.cssText = ""; } catch(e) { result = false; } if( result ) { testElement.id = uuid; _document.body.appendChild(testElement); if( (prevPropValue = testElement.currentStyle[propertyName]) != propertyValue ) { _document.body.insertAdjacentHTML("beforeend", ""); if( !(propertyName in testElement.currentStyle) ) { partOfCompoundPropName } if( /\(|\s/.test(propertyValue) ) { currentPropValue = testElement.currentStyle[propertyName]; result = !!currentPropValue && currentPropValue != prevPropValue; } else { result = testElement.currentStyle[propertyName] == propertyValue; } //_document.documentElement.removeChild(document.getElementById(uuid + "br")); //_document.documentElement.removeChild(document.getElementById(uuid + "style")); } //_document.documentElement.removeChild(testElement); }*/ if( msie ) { if( /\(|\s/.test(propertyValue) ) { try { testStyle[propertyName_CC] = propertyValue; result = !!testStyle[propertyName_CC]; } catch(e) { result = false; } } else { testStyle.cssText = "display:none;height:0;width:0;visibility:hidden;position:absolute;position:fixed;" + propertyName + ":" + propertyValue; document.documentElement.appendChild(testElement); result = testElement.currentStyle[propertyName_CC] == propertyValue; document.documentElement.removeChild(testElement); } } else { testStyle.cssText = propertyName + ":" + propertyValue; result = testStyle[propertyName_CC]; result = result == propertyValue || result && testStyle.length > 0; } } testStyle.cssText = ""; return prevResultsCache[name_and_value] = result; }.bind( /(-)([a-z])/g // __bind__RE_FIRST_LETTER , function(a, b, c) { // ToCamel_replacer return c.toUpperCase() } , testElement.style // testStyle , msie ? testElement : null // testElement ); } // _supportsCondition("(a:b) or (display:block) or (display:none) and (display:block1)") function _supportsCondition(str) { if(!str) { _supportsCondition.throwSyntaxError(); } /** @enum {number} @const */ var RMAP = { NOT: 1 , AND: 2 , OR: 4 , PROPERTY: 8 , VALUE: 16 , GROUP_START: 32 , GROUP_END: 64 }; var resultsStack = [] , chr , result , valid = true , isNot , start , currentPropertyName , expectedPropertyValue , passThisGroup , nextRuleCanBe = RMAP.NOT | RMAP.GROUP_START | RMAP.PROPERTY , currentRule , i = -1 , newI , len = str.length ; resultsStack.push(void 0); function _getResult() { var l = resultsStack.length - 1; if( l < 0 )valid = false; return resultsStack[ l ]; } /** * @param {string=} val * @private */ function _setResult(val) { var l = resultsStack.length - 1; if( l < 0 )valid = false; result = resultsStack[ l ] = val; } /** * @param {string?} that * @param {string?} notThat * @param {number=} __i * @param {boolean=} cssValue * @return {(number|undefined)} * @private */ function _checkNext(that, notThat, __i, cssValue) { newI = __i || i; var chr , isQuited , isUrl , special ; if(cssValue) { newI--; } do { chr = str.charAt(++newI); if(cssValue) { special = chr && (isQuited || isUrl); if(chr == "'" || chr == "\"") { special = (isQuited = !isQuited); } else if(!isQuited) { if(!isUrl && chr == "(") { // TODO:: in Chrome: $0.style.background = "url('http://asd))')"; $0.style.background == "url(http://asd%29%29/)" isUrl = true; special = true; } else if(isUrl && chr == ")") { isUrl = false; special = true; } } } } while(special || (chr && (!that || chr != that) && (!notThat || chr == notThat))); if(that == null || chr == that) { return newI; } } while(++i < len) { if(currentRule == RMAP.NOT) { nextRuleCanBe = RMAP.GROUP_START | RMAP.PROPERTY; } else if(currentRule == RMAP.AND || currentRule == RMAP.OR || currentRule == RMAP.GROUP_START) { nextRuleCanBe = RMAP.GROUP_START | RMAP.PROPERTY | RMAP.NOT; } else if(currentRule == RMAP.GROUP_END) { nextRuleCanBe = RMAP.GROUP_START | RMAP.NOT | RMAP.OR | RMAP.AND; } else if(currentRule == RMAP.VALUE) { nextRuleCanBe = RMAP.GROUP_END | RMAP.GROUP_START | RMAP.NOT | RMAP.OR | RMAP.AND; } else if(currentRule == RMAP.PROPERTY) { nextRuleCanBe = RMAP.VALUE; } chr = str.charAt(i); if(nextRuleCanBe & RMAP.NOT && chr == "n" && str.substr(i, 3) == "not") { currentRule = RMAP.NOT; i += 2; } else if(nextRuleCanBe & RMAP.AND && chr == "a" && str.substr(i, 3) == "and") { currentRule = RMAP.AND; i += 2; } else if(nextRuleCanBe & RMAP.OR && chr == "o" && str.substr(i, 2) == "or") { currentRule = RMAP.OR; i++; } else if(nextRuleCanBe & RMAP.GROUP_START && chr == "(" && _checkNext("(", " ")) { currentRule = RMAP.GROUP_START; i = newI - 1; } else if(nextRuleCanBe & RMAP.GROUP_END && chr == ")" && resultsStack.length > 1) { currentRule = RMAP.GROUP_END; } else if(nextRuleCanBe & RMAP.PROPERTY && chr == "(" && (start = _checkNext(null, " ")) && _checkNext(":", null, start)) { currentRule = RMAP.PROPERTY; i = newI - 1; currentPropertyName = str.substr(start, i - start + 1).trim(); start = 0; expectedPropertyValue = null; continue; } else if(nextRuleCanBe & RMAP.VALUE && (start = _checkNext(null, " ")) && _checkNext(")", null, start, true)) { currentRule = RMAP.VALUE; i = newI; expectedPropertyValue = str.substr(start, i - start).trim(); start = 0; chr = " "; } else if(chr == " ") { continue; } else { currentRule = 0; } if(!valid || !chr || !(currentRule & nextRuleCanBe)) { _supportsCondition.throwSyntaxError(); } valid = true; if(currentRule == RMAP.OR) { if(result === false) { _setResult(); passThisGroup = false; } else if(result === true) { passThisGroup = true; } continue; } if( passThisGroup ) { continue; } result = _getResult(); if(currentRule == RMAP.NOT) { isNot = true; continue; } if(currentRule == RMAP.AND) { if(result === false) { passThisGroup = true; } else { _setResult(); } continue; } if(result === false && !(currentRule & (RMAP.GROUP_END | RMAP.GROUP_START))) { _setResult(result); continue; } if( currentRule == RMAP.GROUP_START ) { // Group start resultsStack.push(void 0); } else if( currentRule == RMAP.GROUP_END ) { // Group end passThisGroup = false; resultsStack.pop(); if( _getResult() !== void 0) { result = !!(result & _getResult()); } isNot = false; } else if( currentRule == RMAP.VALUE ) { // Property value _setResult(_CSS_supports(currentPropertyName, expectedPropertyValue)); if(isNot)result = !result; isNot = false; expectedPropertyValue = currentPropertyName = null; } _setResult(result); } if(!valid || result === void 0 || resultsStack.length > 1) { _supportsCondition.throwSyntaxError(); } return result; } _supportsCondition.throwSyntaxError = function() { throw new Error("SYNTAX_ERR"); }; /** * @expose */ _CSS.supports = function(a, b) { if(!arguments.length) { throw new Error("WRONG_ARGUMENTS_ERR");//TODO:: DOMException ? } if(arguments.length == 1) { return _supportsCondition(a); } return _CSS_supports(a, b); }; global = testElement = null;// no need this any more })();