├── LICENSE.txt ├── README.md ├── bower.json ├── examples ├── animating-natively-vs-jquery │ ├── jquery.html │ └── natively.html ├── assets │ ├── browserlogos │ │ ├── chrome.png │ │ ├── firefox.png │ │ ├── ie.png │ │ ├── ios.png │ │ ├── opera.png │ │ └── safari.png │ ├── common.css │ ├── picture-small.jpg │ └── picture.jpg ├── declarative-vs-programmatic │ ├── declarative.html │ ├── programmatic-via-jquery.html │ └── programmatic.html ├── index.html ├── loading-via-vanillajs-vs-contentloaded-vs-jquery │ ├── contentloaded.html │ ├── jquery.html │ └── vanilla.html ├── static-vs-animated │ ├── animated.html │ └── static.html └── stylesheet-embedded-vs-external │ ├── embedded.html │ ├── external.html │ └── filters.css └── lib ├── contentloaded.js ├── css-filters-polyfill-parser.js ├── css-filters-polyfill.js ├── cssParser.js └── htc ├── .htaccess ├── brightness.htc ├── drop-shadow.htc └── sepia.htc /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Christian Schepp Schaefer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Polyfilter - a CSS Filters Polyfill 2 | =================================== 3 | 4 | This polyfill takes the official CSS Filter Effects syntax, which spec you can find over [here](http://www.w3.org/TR/filter-effects/#FilterProperty) and translates it to the different equivalent techniques that the browsers know for those effects: 5 | 6 | * Prefixing for WebKit/Blink-based browsers 7 | * Translating into SVG-filters for Firefox 8 | * Translating into DirectX-filters for IE 6-9 9 | 10 | For instance, this allows you to assign a property like 11 | 12 | `filter: blur(10px);` 13 | 14 | in your stylesheets and the polyfill will take care that it works in as many browsers as possible. 15 | 16 | ## Supported Filters 17 | 18 | * grayscale* 19 | * sepia* 20 | * blur 21 | * invert* 22 | * brightness 23 | * drop-shadow 24 | 25 | Have a look at [this overview](http://schepp.github.io/CSS-Filters-Polyfill/examples/static-vs-animated/static.html). 26 | 27 | \* _the IEs only support 0% or 100% values_ 28 | 29 | ## Supported Browsers 30 | 31 | Currently the polyfill is able to support 32 | 33 | * Chrome 20+ (brightness filter 28+), 34 | * Opera 15+ 35 | * Safari 6+, 36 | * Yandex 1+, 37 | * Firefox 4+, 38 | * IE 6 - 9 on desktop (IE 6 & 7 slightly degraded), 39 | 40 | and 41 | 42 | * iOS 6+ Safari/Chrome/Webview 43 | * Chrome 28+ on Android, 44 | * Opera Mobile 14+, 45 | * Blackberry Browser 10+, 46 | * Firefox 4+ on Mobile, 47 | * IE on Windows Phone 7, which just supports grayscale. 48 | 49 | Also have a look at [http://caniuse.com/css-filters](http://caniuse.com/css-filters) 50 | 51 | ## Not supported Browsers 52 | 53 | * IE 10+, 54 | * older Presto-based Operas, 55 | * Opera Mini, 56 | * Android browser 57 | 58 | ### A word regarding IE 10+ 59 | 60 | Why is IE 6 - 9 supported, but not IE 10 or higher? Well, since Microsoft decided to switch sides and to now follow standards, they killed their old proprietary filters beginning with IE 10 which I rely on for emulation. 61 | 62 | Altough they did introduce SVG filters sadly those are limited to a usage inside SVGs. They cannot be applied to HTML-elements. 63 | 64 | Even triggering legacy mode does not help any more. So this is why we are left at the end with no hook/support at all in IE10+ :( 65 | 66 | ### And what about those Presto-based Opera? 67 | 68 | Older Operas with Presto engine are not supported, as they offer none of the hooks I used. 69 | 70 | ## Setup 71 | 72 | First create a ` 78 | ``` 79 | 80 | This is important both for the old IEs and the web worker script. 81 | 82 | Should you not want the document stylesheets to not get automatically parsed, like when your plan is to apply filters only via JavaScript, then you can additionally set a `polyfilter_skip_stylesheets` switch: 83 | 84 | ```html 85 | 89 | ``` 90 | 91 | Then you link `cssParser.js` and `css-filters-polyfill.js` from the polyfill library. 92 | 93 | ```html 94 | 95 | 96 | ``` 97 | 98 | In an ideal world you should minify and concatenate both of them together with your other JavaScript. If you don't want your page to get blocked by script-loading you put the scripts way down before the closing ``. This might lead to some flickering of the filter effects during loading. If you can't live with the short flickering, put the scripts in the `` of the page. Then it'll be gone, but your page will load slower. You decide. 99 | 100 | ## Usage 101 | 102 | ### Declarative assignment 103 | 104 | This polyfill supports filter declarations in embedded (` 14 | 15 | 16 | 17 |

Declarative Filter Definition

18 | 19 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/declarative-vs-programmatic/programmatic-via-jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Programmatic Definition via jQuery 6 | 7 | 8 | 9 |

Programmatic Definition via jQuery

10 | 11 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/declarative-vs-programmatic/programmatic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Programmatic Filter Definition 6 | 7 | 8 | 14 | 15 | 16 | 17 |

Programmatic Filter Definition

18 | 19 | 30 | 31 | 35 | 36 | 37 | 38 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Examples & Howtos 6 | 7 | 8 | 9 |

Examples & Howtos

10 | 11 |

List of Supported Filters

12 |

This polyfill supports the filters grayscale, sepia, blur, invert, brightness and drop-shadow.

13 | 14 |

Declarative or Programmatic Definition

15 |

You can apply filters in a declarative way via stylesheets, or you can set them programmatically via JavaScript or jQuery.

16 | 17 |

Embedded or External Stylesheets

18 |

If you specify filters in a stylesheet, this stylesheet may be embedded or referenced externally. Inline styles are currently not supported.

19 | 20 |

Animated Filters

21 |

Most of the time filters will be static. But if you need to animate them, you can do so using plain JavaScript, or you can fiddle it in jQuery. The latter one just making sense if you are after one of the easing-methods of jQuery.

22 | 23 |

Initialization of the Polyfill

24 |

For the polyfill to initialize as quickly as possible, it either attachs itself do jQuery's $(document).ready, or you can offer it the contentLoaded library. If none of both is available, the polyfill will use one of the native events that fits best.

25 | 26 |

Compatibility

27 |

Desktop

28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
Google ChromeOperaSafariMozilla FirefoxInternet Explorer
20+15+6+4+6-9 (no 10+)
48 | 49 |

Mobile

50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
Google ChromeOperaiOS SafariMozilla FirefoxInternet Explorer
28+14+6+4+just grayscale works in WP7
70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /examples/loading-via-vanillajs-vs-contentloaded-vs-jquery/contentloaded.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Initialization on DOM Ready using contentLoaded Library 6 | 7 | 8 | 13 | 14 | 15 | 16 |

Initialization on DOM Ready using contentLoaded Library

17 | 18 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/loading-via-vanillajs-vs-contentloaded-vs-jquery/jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Initialization on DOM Ready using jQuery 6 | 7 | 8 | 13 | 14 | 15 | 16 |

Initialization on DOM Ready using jQuery

17 | 18 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/loading-via-vanillajs-vs-contentloaded-vs-jquery/vanilla.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Initialization on DOM Ready / onload using vanilla JavaScript 6 | 7 | 8 | 13 | 14 | 15 | 16 |

Initialization on DOM Ready / onload using vanilla JavaScript

17 | 18 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /examples/static-vs-animated/animated.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Animated Filter 6 | 7 | 8 | 9 |

Animated Filter

10 | 11 | 22 | 23 | 24 | var value = 0, 25 | increment = 1 26 | elem = document.getElementById('filtered'); 27 | 28 | window.setInterval(function(){ 29 | value += increment; 30 | elem.style.polyfilter = 'blur(' + value + 'px)'; 31 | if(value >= 10 || value <= 0) increment *= -1; 32 | },100); 33 | 34 | 35 | 36 | 40 | 41 | 42 | 43 | 44 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /examples/static-vs-animated/static.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Static Filters 6 | 7 | 8 | 43 | 44 | 45 |

Static Filters

46 | 47 |

Mouseover to see :hover {filter: none;}

48 | 131 | 132 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /examples/stylesheet-embedded-vs-external/embedded.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Embedded Stylesheet 6 | 7 | 8 | 9 | 18 | 19 | 20 | 21 | 22 |

Embedded Stylesheet

23 | 24 |

Stylesheet is embedded via <style>

25 | 26 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/stylesheet-embedded-vs-external/external.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | External Stylesheet 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

External Stylesheet

15 | 16 |

Stylesheet is referenced via <link rel="stylesheet">

17 | 18 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/stylesheet-embedded-vs-external/filters.css: -------------------------------------------------------------------------------- 1 | /* Filter definitions */ 2 | .filter1 { 3 | filter: grayscale(1); 4 | } 5 | .filter:hover { 6 | filter: none; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /lib/contentloaded.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * contentloaded.js 3 | * 4 | * Author: Diego Perini (diego.perini at gmail.com) 5 | * Summary: cross-browser wrapper for DOMContentLoaded 6 | * Updated: 20101020 7 | * License: MIT 8 | * Version: 1.2 9 | * 10 | * URL: 11 | * http://javascript.nwbox.com/ContentLoaded/ 12 | * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE 13 | * 14 | */ 15 | 16 | // @win window reference 17 | // @fn function reference 18 | ;function contentLoaded(win, fn) { 19 | 20 | var done = false, top = true, 21 | 22 | doc = win.document, root = doc.documentElement, 23 | 24 | add = doc.addEventListener ? 'addEventListener' : 'attachEvent', 25 | rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent', 26 | pre = doc.addEventListener ? '' : 'on', 27 | 28 | init = function(e) { 29 | if (e.type == 'readystatechange' && doc.readyState != 'complete') return; 30 | (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false); 31 | if (!done && (done = true)) fn.call(win, e.type || e); 32 | }, 33 | 34 | poll = function() { 35 | try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; } 36 | init('poll'); 37 | }; 38 | 39 | if (doc.readyState == 'complete') fn.call(win, 'lazy'); 40 | else { 41 | if (doc.createEventObject && root.doScroll) { 42 | try { top = !win.frameElement; } catch(e) { } 43 | if (top) poll(); 44 | } 45 | doc[add](pre + 'DOMContentLoaded', init, false); 46 | doc[add](pre + 'readystatechange', init, false); 47 | win[add](pre + 'load', init, false); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /lib/css-filters-polyfill-parser.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * css-filters-polyfill-parser.js 3 | * 4 | * Author: Christian Schepp Schaefer 5 | * Summary: A polyfill for CSS filter effects 6 | * License: MIT 7 | * Version: 0.3.0 8 | * 9 | * URL: 10 | * https://github.com/Schepp/ 11 | * 12 | */ 13 | importScripts('cssParser.js'); 14 | 15 | var parser = new CSSParser(); 16 | 17 | self.addEventListener('message', function(e) { 18 | var sheet = { 19 | content: parser.parse(e.data.content, false, true), 20 | media: e.data.media 21 | } 22 | self.postMessage(sheet); 23 | }, false); -------------------------------------------------------------------------------- /lib/css-filters-polyfill.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * css-filters-polyfill.js 3 | * 4 | * Author: Christian Schepp Schaefer 5 | * Summary: A polyfill for CSS filter effects 6 | * License: MIT 7 | * Version: 0.3.0 8 | * 9 | * URL: 10 | * https://github.com/Schepp/ 11 | * 12 | */ 13 | ;(function(window){ 14 | var polyfilter = { 15 | // Detect if we are dealing with IE <= 9 16 | // http://james.padolsey.com/javascript/detect-_ie-in-js-using-conditional-comments/ 17 | _ie: (function(){ 18 | var undef, 19 | v = 3, 20 | div = document.createElement('div'), 21 | all = div.getElementsByTagName('i'); 22 | 23 | while( 24 | div.innerHTML = '', 25 | all[0] 26 | ); 27 | 28 | return v > 4 ? v : undef; 29 | }()), 30 | 31 | _svg_cache: {}, 32 | 33 | _create_svg_element: function(tagname,attributes){ 34 | var xmlns = 'http://www.w3.org/2000/svg'; 35 | var elem = document.createElementNS(xmlns,tagname); 36 | for(key in attributes){ 37 | elem.setAttributeNS(null,key,attributes[key]); 38 | } 39 | 40 | return elem; 41 | }, 42 | 43 | _create_svg: function(id,filterelements){ 44 | var xmlns = 'http://www.w3.org/2000/svg'; 45 | var svg = document.createElementNS(xmlns,'svg'); 46 | svg.setAttributeNS(null,'width','0'); 47 | svg.setAttributeNS(null,'height','0'); 48 | svg.setAttributeNS(null,'style','position:absolute'); 49 | 50 | var svg_filter = document.createElementNS(xmlns,'filter'); 51 | svg_filter.setAttributeNS(null,'id',id); 52 | svg.appendChild(svg_filter); 53 | 54 | for(var i = 0; i < filterelements.length; i++){ 55 | svg_filter.appendChild(filterelements[i]); 56 | } 57 | 58 | return svg; 59 | }, 60 | 61 | _pending_stylesheets: 0, 62 | 63 | _stylesheets: [], 64 | 65 | process_stylesheets: function(){ 66 | var xmlHttp = []; 67 | 68 | // Check if path to library is correct, do that 2 secs. after this to not disturb initial processing 69 | window.setTimeout(function(){ 70 | if (window.XMLHttpRequest) { 71 | var xmlHttpCheck = new XMLHttpRequest(); 72 | } else if (window.ActiveXObject) { 73 | var xmlHttpCheck = new ActiveXObject("Microsoft.XMLHTTP"); 74 | } 75 | xmlHttpCheck.open('GET', window.polyfilter_scriptpath + 'css-filters-polyfill-parser.js', true); 76 | xmlHttpCheck.onreadystatechange = function(){ 77 | if(xmlHttp.readyState == 4 && xmlHttp.status != 200){ 78 | alert('The configured path \r\rvar polyfilter_scriptpath = "' + window.polyfilter_scriptpath + '"\r\rseems wrong!\r\rConfigure the polyfill\'s correct absolute(!) script path before referencing the css-filters-polyfill.js, like so:\r\rvar polyfilter_scriptpath = "/js/css-filters-polyfill/";\r\rLeaving IE dead in the water is no option. You damn Mac user... ;)'); 79 | } 80 | }; 81 | try{ 82 | xmlHttpCheck.send(null); 83 | } catch(e){} 84 | },2000); 85 | 86 | // Is the polyfill supposed to automatically fetch and parse all stylesheets (polyfilter_skip_stylesheets: false)? (default) 87 | // Or do you rather prefer to apply filters only via JavaScript (polyfilter_skip_stylesheets: true)? 88 | // Configure via var polyfilter_skip_stylesheets = (true|false); before loading the polyfill script 89 | if(!window.polyfilter_skip_stylesheets){ 90 | var stylesheets = document.querySelectorAll ? document.querySelectorAll('style,link[rel="stylesheet"]') : document.getElementsByTagName('*'); 91 | 92 | for(var i = 0; i < stylesheets.length; i++){ 93 | (function(i){ 94 | switch(stylesheets[i].nodeName){ 95 | default: 96 | break; 97 | 98 | case 'STYLE': 99 | polyfilter._stylesheets.push({ 100 | media: stylesheets[i].media || 'all', 101 | content: stylesheets[i].innerHTML 102 | }); 103 | break; 104 | 105 | case 'LINK': 106 | if(stylesheets[i].rel === 'stylesheet'){ 107 | var index = polyfilter._stylesheets.length; 108 | 109 | polyfilter._stylesheets.push({ 110 | media: stylesheets[i].media || 'all' 111 | }); 112 | 113 | polyfilter._pending_stylesheets++; 114 | 115 | // Fetch external stylesheet 116 | var href = stylesheets[i].href; 117 | 118 | // Always fetch stylesheets to reflect possible changes 119 | try{ 120 | if(window.XMLHttpRequest) { 121 | var xmlHttp = new XMLHttpRequest(); 122 | } else if(window.ActiveXObject) { 123 | var xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); 124 | } 125 | xmlHttp.open('GET', href, true); 126 | xmlHttp.onreadystatechange = function(){ 127 | if(xmlHttp.readyState === 4){ 128 | if(xmlHttp.status === 0){ 129 | if(window.console) console.log('Could not fetch external CSS via HTTP-Request ' + href + '. Probably because of cross origin limitations. Try turning on CORS headers on the machine serving the stylesheets, like so: https://gist.github.com/Schepp/6338742'); 130 | if(!polyfilter._stylesheets[index].content){ 131 | polyfilter._pending_stylesheets--; 132 | polyfilter._stylesheets[index].content = xmlHttp.responseText; 133 | if(polyfilter._pending_stylesheets === 0){ 134 | polyfilter.process(); 135 | } 136 | } 137 | } else { 138 | if(!polyfilter._stylesheets[index].content){ 139 | polyfilter._pending_stylesheets--; 140 | polyfilter._stylesheets[index].content = xmlHttp.responseText; 141 | if(polyfilter._pending_stylesheets === 0){ 142 | polyfilter.process(); 143 | } 144 | } 145 | } 146 | } 147 | }; 148 | try{ 149 | xmlHttp.send(null); 150 | } catch(e){ 151 | if(window.console) console.log('Could not fetch external CSS via HTTP-Request ' + href + '. Are you maybe testing using the file://-protocol?'); 152 | if(!polyfilter._stylesheets[index].content){ 153 | polyfilter._pending_stylesheets--; 154 | if(polyfilter._pending_stylesheets === 0){ 155 | polyfilter.process(); 156 | } 157 | } 158 | } 159 | } catch(e){} 160 | } 161 | break; 162 | } 163 | })(i); 164 | } 165 | if(this._pending_stylesheets === 0){ 166 | this.process(); 167 | } 168 | } 169 | }, 170 | 171 | _processDeclarations: function(rule){ 172 | var newstyles = ''; 173 | for(var k in rule.declarations){ 174 | var declaration = rule.declarations[k]; 175 | 176 | if(declaration.property === 'filter'){ 177 | 178 | if(document.querySelectorAll){ 179 | var elems = document.querySelectorAll(rule.mSelectorText); 180 | for(var k = 0; k < elems.length; k++){ 181 | elems[k].style.polyfilterStore = declaration.valueText; 182 | } 183 | } 184 | 185 | var gluedvalues = declaration.valueText; 186 | var values = gluedvalues.split(/\)\s+/), 187 | properties = { 188 | filtersW3C: [], 189 | filtersWebKit: [], 190 | filtersSVG: [], 191 | filtersIE: [], 192 | behaviorsIE: [] 193 | }; 194 | 195 | for(idx in values){ 196 | var value = values[idx] + ')'; 197 | 198 | currentproperties = polyfilter.convert(value); 199 | 200 | for(key in currentproperties){ 201 | if(typeof properties[key] !== 'undefined'){ 202 | properties[key] = properties[key].concat(currentproperties[key]); 203 | } 204 | } 205 | } 206 | 207 | newstyles += rule.mSelectorText + '{'; 208 | if(properties['filtersW3C'].length > 0){ 209 | var filter = 210 | webkitFilter = 211 | mozFilter = 212 | oFilter = 213 | msFilter = 214 | properties['filtersW3C'].join(' '); 215 | 216 | if(properties['filtersWebKit'] && properties['filtersWebKit'].length > 0){ 217 | webkitFilter = properties['filtersWebKit'].join(' '); 218 | } 219 | 220 | if(typeof this._ie === 'undefined'){ 221 | newstyles += '-ms-filter:' + msFilter + ';'; 222 | } 223 | 224 | newstyles += '-webkit-filter:' + webkitFilter + ';'; 225 | newstyles += '-moz-filter:' + mozFilter + ';'; 226 | newstyles += '-o-filter:' + oFilter + ';'; 227 | } 228 | if(properties['filtersSVG'].length > 0){ 229 | if(properties['filtersSVG'][0] != 'none'){ 230 | var id = gluedvalues.replace(/[^a-z0-9]/g,''); 231 | 232 | if(typeof this._svg_cache[id] === 'undefined'){ 233 | this._svg_cache[id] = this._create_svg(id,properties['filtersSVG']); 234 | 235 | if(typeof XMLSerializer === 'undefined'){ 236 | document.body.appendChild(this._svg_cache[id]); 237 | } 238 | else { 239 | var s = new XMLSerializer(); 240 | var svgString = s.serializeToString(this._svg_cache[id]); 241 | if(svgString.search('SourceGraphic') != -1){ 242 | document.body.appendChild(this._svg_cache[id]); 243 | } 244 | } 245 | } 246 | 247 | if(typeof XMLSerializer === 'undefined'){ 248 | newstyles += 'filter: url(#' + id + ')'; 249 | } 250 | else { 251 | var s = new XMLSerializer(); 252 | var svgString = s.serializeToString(this._svg_cache[id]); 253 | 254 | if(svgString.search('SourceGraphic') != -1){ 255 | newstyles += 'filter: url(#' + id + ')'; 256 | } 257 | else { 258 | newstyles += 'filter: url(\'data:image/svg+xml;utf8,' + svgString + '#' + id + '\')'; 259 | } 260 | } 261 | } 262 | else { 263 | newstyles += 'filter: none;'; 264 | } 265 | } 266 | if(typeof this._ie !== 'undefined'){ 267 | if(properties['filtersIE'].length > 0){ 268 | var filtersIE = properties['filtersIE'].join(' '); 269 | 270 | newstyles += 'filter:' + filtersIE + ';'; 271 | } 272 | if(properties['behaviorsIE'].length > 0){ 273 | var behaviorsIE = properties['behaviorsIE'].join(' '); 274 | 275 | newstyles += 'behavior:' + behaviorsIE + ';'; 276 | } 277 | } 278 | newstyles += '}\r\n'; 279 | } 280 | } 281 | return newstyles; 282 | }, 283 | 284 | // Absolute path to the .htc-files 285 | // Configure via var polyfilter_scriptpath = "/js/css-filters-polyfill/"; before loading the polyfill script 286 | scriptpath: 287 | window.polyfilter_scriptpath ? window.polyfilter_scriptpath : (function(){ 288 | alert('Please configure the polyfill\'s absolute(!) script path before referencing the css-filters-polyfill.js, like so:\r\nvar polyfilter_scriptpath = "/js/css-filters-polyfill/";'); 289 | return './' 290 | })(), 291 | 292 | // process stylesheets 293 | process: function(){ 294 | if(!window.Worker){ 295 | var parser = new CSSParser(); 296 | } 297 | else { 298 | var worker = new Worker(this.scriptpath + 'css-filters-polyfill-parser.js'); 299 | worker.addEventListener('message', function(e) { 300 | polyfilter.create(e.data.content, e.data.media); 301 | }, false); 302 | } 303 | for(var i = 0; i < this._stylesheets.length; i++){ 304 | if(!window.Worker){ 305 | var sheet = parser.parse(this._stylesheets[i].content, false, true); 306 | this.create(sheet,this._stylesheets[i].media); 307 | } 308 | else { 309 | worker.postMessage(this._stylesheets[i]); 310 | } 311 | } 312 | }, 313 | 314 | // create filter stylesheets 315 | create: function(sheet,media){ 316 | var newstyles = ''; 317 | 318 | if(sheet !== null) for(var j in sheet.cssRules){ 319 | var rule = sheet.cssRules[j]; 320 | 321 | switch(rule.type){ 322 | default: 323 | break; 324 | 325 | case 1: 326 | newstyles += this._processDeclarations(rule); 327 | break; 328 | 329 | case 4: 330 | newstyles += '@media ' + rule.media.join(',') + '{'; 331 | for(var k in rule.cssRules){ 332 | var mediarule = rule.cssRules[k]; 333 | 334 | newstyles += this._processDeclarations(mediarule); 335 | } 336 | newstyles += '}'; 337 | break; 338 | } 339 | } 340 | var newstylesheet = document.createElement('style'); 341 | newstylesheet.setAttribute('media',media); 342 | 343 | if(typeof polyfilter._ie === 'undefined'){ 344 | newstylesheet.innerHTML = newstyles; 345 | document.getElementsByTagName('head')[0].appendChild(newstylesheet); 346 | } 347 | else { 348 | document.getElementsByTagName('head')[0].appendChild(newstylesheet); 349 | newstylesheet.styleSheet.cssText = newstyles; 350 | } 351 | }, 352 | 353 | init: function(){ 354 | if(Object.defineProperty){ 355 | Object.defineProperty(CSSStyleDeclaration.prototype, 'polyfilter', { 356 | get: function(){ 357 | return this.polyfilterStore; 358 | }, 359 | set: function(gluedvalues){ 360 | values = gluedvalues.split(/\)\s+/); 361 | var properties = { 362 | filtersW3C: [], 363 | filtersWebKit: [], 364 | filtersSVG: [], 365 | filtersIE: [], 366 | behaviorsIE: [] 367 | } 368 | 369 | for(idx in values){ 370 | var value = values[idx] + ')'; 371 | 372 | currentproperties = polyfilter.convert(value); 373 | 374 | for(key in currentproperties){ 375 | if(typeof properties[key] !== 'undefined'){ 376 | properties[key] = properties[key].concat(currentproperties[key]); 377 | } 378 | } 379 | } 380 | 381 | if(properties['filtersW3C'].length > 0){ 382 | if(typeof polyfilter._ie === 'undefined'){ 383 | this.msFilter = 384 | properties['filtersW3C'].join(' '); 385 | } 386 | 387 | this.webkitFilter = 388 | this.mozFilter = 389 | this.oFilter = 390 | properties['filtersW3C'].join(' '); 391 | } 392 | if(properties['filtersWebKit'].length > 0){ 393 | this.webkitFilter = properties['filtersWebKit'].join(' '); 394 | } 395 | if(properties['filtersSVG'].length > 0){ 396 | if(properties['filtersSVG'][0] != 'none'){ 397 | var id = gluedvalues.replace(/[^a-z0-9]/g,''); 398 | 399 | if(typeof polyfilter._svg_cache[id] === 'undefined'){ 400 | polyfilter._svg_cache[id] = polyfilter._create_svg(id,properties['filtersSVG']); 401 | 402 | if(typeof XMLSerializer === 'undefined'){ 403 | document.body.appendChild(polyfilter._svg_cache[id]); 404 | } 405 | else { 406 | var s = new XMLSerializer(); 407 | var svgString = s.serializeToString(polyfilter._svg_cache[id]); 408 | if(svgString.search('SourceGraphic') != -1){ 409 | document.body.appendChild(polyfilter._svg_cache[id]); 410 | } 411 | } 412 | } 413 | 414 | if(typeof XMLSerializer === 'undefined'){ 415 | this.filter = 'url(#' + id + ')'; 416 | } 417 | else { 418 | var s = new XMLSerializer(); 419 | var svgString = s.serializeToString(polyfilter._svg_cache[id]); 420 | if(svgString.search('SourceGraphic') != -1){ 421 | this.filter = 'url(#' + id + ')'; 422 | } 423 | else { 424 | this.filter = 'url(\'data:image/svg+xml;utf8,' + svgString + '#' + id + '\')'; 425 | } 426 | } 427 | } 428 | else { 429 | this.filter = 'none'; 430 | } 431 | } 432 | if(typeof polyfilter._ie !== 'undefined'){ 433 | if(properties['filtersIE'].length > 0){ 434 | this.filter = 435 | properties['filtersIE'].join(' '); 436 | } 437 | else { 438 | this.filter = ''; 439 | } 440 | if(properties['behaviorsIE'].length > 0){ 441 | this.behavior = 442 | properties['behaviorsIE'].join(' '); 443 | } 444 | else { 445 | this.behavior = ''; 446 | } 447 | } 448 | this.polyfilterStore = gluedvalues; 449 | } 450 | }); 451 | } 452 | }, 453 | 454 | convert: function(value){ 455 | // None 456 | var fmatch = value.match(/none/i); 457 | if(fmatch !== null){ 458 | var properties = this.filters.none(); 459 | } 460 | // Grayscale 461 | var fmatch = value.match(/(grayscale)\(([0-9\.]+)\)/i); 462 | if(fmatch !== null){ 463 | var amount = parseFloat(fmatch[2],10), 464 | properties = this.filters.grayscale(amount); 465 | } 466 | // Sepia 467 | var fmatch = value.match(/(sepia)\(([0-9\.]+)\)/i); 468 | if(fmatch !== null){ 469 | var amount = parseFloat(fmatch[2],10), 470 | properties = this.filters.sepia(amount); 471 | } 472 | // Blur 473 | var fmatch = value.match(/(blur)\(([0-9]+)[px]*\)/i); 474 | if(fmatch !== null){ 475 | var amount = parseInt(fmatch[2],10), 476 | properties = this.filters.blur(amount); 477 | } 478 | // Invert 479 | var fmatch = value.match(/(invert)\(([0-9\.]+)\)/i); 480 | if(fmatch !== null){ 481 | var amount = parseFloat(fmatch[2],10), 482 | properties = this.filters.invert(amount); 483 | } 484 | // Brightness 485 | var fmatch = value.match(/(brightness)\(([0-9\.]+)%\)/i); 486 | if(fmatch !== null){ 487 | var amount = parseFloat(fmatch[2],10), 488 | properties = this.filters.brightness(amount); 489 | } 490 | // Saturate 491 | var fmatch = value.match(/(saturate)\(([0-9\.]+)%\)/i); 492 | if(fmatch !== null){ 493 | var amount = parseFloat(fmatch[2],10), 494 | properties = this.filters.saturate(amount); 495 | } 496 | // Hue Rotate 497 | var fmatch = value.match(/(hue-rotate)\(([0-9\.]+)deg\)/i); 498 | if(fmatch !== null){ 499 | var amount = parseFloat(fmatch[2],10), 500 | properties = this.filters.hueRotate(amount); 501 | } 502 | // Drop Shadow 503 | var fmatch = value.match(/(drop\-shadow)\(([0-9]+)[px]*\s+([0-9]+)[px]*\s+([0-9]+)[px]*\s+([#0-9]+)\)/i); 504 | if(fmatch !== null){ 505 | var offsetX = parseInt(fmatch[2],10), 506 | offsetY = parseInt(fmatch[3],10), 507 | radius = parseInt(fmatch[4],10), 508 | color = fmatch[5], 509 | properties = this.filters.dropShadow(offsetX,offsetY,radius,color); 510 | } 511 | 512 | return properties; 513 | }, 514 | 515 | // EFFECTS SECTION ------------------------------------------------------------------------------------------------------------- 516 | 517 | filters: { 518 | // None 519 | none: function(){ 520 | var properties = {}; 521 | 522 | if(typeof polyfilter._ie === 'undefined'){ 523 | // Proposed spec 524 | properties['filtersW3C'] = ['none']; 525 | 526 | // Firefox 527 | properties['filtersSVG'] = ['none']; 528 | } 529 | else { 530 | // IE 531 | properties['filtersIE'] = ['none']; 532 | } 533 | 534 | return properties; 535 | }, 536 | 537 | // Grayscale 538 | grayscale: function(amount){ 539 | amount = amount || 0; 540 | 541 | var properties = {}; 542 | 543 | if(typeof polyfilter._ie === 'undefined'){ 544 | // Proposed spec 545 | properties['filtersW3C'] = ['grayscale(' + amount + ')']; 546 | 547 | // Firefox 548 | // https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html 549 | var svg_fe1 = polyfilter._create_svg_element('feColorMatrix',{ 550 | type: 'matrix', 551 | values: (0.2126 + 0.7874 * (1 - amount)) + ' ' 552 | + (0.7152 - 0.7152 * (1 - amount)) + ' ' 553 | + (0.0722 - 0.0722 * (1 - amount)) + ' 0 0 ' 554 | + (0.2126 - 0.2126 * (1 - amount)) + ' ' 555 | + (0.7152 + 0.2848 * (1 - amount)) + ' ' 556 | + (0.0722 - 0.0722 * (1 - amount)) + ' 0 0 ' 557 | + (0.2126 - 0.2126 * (1 - amount)) + ' ' 558 | + (0.7152 - 0.7152 * (1 - amount)) + ' ' 559 | + (0.0722 + 0.9278 * (1 - amount)) + ' 0 0 0 0 0 1 0' 560 | }); 561 | properties['filtersSVG'] = [svg_fe1]; 562 | } 563 | else { 564 | // IE 565 | properties['filtersIE'] = amount >= 0.5 ? ['gray'] : []; 566 | } 567 | 568 | return properties; 569 | }, 570 | 571 | // Sepia 572 | sepia: function(amount){ 573 | amount = amount || 0; 574 | 575 | var properties = {}; 576 | 577 | if(typeof polyfilter._ie === 'undefined'){ 578 | 579 | // Proposed spec 580 | properties['filtersW3C'] = ['sepia(' + amount + ')']; 581 | 582 | // Firefox 583 | // https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html 584 | var svg_fe1 = polyfilter._create_svg_element('feColorMatrix',{ 585 | type: 'matrix', 586 | values: (0.393 + 0.607 * (1 - amount)) + ' ' 587 | + (0.769 - 0.769 * (1 - amount)) + ' ' 588 | + (0.189 - 0.189 * (1 - amount)) + ' 0 0 ' 589 | + (0.349 - 0.349 * (1 - amount)) + ' ' 590 | + (0.686 + 0.314 * (1 - amount)) + ' ' 591 | + (0.168 - 0.168 * (1 - amount)) + ' 0 0 ' 592 | + (0.272 - 0.272 * (1 - amount)) + ' ' 593 | + (0.534 - 0.534 * (1 - amount)) + ' ' 594 | + (0.131 + 0.869 * (1 - amount)) + ' 0 0 0 0 0 1 0' 595 | }); 596 | properties['filtersSVG'] = [svg_fe1]; 597 | } 598 | else { 599 | // IE 600 | properties['filtersIE'] = amount >= 0.5 ? ['gray','progid:DXImageTransform.Microsoft.Light()'] : []; 601 | properties['behaviorsIE'] = amount >= 0.5 ? ['url("' + polyfilter.scriptpath + 'htc/sepia.htc")'] : []; 602 | } 603 | 604 | return properties; 605 | }, 606 | 607 | // Blur 608 | blur: function(amount){ 609 | amount = Math.round(amount) || 0; 610 | 611 | var properties = {}; 612 | 613 | if(typeof polyfilter._ie === 'undefined'){ 614 | // Proposed spec 615 | properties['filtersW3C'] = ['blur(' + amount + 'px)']; 616 | 617 | // Firefox 618 | // https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html 619 | var svg_fe1 = polyfilter._create_svg_element('feGaussianBlur',{ 620 | 'in': 'SourceGraphic', 621 | stdDeviation: amount 622 | }); 623 | properties['filtersSVG'] = [svg_fe1]; 624 | } 625 | else { 626 | // IE 627 | properties['filtersIE'] = ['progid:DXImageTransform.Microsoft.Blur(pixelradius=' + amount + ')']; 628 | } 629 | 630 | return properties; 631 | }, 632 | 633 | // Invert 634 | invert: function(amount){ 635 | amount = amount || 0; 636 | 637 | var properties = {}; 638 | 639 | if(typeof polyfilter._ie === 'undefined'){ 640 | // Proposed spec 641 | properties['filtersW3C'] = ['invert(' + amount + ')']; 642 | 643 | // Firefox 644 | // https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html 645 | var svg_fe1 = polyfilter._create_svg_element('feComponentTransfer',{ 646 | 'color-interpolation-filters': 'sRGB' 647 | }); 648 | var svg_fe1sub = polyfilter._create_svg_element('feFuncR',{ 649 | type: 'table', 650 | tableValues: amount + ' ' + (1 - amount) 651 | }); 652 | svg_fe1.appendChild(svg_fe1sub); 653 | var svg_fe1sub = polyfilter._create_svg_element('feFuncG',{ 654 | type: 'table', 655 | tableValues: amount + ' ' + (1 - amount) 656 | }); 657 | svg_fe1.appendChild(svg_fe1sub); 658 | var svg_fe1sub = polyfilter._create_svg_element('feFuncB',{ 659 | type: 'table', 660 | tableValues: amount + ' ' + (1 - amount) 661 | }); 662 | svg_fe1.appendChild(svg_fe1sub); 663 | properties['filtersSVG'] = [svg_fe1]; 664 | } 665 | else { 666 | // IE 667 | properties['filtersIE'] = amount >= 0.5 ? ['invert'] : []; 668 | } 669 | 670 | return properties; 671 | }, 672 | 673 | // Brightness 674 | brightness: function(amount){ 675 | amount = amount || 0; 676 | 677 | var properties = {}; 678 | 679 | if(typeof polyfilter._ie === 'undefined'){ 680 | // Proposed spec 681 | properties['filtersW3C'] = ['brightness(' + amount + '%)']; 682 | 683 | // WebKit "specialty" 684 | // properties['filtersWebKit'] = ['brightness(' + (amount - 100) + '%)']; 685 | 686 | // Firefox 687 | // https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html 688 | var svg_fe1 = polyfilter._create_svg_element('feComponentTransfer',{ 689 | 'color-interpolation-filters': 'sRGB' 690 | }); 691 | var svg_fe1sub = polyfilter._create_svg_element('feFuncR',{ 692 | type: 'linear', 693 | slope: amount / 100 694 | }); 695 | svg_fe1.appendChild(svg_fe1sub); 696 | var svg_fe1sub = polyfilter._create_svg_element('feFuncG',{ 697 | type: 'linear', 698 | slope: amount / 100 699 | }); 700 | svg_fe1.appendChild(svg_fe1sub); 701 | var svg_fe1sub = polyfilter._create_svg_element('feFuncB',{ 702 | type: 'linear', 703 | slope: amount / 100 704 | }); 705 | svg_fe1.appendChild(svg_fe1sub); 706 | properties['filtersSVG'] = [svg_fe1]; 707 | } 708 | else { 709 | // IE 710 | properties['filtersIE'] = ['progid:DXImageTransform.Microsoft.Light()']; 711 | properties['behaviorsIE'] = ['url("' + polyfilter.scriptpath + 'htc/brightness.htc")']; 712 | } 713 | 714 | return properties; 715 | }, 716 | 717 | // Saturate 718 | saturate: function(amount){ 719 | amount = amount || 0; 720 | 721 | var properties = {}; 722 | 723 | if(typeof polyfilter._ie === 'undefined'){ 724 | // Proposed spec 725 | properties['filtersW3C'] = ['saturate(' + amount + '%)']; 726 | 727 | // Firefox 728 | // https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html 729 | var svg_fe1 = polyfilter._create_svg_element('feColorMatrix',{ 730 | type: 'saturate', 731 | values: amount / 100 732 | }); 733 | properties['filtersSVG'] = [svg_fe1]; 734 | } 735 | 736 | return properties; 737 | }, 738 | 739 | // Hue Rotate 740 | hueRotate: function(amount){ 741 | amount = amount || 0; 742 | 743 | var properties = {}; 744 | 745 | if(typeof polyfilter._ie === 'undefined'){ 746 | // Proposed spec 747 | properties['filtersW3C'] = ['hue-rotate(' + amount + 'deg)']; 748 | 749 | // Firefox 750 | // https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html 751 | var svg_fe1 = polyfilter._create_svg_element('feColorMatrix',{ 752 | type: 'hueRotate', 753 | values: amount 754 | }); 755 | properties['filtersSVG'] = [svg_fe1]; 756 | } 757 | 758 | return properties; 759 | }, 760 | 761 | // Drop Shadow 762 | dropShadow: function(offsetX,offsetY,radius,color){ 763 | offsetX = Math.round(offsetX) || 0; 764 | offsetY = Math.round(offsetY) || 0; 765 | radius = Math.round(radius) || 0; 766 | color = color || '#000000'; 767 | 768 | var properties = {}; 769 | 770 | if(typeof polyfilter._ie === 'undefined'){ 771 | // Proposed spec 772 | properties['filtersW3C'] = ['drop-shadow(' + offsetX + 'px ' + offsetY + 'px ' + radius + 'px ' + color + ')']; 773 | 774 | // Firefox 775 | // https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html 776 | var svg_fe1 = polyfilter._create_svg_element('feGaussianBlur',{ 777 | 'in': 'SourceAlpha', 778 | stdDeviation: radius 779 | }); 780 | var svg_fe2 = polyfilter._create_svg_element('feOffset',{ 781 | dx: offsetX + 1, 782 | dy: offsetY + 1, 783 | result: 'offsetblur' 784 | }); 785 | var svg_fe3 = polyfilter._create_svg_element('feFlood',{ 786 | 'flood-color': color 787 | }); 788 | var svg_fe4 = polyfilter._create_svg_element('feComposite',{ 789 | in2: 'offsetblur', 790 | operator: 'in' 791 | }); 792 | var svg_fe5 = polyfilter._create_svg_element('feMerge',{}); 793 | var svg_fe5sub = polyfilter._create_svg_element('feMergeNode',{}); 794 | svg_fe5.appendChild(svg_fe5sub); 795 | var svg_fe5sub = polyfilter._create_svg_element('feMergeNode',{ 796 | 'in': 'SourceGraphic' 797 | }); 798 | svg_fe5.appendChild(svg_fe5sub); 799 | properties['filtersSVG'] = [svg_fe1,svg_fe2,svg_fe3,svg_fe4,svg_fe5]; 800 | } 801 | else { 802 | // IE 803 | properties['filtersIE'] = ['progid:DXImageTransform.Microsoft.Glow(color=' + color + ',strength=0)','progid:DXImageTransform.Microsoft.Shadow(color=' + color + ',strength=0)']; 804 | properties['behaviorsIE'] = ['url("' + polyfilter.scriptpath + 'htc/drop-shadow.htc")']; 805 | } 806 | 807 | return properties; 808 | } 809 | } 810 | } 811 | 812 | // Inialize, either via jQuery... 813 | if(window.jQuery){ 814 | window.jQuery(document).ready(function(e) { 815 | polyfilter.process_stylesheets(); 816 | }); 817 | } 818 | // or via contentLoaded... 819 | else if(window.contentLoaded){ 820 | contentLoaded(window,function(){ 821 | polyfilter.process_stylesheets(); 822 | }); 823 | } 824 | // or on DOM ready / load 825 | else { 826 | if(window.addEventListener) // W3C standard 827 | { 828 | document.addEventListener('DOMContentLoaded', function(){ 829 | polyfilter.process_stylesheets(); 830 | }, false); 831 | } 832 | else if(window.attachEvent) // Microsoft 833 | { 834 | window.attachEvent('onload', function(){ 835 | polyfilter.process_stylesheets(); 836 | }); 837 | } 838 | } 839 | 840 | // Install style setters and getters 841 | polyfilter.init(); 842 | })(window); -------------------------------------------------------------------------------- /lib/htc/.htaccess: -------------------------------------------------------------------------------- 1 | #We make sure that .htc files are served with the proper MIME type, which is critical for XP SP2 2 | AddType text/x-component .htc 3 | 4 | #We add logn term caching so that the IEs experience less of a slowdown 5 | 6 | ExpiresActive on 7 | ExpiresDefault "access plus 1 month" 8 | -------------------------------------------------------------------------------- /lib/htc/brightness.htc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 60 | -------------------------------------------------------------------------------- /lib/htc/drop-shadow.htc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 110 | -------------------------------------------------------------------------------- /lib/htc/sepia.htc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | --------------------------------------------------------------------------------