├── .gitignore ├── LICENSE.txt ├── README.md ├── bower.json ├── build.xml ├── cssua.js ├── cssua.min.js ├── faq.html ├── favicon.ico ├── index.html ├── lib ├── closure │ ├── COPYING │ ├── README │ ├── compiler.jar │ ├── docs.url │ └── download.url ├── html5shiv │ └── html5.js ├── jslint │ ├── download.url │ ├── jslint.js │ └── rhino.js ├── qunit │ ├── MIT-LICENSE.txt │ ├── download.url │ ├── qunit.css │ └── qunit.js └── rhino │ ├── LICENSE.txt │ ├── download.url │ └── js.jar ├── package.json └── test ├── demo.html ├── encodeTests.js ├── formatTests.js ├── lint.js ├── parseTests.js ├── styles ├── browsers.css ├── browsers │ ├── BrowserStack_128.png │ ├── Camino_128.png │ ├── Chrome11_128.png │ ├── Chrome_128.png │ ├── Edge_128.png │ ├── Epiphany_64.png │ ├── Firebird2003_128.png │ ├── Firefox2004_128.png │ ├── Firefox2005_128.png │ ├── Firefox2009_128.png │ ├── Firefox2013_128.png │ ├── Flock_128.png │ ├── Fluid_128.png │ ├── Galeon_128.png │ ├── Gecko_128.png │ ├── IE4-Mac_128.png │ ├── IE5-Mac_128.png │ ├── IE5_48.png │ ├── IE6_128.png │ ├── IE7_128.png │ ├── IE9_128.png │ ├── K-Meleon_82.png │ ├── Konqueror_128.png │ ├── Mozilla_128.png │ ├── Netscape8_128.png │ ├── Netscape9_128.png │ ├── Netscape_128.png │ ├── OmniWeb_128.png │ ├── Opera_128.png │ ├── PhantomJS_128.png │ ├── Presto_128.png │ ├── README.txt │ ├── Safari_128.png │ ├── SeaMonkey_128.png │ ├── Shiira_128.png │ ├── Silk_128.png │ ├── Swift_128.png │ ├── Unknown_128.png │ ├── Vivaldi_128.png │ ├── WebKit_128.png │ ├── iCab3_128.png │ └── iCab4_128.png ├── fonts │ ├── League_Gothic-webfont.eot │ ├── League_Gothic-webfont.svg │ ├── League_Gothic-webfont.ttf │ ├── League_Gothic-webfont.woff │ └── SIL Open Font License 1.1.txt ├── main.css ├── syntax.css ├── validation.css ├── validation.png └── zebra.css └── unit.html /.gitignore: -------------------------------------------------------------------------------- 1 | \.DS_Store 2 | \.classpath 3 | \.project 4 | \.settings 5 | \.iml 6 | \.ipr 7 | \.iws 8 | *\.bat 9 | *\.psd 10 | *\.ai 11 | *\.sh 12 | ga\.js 13 | antixss\.js 14 | cssua\.demo\.js 15 | test/styles/browsers/originals/ 16 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2006-2010 Stephen M. McKamey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [.css{user:agent;}][1] 2 | Distributed under the terms of [The MIT license][2]. 3 | 4 | ## Special CSS Classes... 5 | 6 | Adding cssua.js to the page preps the document with special CSS classes which enable targeting of browsers with varying degrees of precision. 7 | 8 | 9 | 13 | ... 14 | 15 | 16 | ## UserAgent map... 17 | 18 | UserAgent-sniffing is regarded as something to be used sparingly, but pragmatic developers know it is 19 | sometimes the simplest approach to fixing a particular browser's quirk. When it is, cssua.js produces 20 | a helper object as a side-effect which makes user-agent-siffing easier and more consistent. 21 | 22 | An object map is also built which allows you to test the user agent from your script in a simplified 23 | manner that doesn't require string parsing. For example, this object is effectively produced: 24 | 25 | cssua.userAgent = cssua.ua = { 26 | chrome: "25.0.1364.172", 27 | desktop: "macintosh", 28 | mac_os_x: "10.8.3", 29 | webkit: "537.22" 30 | }; 31 | 32 | ## Examples 33 | 34 | This makes applying slight layout differences a snap: 35 | 36 | /* CssUserAgent lets you target specific browsers without resorting CSS hacks */ 37 | .foo 38 | { 39 | background-image: url(foo.png); 40 | background-repeat: no-repeat; 41 | background-position: left top; 42 | } 43 | 44 | .ua-ie-5 .foo, 45 | .ua-ie-6 .foo 46 | { 47 | /* IE versions < 7.0 don't fully support transparent 24-bit PNGs */ 48 | background-image: url(foo.gif); 49 | } 50 | 51 | Testing for older Internet Explorer has never been easier than 52 | 53 | if (cssua.userAgent.ie < 8) { /* proof of Pareto principle here */ } 54 | 55 | Or testing if is a mobile browser: 56 | 57 | if (cssua.userAgent.mobile) { /* consider your audience */ } 58 | 59 | Remember to convert a version `String` to a `Number` with `parseFloat` if the value isn't a simple number, 60 | e.g., it has multiple decimal points or trailing letters. Otherwise, JavaScript may interpret 61 | the value as `NaN` (not-a-number): 62 | 63 | if (parseFloat(cssua.ua.chrome) > 101) { /* enter the Matrix */ } 64 | 65 | ## Mobile browser detection 66 | 67 | CssUserAgent helps detect the increasingly ambiguous category of mobile browsers. 68 | cssua.js adds specific classes when it detects mobile browsers: 69 | 70 | ... 71 | 72 | ## No Browser Hacks... 73 | 74 | This technique also avoids *all* CSS hacks. It allows you to target the browser rendering engine 75 | (e.g. "ua-gecko"), or a specific browser (e.g. "ua-firefox"). The version can be targeted at the major version 76 | number (e.g. "ua-ie-5" includes 5.0, 5.5) or minor (e.g. "ua-ie-5-0" includes only 5.0) all the way down 77 | to a very specific case (e.g. "ua-chrome-8-0-552-224"). 78 | 79 | ## Future UserAgents... 80 | 81 | This script understands the common structures of user agent strings enabling future user agent strings to simply 82 | work without changes. For example, when the early beta builds of Google Chrome were first released, 83 | it just worked. 84 | 85 | ---- 86 | 87 | Tested with 88 | [![BrowserStack](./test/styles/browsers/BrowserStack_128.png)](http://www.browserstack.com) 89 | 90 | ---- 91 | 92 | Copyright (c) 2006-2013 Stephen M. McKamey 93 | 94 | [1]: http://cssuseragent.org 95 | [2]: https://raw.githubusercontent.com/mckamey/cssuseragent/master/LICENSE.txt 96 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cssua.js", 3 | "version": "2.1.31", 4 | "description": "CssUserAgent: user-agent specific CSS support", 5 | "main": "cssua.js", 6 | "keywords": [ 7 | "cssua", 8 | "cssseragent", 9 | "css", 10 | "user-agent", 11 | "userAgent", 12 | "browser", 13 | "detection", 14 | "styles", 15 | "quirks" 16 | ], 17 | "authors": [ 18 | "Stephen M. McKamey (http://mck.me)" 19 | ], 20 | "license": "MIT", 21 | "homepage": "http://cssuseragent.org", 22 | "ignore": [ 23 | "./!(cssua.js)" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /cssua.js: -------------------------------------------------------------------------------- 1 | /** 2 | * CssUserAgent (cssua.js) v2.1.31 3 | * http://cssuseragent.org 4 | * 5 | * Copyright (c)2006-2015 Stephen M. McKamey. 6 | * Licensed under The MIT License. 7 | */ 8 | /*jshint smarttabs:true, regexp:false, browser:true */ 9 | 10 | /** 11 | * @type {Object} 12 | */ 13 | var cssua = ( 14 | 15 | /** 16 | * @param html {Object} root DOM element 17 | * @param userAgent {string} browser userAgent string 18 | * @return {Object} 19 | */ 20 | function(html, userAgent, sa) { 21 | 'use strict'; 22 | 23 | /** 24 | * @const 25 | * @type {string} 26 | */ 27 | var PREFIX = ' ua-'; 28 | 29 | /** 30 | * @const 31 | * @type {RegExp} 32 | */ 33 | var R_Platform = /\s*([\-\w ]+)[\s\/\:]([\d_]+\b(?:[\-\._\/]\w+)*)/; 34 | 35 | /** 36 | * @const 37 | * @type {RegExp} 38 | */ 39 | var R_Version = /([\w\-\.]+[\s\/][v]?[\d_]+\b(?:[\-\._\/]\w+)*)/g; 40 | 41 | /** 42 | * @const 43 | * @type {RegExp} 44 | */ 45 | var R_BlackBerry = /\b(?:(blackberry\w*|bb10)|(rim tablet os))(?:\/(\d+\.\d+(?:\.\w+)*))?/; 46 | 47 | /** 48 | * @const 49 | * @type {RegExp} 50 | */ 51 | var R_Silk = /\bsilk-accelerated=true\b/; 52 | 53 | /** 54 | * @const 55 | * @type {RegExp} 56 | */ 57 | var R_FluidApp = /\bfluidapp\b/; 58 | 59 | /** 60 | * @const 61 | * @type {RegExp} 62 | */ 63 | var R_desktop = /(\bwindows\b|\bmacintosh\b|\blinux\b|\bunix\b)/; 64 | 65 | /** 66 | * @const 67 | * @type {RegExp} 68 | */ 69 | var R_mobile = /(\bandroid\b|\bipad\b|\bipod\b|\bwindows phone\b|\bwpdesktop\b|\bxblwp7\b|\bzunewp7\b|\bwindows ce\b|\bblackberry\w*|\bbb10\b|\brim tablet os\b|\bmeego|\bwebos\b|\bpalm|\bsymbian|\bj2me\b|\bdocomo\b|\bpda\b|\bchtml\b|\bmidp\b|\bcldc\b|\w*?mobile\w*?|\w*?phone\w*?)/; 70 | 71 | /** 72 | * @const 73 | * @type {RegExp} 74 | */ 75 | var R_game = /(\bxbox\b|\bplaystation\b|\bnintendo\s+\w+)/; 76 | 77 | /** 78 | * The root CssUserAgent 79 | * @type {Object} 80 | */ 81 | var cssua = { 82 | 83 | parse: 84 | /** 85 | * @param uaStr {string} 86 | * @return {Object} 87 | */ 88 | function(uaStr, sa) { 89 | 90 | /** 91 | * @type {Object} 92 | */ 93 | var ua = {}; 94 | if (sa) { 95 | ua.standalone = sa; 96 | } 97 | 98 | uaStr = (''+uaStr).toLowerCase(); 99 | if (!uaStr) { 100 | return ua; 101 | } 102 | 103 | var i, count, raw = uaStr.split(/[()]/); 104 | for (var j=0, rawCount=raw.length; j 0) { 289 | // loop through chopping last '-' to end off 290 | // concat result onto return string 291 | css += PREFIX+b+'-'+v.substring(0, i); 292 | i = v.indexOf('-', i+1); 293 | } 294 | css += PREFIX+b+'-'+v; 295 | } 296 | return css; 297 | } 298 | 299 | /** 300 | * @type {string} 301 | */ 302 | var uaCss = ''; 303 | for (var b in ua) { 304 | if (b && ua.hasOwnProperty(b)) { 305 | uaCss += format(b, ua[b]); 306 | } 307 | } 308 | 309 | // user-agent classNames 310 | return uaCss; 311 | }, 312 | 313 | encode: 314 | /** 315 | * Encodes parsed userAgent object as a compact URI-Encoded key-value collection 316 | * @param ua {Object} 317 | * @return {string} 318 | */ 319 | function(ua) { 320 | var query = ''; 321 | for (var b in ua) { 322 | if (b && ua.hasOwnProperty(b)) { 323 | if (query) { 324 | query += '&'; 325 | } 326 | query += encodeURIComponent(b)+'='+encodeURIComponent(ua[b]); 327 | } 328 | } 329 | return query; 330 | } 331 | }; 332 | 333 | /** 334 | * @const 335 | * @type {Object} 336 | */ 337 | cssua.userAgent = cssua.ua = cssua.parse(userAgent, sa); 338 | 339 | /** 340 | * @const 341 | * @type {string} 342 | */ 343 | var ua = cssua.format(cssua.ua)+' js'; 344 | 345 | // append CSS classes to HTML node 346 | if (html.className) { 347 | html.className = html.className.replace(/\bno-js\b/g, '') + ua; 348 | 349 | } else { 350 | html.className = ua.substr(1); 351 | } 352 | 353 | return cssua; 354 | 355 | })(document.documentElement, navigator.userAgent, navigator.standalone); 356 | -------------------------------------------------------------------------------- /cssua.min.js: -------------------------------------------------------------------------------- 1 | var cssua=function(n,l,p){var q=/\s*([\-\w ]+)[\s\/\:]([\d_]+\b(?:[\-\._\/]\w+)*)/,r=/([\w\-\.]+[\s\/][v]?[\d_]+\b(?:[\-\._\/]\w+)*)/g,s=/\b(?:(blackberry\w*|bb10)|(rim tablet os))(?:\/(\d+\.\d+(?:\.\w+)*))?/,t=/\bsilk-accelerated=true\b/,u=/\bfluidapp\b/,v=/(\bwindows\b|\bmacintosh\b|\blinux\b|\bunix\b)/,w=/(\bandroid\b|\bipad\b|\bipod\b|\bwindows phone\b|\bwpdesktop\b|\bxblwp7\b|\bzunewp7\b|\bwindows ce\b|\bblackberry\w*|\bbb10\b|\brim tablet os\b|\bmeego|\bwebos\b|\bpalm|\bsymbian|\bj2me\b|\bdocomo\b|\bpda\b|\bchtml\b|\bmidp\b|\bcldc\b|\w*?mobile\w*?|\w*?phone\w*?)/, 2 | x=/(\bxbox\b|\bplaystation\b|\bnintendo\s+\w+)/,k={parse:function(b,d){var a={};d&&(a.standalone=d);b=(""+b).toLowerCase();if(!b)return a;for(var c,e,g=b.split(/[()]/),f=0,k=g.length;f+a.ie? 6 | "7.0":10>+a.ie?"7.5":"8.0",delete a.windows_nt}else if(a.gecko||a.firefox)a.gecko=a.rv;a.rv&&delete a.rv;a.version&&delete a.version;return a},format:function(b){var d="",a;for(a in b)if(a&&b.hasOwnProperty(a)){var c=a,e=b[a],c=c.split(".").join("-"),g=" ua-"+c;if("string"===typeof e){for(var e=e.split(" ").join("_").split(".").join("-"),f=e.indexOf("-");0 2 | 3 | 4 | 5 | CssUserAgent (cssua.js) FAQ 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

.css{user:agent;}

15 | 16 |

Frequently Asked Questions

17 | 18 |

Why CssUserAgent?

19 |

CssUserAgent was started back in 2006 as a less chaotic solution to fixing browser-specific quirks when polishing up web app UIs. 20 | Originally, it was part of a larger Ajax framework that has since been modularized into smaller independent libraries.

21 |

The intention is to build valid standards-based cross-plaform CSS and sparingly use CssUserAgent to finish the job. 22 | Test your code in many browsers as early and frequently as you can. 23 | Don't just design for your favorite browser and once finished look at it in everything else. 24 | This tends to create more workarounds then are necessary.

25 | 26 |

Why would you use CssUserAgent and subsequently (kill the Internet | disrupt the natural order of the digital universe | etc.)?

27 |

Chill, buddy. It's just a tool. If you don't need it, don't use it. The fact is there are tons of people out there writing cryptic CSS hacks and this gives them a better option.

28 |

UserAgent-sniffing, in all its various forms, gets lots of flak in the web UI community. Typically this is because it is abused by those new to the trade. L337 haxors tend to love to bash any pragmatic shortcuts which trade code purity for time.

29 |

The reality is, as in all of life, there are trade-offs to be made. Beware "always" and "never" statements; choose wisely and use shortcuts sparingly. Deep breaths. It's going to be okay.

30 | 31 |

Why bother creating CssUserAgent when Modernizr exists?

32 |

CssUserAgent actually predates Modernizr by a couple years; however, this is a valid point as Modernizr is a great tool. The existence of both is further evidence that browser differentiating layout is indeed a problem in need of a solution.

33 |

These two tools serve slightly different but complimentary purposes. Modernizr uses feature detection to allow differentiating layout. This works well for feature-oriented differences but some layout differences aren't necessarily the result of missing features or cannot be detected through feature testing. 34 | See the "Undetectables" topic in Modernizr's docs for specific examples. 35 | Sometimes you just need a slightly different padding or font size for one browser.

36 |

Modernizr may or may not be the right tool for the job. Keep in mind, Modernizr is also about 4x larger and requires more complex DOM testing logic on each page load. Again choose your tools based upon your needs.

37 | 38 |

What are some other alternative techniques to CssUserAgent?

39 |

As mentioned earlier, CSS hacks have been the traditional solution. 40 | These techniques rely upon bugs within various browser implementations to be able to target specific browsers. 41 | CSS hacks have many downsides, not the least of which is they typically employ invalid CSS syntax which could be misinterpreted by CSS compaction utilities. 42 | Another common issue is their behavior is largely unknown for future browsers.

43 |

Since Internet Explorer typically is the greatest deviant in web design, another common solution uses different stylesheets for IE by using IE-only conditional comments to include or exclude certain <link/> or <style/> tags. 44 | This only applies to IE, and tends to become difficult to maintainable in larger applications. 45 | Even worse is that seemingly valid uses of conditional comments can drastically slow down page load in IE-8.

46 |

Another solution which only targets Internet Explorer 47 | uses IE-only conditional comments to apply classes to the HTML tag. 48 | This clever solution has the advantage of not requiring CSS Hacks or JavaScript, but only works with IE and makes your markup ugly as sin.

49 | 50 |

What else should I know?

51 |

The most important caveat to know is that CssUserAgent requires JavaScript. 52 | This typically isn't an issue for most users. But be warned not to rely upon browser-specific CSS classes for the accessibility of your content. 53 | As mentioned earlier, this is intended for layout fixes and polish not core business logic.

54 | 55 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CssUserAgent (cssua.js) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

.css{user:agent;}

15 |

Avoid CSS hacks! Use cssua.js to apply special CSS classes to your pages allowing you to use valid CSS to work around browser-specific quirks. Read more…

16 | 17 |
18 |
19 |

Docs

20 | 28 |
29 | 30 |
31 |

Downloads

32 | 40 |
41 | 42 |
43 |

Links

44 | 50 |
51 |
52 | 53 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /lib/closure/COPYING: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /lib/closure/README: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2009 The Closure Compiler Authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // 18 | // Contents 19 | // 20 | 21 | The Closure Compiler performs checking, instrumentation, and 22 | optimizations on JavaScript code. The purpose of this README is to 23 | explain how to build and run the Closure Compiler. 24 | 25 | The Closure Compiler requires Java 6 or higher. 26 | http://www.java.com/ 27 | 28 | 29 | // 30 | // Building The Closure Compiler 31 | // 32 | 33 | There are three ways to get a Closure Compiler executable. 34 | 35 | 1) Use one we built for you. 36 | 37 | Pre-built Closure binaries can be found at 38 | http://code.google.com/p/closure-compiler/downloads/list 39 | 40 | 41 | 2) Check out the source and build it with Apache Ant. 42 | 43 | First, check out the full source tree of the Closure Compiler. There 44 | are instructions on how to do this at the project site. 45 | http://code.google.com/p/closure-compiler/source/checkout 46 | 47 | Apache Ant is a cross-platform build tool. 48 | http://ant.apache.org/ 49 | 50 | At the root of the source tree, there is an Ant file named 51 | build.xml. To use it, navigate to the same directory and type the 52 | command 53 | 54 | ant jar 55 | 56 | This will produce a jar file called "build/compiler.jar". 57 | 58 | 59 | 3) Check out the source and build it with Eclipse. 60 | 61 | Eclipse is a cross-platform IDE. 62 | http://www.eclipse.org/ 63 | 64 | Under Eclipse's File menu, click "New > Project ..." and create a 65 | "Java Project." You will see an options screen. Give the project a 66 | name, select "Create project from existing source," and choose the 67 | root of the checked-out source tree as the existing directory. Verify 68 | that you are using JRE version 6 or higher. 69 | 70 | Eclipse can use the build.xml file to discover rules. When you 71 | navigate to the build.xml file, you will see all the build rules in 72 | the "Outline" pane. Run the "jar" rule to build the compiler in 73 | build/compiler.jar. 74 | 75 | 76 | // 77 | // Running The Closure Compiler 78 | // 79 | 80 | Once you have the jar binary, running the Closure Compiler is straightforward. 81 | 82 | On the command line, type 83 | 84 | java -jar compiler.jar 85 | 86 | This starts the compiler in interactive mode. Type 87 | 88 | var x = 17 + 25; 89 | 90 | then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux) 91 | and "Enter" again. The Compiler will respond: 92 | 93 | var x=42; 94 | 95 | The Closure Compiler has many options for reading input from a file, 96 | writing output to a file, checking your code, and running 97 | optimizations. To learn more, type 98 | 99 | java -jar compiler.jar --help 100 | 101 | You can read more detailed documentation about the many flags at 102 | http://code.google.com/closure/compiler/docs/gettingstarted_app.html 103 | 104 | 105 | // 106 | // Compiling Multiple Scripts 107 | // 108 | 109 | If you have multiple scripts, you should compile them all together with 110 | one compile command. 111 | 112 | java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js 113 | 114 | The Closure Compiler will concatenate the files in the order they're 115 | passed at the command line. 116 | 117 | If you need to compile many, many scripts together, you may start to 118 | run into problems with managing dependencies between scripts. You 119 | should check out the Closure Library. It contains functions for 120 | enforcing dependencies between scripts, and a tool called calcdeps.py 121 | that knows how to give scripts to the Closure Compiler in the right 122 | order. 123 | 124 | http://code.google.com/p/closure-library/ 125 | 126 | // 127 | // Licensing 128 | // 129 | 130 | Unless otherwise stated, all source files are licensed under 131 | the Apache License, Version 2.0. 132 | 133 | 134 | ----- 135 | Code under: 136 | src/com/google/javascript/rhino 137 | test/com/google/javascript/rhino 138 | 139 | URL: http://www.mozilla.org/rhino 140 | Version: 1.5R3, with heavy modifications 141 | License: Netscape Public License and MPL / GPL dual license 142 | 143 | Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an 144 | implementation of JavaScript for the JVM. The JavaScript parser and 145 | the parse tree data structures were extracted and modified 146 | significantly for use by Google's JavaScript compiler. 147 | 148 | Local Modifications: The packages have been renamespaced. All code not 149 | relevant to parsing has been removed. A JsDoc parser and static typing 150 | system have been added. 151 | 152 | 153 | ----- 154 | Code in: 155 | lib/rhino 156 | 157 | Rhino 158 | URL: http://www.mozilla.org/rhino 159 | Version: Trunk 160 | License: Netscape Public License and MPL / GPL dual license 161 | 162 | Description: Mozilla Rhino is an implementation of JavaScript for the JVM. 163 | 164 | Local Modifications: Minor changes to parsing JSDoc that usually get pushed 165 | up-stream to Rhino trunk. 166 | 167 | 168 | ----- 169 | Code in: 170 | lib/args4j.jar 171 | 172 | Args4j 173 | URL: https://args4j.dev.java.net/ 174 | Version: 2.0.16 175 | License: MIT 176 | 177 | Description: 178 | args4j is a small Java class library that makes it easy to parse command line 179 | options/arguments in your CUI application. 180 | 181 | Local Modifications: None. 182 | 183 | 184 | ----- 185 | Code in: 186 | lib/guava.jar 187 | 188 | Guava Libraries 189 | URL: http://code.google.com/p/guava-libraries/ 190 | Version: 14.0 191 | License: Apache License 2.0 192 | 193 | Description: Google's core Java libraries. 194 | 195 | Local Modifications: None. 196 | 197 | 198 | ----- 199 | Code in: 200 | lib/jsr305.jar 201 | 202 | Annotations for software defect detection 203 | URL: http://code.google.com/p/jsr-305/ 204 | Version: svn revision 47 205 | License: BSD License 206 | 207 | Description: Annotations for software defect detection. 208 | 209 | Local Modifications: None. 210 | 211 | 212 | ----- 213 | Code in: 214 | lib/jarjar.jar 215 | 216 | Jar Jar Links 217 | URL: http://jarjar.googlecode.com/ 218 | Version: 1.1 219 | License: Apache License 2.0 220 | 221 | Description: 222 | A utility for repackaging Java libraries. 223 | 224 | Local Modifications: None. 225 | 226 | 227 | ---- 228 | Code in: 229 | lib/junit.jar 230 | 231 | JUnit 232 | URL: http://sourceforge.net/projects/junit/ 233 | Version: 4.10 234 | License: Common Public License 1.0 235 | 236 | Description: A framework for writing and running automated tests in Java. 237 | 238 | Local Modifications: None. 239 | 240 | 241 | --- 242 | Code in: 243 | lib/protobuf-java.jar 244 | 245 | Protocol Buffers 246 | URL: http://code.google.com/p/protobuf/ 247 | Version: 2.4.1 248 | License: New BSD License 249 | 250 | Description: Supporting libraries for protocol buffers, 251 | an encoding of structured data. 252 | 253 | Local Modifications: None 254 | 255 | 256 | --- 257 | Code in: 258 | lib/ant.jar 259 | lib/ant-launcher.jar 260 | 261 | URL: http://ant.apache.org/bindownload.cgi 262 | Version: 1.8.1 263 | License: Apache License 2.0 264 | Description: 265 | Ant is a Java based build tool. In theory it is kind of like "make" 266 | without make's wrinkles and with the full portability of pure java code. 267 | 268 | Local Modifications: None 269 | 270 | 271 | --- 272 | Code in: 273 | lib/json.jar 274 | URL: http://json.org/java/index.html 275 | Version: JSON version 20090211 276 | License: MIT license 277 | Description: 278 | JSON is a set of java files for use in transmitting data in JSON format. 279 | 280 | Local Modifications: None 281 | 282 | --- 283 | Code in: 284 | tools/maven-ant-tasks-2.1.3.jar 285 | URL: http://maven.apache.org 286 | Version 2.1.3 287 | License: Apache License 2.0 288 | Description: 289 | Maven Ant tasks are used to manage dependencies and to install/deploy to 290 | maven repositories. 291 | 292 | Local Modifications: None 293 | -------------------------------------------------------------------------------- /lib/closure/compiler.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/lib/closure/compiler.jar -------------------------------------------------------------------------------- /lib/closure/docs.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=http://code.google.com/closure/compiler/docs/js-for-compiler.html 3 | -------------------------------------------------------------------------------- /lib/closure/download.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=http://code.google.com/closure/compiler/ 3 | -------------------------------------------------------------------------------- /lib/html5shiv/html5.js: -------------------------------------------------------------------------------- 1 | // html5shiv MIT @rem remysharp.com/html5-enabling-script 2 | // iepp v1.6.2 MIT @jon_neal iecss.com/print-protector 3 | /*@cc_on(function(m,c){var z="abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video";function n(d){for(var a=-1;++ai";if(g.childNodes.length!==1){var i=z.split("|"),o=i.length,s=RegExp("(^|\\s)("+z+")", 4 | "gi"),t=RegExp("<(/*)("+z+")","gi"),u=RegExp("(^|[^\\n]*?\\s)("+z+")([^\\n]*)({[\\n\\w\\W]*?})","gi"),r=c.createDocumentFragment(),k=c.documentElement;g=k.firstChild;var h=c.createElement("body"),l=c.createElement("style"),f;n(c);n(r);g.insertBefore(l, 5 | g.firstChild);l.media="print";m.attachEvent("onbeforeprint",function(){var d=-1,a=p(c.styleSheets,"all"),e=[],b;for(f=f||c.body;(b=u.exec(a))!=null;)e.push((b[1]+b[2]+b[3]).replace(s,"$1.iepp_$2")+b[4]);for(l.styleSheet.cssText=e.join("\n");++d(" + bad + ", " + good + ", " + this.assertions.length + ")"; 152 | 153 | addEvent(b, "click", function() { 154 | var next = b.nextSibling, display = next.style.display; 155 | next.style.display = display === "none" ? "block" : "none"; 156 | }); 157 | 158 | addEvent(b, "dblclick", function(e) { 159 | var target = e && e.target ? e.target : window.event.srcElement; 160 | if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { 161 | target = target.parentNode; 162 | } 163 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) { 164 | window.location.search = "?" + encodeURIComponent(getText([target]).replace(/\(.+\)$/, "").replace(/(^\s*|\s*$)/g, "")); 165 | } 166 | }); 167 | 168 | var li = id(this.id); 169 | li.className = bad ? "fail" : "pass"; 170 | li.style.display = resultDisplayStyle(!bad); 171 | li.removeChild( li.firstChild ); 172 | li.appendChild( b ); 173 | li.appendChild( ol ); 174 | 175 | if ( bad ) { 176 | var toolbar = id("qunit-testrunner-toolbar"); 177 | if ( toolbar ) { 178 | toolbar.style.display = "block"; 179 | id("qunit-filter-pass").disabled = null; 180 | } 181 | } 182 | 183 | } else { 184 | for ( var i = 0; i < this.assertions.length; i++ ) { 185 | if ( !this.assertions[i].result ) { 186 | bad++; 187 | config.stats.bad++; 188 | config.moduleStats.bad++; 189 | } 190 | } 191 | } 192 | 193 | try { 194 | QUnit.reset(); 195 | } catch(e) { 196 | // TODO use testName instead of name for no-markup message? 197 | fail("reset() failed, following Test " + this.name + ", exception and reset fn follows", e, QUnit.reset); 198 | } 199 | 200 | QUnit.testDone( this.testName, bad, this.assertions.length ); 201 | }, 202 | 203 | queue: function() { 204 | var test = this; 205 | synchronize(function() { 206 | test.init(); 207 | }); 208 | function run() { 209 | // each of these can by async 210 | synchronize(function() { 211 | test.setup(); 212 | }); 213 | synchronize(function() { 214 | test.run(); 215 | }); 216 | synchronize(function() { 217 | test.teardown(); 218 | }); 219 | synchronize(function() { 220 | test.finish(); 221 | }); 222 | } 223 | // defer when previous test run passed, if storage is available 224 | var bad = defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.testName); 225 | if (bad) { 226 | run(); 227 | } else { 228 | synchronize(run); 229 | }; 230 | } 231 | 232 | } 233 | 234 | var QUnit = { 235 | 236 | // call on start of module test to prepend name to all tests 237 | module: function(name, testEnvironment) { 238 | config.previousModule = config.currentModule; 239 | config.currentModule = name; 240 | config.currentModuleTestEnviroment = testEnvironment; 241 | }, 242 | 243 | asyncTest: function(testName, expected, callback) { 244 | if ( arguments.length === 2 ) { 245 | callback = expected; 246 | expected = 0; 247 | } 248 | 249 | QUnit.test(testName, expected, callback, true); 250 | }, 251 | 252 | test: function(testName, expected, callback, async) { 253 | var name = '' + testName + '', testEnvironmentArg; 254 | 255 | if ( arguments.length === 2 ) { 256 | callback = expected; 257 | expected = null; 258 | } 259 | // is 2nd argument a testEnvironment? 260 | if ( expected && typeof expected === 'object') { 261 | testEnvironmentArg = expected; 262 | expected = null; 263 | } 264 | 265 | if ( config.currentModule ) { 266 | name = '' + config.currentModule + ": " + name; 267 | } 268 | 269 | if ( !validTest(config.currentModule + ": " + testName) ) { 270 | return; 271 | } 272 | 273 | var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); 274 | test.previousModule = config.previousModule; 275 | test.module = config.currentModule; 276 | test.moduleTestEnvironment = config.currentModuleTestEnviroment; 277 | test.queue(); 278 | }, 279 | 280 | /** 281 | * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. 282 | */ 283 | expect: function(asserts) { 284 | config.current.expected = asserts; 285 | }, 286 | 287 | /** 288 | * Asserts true. 289 | * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); 290 | */ 291 | ok: function(a, msg) { 292 | a = !!a; 293 | var details = { 294 | result: a, 295 | message: msg 296 | }; 297 | msg = escapeHtml(msg); 298 | QUnit.log(a, msg, details); 299 | config.current.assertions.push({ 300 | result: a, 301 | message: msg 302 | }); 303 | }, 304 | 305 | /** 306 | * Checks that the first two arguments are equal, with an optional message. 307 | * Prints out both actual and expected values. 308 | * 309 | * Prefered to ok( actual == expected, message ) 310 | * 311 | * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); 312 | * 313 | * @param Object actual 314 | * @param Object expected 315 | * @param String message (optional) 316 | */ 317 | equal: function(actual, expected, message) { 318 | QUnit.push(expected == actual, actual, expected, message); 319 | }, 320 | 321 | notEqual: function(actual, expected, message) { 322 | QUnit.push(expected != actual, actual, expected, message); 323 | }, 324 | 325 | deepEqual: function(actual, expected, message) { 326 | QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); 327 | }, 328 | 329 | notDeepEqual: function(actual, expected, message) { 330 | QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); 331 | }, 332 | 333 | strictEqual: function(actual, expected, message) { 334 | QUnit.push(expected === actual, actual, expected, message); 335 | }, 336 | 337 | notStrictEqual: function(actual, expected, message) { 338 | QUnit.push(expected !== actual, actual, expected, message); 339 | }, 340 | 341 | raises: function(block, expected, message) { 342 | var actual, ok = false; 343 | 344 | if (typeof expected === 'string') { 345 | message = expected; 346 | expected = null; 347 | } 348 | 349 | try { 350 | block(); 351 | } catch (e) { 352 | actual = e; 353 | } 354 | 355 | if (actual) { 356 | // we don't want to validate thrown error 357 | if (!expected) { 358 | ok = true; 359 | // expected is a regexp 360 | } else if (QUnit.objectType(expected) === "regexp") { 361 | ok = expected.test(actual); 362 | // expected is a constructor 363 | } else if (actual instanceof expected) { 364 | ok = true; 365 | // expected is a validation function which returns true is validation passed 366 | } else if (expected.call({}, actual) === true) { 367 | ok = true; 368 | } 369 | } 370 | 371 | QUnit.ok(ok, message); 372 | }, 373 | 374 | start: function() { 375 | // A slight delay, to avoid any current callbacks 376 | if ( defined.setTimeout ) { 377 | window.setTimeout(function() { 378 | if ( config.timeout ) { 379 | clearTimeout(config.timeout); 380 | } 381 | 382 | config.blocking = false; 383 | process(); 384 | }, 13); 385 | } else { 386 | config.blocking = false; 387 | process(); 388 | } 389 | }, 390 | 391 | stop: function(timeout) { 392 | config.blocking = true; 393 | 394 | if ( timeout && defined.setTimeout ) { 395 | config.timeout = window.setTimeout(function() { 396 | QUnit.ok( false, "Test timed out" ); 397 | QUnit.start(); 398 | }, timeout); 399 | } 400 | } 401 | 402 | }; 403 | 404 | // Backwards compatibility, deprecated 405 | QUnit.equals = QUnit.equal; 406 | QUnit.same = QUnit.deepEqual; 407 | 408 | // Maintain internal state 409 | var config = { 410 | // The queue of tests to run 411 | queue: [], 412 | 413 | // block until document ready 414 | blocking: true 415 | }; 416 | 417 | // Load paramaters 418 | (function() { 419 | var location = window.location || { search: "", protocol: "file:" }, 420 | GETParams = location.search.slice(1).split('&'); 421 | 422 | for ( var i = 0; i < GETParams.length; i++ ) { 423 | GETParams[i] = decodeURIComponent( GETParams[i] ); 424 | if ( GETParams[i] === "noglobals" ) { 425 | GETParams.splice( i, 1 ); 426 | i--; 427 | config.noglobals = true; 428 | } else if ( GETParams[i].search('=') > -1 ) { 429 | GETParams.splice( i, 1 ); 430 | i--; 431 | } 432 | } 433 | 434 | // restrict modules/tests by get parameters 435 | config.filters = GETParams; 436 | 437 | // Figure out if we're running the tests from a server or not 438 | QUnit.isLocal = !!(location.protocol === 'file:'); 439 | })(); 440 | 441 | // Expose the API as global variables, unless an 'exports' 442 | // object exists, in that case we assume we're in CommonJS 443 | if ( typeof exports === "undefined" || typeof require === "undefined" ) { 444 | extend(window, QUnit); 445 | window.QUnit = QUnit; 446 | } else { 447 | extend(exports, QUnit); 448 | exports.QUnit = QUnit; 449 | } 450 | 451 | // define these after exposing globals to keep them in these QUnit namespace only 452 | extend(QUnit, { 453 | config: config, 454 | 455 | // Initialize the configuration options 456 | init: function() { 457 | extend(config, { 458 | stats: { all: 0, bad: 0 }, 459 | moduleStats: { all: 0, bad: 0 }, 460 | started: +new Date, 461 | updateRate: 1000, 462 | blocking: false, 463 | autostart: true, 464 | autorun: false, 465 | filters: [], 466 | queue: [] 467 | }); 468 | 469 | var tests = id("qunit-tests"), 470 | banner = id("qunit-banner"), 471 | result = id("qunit-testresult"); 472 | 473 | if ( tests ) { 474 | tests.innerHTML = ""; 475 | } 476 | 477 | if ( banner ) { 478 | banner.className = ""; 479 | } 480 | 481 | if ( result ) { 482 | result.parentNode.removeChild( result ); 483 | } 484 | }, 485 | 486 | /** 487 | * Resets the test setup. Useful for tests that modify the DOM. 488 | * 489 | * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. 490 | */ 491 | reset: function() { 492 | if ( window.jQuery ) { 493 | jQuery( "#main, #qunit-fixture" ).html( config.fixture ); 494 | } else { 495 | var main = id( 'main' ) || id( 'qunit-fixture' ); 496 | if ( main ) { 497 | main.innerHTML = config.fixture; 498 | } 499 | } 500 | }, 501 | 502 | /** 503 | * Trigger an event on an element. 504 | * 505 | * @example triggerEvent( document.body, "click" ); 506 | * 507 | * @param DOMElement elem 508 | * @param String type 509 | */ 510 | triggerEvent: function( elem, type, event ) { 511 | if ( document.createEvent ) { 512 | event = document.createEvent("MouseEvents"); 513 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 514 | 0, 0, 0, 0, 0, false, false, false, false, 0, null); 515 | elem.dispatchEvent( event ); 516 | 517 | } else if ( elem.fireEvent ) { 518 | elem.fireEvent("on"+type); 519 | } 520 | }, 521 | 522 | // Safe object type checking 523 | is: function( type, obj ) { 524 | return QUnit.objectType( obj ) == type; 525 | }, 526 | 527 | objectType: function( obj ) { 528 | if (typeof obj === "undefined") { 529 | return "undefined"; 530 | 531 | // consider: typeof null === object 532 | } 533 | if (obj === null) { 534 | return "null"; 535 | } 536 | 537 | var type = Object.prototype.toString.call( obj ) 538 | .match(/^\[object\s(.*)\]$/)[1] || ''; 539 | 540 | switch (type) { 541 | case 'Number': 542 | if (isNaN(obj)) { 543 | return "nan"; 544 | } else { 545 | return "number"; 546 | } 547 | case 'String': 548 | case 'Boolean': 549 | case 'Array': 550 | case 'Date': 551 | case 'RegExp': 552 | case 'Function': 553 | return type.toLowerCase(); 554 | } 555 | if (typeof obj === "object") { 556 | return "object"; 557 | } 558 | return undefined; 559 | }, 560 | 561 | push: function(result, actual, expected, message) { 562 | var details = { 563 | result: result, 564 | message: message, 565 | actual: actual, 566 | expected: expected 567 | }; 568 | 569 | message = escapeHtml(message) || (result ? "okay" : "failed"); 570 | message = '' + message + ""; 571 | expected = escapeHtml(QUnit.jsDump.parse(expected)); 572 | actual = escapeHtml(QUnit.jsDump.parse(actual)); 573 | var output = message + ''; 574 | if (actual != expected) { 575 | output += ''; 576 | output += ''; 577 | } 578 | if (!result) { 579 | var source = sourceFromStacktrace(); 580 | if (source) { 581 | details.source = source; 582 | output += ''; 583 | } 584 | } 585 | output += "
Expected:
' + expected + '
Result:
' + actual + '
Diff:
' + QUnit.diff(expected, actual) +'
Source:
' + source +'
"; 586 | 587 | QUnit.log(result, message, details); 588 | 589 | config.current.assertions.push({ 590 | result: !!result, 591 | message: output 592 | }); 593 | }, 594 | 595 | // Logging callbacks 596 | begin: function() {}, 597 | done: function(failures, total) {}, 598 | log: function(result, message) {}, 599 | testStart: function(name, testEnvironment) {}, 600 | testDone: function(name, failures, total) {}, 601 | moduleStart: function(name, testEnvironment) {}, 602 | moduleDone: function(name, failures, total) {} 603 | }); 604 | 605 | if ( typeof document === "undefined" || document.readyState === "complete" ) { 606 | config.autorun = true; 607 | } 608 | 609 | addEvent(window, "load", function() { 610 | QUnit.begin(); 611 | 612 | // Initialize the config, saving the execution queue 613 | var oldconfig = extend({}, config); 614 | QUnit.init(); 615 | extend(config, oldconfig); 616 | 617 | config.blocking = false; 618 | 619 | var userAgent = id("qunit-userAgent"); 620 | if ( userAgent ) { 621 | userAgent.innerHTML = navigator.userAgent; 622 | } 623 | var banner = id("qunit-header"); 624 | if ( banner ) { 625 | var paramsIndex = location.href.lastIndexOf(location.search); 626 | if ( paramsIndex > -1 ) { 627 | var mainPageLocation = location.href.slice(0, paramsIndex); 628 | if ( mainPageLocation == location.href ) { 629 | banner.innerHTML = ' ' + banner.innerHTML + ' '; 630 | } else { 631 | var testName = decodeURIComponent(location.search.slice(1)); 632 | banner.innerHTML = '' + banner.innerHTML + '' + testName + ''; 633 | } 634 | } 635 | } 636 | 637 | var toolbar = id("qunit-testrunner-toolbar"); 638 | if ( toolbar ) { 639 | toolbar.style.display = "none"; 640 | 641 | var filter = document.createElement("input"); 642 | filter.type = "checkbox"; 643 | filter.id = "qunit-filter-pass"; 644 | filter.disabled = true; 645 | addEvent( filter, "click", function() { 646 | var li = document.getElementsByTagName("li"); 647 | for ( var i = 0; i < li.length; i++ ) { 648 | if ( li[i].className.indexOf("pass") > -1 ) { 649 | li[i].style.display = filter.checked ? "none" : ""; 650 | } 651 | } 652 | }); 653 | toolbar.appendChild( filter ); 654 | 655 | var label = document.createElement("label"); 656 | label.setAttribute("for", "qunit-filter-pass"); 657 | label.innerHTML = "Hide passed tests"; 658 | toolbar.appendChild( label ); 659 | } 660 | 661 | var main = id('main') || id('qunit-fixture'); 662 | if ( main ) { 663 | config.fixture = main.innerHTML; 664 | } 665 | 666 | if (config.autostart) { 667 | QUnit.start(); 668 | } 669 | }); 670 | 671 | function done() { 672 | config.autorun = true; 673 | 674 | // Log the last module results 675 | if ( config.currentModule ) { 676 | QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); 677 | } 678 | 679 | var banner = id("qunit-banner"), 680 | tests = id("qunit-tests"), 681 | html = ['Tests completed in ', 682 | +new Date - config.started, ' milliseconds.
', 683 | '', config.stats.all - config.stats.bad, ' tests of ', config.stats.all, ' passed, ', config.stats.bad,' failed.'].join(''); 684 | 685 | if ( banner ) { 686 | banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); 687 | } 688 | 689 | if ( tests ) { 690 | var result = id("qunit-testresult"); 691 | 692 | if ( !result ) { 693 | result = document.createElement("p"); 694 | result.id = "qunit-testresult"; 695 | result.className = "result"; 696 | tests.parentNode.insertBefore( result, tests.nextSibling ); 697 | } 698 | 699 | result.innerHTML = html; 700 | } 701 | 702 | QUnit.done( config.stats.bad, config.stats.all ); 703 | } 704 | 705 | function validTest( name ) { 706 | var i = config.filters.length, 707 | run = false; 708 | 709 | if ( !i ) { 710 | return true; 711 | } 712 | 713 | while ( i-- ) { 714 | var filter = config.filters[i], 715 | not = filter.charAt(0) == '!'; 716 | 717 | if ( not ) { 718 | filter = filter.slice(1); 719 | } 720 | 721 | if ( name.indexOf(filter) !== -1 ) { 722 | return !not; 723 | } 724 | 725 | if ( not ) { 726 | run = true; 727 | } 728 | } 729 | 730 | return run; 731 | } 732 | 733 | // so far supports only Firefox, Chrome and Opera (buggy) 734 | // could be extended in the future to use something like https://github.com/csnover/TraceKit 735 | function sourceFromStacktrace() { 736 | try { 737 | throw new Error(); 738 | } catch ( e ) { 739 | if (e.stacktrace) { 740 | // Opera 741 | return e.stacktrace.split("\n")[6]; 742 | } else if (e.stack) { 743 | // Firefox, Chrome 744 | return e.stack.split("\n")[4]; 745 | } 746 | } 747 | } 748 | 749 | function resultDisplayStyle(passed) { 750 | return passed && id("qunit-filter-pass") && id("qunit-filter-pass").checked ? 'none' : ''; 751 | } 752 | 753 | function escapeHtml(s) { 754 | if (!s) { 755 | return ""; 756 | } 757 | s = s + ""; 758 | return s.replace(/[\&"<>\\]/g, function(s) { 759 | switch(s) { 760 | case "&": return "&"; 761 | case "\\": return "\\\\"; 762 | case '"': return '\"'; 763 | case "<": return "<"; 764 | case ">": return ">"; 765 | default: return s; 766 | } 767 | }); 768 | } 769 | 770 | function synchronize( callback ) { 771 | config.queue.push( callback ); 772 | 773 | if ( config.autorun && !config.blocking ) { 774 | process(); 775 | } 776 | } 777 | 778 | function process() { 779 | var start = (new Date()).getTime(); 780 | 781 | while ( config.queue.length && !config.blocking ) { 782 | if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { 783 | config.queue.shift()(); 784 | } else { 785 | window.setTimeout( process, 13 ); 786 | break; 787 | } 788 | } 789 | if (!config.blocking && !config.queue.length) { 790 | done(); 791 | } 792 | } 793 | 794 | function saveGlobal() { 795 | config.pollution = []; 796 | 797 | if ( config.noglobals ) { 798 | for ( var key in window ) { 799 | config.pollution.push( key ); 800 | } 801 | } 802 | } 803 | 804 | function checkPollution( name ) { 805 | var old = config.pollution; 806 | saveGlobal(); 807 | 808 | var newGlobals = diff( old, config.pollution ); 809 | if ( newGlobals.length > 0 ) { 810 | ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); 811 | config.current.expected++; 812 | } 813 | 814 | var deletedGlobals = diff( config.pollution, old ); 815 | if ( deletedGlobals.length > 0 ) { 816 | ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); 817 | config.current.expected++; 818 | } 819 | } 820 | 821 | // returns a new Array with the elements that are in a but not in b 822 | function diff( a, b ) { 823 | var result = a.slice(); 824 | for ( var i = 0; i < result.length; i++ ) { 825 | for ( var j = 0; j < b.length; j++ ) { 826 | if ( result[i] === b[j] ) { 827 | result.splice(i, 1); 828 | i--; 829 | break; 830 | } 831 | } 832 | } 833 | return result; 834 | } 835 | 836 | function fail(message, exception, callback) { 837 | if ( typeof console !== "undefined" && console.error && console.warn ) { 838 | console.error(message); 839 | console.error(exception); 840 | console.warn(callback.toString()); 841 | 842 | } else if ( window.opera && opera.postError ) { 843 | opera.postError(message, exception, callback.toString); 844 | } 845 | } 846 | 847 | function extend(a, b) { 848 | for ( var prop in b ) { 849 | a[prop] = b[prop]; 850 | } 851 | 852 | return a; 853 | } 854 | 855 | function addEvent(elem, type, fn) { 856 | if ( elem.addEventListener ) { 857 | elem.addEventListener( type, fn, false ); 858 | } else if ( elem.attachEvent ) { 859 | elem.attachEvent( "on" + type, fn ); 860 | } else { 861 | fn(); 862 | } 863 | } 864 | 865 | function id(name) { 866 | return !!(typeof document !== "undefined" && document && document.getElementById) && 867 | document.getElementById( name ); 868 | } 869 | 870 | // Test for equality any JavaScript type. 871 | // Discussions and reference: http://philrathe.com/articles/equiv 872 | // Test suites: http://philrathe.com/tests/equiv 873 | // Author: Philippe Rathé 874 | QUnit.equiv = function () { 875 | 876 | var innerEquiv; // the real equiv function 877 | var callers = []; // stack to decide between skip/abort functions 878 | var parents = []; // stack to avoiding loops from circular referencing 879 | 880 | // Call the o related callback with the given arguments. 881 | function bindCallbacks(o, callbacks, args) { 882 | var prop = QUnit.objectType(o); 883 | if (prop) { 884 | if (QUnit.objectType(callbacks[prop]) === "function") { 885 | return callbacks[prop].apply(callbacks, args); 886 | } else { 887 | return callbacks[prop]; // or undefined 888 | } 889 | } 890 | } 891 | 892 | var callbacks = function () { 893 | 894 | // for string, boolean, number and null 895 | function useStrictEquality(b, a) { 896 | if (b instanceof a.constructor || a instanceof b.constructor) { 897 | // to catch short annotaion VS 'new' annotation of a declaration 898 | // e.g. var i = 1; 899 | // var j = new Number(1); 900 | return a == b; 901 | } else { 902 | return a === b; 903 | } 904 | } 905 | 906 | return { 907 | "string": useStrictEquality, 908 | "boolean": useStrictEquality, 909 | "number": useStrictEquality, 910 | "null": useStrictEquality, 911 | "undefined": useStrictEquality, 912 | 913 | "nan": function (b) { 914 | return isNaN(b); 915 | }, 916 | 917 | "date": function (b, a) { 918 | return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); 919 | }, 920 | 921 | "regexp": function (b, a) { 922 | return QUnit.objectType(b) === "regexp" && 923 | a.source === b.source && // the regex itself 924 | a.global === b.global && // and its modifers (gmi) ... 925 | a.ignoreCase === b.ignoreCase && 926 | a.multiline === b.multiline; 927 | }, 928 | 929 | // - skip when the property is a method of an instance (OOP) 930 | // - abort otherwise, 931 | // initial === would have catch identical references anyway 932 | "function": function () { 933 | var caller = callers[callers.length - 1]; 934 | return caller !== Object && 935 | typeof caller !== "undefined"; 936 | }, 937 | 938 | "array": function (b, a) { 939 | var i, j, loop; 940 | var len; 941 | 942 | // b could be an object literal here 943 | if ( ! (QUnit.objectType(b) === "array")) { 944 | return false; 945 | } 946 | 947 | len = a.length; 948 | if (len !== b.length) { // safe and faster 949 | return false; 950 | } 951 | 952 | //track reference to avoid circular references 953 | parents.push(a); 954 | for (i = 0; i < len; i++) { 955 | loop = false; 956 | for(j=0;j= 0) { 1101 | type = "array"; 1102 | } else { 1103 | type = typeof obj; 1104 | } 1105 | return type; 1106 | }, 1107 | separator:function() { 1108 | return this.multiline ? this.HTML ? '
' : '\n' : this.HTML ? ' ' : ' '; 1109 | }, 1110 | indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing 1111 | if ( !this.multiline ) 1112 | return ''; 1113 | var chr = this.indentChar; 1114 | if ( this.HTML ) 1115 | chr = chr.replace(/\t/g,' ').replace(/ /g,' '); 1116 | return Array( this._depth_ + (extra||0) ).join(chr); 1117 | }, 1118 | up:function( a ) { 1119 | this._depth_ += a || 1; 1120 | }, 1121 | down:function( a ) { 1122 | this._depth_ -= a || 1; 1123 | }, 1124 | setParser:function( name, parser ) { 1125 | this.parsers[name] = parser; 1126 | }, 1127 | // The next 3 are exposed so you can use them 1128 | quote:quote, 1129 | literal:literal, 1130 | join:join, 1131 | // 1132 | _depth_: 1, 1133 | // This is the list of parsers, to modify them, use jsDump.setParser 1134 | parsers:{ 1135 | window: '[Window]', 1136 | document: '[Document]', 1137 | error:'[ERROR]', //when no parser is found, shouldn't happen 1138 | unknown: '[Unknown]', 1139 | 'null':'null', 1140 | undefined:'undefined', 1141 | 'function':function( fn ) { 1142 | var ret = 'function', 1143 | name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE 1144 | if ( name ) 1145 | ret += ' ' + name; 1146 | ret += '('; 1147 | 1148 | ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); 1149 | return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); 1150 | }, 1151 | array: array, 1152 | nodelist: array, 1153 | arguments: array, 1154 | object:function( map ) { 1155 | var ret = [ ]; 1156 | QUnit.jsDump.up(); 1157 | for ( var key in map ) 1158 | ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) ); 1159 | QUnit.jsDump.down(); 1160 | return join( '{', ret, '}' ); 1161 | }, 1162 | node:function( node ) { 1163 | var open = QUnit.jsDump.HTML ? '<' : '<', 1164 | close = QUnit.jsDump.HTML ? '>' : '>'; 1165 | 1166 | var tag = node.nodeName.toLowerCase(), 1167 | ret = open + tag; 1168 | 1169 | for ( var a in QUnit.jsDump.DOMAttrs ) { 1170 | var val = node[QUnit.jsDump.DOMAttrs[a]]; 1171 | if ( val ) 1172 | ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); 1173 | } 1174 | return ret + close + open + '/' + tag + close; 1175 | }, 1176 | functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function 1177 | var l = fn.length; 1178 | if ( !l ) return ''; 1179 | 1180 | var args = Array(l); 1181 | while ( l-- ) 1182 | args[l] = String.fromCharCode(97+l);//97 is 'a' 1183 | return ' ' + args.join(', ') + ' '; 1184 | }, 1185 | key:quote, //object calls it internally, the key part of an item in a map 1186 | functionCode:'[code]', //function calls it internally, it's the content of the function 1187 | attribute:quote, //node calls it internally, it's an html attribute value 1188 | string:quote, 1189 | date:quote, 1190 | regexp:literal, //regex 1191 | number:literal, 1192 | 'boolean':literal 1193 | }, 1194 | DOMAttrs:{//attributes to dump from nodes, name=>realName 1195 | id:'id', 1196 | name:'name', 1197 | 'class':'className' 1198 | }, 1199 | HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) 1200 | indentChar:' ',//indentation unit 1201 | multiline:true //if true, items in a collection, are separated by a \n, else just a space. 1202 | }; 1203 | 1204 | return jsDump; 1205 | })(); 1206 | 1207 | // from Sizzle.js 1208 | function getText( elems ) { 1209 | var ret = "", elem; 1210 | 1211 | for ( var i = 0; elems[i]; i++ ) { 1212 | elem = elems[i]; 1213 | 1214 | // Get the text from text nodes and CDATA nodes 1215 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) { 1216 | ret += elem.nodeValue; 1217 | 1218 | // Traverse everything else, except comment nodes 1219 | } else if ( elem.nodeType !== 8 ) { 1220 | ret += getText( elem.childNodes ); 1221 | } 1222 | } 1223 | 1224 | return ret; 1225 | }; 1226 | 1227 | /* 1228 | * Javascript Diff Algorithm 1229 | * By John Resig (http://ejohn.org/) 1230 | * Modified by Chu Alan "sprite" 1231 | * 1232 | * Released under the MIT license. 1233 | * 1234 | * More Info: 1235 | * http://ejohn.org/projects/javascript-diff-algorithm/ 1236 | * 1237 | * Usage: QUnit.diff(expected, actual) 1238 | * 1239 | * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" 1240 | */ 1241 | QUnit.diff = (function() { 1242 | function diff(o, n){ 1243 | var ns = new Object(); 1244 | var os = new Object(); 1245 | 1246 | for (var i = 0; i < n.length; i++) { 1247 | if (ns[n[i]] == null) 1248 | ns[n[i]] = { 1249 | rows: new Array(), 1250 | o: null 1251 | }; 1252 | ns[n[i]].rows.push(i); 1253 | } 1254 | 1255 | for (var i = 0; i < o.length; i++) { 1256 | if (os[o[i]] == null) 1257 | os[o[i]] = { 1258 | rows: new Array(), 1259 | n: null 1260 | }; 1261 | os[o[i]].rows.push(i); 1262 | } 1263 | 1264 | for (var i in ns) { 1265 | if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { 1266 | n[ns[i].rows[0]] = { 1267 | text: n[ns[i].rows[0]], 1268 | row: os[i].rows[0] 1269 | }; 1270 | o[os[i].rows[0]] = { 1271 | text: o[os[i].rows[0]], 1272 | row: ns[i].rows[0] 1273 | }; 1274 | } 1275 | } 1276 | 1277 | for (var i = 0; i < n.length - 1; i++) { 1278 | if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && 1279 | n[i + 1] == o[n[i].row + 1]) { 1280 | n[i + 1] = { 1281 | text: n[i + 1], 1282 | row: n[i].row + 1 1283 | }; 1284 | o[n[i].row + 1] = { 1285 | text: o[n[i].row + 1], 1286 | row: i + 1 1287 | }; 1288 | } 1289 | } 1290 | 1291 | for (var i = n.length - 1; i > 0; i--) { 1292 | if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && 1293 | n[i - 1] == o[n[i].row - 1]) { 1294 | n[i - 1] = { 1295 | text: n[i - 1], 1296 | row: n[i].row - 1 1297 | }; 1298 | o[n[i].row - 1] = { 1299 | text: o[n[i].row - 1], 1300 | row: i - 1 1301 | }; 1302 | } 1303 | } 1304 | 1305 | return { 1306 | o: o, 1307 | n: n 1308 | }; 1309 | } 1310 | 1311 | return function(o, n){ 1312 | o = o.replace(/\s+$/, ''); 1313 | n = n.replace(/\s+$/, ''); 1314 | var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); 1315 | 1316 | var str = ""; 1317 | 1318 | var oSpace = o.match(/\s+/g); 1319 | if (oSpace == null) { 1320 | oSpace = [" "]; 1321 | } 1322 | else { 1323 | oSpace.push(" "); 1324 | } 1325 | var nSpace = n.match(/\s+/g); 1326 | if (nSpace == null) { 1327 | nSpace = [" "]; 1328 | } 1329 | else { 1330 | nSpace.push(" "); 1331 | } 1332 | 1333 | if (out.n.length == 0) { 1334 | for (var i = 0; i < out.o.length; i++) { 1335 | str += '' + out.o[i] + oSpace[i] + ""; 1336 | } 1337 | } 1338 | else { 1339 | if (out.n[0].text == null) { 1340 | for (n = 0; n < out.o.length && out.o[n].text == null; n++) { 1341 | str += '' + out.o[n] + oSpace[n] + ""; 1342 | } 1343 | } 1344 | 1345 | for (var i = 0; i < out.n.length; i++) { 1346 | if (out.n[i].text == null) { 1347 | str += '' + out.n[i] + nSpace[i] + ""; 1348 | } 1349 | else { 1350 | var pre = ""; 1351 | 1352 | for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { 1353 | pre += '' + out.o[n] + oSpace[n] + ""; 1354 | } 1355 | str += " " + out.n[i].text + nSpace[i] + pre; 1356 | } 1357 | } 1358 | } 1359 | 1360 | return str; 1361 | }; 1362 | })(); 1363 | 1364 | })(this); 1365 | -------------------------------------------------------------------------------- /lib/rhino/download.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=http://www.mozilla.org/rhino/download.html 3 | -------------------------------------------------------------------------------- /lib/rhino/js.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/lib/rhino/js.jar -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cssuseragent", 3 | "version": "2.1.31", 4 | "description": "Automatically adds UserAgent-specific CSS classes to the document allowing for browser variations without resorting to CSS hacks.", 5 | "homepage": "http://cssuseragent.org", 6 | "author": "Stephen McKamey (http://mck.me)", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/mckamey/cssuseragent.git" 11 | }, 12 | "files": [ "cssua.js", "README.md", "LICENSE.txt" ], 13 | "main": "cssua.js", 14 | "keywords": [ 15 | "cssuseragent", 16 | "cssua", 17 | "css", 18 | "useragent" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /test/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CssUserAgent (cssua.js) Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |

.css{user:agent;}

22 | 23 |
24 |

Looks like you're using…

25 |
    26 |
  • javascript disabledenabled
  • 27 | 43 |
44 | 45 |

Avoid CSS hacks!

46 | 47 |
    48 |
  • 52 |
  • 56 |
  • 57 |
58 |
59 | 60 |
61 |

CssUserAgent Demo

62 | 63 |

This page demonstrates the power of CssUserAgent. This feature applies User-Agent specific CSS classes to the <html> tag to allow browser-specific CSS variation without resorting to CSS hacks. Since these are performed once at startup, CSS may be statically defined without the need to mix browser-specific logic into the presentation.

64 |

Multiple classes are created for each user-agent, allowing the web developer to target browser classes on differing levels of granularity. The general pattern is browser name followed by version number of varying precision:

65 |
    66 |
  • ua-browsername
  • 67 |
  • ua-browsername-major
  • 68 |
  • ua-browsername-major-minor
  • 69 |
  • ua-browsername-major-minor-build
  • 70 |
  • ua-browsername-major-minor-build-revision
  • 71 |
72 | 73 |

Customized presentation

74 | 75 |

Notice how the sidebar color scheme and browser icon change depending upon which browser you are using.

76 |

This is all done with CSS since cssua.js has prepped the HTML tag with special CSS classes which allow you to target browsers with varying degrees of precision.

77 | 78 | 79 | <!-- the CSS classes currently applied to this page tag are: --> 80 | <html class=""> 83 | 84 | <head>…</head>
85 | <body>…</body> 86 |
87 | </html> 88 |
89 | 90 |

UserAgent map

91 |

UserAgent-sniffing is regarded as something to be used sparingly, but pragmatic developers know it is sometimes the simplest approach to fixing a particular browser's quirk. When it is, cssua.js produces a helper object as a side-effect which makes user-agent-siffing easier and more consistent.

92 |

An object map is also built which allows you to test the user agent from your script in a simplified manner that doesn't require string parsing. For example, this object is effectively produced:

93 | 94 | cssua.userAgent = cssua.ua = { 95 | 113 | }; 114 | 115 | 116 |

Try it out

117 |

Change the input userAgent value and watch how the key-value pairs and the CSS class names respond to the change. This gives you an idea of what to expect from browsers other than this one.

118 |
119 | 156 |

navigator.userAgent:

157 | 158 |
159 |

document.documentElement.className:

160 | 161 |
162 |
163 |

cssua.userAgent | cssua.ua:

164 | 165 |
166 | 167 | 168 |
169 | 170 |

Examples

171 |

Testing for older Internet Explorer has never been easier than this:

172 | 173 | 174 | if (cssua.ua.ie < 8.0) { /* proof of Pareto principle here */ } 175 | 176 | 177 |

Or get a hint if this user might need a mobile experience:

178 | 179 | 180 | if (cssua.ua.mobile) { /* consider your audience */ } 181 | 182 | 183 |

Remember to convert the version String to a Number with parseFloat if the value isn't a simple number, e.g., it has multiple decimal points or trailing letters. Otherwise JavaScript may interpret the value as NaN (not a number):

184 | 185 | 186 | if (parseFloat(cssua.ua.chrome) > 101) { /* enter the Matrix */ } 187 | 188 | 189 |

Mobile browser detection

190 |

CssUserAgent helps detect the increasingly ambiguous category of mobile browsers. cssua.js adds specific classes when it detects mobile browsers:

191 | 192 | <html class="… ua-mobile ua-mobile-iphone …"><html> 193 | 194 | 195 |

No browser hacks

196 |

This allows you to target the browser rendering engine (e.g. "webkit"), or a specific browser (e.g. "safari"). The version can be targeted at the major version number (e.g. "ie-5" includes 5.0, 5.5) or minor (e.g. "ie-5-0" includes only 5.0) all the way down (e.g. "ua-chrome-8-0-552-224") for a very specific case.

197 | 198 | 199 | /* CssUserAgent lets you target specific browsers without CSS hacks */ 200 | .logo-area 201 | { 202 | 203 | background-image: url(logo.png);
204 | background-repeat: no-repeat;
205 | background-position: left top; 206 |
207 | }

208 | 209 | /* target IE 5.0, 5.5, 6.0 */
210 | .ua-ie-5 .logo-area,
211 | .ua-ie-6 .logo-area
212 | { 213 | 214 | /* IE versions < 7.0 don't fully support transparent 24-bit PNGs */
215 | background-image: url(logo.gif); 216 |
217 | } 218 | 219 |
220 | 221 |

Some browsers have multiple listings if the rendering engine is significant. For instance, Gecko-based browsers (such as Firefox, Mozilla, Netscape, Camino, SeaMonkey) also define Gecko CSS classes, to target all browsers of a specific Gecko version at once.

222 |

Below is an example of a number of browser labels which display differently under the presence of different CssUserAgent classes.

223 | 224 |
225 |
    226 |
  • Gecko
  • 227 |
  • Gecko 1.7
  • 228 |
  • Gecko 1.8
  • 229 |
  • Gecko 1.9
  • 230 |
  • Gecko 2
  • 231 |
  • Firefox
  • 232 |
  • Firefox 1.x
  • 233 |
  • Firefox 1.0
  • 234 |
  • Firefox 1.5
  • 235 |
  • Firefox 2.x
  • 236 |
  • Firefox 3.x
  • 237 |
  • Firefox 3.0.x
  • 238 |
  • Firefox 3.1
  • 239 |
  • Firefox 3.5.x
  • 240 |
  • Firefox 3.6.x
  • 241 |
  • Firefox 4.x
  • 242 |
  • Firefox 4.0.x
  • 243 |
  • SeaMonkey
  • 244 |
  • SeaMonkey 1.x
  • 245 |
  • SeaMonkey 1.1
  • 246 |
  • Netscape
  • 247 |
  • Netscape 5.x
  • 248 |
  • Netscape 6.x
  • 249 |
  • Netscape 7.x
  • 250 |
  • Netscape 7.2
  • 251 |
  • Netscape 8.x
  • 252 |
  • Netscape 9.x
  • 253 |
    254 |
  • Safari
  • 255 |
  • Safari 1.x (85.x)
  • 256 |
  • Safari 2.x (419.x)
  • 257 |
  • Safari 2.0.4 (419.3)
  • 258 |
  • Safari (522.x)
  • 259 |
  • Safari 3.0 (523.x)
  • 260 |
  • Safari 3.1 (525.21)
  • 261 |
  • Safari 3.2 (525.27)
  • 262 |
  • Safari 4.0 (526.x)
  • 263 |
  • Safari 4.0 (530.17)
  • 264 |
  • Apple Webkit
  • 265 |
  • Apple Webkit 419.x
  • 266 |
  • Apple Webkit Nightly 420.x
  • 267 |
  • Apple Webkit 525.x
  • 268 |
  • Apple Webkit 525.27.1
  • 269 |
  • Apple Webkit 530.x
  • 270 |
  • Apple Webkit 532.x
  • 271 |
  • Chrome
  • 272 |
  • Chrome 0.2.x
  • 273 |
  • Chrome 1.x
  • 274 |
  • Chrome 4.x
  • 275 |
  • Chrome 5.x
  • 276 |
  • Chrome 7.x
  • 277 |
  • Chrome 8.x
  • 278 |
  • Chrome 9.x
  • 279 |
  • Chrome 10.x
  • 280 |
  • Chrome 11.x
  • 281 |
    282 |
  • Internet Explorer
  • 283 |
  • Internet Explorer 5.x
  • 284 |
  • Internet Explorer 5.0
  • 285 |
  • Internet Explorer 5.5
  • 286 |
  • Internet Explorer 6.x
  • 287 |
  • Internet Explorer 7.x
  • 288 |
  • Internet Explorer 8.x
  • 289 |
  • Internet Explorer 9.x
  • 290 |
  • Internet Explorer 10.x
  • 291 |
    292 |
  • Opera
  • 293 |
  • Opera 7.x
  • 294 |
  • Opera 8.x
  • 295 |
  • Opera 9.x
  • 296 |
  • Opera 9.52
  • 297 |
  • Opera 10.x
  • 298 |
    299 |
  • iCab
  • 300 |
  • iCab 2.x
  • 301 |
  • iCab 3.x
  • 302 |
  • iCab 4.x
  • 303 |
    304 |
  • Camino
  • 305 |
  • Camino 1.x
  • 306 |
307 |
308 |
309 | 310 | 315 | 316 | 317 | 318 | 319 | -------------------------------------------------------------------------------- /test/encodeTests.js: -------------------------------------------------------------------------------- 1 | try{ 2 | 3 | module('cssua.encode()'); 4 | 5 | test('IE 5.5', function() { 6 | 7 | var input = 8 | { 9 | ie: '5.5' 10 | }; 11 | 12 | var expected = 'ie=5.5'; 13 | 14 | var actual = cssua.encode(input); 15 | 16 | same(actual, expected, ''); 17 | }); 18 | 19 | test('Firefox 3.6', function() { 20 | 21 | var input = 22 | { 23 | gecko: '1.9.2.13', 24 | firefox: '3.6.13' 25 | }; 26 | 27 | var expected = 'gecko=1.9.2.13&firefox=3.6.13'; 28 | 29 | var actual = cssua.encode(input); 30 | 31 | same(actual, expected, ''); 32 | }); 33 | 34 | test('Chrome 0.2', function() { 35 | 36 | var input = 37 | { 38 | webkit: '525.13', 39 | chrome: '0.2.149.30' 40 | }; 41 | 42 | var expected = 'webkit=525.13&chrome=0.2.149.30'; 43 | 44 | var actual = cssua.encode(input); 45 | 46 | same(actual, expected, ''); 47 | }); 48 | 49 | test('Chrome 10', function() { 50 | 51 | var input = 52 | { 53 | webkit: '534.15', 54 | chrome: '10.0.612.3' 55 | }; 56 | 57 | var expected = 'webkit=534.15&chrome=10.0.612.3'; 58 | 59 | var actual = cssua.encode(input); 60 | 61 | same(actual, expected, ''); 62 | }); 63 | 64 | test('Safari 4.0.4, iPad', function() { 65 | 66 | var input = 67 | { 68 | webkit: '531.21.10', 69 | mobile: 'ipad', 70 | safari: '4.0.4' 71 | }; 72 | 73 | var expected = 'webkit=531.21.10&mobile=ipad&safari=4.0.4'; 74 | 75 | var actual = cssua.encode(input); 76 | 77 | same(actual, expected, ''); 78 | }); 79 | 80 | test('iPhone (standalone)', function() { 81 | 82 | var input = 83 | { 84 | standalone: true, 85 | mobile: 'iphone', 86 | ios: '7.0.4', 87 | webkit: '537.51.1' 88 | }; 89 | 90 | var expected = 'standalone=true&mobile=iphone&ios=7.0.4&webkit=537.51.1'; 91 | 92 | var actual = cssua.encode(input); 93 | 94 | same(actual, expected, ''); 95 | }); 96 | 97 | }catch(ex){alert(ex);} -------------------------------------------------------------------------------- /test/formatTests.js: -------------------------------------------------------------------------------- 1 | try{ 2 | 3 | module('cssua.format()'); 4 | 5 | test('IE 5.5', function() { 6 | 7 | var input = 8 | { 9 | windows_nt: '5.0', 10 | os: 'windows', 11 | ie: '5.5' 12 | }; 13 | 14 | var expected = ' ua-windows_nt ua-windows_nt-5 ua-windows_nt-5-0 ua-os ua-os-windows ua-ie ua-ie-5 ua-ie-5-5'; 15 | 16 | var actual = cssua.format(input); 17 | 18 | same(actual, expected, ''); 19 | }); 20 | 21 | test('Firefox 3.6', function() { 22 | 23 | var input = 24 | { 25 | gecko: '1.9.2.13', 26 | firefox: '3.6.13' 27 | }; 28 | 29 | var expected = ' ua-gecko ua-gecko-1 ua-gecko-1-9 ua-gecko-1-9-2 ua-gecko-1-9-2-13 ua-firefox ua-firefox-3 ua-firefox-3-6 ua-firefox-3-6-13'; 30 | 31 | var actual = cssua.format(input); 32 | 33 | same(actual, expected, ''); 34 | }); 35 | 36 | test('Chrome 0.2', function() { 37 | 38 | var input = 39 | { 40 | webkit: '525.13', 41 | chrome: '0.2.149.30' 42 | }; 43 | 44 | var expected = ' ua-webkit ua-webkit-525 ua-webkit-525-13 ua-chrome ua-chrome-0 ua-chrome-0-2 ua-chrome-0-2-149 ua-chrome-0-2-149-30'; 45 | 46 | var actual = cssua.format(input); 47 | 48 | same(actual, expected, ''); 49 | }); 50 | 51 | test('Chrome 10', function() { 52 | 53 | var input = 54 | { 55 | webkit: '534.15', 56 | chrome: '10.0.612.3' 57 | }; 58 | 59 | var expected = ' ua-webkit ua-webkit-534 ua-webkit-534-15 ua-chrome ua-chrome-10 ua-chrome-10-0 ua-chrome-10-0-612 ua-chrome-10-0-612-3'; 60 | 61 | var actual = cssua.format(input); 62 | 63 | same(actual, expected, ''); 64 | }); 65 | 66 | test('Safari 4.0.4, iPad', function() { 67 | 68 | var input = 69 | { 70 | mobile: 'ipad', 71 | safari: '4.0.4', 72 | ios: '3.2', 73 | webkit: '531.21.10' 74 | }; 75 | 76 | var expected = ' ua-mobile ua-mobile-ipad ua-safari ua-safari-4 ua-safari-4-0 ua-safari-4-0-4 ua-ios ua-ios-3 ua-ios-3-2 ua-webkit ua-webkit-531 ua-webkit-531-21 ua-webkit-531-21-10'; 77 | 78 | var actual = cssua.format(input); 79 | 80 | same(actual, expected, ''); 81 | }); 82 | 83 | test('IE 7, WinPhone', function() { 84 | 85 | var input = 86 | { 87 | trident: '3.1', 88 | iemobile: '7.0', 89 | ie: '7.0', 90 | mobile: 'windows phone os' 91 | }; 92 | 93 | var expected = ' ua-trident ua-trident-3 ua-trident-3-1 ua-iemobile ua-iemobile-7 ua-iemobile-7-0 ua-ie ua-ie-7 ua-ie-7-0 ua-mobile ua-mobile-windows_phone_os'; 94 | 95 | var actual = cssua.format(input); 96 | 97 | same(actual, expected, ''); 98 | }); 99 | 100 | test('iPhone (standalone)', function() { 101 | 102 | var input = 103 | { 104 | standalone: true, 105 | mobile: 'iphone', 106 | ios: '7.0.4', 107 | webkit: '537.51.1' 108 | }; 109 | 110 | var expected = ' ua-standalone ua-mobile ua-mobile-iphone ua-ios ua-ios-7 ua-ios-7-0 ua-ios-7-0-4 ua-webkit ua-webkit-537 ua-webkit-537-51 ua-webkit-537-51-1'; 111 | 112 | var actual = cssua.format(input); 113 | 114 | same(actual, expected, ''); 115 | }); 116 | 117 | }catch(ex){alert(ex);} -------------------------------------------------------------------------------- /test/lint.js: -------------------------------------------------------------------------------- 1 | load("lib/jslint/jslint.js"); 2 | 3 | var src = readFile("cssua.js"); 4 | 5 | JSLINT(src, { browser: true, undef: true, eqeqeq: true, regexp: true, newcap: true, maxerr: 100 }); 6 | 7 | var ok = { 8 | "Use '===' to compare with 'null'.": true, 9 | "Use '!==' to compare with 'null'.": true 10 | }; 11 | 12 | var e = JSLINT.errors, found = 0, w; 13 | 14 | for (var i = 0; i < e.length; i++) { 15 | w = e[i]; 16 | 17 | if (!ok[ w.reason ]) { 18 | found++; 19 | print("\n" + w.evidence + "\n"); 20 | print(" Problem at line " + w.line + " character " + w.character + ": " + w.reason); 21 | } 22 | } 23 | 24 | if (found > 0) { 25 | print("\n" + found + " Error(s) found."); 26 | 27 | } else { 28 | print("JSLint check passed."); 29 | } 30 | -------------------------------------------------------------------------------- /test/styles/browsers.css: -------------------------------------------------------------------------------- 1 | /* 2 | browsers.css 3 | 4 | browser info stylesheet 5 | */ 6 | 7 | .browser-info { 8 | clear: right; 9 | float: right; 10 | margin: 10px 0 10px 10px; 11 | background-color: #F1F1F1; 12 | background-position: center 1em; 13 | background-repeat: no-repeat; 14 | background-attachment: scroll; 15 | padding: 0.5em 1em; 16 | border: 1px solid #666666; 17 | text-align: center; 18 | width: 138px; 19 | -moz-border-radius: 4px; 20 | -webkit-border-radius: 4px; 21 | border-radius: 4px; 22 | -webkit-box-shadow: 0px 0px 5px #999999; 23 | -moz-box-shadow: 0px 0px 5px #999999; 24 | box-shadow: 0px 0px 5px #999999; 25 | } 26 | .ua-ie-5 .browser-info, 27 | .ua-ie-6 .browser-info { 28 | /* IE6 24-bit PNGs lack alpha */ 29 | background-color: #C8D0D4;/*#C1CACF;*/ 30 | /*border-color: #999999;*/ 31 | } 32 | 33 | .browser-info { 34 | background-image: url(browsers/Unknown_128.png); 35 | padding-top: 150px; 36 | } 37 | 38 | /* Trident-based browsers */ 39 | .ua-ie .browser-info { 40 | border-color: #50BEF2; 41 | border-top-color: #FFE446; 42 | border-left-color: #FFE446; 43 | background-image: url(browsers/IE9_128.png); 44 | } 45 | .ua-ie-8 .browser-info, 46 | .ua-ie-7 .browser-info { 47 | border-color: #0979CF; 48 | border-top-color: #F6B718; 49 | border-left-color: #F6B718; 50 | background-image: url(browsers/IE7_128.png); 51 | } 52 | .ua-ie-6 .browser-info { 53 | border-color: #37A9F4; 54 | border-top-color: #ADEFFD; 55 | border-left-color: #ADEFFD; 56 | background-image: url(browsers/IE6_128.png); 57 | height: 128px; 58 | } 59 | .ua-ie-5-5 .browser-info { 60 | border-color: #2E5AFF; 61 | border-top-color: #FFD573; 62 | border-left-color: #FFD573; 63 | background-image: url(browsers/IE5_48.png); 64 | background-position: center 40px; 65 | height: 128px; 66 | } 67 | .ua-edge .browser-info { 68 | border-color: #50BEF2; 69 | border-top-color: #FFE446; 70 | border-left-color: #FFE446; 71 | background-image: url(browsers/Edge_128.png); 72 | } 73 | 74 | /* WebKit-based browsers */ 75 | .ua-webkit .browser-info { 76 | border-color: #2D3494; 77 | border-top-color: #DDBB7A; 78 | border-left-color: #DDBB7A; 79 | background-image: url(browsers/WebKit_128.png); 80 | } 81 | .ua-safari .browser-info { 82 | border-color: #1758A4; 83 | border-top-color: #999999; 84 | border-left-color: #999999; 85 | background-image: url(browsers/Safari_128.png); 86 | } 87 | .ua-chrome .browser-info, 88 | .ua-crios .browser-info { 89 | border-color: #2BAB33; 90 | border-top-color: #FFD600; 91 | border-left-color: #FFD600; 92 | background-image: url(browsers/Chrome11_128.png); 93 | } 94 | .ua-chrome-0 .browser-info, 95 | .ua-chrome-1 .browser-info, 96 | .ua-chrome-2 .browser-info, 97 | .ua-chrome-3 .browser-info, 98 | .ua-chrome-4 .browser-info, 99 | .ua-chrome-5 .browser-info, 100 | .ua-chrome-6 .browser-info, 101 | .ua-chrome-7 .browser-info, 102 | .ua-chrome-8 .browser-info, 103 | .ua-chrome-9 .browser-info, 104 | .ua-chrome-10 .browser-info { 105 | background-image: url(browsers/Chrome_128.png); 106 | } 107 | .ua-silk .browser-info { 108 | border-color: #0176E0; 109 | border-top-color: #F78013; 110 | border-left-color: #F78013; 111 | background-image: url(browsers/Silk_128.png); 112 | } 113 | .ua-fluidapp .browser-info { 114 | border-color: #0E4EB7; 115 | border-top-color: #7AB35D; 116 | border-left-color: #7AB35D; 117 | background-image: url(browsers/Fluid_128.png); 118 | } 119 | .ua-phantomjs .browser-info { 120 | border-color: #008D92; 121 | border-top-color: #00CDD7; 122 | border-left-color: #00CDD7; 123 | background-image: url(browsers/PhantomJS_128.png); 124 | } 125 | .ua-vivaldi .browser-info { 126 | border-color: #D31D27; 127 | border-top-color: #F83D45; 128 | border-left-color: #F83D45; 129 | background-image: url(browsers/Vivaldi_128.png); 130 | } 131 | .ua-shiira .browser-info { 132 | border-color: #0198C2; 133 | border-top-color: #A86FE6; 134 | border-left-color: #A86FE6; 135 | background-image: url(browsers/Shiira_128.png); 136 | } 137 | .ua-omniweb .browser-info { 138 | border-color: #2B84EA; 139 | border-top-color: #62AE0C; 140 | border-left-color: #62AE0C; 141 | background-image: url(browsers/OmniWeb_128.png); 142 | } 143 | 144 | /* KHTML-based browsers */ 145 | .ua-khtml .browser-info, 146 | .ua-konqueror .browser-info { 147 | border-color: #5CAFFF; 148 | border-top-color: #E0E0E6; 149 | border-left-color: #E0E0E6; 150 | background-image: url(browsers/Konqueror_128.png); 151 | } 152 | 153 | /* Opera & Presto-based browsers */ 154 | .ua-opera .browser-info { 155 | border-color: #7C0B15; 156 | border-top-color: #D91728; 157 | border-left-color: #D91728; 158 | background-image: url(browsers/Opera_128.png); 159 | } 160 | .ua-opera-1 .browser-info, 161 | .ua-opera-2 .browser-info, 162 | .ua-opera-3 .browser-info, 163 | .ua-opera-4 .browser-info, 164 | .ua-opera-5 .browser-info, 165 | .ua-opera-6 .browser-info, 166 | .ua-opera-7 .browser-info, 167 | .ua-opera-8 .browser-info, 168 | .ua-opera-9 .browser-info, 169 | .ua-opera-10 .browser-info, 170 | .ua-opera-11 .browser-info, 171 | .ua-opera-12 .browser-info, 172 | .ua-opera-13 .browser-info, 173 | .ua-opera-14 .browser-info { 174 | border-color: #3B4959; 175 | border-top-color: #961D1F; 176 | border-left-color: #961D1F; 177 | background-image: url(browsers/Presto_128.png); 178 | } 179 | 180 | /* Gecko-based browsers */ 181 | .ua-gecko .browser-info { 182 | border-color: #075090; 183 | border-top-color: #2394C7; 184 | border-left-color: #2394C7; 185 | background-image: url(browsers/Gecko_128.png); 186 | } 187 | .ua-firebird .browser-info { 188 | border-color: #FB0F0C; 189 | border-top-color: #F96611; 190 | border-left-color: #F96611; 191 | background-image: url(browsers/Firebird2003_128.png); 192 | background-position: center 40px; 193 | } 194 | .ua-firefox .browser-info { 195 | border-color: #DE730C; 196 | border-top-color: #F9BE00; 197 | border-left-color: #F9BE00; 198 | background-image: url(browsers/Firefox2013_128.png); 199 | } 200 | .ua-firefox-22 .browser-info, 201 | .ua-firefox-21 .browser-info, 202 | .ua-firefox-20 .browser-info, 203 | .ua-firefox-19 .browser-info, 204 | .ua-firefox-18 .browser-info, 205 | .ua-firefox-17 .browser-info, 206 | .ua-firefox-16 .browser-info, 207 | .ua-firefox-15 .browser-info, 208 | .ua-firefox-14 .browser-info, 209 | .ua-firefox-13 .browser-info, 210 | .ua-firefox-12 .browser-info, 211 | .ua-firefox-11 .browser-info, 212 | .ua-firefox-10 .browser-info, 213 | .ua-firefox-9 .browser-info, 214 | .ua-firefox-8 .browser-info, 215 | .ua-firefox-7 .browser-info, 216 | .ua-firefox-6 .browser-info, 217 | .ua-firefox-5 .browser-info, 218 | .ua-firefox-4 .browser-info, 219 | .ua-firefox-3 .browser-info { 220 | background-image: url(browsers/Firefox2009_128.png); 221 | } 222 | .ua-firefox-3-1 .browser-info, 223 | .ua-firefox-3-0 .browser-info, 224 | .ua-firefox-2 .browser-info, 225 | .ua-firefox-1 .browser-info { 226 | background-image: url(browsers/Firefox2005_128.png); 227 | } 228 | .ua-firefox-1-1 .browser-info, 229 | .ua-firefox-1-0 .browser-info, 230 | .ua-firefox-0 .browser-info { 231 | background-image: url(browsers/Firefox2004_128.png); 232 | } 233 | .ua-seamonkey .browser-info { 234 | border-color: #2424E4; 235 | border-top-color: #8484FE; 236 | border-left-color: #8484FE; 237 | background-image: url(browsers/SeaMonkey_128.png); 238 | } 239 | .ua-camino .browser-info { 240 | border-color: #5C8FD6; 241 | border-top-color: #E5E5E5; 242 | border-left-color: #E5E5E5; 243 | background-image: url(browsers/Camino_128.png); 244 | } 245 | .ua-flock .browser-info { 246 | border-color: #9A9FA4; 247 | border-top-color: #358DEC; 248 | border-left-color: #358DEC; 249 | background-image: url(browsers/Flock_128.png); 250 | } 251 | .ua-epiphany .browser-info { 252 | border-color: #526F8B; 253 | border-top-color: #358B35; 254 | border-left-color: #358B35; 255 | background-image: url(browsers/Epiphany_64.png); 256 | background-position: center 32px; 257 | } 258 | .ua-galeon .browser-info { 259 | border-color: #2B468D; 260 | border-top-color: #A6CBF1; 261 | border-left-color: #A6CBF1; 262 | background-image: url(browsers/Galeon_128.png); 263 | } 264 | .ua-k-meleon .browser-info { 265 | border-color: #B24E51; 266 | border-top-color: #7BBA4A; 267 | border-left-color: #7BBA4A; 268 | background-image: url(browsers/K-Meleon_82.png); 269 | background-position: center 23px; 270 | } 271 | .ua-netscape .browser-info { 272 | border-color: #12424E; 273 | border-top-color: #36ABB1; 274 | border-left-color: #36ABB1; 275 | background-image: url(browsers/Netscape_128.png); 276 | } 277 | .ua-netscape-8 .browser-info { 278 | background-image: url(browsers/Netscape8_128.png); 279 | } 280 | .ua-netscape-9 .browser-info { 281 | background-image: url(browsers/Netscape9_128.png); 282 | } 283 | .ua-icab .browser-info { 284 | border-color: #FA2C2C; 285 | border-top-color: #FFD00A; 286 | border-left-color: #FFD00A; 287 | background-image: url(browsers/iCab3_128.png); 288 | } 289 | .ua-icab-4 .browser-info { 290 | background-image: url(browsers/iCab4_128.png); 291 | } 292 | 293 | /* Mozilla catch all */ 294 | .ua-mozilla .browser-info { 295 | border-color: #024690; 296 | border-top-color: #3688E0; 297 | border-left-color: #3688E0; 298 | background-image: url(browsers/Mozilla_128.png); 299 | } 300 | 301 | .no-js .js-enabled, 302 | .js .js-disabled { 303 | display: none; 304 | } 305 | 306 | @media print { 307 | .browser-info { 308 | background-color: white !important; 309 | border-color: black !important; 310 | } 311 | } 312 | 313 | .ua-test ul { 314 | float: left; 315 | width: 25%; 316 | margin-top: 0.5em; 317 | margin-bottom: 0.5em; 318 | } 319 | .ua-test li { 320 | text-decoration: line-through; 321 | white-space: nowrap; 322 | } 323 | 324 | .ua-ie .is-ie, 325 | .ua-ie-5 .is-ie-5, 326 | .ua-ie-5-0 .is-ie-5-0, 327 | .ua-ie-5-5 .is-ie-5-5, 328 | .ua-ie-6 .is-ie-6, 329 | .ua-ie-7 .is-ie-7, 330 | .ua-ie-8 .is-ie-8, 331 | .ua-ie-9 .is-ie-9, 332 | .ua-ie-10 .is-ie-10, 333 | .ua-gecko .is-gecko, 334 | .ua-gecko-1-7 .is-gecko-1-7, 335 | .ua-gecko-1-8 .is-gecko-1-8, 336 | .ua-gecko-1-9 .is-gecko-1-9, 337 | .ua-gecko-2 .is-gecko-2, 338 | .ua-firefox .is-firefox, 339 | .ua-firefox-1 .is-firefox-1, 340 | .ua-firefox-1-0 .is-firefox-1-0, 341 | .ua-firefox-1-5 .is-firefox-1-5, 342 | .ua-firefox-2 .is-firefox-2, 343 | .ua-firefox-3 .is-firefox-3, 344 | .ua-firefox-3-0 .is-firefox-3-0, 345 | .ua-firefox-3-1 .is-firefox-3-1, 346 | .ua-firefox-3-5 .is-firefox-3-5, 347 | .ua-firefox-3-6 .is-firefox-3-6, 348 | .ua-firefox-4 .is-firefox-4, 349 | .ua-firefox-4-0 .is-firefox-4-0, 350 | .ua-seamonkey .is-seamonkey, 351 | .ua-seamonkey-1 .is-seamonkey-1, 352 | .ua-seamonkey-1-1 .is-seamonkey-1-1, 353 | .ua-netscape .is-netscape, 354 | .ua-netscape-5 .is-netscape-5, 355 | .ua-netscape-6 .is-netscape-6, 356 | .ua-netscape-7 .is-netscape-7, 357 | .ua-netscape-7-2 .is-netscape-7-2, 358 | .ua-netscape-8 .is-netscape-8, 359 | .ua-netscape-9 .is-netscape-9, 360 | .ua-safari .is-safari, 361 | .ua-safari-85 .is-safari-85, 362 | .ua-safari-419 .is-safari-419, 363 | .ua-safari-419-3 .is-safari-419-3, 364 | .ua-safari-522 .is-safari-522, 365 | .ua-safari-523 .is-safari-523, 366 | .ua-safari-525 .is-safari-525, 367 | .ua-safari-525-21 .is-safari-525-21, 368 | .ua-safari-525-27 .is-safari-525-27, 369 | .ua-safari-526 .is-safari-526, 370 | .ua-safari-530-17 .is-safari-530-17, 371 | .ua-webkit .is-webkit, 372 | .ua-webkit-419 .is-webkit-419, 373 | .ua-webkit-420 .is-webkit-420, 374 | .ua-webkit-525 .is-webkit-525, 375 | .ua-webkit-525-27-1 .is-webkit-525-27-1, 376 | .ua-webkit-530 .is-webkit-530, 377 | .ua-webkit-532 .is-webkit-532, 378 | .ua-chrome .is-chrome, 379 | .ua-chrome-0 .is-chrome-0, 380 | .ua-chrome-0-2 .is-chrome-0-2, 381 | .ua-chrome-1 .is-chrome-1, 382 | .ua-chrome-2 .is-chrome-2, 383 | .ua-chrome-3-0-201 .is-chrome-3-0-201, 384 | .ua-chrome-4-0 .is-chrome-4-0, 385 | .ua-chrome-4-0-249-89 .is-chrome-4-0-249-89, 386 | .ua-chrome-5 .is-chrome-5, 387 | .ua-chrome-6 .is-chrome-6, 388 | .ua-chrome-7 .is-chrome-7, 389 | .ua-chrome-8 .is-chrome-8, 390 | .ua-chrome-9 .is-chrome-9, 391 | .ua-chrome-10 .is-chrome-10, 392 | .ua-chrome-11 .is-chrome-11, 393 | .ua-opera .is-opera, 394 | .ua-opera-7 .is-opera-7, 395 | .ua-opera-8 .is-opera-8, 396 | .ua-opera-9 .is-opera-9, 397 | .ua-opera-9-52 .is-opera-9-52, 398 | .ua-opera-10 .is-opera-10, 399 | .ua-camino .is-camino, 400 | .ua-camino-1 .is-camino-1, 401 | .ua-icab .is-icab, 402 | .ua-icab-2 .is-icab-2, 403 | .ua-icab-4 .is-icab-4, 404 | .ua-icab-4 .is-icab-4 { 405 | font-weight: bold; 406 | color: #CC0000; 407 | background-color: #FFFF00; 408 | text-decoration: none; 409 | } -------------------------------------------------------------------------------- /test/styles/browsers/BrowserStack_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/BrowserStack_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Camino_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Camino_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Chrome11_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Chrome11_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Chrome_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Chrome_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Edge_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Edge_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Epiphany_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Epiphany_64.png -------------------------------------------------------------------------------- /test/styles/browsers/Firebird2003_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Firebird2003_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Firefox2004_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Firefox2004_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Firefox2005_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Firefox2005_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Firefox2009_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Firefox2009_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Firefox2013_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Firefox2013_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Flock_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Flock_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Fluid_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Fluid_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Galeon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Galeon_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Gecko_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Gecko_128.png -------------------------------------------------------------------------------- /test/styles/browsers/IE4-Mac_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/IE4-Mac_128.png -------------------------------------------------------------------------------- /test/styles/browsers/IE5-Mac_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/IE5-Mac_128.png -------------------------------------------------------------------------------- /test/styles/browsers/IE5_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/IE5_48.png -------------------------------------------------------------------------------- /test/styles/browsers/IE6_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/IE6_128.png -------------------------------------------------------------------------------- /test/styles/browsers/IE7_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/IE7_128.png -------------------------------------------------------------------------------- /test/styles/browsers/IE9_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/IE9_128.png -------------------------------------------------------------------------------- /test/styles/browsers/K-Meleon_82.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/K-Meleon_82.png -------------------------------------------------------------------------------- /test/styles/browsers/Konqueror_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Konqueror_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Mozilla_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Mozilla_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Netscape8_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Netscape8_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Netscape9_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Netscape9_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Netscape_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Netscape_128.png -------------------------------------------------------------------------------- /test/styles/browsers/OmniWeb_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/OmniWeb_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Opera_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Opera_128.png -------------------------------------------------------------------------------- /test/styles/browsers/PhantomJS_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/PhantomJS_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Presto_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Presto_128.png -------------------------------------------------------------------------------- /test/styles/browsers/README.txt: -------------------------------------------------------------------------------- 1 | These browser logos are protected by copyright and/or trademark. 2 | 3 | It is believed that the use of low-resolution images of logos for certain uses involving 4 | identification may qualify as fair use under United States copyright law. 5 | 6 | Any other uses of this image, may be copyright infringement. Certain commercial use of this 7 | image may also be trademark infringement. 8 | -------------------------------------------------------------------------------- /test/styles/browsers/Safari_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Safari_128.png -------------------------------------------------------------------------------- /test/styles/browsers/SeaMonkey_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/SeaMonkey_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Shiira_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Shiira_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Silk_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Silk_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Swift_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Swift_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Unknown_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Unknown_128.png -------------------------------------------------------------------------------- /test/styles/browsers/Vivaldi_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/Vivaldi_128.png -------------------------------------------------------------------------------- /test/styles/browsers/WebKit_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/WebKit_128.png -------------------------------------------------------------------------------- /test/styles/browsers/iCab3_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/iCab3_128.png -------------------------------------------------------------------------------- /test/styles/browsers/iCab4_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/browsers/iCab4_128.png -------------------------------------------------------------------------------- /test/styles/fonts/League_Gothic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/fonts/League_Gothic-webfont.eot -------------------------------------------------------------------------------- /test/styles/fonts/League_Gothic-webfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | This is a custom SVG webfont generated by Font Squirrel. 6 | Copyright : Generated in 2009 by FontLab Studio Copyright info pending 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /test/styles/fonts/League_Gothic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/fonts/League_Gothic-webfont.ttf -------------------------------------------------------------------------------- /test/styles/fonts/League_Gothic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/fonts/League_Gothic-webfont.woff -------------------------------------------------------------------------------- /test/styles/fonts/SIL Open Font License 1.1.txt: -------------------------------------------------------------------------------- 1 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 2 | This license is copied below, and is also available with a FAQ at: 3 | http://scripts.sil.org/OFL 4 | 5 | 6 | ----------------------------------------------------------- 7 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 8 | ----------------------------------------------------------- 9 | 10 | PREAMBLE 11 | The goals of the Open Font License (OFL) are to stimulate worldwide 12 | development of collaborative font projects, to support the font creation 13 | efforts of academic and linguistic communities, and to provide a free and 14 | open framework in which fonts may be shared and improved in partnership 15 | with others. 16 | 17 | The OFL allows the licensed fonts to be used, studied, modified and 18 | redistributed freely as long as they are not sold by themselves. The 19 | fonts, including any derivative works, can be bundled, embedded, 20 | redistributed and/or sold with any software provided that any reserved 21 | names are not used by derivative works. The fonts and derivatives, 22 | however, cannot be released under any other type of license. The 23 | requirement for fonts to remain under this license does not apply 24 | to any document created using the fonts or their derivatives. 25 | 26 | DEFINITIONS 27 | "Font Software" refers to the set of files released by the Copyright 28 | Holder(s) under this license and clearly marked as such. This may 29 | include source files, build scripts and documentation. 30 | 31 | "Reserved Font Name" refers to any names specified as such after the 32 | copyright statement(s). 33 | 34 | "Original Version" refers to the collection of Font Software components as 35 | distributed by the Copyright Holder(s). 36 | 37 | "Modified Version" refers to any derivative made by adding to, deleting, 38 | or substituting -- in part or in whole -- any of the components of the 39 | Original Version, by changing formats or by porting the Font Software to a 40 | new environment. 41 | 42 | "Author" refers to any designer, engineer, programmer, technical 43 | writer or other person who contributed to the Font Software. 44 | 45 | PERMISSION & CONDITIONS 46 | Permission is hereby granted, free of charge, to any person obtaining 47 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 48 | redistribute, and sell modified and unmodified copies of the Font 49 | Software, subject to the following conditions: 50 | 51 | 1) Neither the Font Software nor any of its individual components, 52 | in Original or Modified Versions, may be sold by itself. 53 | 54 | 2) Original or Modified Versions of the Font Software may be bundled, 55 | redistributed and/or sold with any software, provided that each copy 56 | contains the above copyright notice and this license. These can be 57 | included either as stand-alone text files, human-readable headers or 58 | in the appropriate machine-readable metadata fields within text or 59 | binary files as long as those fields can be easily viewed by the user. 60 | 61 | 3) No Modified Version of the Font Software may use the Reserved Font 62 | Name(s) unless explicit written permission is granted by the corresponding 63 | Copyright Holder. This restriction only applies to the primary font name as 64 | presented to the users. 65 | 66 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 67 | Software shall not be used to promote, endorse or advertise any 68 | Modified Version, except to acknowledge the contribution(s) of the 69 | Copyright Holder(s) and the Author(s) or with their explicit written 70 | permission. 71 | 72 | 5) The Font Software, modified or unmodified, in part or in whole, 73 | must be distributed entirely under this license, and must not be 74 | distributed under any other license. The requirement for fonts to 75 | remain under this license does not apply to any document created 76 | using the Font Software. 77 | 78 | TERMINATION 79 | This license becomes null and void if any of the above conditions are 80 | not met. 81 | 82 | DISCLAIMER 83 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 84 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 85 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 86 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 87 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 88 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 89 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 90 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 91 | OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /test/styles/main.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'LeagueGothicRegular'; 3 | src: url('fonts/League_Gothic-webfont.eot'); /* IE9 Compat Modes */ 4 | src: url('fonts/League_Gothic-webfont.eot?iefix') format('eot'), /* IE6-IE8 */ 5 | url('fonts/League_Gothic-webfont.woff') format('woff'), /* Modern Browsers */ 6 | url('fonts/League_Gothic-webfont.ttf') format('truetype'), /* Safari, Android, iOS */ 7 | url('fonts/League_Gothic-webfont.svg#svgFontName') format('svg'); /* Legacy iOS */ 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | 12 | body 13 | { 14 | background-color: #F8F8F8; 15 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 16 | font-size: 9pt; 17 | margin: 0 auto; 18 | padding: 0; 19 | width: 60%; 20 | min-width: 600px; 21 | } 22 | 23 | .logo, .logo a:link, .logo a:hover, .logo a:active, .logo a:visited 24 | { 25 | color: #999; 26 | font-family: LeagueGothicRegular, 'League Gothic', Helvetica, Tahoma, Arial, sans-serif; 27 | font-size: 125px; 28 | font-weight: normal;/* http://stackoverflow.com/q/5069752/43217 */ 29 | margin: 0 auto 0.25em; 30 | padding: 0; 31 | text-align: center; 32 | text-decoration: none; 33 | text-shadow: 0px -1px 1px silver; 34 | white-space: nowrap; 35 | } 36 | 37 | .logo .sel 38 | { 39 | color: #555; 40 | } 41 | .logo .rule 42 | { 43 | color: #666; 44 | } 45 | .logo .expr 46 | { 47 | color: #444; 48 | } 49 | 50 | h2 51 | { 52 | border-bottom: 1px solid #CCE5FF; 53 | font-size: 12pt; 54 | } 55 | 56 | dl, ul 57 | { 58 | margin: 0; 59 | padding: 0.75em 0.25em; 60 | } 61 | 62 | dl dt 63 | { 64 | font-weight: bold; 65 | margin: 0 0 0 0.5em; 66 | padding: 0.25em 0 0.25em 0.5em; 67 | } 68 | 69 | dl dd 70 | { 71 | list-style-type: none; 72 | margin: 0 0 0 1.5em; 73 | padding: 0.25em 0 0.25em 1.5em; 74 | } 75 | 76 | ul li 77 | { 78 | list-style-type: none; 79 | margin: 0 0 0 0.5em; 80 | padding: 0.25em 0 0.25em 0.5em; 81 | } 82 | 83 | a:link, a:hover, a:active, a:visited 84 | { 85 | color: #006699; 86 | } 87 | 88 | footer 89 | { 90 | border-top: 1px solid #F1F1F1; 91 | clear: both; 92 | margin: 4em auto; 93 | padding-top: 1em; 94 | text-align: right; 95 | } 96 | 97 | /* http://ubuwaits.github.com/css3-buttons/ */ 98 | 99 | a.minimal:link, a.minimal:visited 100 | { 101 | background: #e3e3e3; 102 | border: 1px solid #ccc; 103 | border-radius: 3px; 104 | -moz-border-radius: 3px; 105 | -webkit-border-radius: 3px; 106 | box-shadow: inset 0px 0px 1px 1px #f6f6f6; 107 | -moz-box-shadow: inset 0px 0px 1px 1px #f6f6f6; 108 | -webkit-box-shadow: inset 0px 0px 1px 1px #f6f6f6; 109 | color: #333; 110 | display: inline-block; 111 | font-family: "helvetica neue", helvetica, arial, sans-serif; 112 | font-size: 12px; 113 | font-weight: bold; 114 | line-height: 1; 115 | padding: 8px 0 9px; 116 | text-align: center; 117 | text-decoration: none; 118 | text-shadow: 0 1px 0px #fff; 119 | width: 150px; 120 | } 121 | 122 | a.minimal:hover 123 | { 124 | background: #d9d9d9; 125 | box-shadow: inset 0px 0px 1px 1px #eaeaea; 126 | -moz-box-shadow: inset 0px 0px 1px 1px #eaeaea; 127 | -webkit-box-shadow: inset 0px 0px 1px 1px #eaeaea; 128 | color: #222; 129 | } 130 | 131 | a.minimal:active 132 | { 133 | background: #d0d0d0; 134 | box-shadow: inset 0px 0px 1px 1px #e3e3e3; 135 | -moz-box-shadow: inset 0px 0px 1px 1px #e3e3e3; 136 | -webkit-box-shadow: inset 0px 0px 1px 1px #e3e3e3; 137 | color: #000; 138 | } 139 | 140 | .summary 141 | { 142 | font-size: 12pt; 143 | text-align: center; 144 | } 145 | 146 | .box 147 | { 148 | background-color: #FFF; 149 | border: 1px solid silver; 150 | border-radius: 4px; 151 | -moz-border-radius: 4px; 152 | -webkit-border-radius: 4px; 153 | box-shadow: 0px 0px 4px #DDDDDD; 154 | -moz-box-shadow: 0px 0px 4px #DDDDDD; 155 | -webkit-box-shadow: 0px 0px 4px #DDDDDD; 156 | float: left; 157 | height: 200px; 158 | line-height: 1.75em; 159 | margin: 1% 0 1% 7%; 160 | padding: 0.125em 0.5em; 161 | 162 | width: 20em; 163 | } 164 | 165 | .box h2 166 | { 167 | border-bottom: none; 168 | color: #666; 169 | font-size: 30px; 170 | font-weight: bold; 171 | text-align: center; 172 | text-shadow: 0 -1px 1px silver; 173 | text-transform: lowercase; 174 | } 175 | 176 | .box ul, .box li 177 | { 178 | list-style-type: none; 179 | margin: 0; 180 | padding: 0; 181 | white-space: nowrap; 182 | } 183 | 184 | .box a.minimal 185 | { 186 | margin: 0.25em; 187 | text-transform: lowercase; 188 | } 189 | 190 | .box a.glow 191 | { 192 | box-shadow: 0px 0px 4px #FFCC00; 193 | -moz-box-shadow: 0px 0px 4px #FFCC00; 194 | -webkit-box-shadow: 0px 0px 4px #FFCC00; 195 | } 196 | 197 | .action-info 198 | { 199 | font-style: italic; 200 | white-space: nowrap; 201 | } 202 | -------------------------------------------------------------------------------- /test/styles/syntax.css: -------------------------------------------------------------------------------- 1 | /* 2 | syntax.css 3 | 4 | syntax highlighting stylesheet 5 | */ 6 | 7 | code 8 | { 9 | font-family: Consolas, Courier New, Courier, Monospace; 10 | } 11 | .ua-webkit code 12 | { 13 | font-size: 1.2em; 14 | } 15 | 16 | .example 17 | { 18 | background-color: #FFFFFF; 19 | border: 1px solid silver; 20 | border-radius: 4px; 21 | -moz-border-radius: 4px; 22 | -webkit-border-radius: 4px; 23 | box-shadow: 0px 0px 4px #DDDDDD; 24 | -moz-box-shadow: 0px 0px 4px #DDDDDD; 25 | -webkit-box-shadow: 0px 0px 4px #DDDDDD; 26 | display: block; 27 | padding: 0.125em 0.5em; 28 | padding: 1em; 29 | margin: auto 180px auto 4em; 30 | line-height: 1.75em; 31 | } 32 | code.example 33 | { 34 | line-height: normal; 35 | } 36 | p.example 37 | { 38 | padding: 0.25em; 39 | } 40 | 41 | /* Block */ 42 | .syntax .indent 43 | { 44 | padding: 0 0 0 2em; 45 | } 46 | 47 | /* Operator */ 48 | .syntax .op 49 | { 50 | color: #0000CC; 51 | white-space: nowrap; 52 | } 53 | 54 | /* Keyword */ 55 | .syntax .key 56 | { 57 | color: #0000FF; 58 | white-space: nowrap; 59 | } 60 | 61 | /* type */ 62 | .syntax .type 63 | { 64 | color: #2B91AF; 65 | white-space: nowrap; 66 | } 67 | 68 | /* String Literal */ 69 | .syntax .lit 70 | { 71 | color: #A31515; 72 | } 73 | 74 | /* comments */ 75 | .syntax .comment, 76 | .syntax .comment * 77 | { 78 | color: #008000; 79 | } 80 | 81 | /* Code Block */ 82 | .syntax .serv 83 | { 84 | color: black; 85 | background-color: #FFEE62; 86 | padding: 0 0.125em; 87 | } 88 | 89 | @media print 90 | { 91 | .syntax .op, 92 | .syntax .key, 93 | .syntax .lit, 94 | .syntax .type, 95 | .syntax .serv, 96 | .syntax .comment, 97 | .syntax .comment * 98 | { 99 | color: black; 100 | } 101 | 102 | .syntax, 103 | .syntax .serv 104 | { 105 | background-color: transparent; 106 | } 107 | 108 | .syntax 109 | { 110 | border-color: black; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /test/styles/validation.css: -------------------------------------------------------------------------------- 1 | /* 2 | validation.css 3 | 4 | validation badges stylesheet 5 | */ 6 | 7 | .badge-list li 8 | { 9 | display: block; 10 | } 11 | 12 | /* badge inactive state */ 13 | .badge-list a.badge-link 14 | { 15 | margin: 0.25em auto; 16 | display: block; 17 | background-image: url(validation.png); 18 | background-repeat: no-repeat; 19 | height: 35px; 20 | width: 92px; 21 | } 22 | .badge-list a.html-badge 23 | { 24 | background-position: top left; 25 | } 26 | .badge-list a.css-badge 27 | { 28 | background-position: top right; 29 | } 30 | 31 | /* badge hover state */ 32 | .badge-list a.html-badge:hover 33 | { 34 | background-position: bottom left; 35 | } 36 | .badge-list a.css-badge:hover 37 | { 38 | background-position: bottom right; 39 | } 40 | -------------------------------------------------------------------------------- /test/styles/validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mckamey/cssuseragent/04be9bfdebff14876c3cf024bc60370fe163763a/test/styles/validation.png -------------------------------------------------------------------------------- /test/styles/zebra.css: -------------------------------------------------------------------------------- 1 | /* 2 | Zebra.css 3 | 4 | zZebra list stylesheet 5 | */ 6 | 7 | .zebra 8 | { 9 | border: 1px solid #DDDDDD; 10 | margin: 0.5em 0; 11 | padding: 0; 12 | } 13 | .ua-ie-5 .zebra, 14 | .ua-ie-6 .zebra 15 | { 16 | border-color: #999999; 17 | } 18 | .zebra-item 19 | { 20 | background-color: #FFFFFF; 21 | padding: 0.25em; 22 | margin: 0; 23 | } 24 | .zebra-item-alt 25 | { 26 | background-color: #F5F5F5; 27 | } 28 | .ua-ie-5 .zebra-item, 29 | .ua-ie-6 .zebra-item 30 | { 31 | /* trigger hasLayout */ 32 | display: inline-block; 33 | } 34 | 35 | @media print 36 | { 37 | .zebra 38 | { 39 | border-color: black; 40 | border-bottom-style: none; 41 | } 42 | 43 | .zebra-item, 44 | .zebra-item-alt 45 | { 46 | background-color: white; 47 | border-bottom: 1px solid black; 48 | } 49 | } -------------------------------------------------------------------------------- /test/unit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CssUserAgent (cssua.js) Unit Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

CssUserAgent (cssua.js) Unit Tests

16 |

17 |

18 |
    19 | 20 | 21 | --------------------------------------------------------------------------------