├── .eslintrc ├── .npmignore ├── .travis.yml ├── CONTRIBUTING.md ├── FAQ.md ├── ISSUE_TEMPLATE.md ├── README.md ├── bower.json ├── dist └── fingerprint2.min.js ├── fingerprint2.js ├── flash ├── FontList.as ├── Makefile └── compiled │ └── FontList.swf ├── gulpfile.js ├── index.html ├── package.json ├── specs ├── lib │ └── jasmine-2.3.4 │ │ ├── boot.js │ │ ├── jasmine-html.js │ │ ├── jasmine-matchers.js │ │ ├── jasmine.css │ │ ├── jasmine.js │ │ ├── jasmine_favicon.png │ │ └── terminal.js ├── phantomjs-testrunner.js ├── phantomjs.runner.sh ├── spec_runner.html └── specs.js ├── test.php └── yarn.lock /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "amd": true 6 | }, 7 | "globals": { 8 | "swfobject": true, 9 | "ActiveXObject": true 10 | }, 11 | "rules": { 12 | "no-fallthrough": false, 13 | "yoda": false, 14 | "no-shadow": false, 15 | "no-new": false, 16 | "no-extend-native": false 17 | }, 18 | 19 | } 20 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | fingerprintjs2.iml 4 | *.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | script: "specs/phantomjs.runner.sh specs/spec_runner.html" 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to FingerprintJS2 2 | ============================== 3 | 4 | # IF YOU WANT TO ASK A QUESTION - USE GITTER.IM OR STACKOVERFLOW.COM with fingerprintjs2 tag 5 | 6 | PLEASE :) 7 | 8 | ISSUES WITH QUESTIONS IN THEM WILL BE CLOSED W/OUT EXPLANATION! 9 | 10 | ## Found a bug? 11 | 12 | Please submit an issue. 13 | Include in the issue: 14 | 15 | * List of components you received in the `get` call (make sure values are not truncated) 16 | * If FP is different every time you call the library, include 2 versions of components 17 | * Include your OS version 18 | * Include steps to reproduce 19 | * Include library call code (I need all options you used when calling the library function) 20 | 21 | ## Want to add a feature / contribute? 22 | * Make sure the issue/suggestion does not exist by searching existing issues 23 | * Fork the project and make the required changes in it (don't forget to add specs) 24 | * PRs w/out specs will not be accepted 25 | * Run `gulp` to catch stylistic errors and produce the minified version. 26 | * Run specs by opening the `specs/spec_runner.html` or typing `npm test` (requires phantomjs for console running). 27 | * Make a PR. 28 | * Make sure you only make one commit per feature you want to add 29 | * Make sure your commit message is descriptive and tells what you changed (`Updated the library` - that's a bad commit message) 30 | 31 | If your code changes the list of fingerprinting sources, please update 32 | the README. 33 | 34 | If you're unsure about the feature you want to add, submit an issue with 35 | a `question` tag. 36 | 37 | ## Want to ask? 38 | * Please read FAQ first 39 | * If you have not found the answer you were looking for - use gitter.im to ask your question (link is in the readme) 40 | 41 | Happy Coding! 42 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | #### Can I use this library to uniquely identify users? 2 | ##### No, you cannot. This library is built to be able to associate string identifiers with devices. Since there are a lot of identical devices, you will get a lot of identical identifiers. 3 | 4 | #### OK, I get it, I cannot _uniquely_ identify users, but can I identify users at all? 5 | ##### No, you cannot. This library is strictly for non-deterministic device identification. 6 | 7 | 8 | #### How good is your library? Can you guarantee that different devices will have different identifiers? 9 | ##### This library is not good. It has an error margin of 10-20% 10 | 11 | #### Can you improve the library to be 100% accurate for device identification? 12 | ##### I don't think it is possible now and don't think it will be possible in the future. 13 | 14 | #### Can you improve the library to be more accurate (since you cannot make it 100% accurate)? 15 | ##### I can, but it takes a lot of time. I need a lot of devices, enviroments and more importantly - time. Since this is my hobby project, I spend very little time on it. 16 | 17 | #### How can I build a complete identification solution? 18 | ##### You should either use commercial services, such as https://augur.io, or develop such service yourself. If you don't know how to do it, please use StackOverflow. 19 | 20 | #### The fingerprint is changing frequently for me, is library broken? 21 | ##### Well, most likely not. You may have different user agents (because of the browser ugprades), or different screen resolutions. You can disable corresponding options (please see README and Wiki for details). V2 will have a callback/extension system which will allow to use UserAgent parsers and cut off the frequently changing parts of user agents (such as verion numbers). If you're sure it is a bug, please submit an issue and don't forget to attach the relevant info about which component, participating in fingerprint building, changes for your from call to call. This will greatly help me fix the issue 22 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Before submitting an issue please read: 2 | 3 | ### https://github.com/Valve/fingerprintjs2/blob/master/FAQ.md 4 | 5 | ### https://github.com/Valve/fingerprintjs2/blob/master/CONTRIBUTING.md 6 | 7 | If your issue is about fingerprint changing fast/being inconsistent, please search for already closed issues about it, you may find your answer there. 8 | 9 | This can be probably because of your browser upgrade (User agent string changing), different screen resolution on various devices (fixable by disabling screen_resolution option). 10 | 11 | If you want to submit a bug, please let me know which browser/os you're using and which fingerprinting component is broken and/or different for you on multiple library calls. 12 | 13 | 14 | If you want to ask a question - please use gitter.im (badge is in the readme) and ping me with @valve handle, I'll gladly answer specific questions about the library. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fingerprintjs2 2 | [![](https://travis-ci.org/Valve/fingerprintjs2.svg?branch=master)](https://travis-ci.org/Valve/fingerprintjs2) 3 | [![](https://badges.gitter.im/Valve/fingerprintjs2.svg)](https://gitter.im/Valve/fingerprintjs2) 4 | 5 | Original fingerprintjs library was developed in 2012, it's now impossible to evolve it 6 | without breaking backwards compatibilty, so this project will be where 7 | all the new development happens. 8 | 9 | This project will use significantly more sources for fingerprinting, all 10 | of them will be configurable, that is it should be possible to 11 | cherry-pick only the options you need or just enable them all. 12 | 13 | I'm also paying special attention to IE plugins, popular in China, such 14 | as QQ, Baidu and others. 15 | 16 | This project will not be backwards compatible with original 17 | fingerprintjs. 18 | 19 | This project uses `semver`. 20 | 21 | ### Installation 22 | 23 | #### CDN: 24 | ``` 25 | //cdn.jsdelivr.net/fingerprintjs2//fingerprint2.min.js 26 | ``` 27 | or 28 | 29 | ``` 30 | https://cdnjs.com/libraries/fingerprintjs2 31 | ``` 32 | 33 | #### Bower 34 | 35 | ``` 36 | bower install fingerprintjs2 37 | ``` 38 | 39 | #### NPM 40 | 41 | ``` 42 | npm install fingerprintjs2 43 | ``` 44 | 45 | 46 | ### Usage 47 | 48 | ```js 49 | new Fingerprint2().get(function(result, components){ 50 | console.log(result); //a hash, representing your device fingerprint 51 | console.log(components); // an array of FP components 52 | }); 53 | ``` 54 | 55 | #### You can pass an object with options (all of which are optional): 56 | 57 | ```js 58 | var options = {swfPath: '/assets/FontList.swf', excludeUserAgent: true}; 59 | new Fingerprint2(options).get(function(result){ 60 | console.log(result); 61 | }); 62 | ``` 63 | 64 | Full list of options will be in the 65 | (https://github.com/Valve/fingerprintjs2/wiki/List-of-options) wiki 66 | page. 67 | 68 | Flash font enumeration is disabled by default. JS code is used by 69 | default to get the list of available fonts. 70 | 71 | The reason for this is that Flash will not work in incognito mode. 72 | 73 | However, you can make the library to use Flash when detecting the fonts 74 | with: 75 | 76 | ```js 77 | excludeJsFonts: true 78 | ``` 79 | option. 80 | 81 | To use Flash font enumeration, make sure you have swfobject available. 82 | If you don't, the library will skip the Flash part entirely. 83 | 84 | #### `detectScreenOrientation` option is `true` by default 85 | 86 | To ensure consistent fingerprints when users rotate their mobile 87 | devices. 88 | 89 | 90 | ##### All fingerprinting sources are enabled by default, i.e. you don't need to explicitly configure the library to include them. 91 | 92 | ```js 93 | new Fingerprint2().get(function(result, components){ 94 | // this will use all available fingerprinting sources 95 | console.log(result); 96 | // components is an array of all fingerprinting components used 97 | console.log(components); 98 | }); 99 | ``` 100 | 101 | #### `userDefinedFonts` option 102 | 103 | While hundreds of the most popular fonts are included in the extended font list, you may wish to increase the entropy of the font fingerprint by specifying the `userDefinedFonts` option as an array of font names. 104 | 105 | ``` 106 | new Fingerprint2({ 107 | userDefinedFonts: ["Nimbus Mono", "Junicode", "Presto"] 108 | }).get(function(result, components){} 109 | console.log(result); 110 | ); 111 | ``` 112 | 113 | #### View the fingerprint locally 114 | 115 | You can view your browser fingerprint locally by starting a webserver and viewing the `index.html` page. 116 | Loading `index.html` from the filesystem won't work due to Flash's ExternalInterface security restrictions. 117 | 118 | To start a web server you can try using one of the following: 119 | 120 | * Ruby 1.9.2+ 121 | 122 | `ruby -run -e httpd . -p 8080` 123 | 124 | * Python 2.x 125 | 126 | `python -m SimpleHTTPServer 8080` 127 | 128 | * Python 3.x 129 | 130 | `python -m http.server 8080` 131 | 132 | * PHP 5.4+ 133 | 134 | `php -S 0.0.0.0:8080` 135 | 136 | 137 | ### List of fingerprinting sources 138 | 139 | 1. UserAgent 140 | 2. Language 141 | 3. Color Depth 142 | 4. Screen Resolution 143 | 5. Timezone 144 | 6. Has session storage or not 145 | 7. Has local storage or not 146 | 8. Has indexed DB 147 | 9. Has IE specific 'AddBehavior' 148 | 10. Has open DB 149 | 11. CPU class 150 | 12. Platform 151 | 13. DoNotTrack or not 152 | 14. Full list of installed fonts (maintaining their order, which increases the entropy), implemented with Flash. 153 | 15. A list of installed fonts, detected with JS/CSS (side-channel technique) - can detect up to 500 installed fonts without flash 154 | 16. Canvas fingerprinting 155 | 17. WebGL fingerprinting 156 | 18. Plugins (IE included) 157 | 19. Is AdBlock installed or not 158 | 20. Has the user tampered with its languages [1](https://github.com/Valve/fingerprintjs2/wiki/Browser-tampering) 159 | 21. Has the user tampered with its screen resolution [1](https://github.com/Valve/fingerprintjs2/wiki/Browser-tampering) 160 | 22. Has the user tampered with its OS [1](https://github.com/Valve/fingerprintjs2/wiki/Browser-tampering) 161 | 23. Has the user tampered with its browser [1](https://github.com/Valve/fingerprintjs2/wiki/Browser-tampering) 162 | 24. Touch screen detection and capabilities 163 | 25. Pixel Ratio 164 | 26. System's total number of logical processors available to the user agent. 165 | 166 | 167 | By default, JS font detection will only detect up to 65 installed fonts. If you want to improve the font detection, 168 | you can pass `extendedJsFonts: true` option. This will increase the number of detectable fonts to ~500. 169 | 170 | On my machine (MBP 2013 Core i5) + Chrome 46 the default FP process takes about 80-100ms. If you use `extendedJsFonts` option this time will increase up to 160-200ms. 171 | This option can incur even more overhead on mobile Firefox browsers, which is much slower in font detection, so use it with caution on mobile devices. 172 | 173 | ### Many more fingerprinting sources will be implemented, such as 174 | (in no particular order) 175 | 176 | * Multi-monitor detection, 177 | * Internal HashTable implementation detection 178 | * WebRTC fingerprinting 179 | * Math constants 180 | * Accessibility fingerprinting 181 | * Camera information 182 | * DRM support 183 | * Accelerometer support 184 | * Virtual keyboards 185 | * List of supported gestures (for touch-enabled devices) 186 | * Pixel density 187 | * Video and audio codecs availability 188 | * Audio stack fingerprinting 189 | 190 | #### To recompile the `FontList.swf` file: 191 | 192 | * Download [Adobe Flex SDK](http://www.adobe.com/devnet/flex/flex-sdk-download.html) 193 | * Unzip it, add the `bin/` directory to your `$PATH` (mxmlc binary should be in path) 194 | * Run `make` 195 | 196 | #### My talk about the library (in Russian) on FrontEnd Conf 2015 197 | 198 | https://player.vimeo.com/video/151208427 199 | 200 | #### License: MIT or Apache, whichever you prefer. 201 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fingerprintjs2", 3 | "description": "Modern & flexible browser fingerprinting library", 4 | "main": "fingerprint2.js", 5 | "moduleType": [ 6 | "es6" 7 | ], 8 | "keywords": [ 9 | "browser", 10 | "fingerprint", 11 | "fingerprinting", 12 | "security", 13 | "privacy" 14 | ], 15 | "authors": [ 16 | "Valentin Vasilev" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "spec" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /dist/fingerprint2.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t,i){"use strict";"function"==typeof define&&define.amd?define(i):"undefined"!=typeof module&&module.exports?module.exports=i():t.exports?t.exports=i():t[e]=i()}("Fingerprint2",this,function(){"use strict";var e=function(t){if(!(this instanceof e))return new e(t);var i={swfContainerId:"fingerprintjs2",swfPath:"flash/compiled/FontList.swf",detectScreenOrientation:!0,sortPluginsFor:[/palemoon/i],userDefinedFonts:[]};this.options=this.extend(t,i),this.nativeForEach=Array.prototype.forEach,this.nativeMap=Array.prototype.map};return e.prototype={extend:function(e,t){if(null==e)return t;for(var i in e)null!=e[i]&&t[i]!==e[i]&&(t[i]=e[i]);return t},get:function(e){var t=[];t=this.userAgentKey(t),t=this.languageKey(t),t=this.colorDepthKey(t),t=this.pixelRatioKey(t),t=this.hardwareConcurrencyKey(t),t=this.screenResolutionKey(t),t=this.availableScreenResolutionKey(t),t=this.timezoneOffsetKey(t),t=this.sessionStorageKey(t),t=this.localStorageKey(t),t=this.indexedDbKey(t),t=this.addBehaviorKey(t),t=this.openDatabaseKey(t),t=this.cpuClassKey(t),t=this.platformKey(t),t=this.doNotTrackKey(t),t=this.pluginsKey(t),t=this.canvasKey(t),t=this.webglKey(t),t=this.adBlockKey(t),t=this.hasLiedLanguagesKey(t),t=this.hasLiedResolutionKey(t),t=this.hasLiedOsKey(t),t=this.hasLiedBrowserKey(t),t=this.touchSupportKey(t),t=this.customEntropyFunction(t);var i=this;this.fontsKey(t,function(t){var a=[];i.each(t,function(e){var t=e.value;"undefined"!=typeof e.value.join&&(t=e.value.join(";")),a.push(t)});var r=i.x64hash128(a.join("~~~"),31);return e(r,t)})},customEntropyFunction:function(e){return"function"==typeof this.options.customFunction&&e.push({key:"custom",value:this.options.customFunction()}),e},userAgentKey:function(e){return this.options.excludeUserAgent||e.push({key:"user_agent",value:this.getUserAgent()}),e},getUserAgent:function(){return navigator.userAgent},languageKey:function(e){return this.options.excludeLanguage||e.push({key:"language",value:navigator.language||navigator.userLanguage||navigator.browserLanguage||navigator.systemLanguage||""}),e},colorDepthKey:function(e){return this.options.excludeColorDepth||e.push({key:"color_depth",value:screen.colorDepth||-1}),e},pixelRatioKey:function(e){return this.options.excludePixelRatio||e.push({key:"pixel_ratio",value:this.getPixelRatio()}),e},getPixelRatio:function(){return window.devicePixelRatio||""},screenResolutionKey:function(e){return this.options.excludeScreenResolution?e:this.getScreenResolution(e)},getScreenResolution:function(e){var t;return t=this.options.detectScreenOrientation&&screen.height>screen.width?[screen.height,screen.width]:[screen.width,screen.height],"undefined"!=typeof t&&e.push({key:"resolution",value:t}),e},availableScreenResolutionKey:function(e){return this.options.excludeAvailableScreenResolution?e:this.getAvailableScreenResolution(e)},getAvailableScreenResolution:function(e){var t;return screen.availWidth&&screen.availHeight&&(t=this.options.detectScreenOrientation?screen.availHeight>screen.availWidth?[screen.availHeight,screen.availWidth]:[screen.availWidth,screen.availHeight]:[screen.availHeight,screen.availWidth]),"undefined"!=typeof t&&e.push({key:"available_resolution",value:t}),e},timezoneOffsetKey:function(e){return this.options.excludeTimezoneOffset||e.push({key:"timezone_offset",value:(new Date).getTimezoneOffset()}),e},sessionStorageKey:function(e){return!this.options.excludeSessionStorage&&this.hasSessionStorage()&&e.push({key:"session_storage",value:1}),e},localStorageKey:function(e){return!this.options.excludeSessionStorage&&this.hasLocalStorage()&&e.push({key:"local_storage",value:1}),e},indexedDbKey:function(e){return!this.options.excludeIndexedDB&&this.hasIndexedDB()&&e.push({key:"indexed_db",value:1}),e},addBehaviorKey:function(e){return document.body&&!this.options.excludeAddBehavior&&document.body.addBehavior&&e.push({key:"add_behavior",value:1}),e},openDatabaseKey:function(e){return!this.options.excludeOpenDatabase&&window.openDatabase&&e.push({key:"open_database",value:1}),e},cpuClassKey:function(e){return this.options.excludeCpuClass||e.push({key:"cpu_class",value:this.getNavigatorCpuClass()}),e},platformKey:function(e){return this.options.excludePlatform||e.push({key:"navigator_platform",value:this.getNavigatorPlatform()}),e},doNotTrackKey:function(e){return this.options.excludeDoNotTrack||e.push({key:"do_not_track",value:this.getDoNotTrack()}),e},canvasKey:function(e){return!this.options.excludeCanvas&&this.isCanvasSupported()&&e.push({key:"canvas",value:this.getCanvasFp()}),e},webglKey:function(e){return this.options.excludeWebGL?e:this.isWebGlSupported()?(e.push({key:"webgl",value:this.getWebglFp()}),e):e},adBlockKey:function(e){return this.options.excludeAdBlock||e.push({key:"adblock",value:this.getAdBlock()}),e},hasLiedLanguagesKey:function(e){return this.options.excludeHasLiedLanguages||e.push({key:"has_lied_languages",value:this.getHasLiedLanguages()}),e},hasLiedResolutionKey:function(e){return this.options.excludeHasLiedResolution||e.push({key:"has_lied_resolution",value:this.getHasLiedResolution()}),e},hasLiedOsKey:function(e){return this.options.excludeHasLiedOs||e.push({key:"has_lied_os",value:this.getHasLiedOs()}),e},hasLiedBrowserKey:function(e){return this.options.excludeHasLiedBrowser||e.push({key:"has_lied_browser",value:this.getHasLiedBrowser()}),e},fontsKey:function(e,t){return this.options.excludeJsFonts?this.flashFontsKey(e,t):this.jsFontsKey(e,t)},flashFontsKey:function(e,t){return this.options.excludeFlashFonts?t(e):this.hasSwfObjectLoaded()&&this.hasMinFlashInstalled()?"undefined"==typeof this.options.swfPath?t(e):void this.loadSwfAndDetectFonts(function(i){e.push({key:"swf_fonts",value:i.join(";")}),t(e)}):t(e)},jsFontsKey:function(e,t){var i=this;return setTimeout(function(){var a=["monospace","sans-serif","serif"],r=["Andale Mono","Arial","Arial Black","Arial Hebrew","Arial MT","Arial Narrow","Arial Rounded MT Bold","Arial Unicode MS","Bitstream Vera Sans Mono","Book Antiqua","Bookman Old Style","Calibri","Cambria","Cambria Math","Century","Century Gothic","Century Schoolbook","Comic Sans","Comic Sans MS","Consolas","Courier","Courier New","Garamond","Geneva","Georgia","Helvetica","Helvetica Neue","Impact","Lucida Bright","Lucida Calligraphy","Lucida Console","Lucida Fax","LUCIDA GRANDE","Lucida Handwriting","Lucida Sans","Lucida Sans Typewriter","Lucida Sans Unicode","Microsoft Sans Serif","Monaco","Monotype Corsiva","MS Gothic","MS Outlook","MS PGothic","MS Reference Sans Serif","MS Sans Serif","MS Serif","MYRIAD","MYRIAD PRO","Palatino","Palatino Linotype","Segoe Print","Segoe Script","Segoe UI","Segoe UI Light","Segoe UI Semibold","Segoe UI Symbol","Tahoma","Times","Times New Roman","Times New Roman PS","Trebuchet MS","Verdana","Wingdings","Wingdings 2","Wingdings 3"],n=["Abadi MT Condensed Light","Academy Engraved LET","ADOBE CASLON PRO","Adobe Garamond","ADOBE GARAMOND PRO","Agency FB","Aharoni","Albertus Extra Bold","Albertus Medium","Algerian","Amazone BT","American Typewriter","American Typewriter Condensed","AmerType Md BT","Andalus","Angsana New","AngsanaUPC","Antique Olive","Aparajita","Apple Chancery","Apple Color Emoji","Apple SD Gothic Neo","Arabic Typesetting","ARCHER","ARNO PRO","Arrus BT","Aurora Cn BT","AvantGarde Bk BT","AvantGarde Md BT","AVENIR","Ayuthaya","Bandy","Bangla Sangam MN","Bank Gothic","BankGothic Md BT","Baskerville","Baskerville Old Face","Batang","BatangChe","Bauer Bodoni","Bauhaus 93","Bazooka","Bell MT","Bembo","Benguiat Bk BT","Berlin Sans FB","Berlin Sans FB Demi","Bernard MT Condensed","BernhardFashion BT","BernhardMod BT","Big Caslon","BinnerD","Blackadder ITC","BlairMdITC TT","Bodoni 72","Bodoni 72 Oldstyle","Bodoni 72 Smallcaps","Bodoni MT","Bodoni MT Black","Bodoni MT Condensed","Bodoni MT Poster Compressed","Bookshelf Symbol 7","Boulder","Bradley Hand","Bradley Hand ITC","Bremen Bd BT","Britannic Bold","Broadway","Browallia New","BrowalliaUPC","Brush Script MT","Californian FB","Calisto MT","Calligrapher","Candara","CaslonOpnface BT","Castellar","Centaur","Cezanne","CG Omega","CG Times","Chalkboard","Chalkboard SE","Chalkduster","Charlesworth","Charter Bd BT","Charter BT","Chaucer","ChelthmITC Bk BT","Chiller","Clarendon","Clarendon Condensed","CloisterBlack BT","Cochin","Colonna MT","Constantia","Cooper Black","Copperplate","Copperplate Gothic","Copperplate Gothic Bold","Copperplate Gothic Light","CopperplGoth Bd BT","Corbel","Cordia New","CordiaUPC","Cornerstone","Coronet","Cuckoo","Curlz MT","DaunPenh","Dauphin","David","DB LCD Temp","DELICIOUS","Denmark","DFKai-SB","Didot","DilleniaUPC","DIN","DokChampa","Dotum","DotumChe","Ebrima","Edwardian Script ITC","Elephant","English 111 Vivace BT","Engravers MT","EngraversGothic BT","Eras Bold ITC","Eras Demi ITC","Eras Light ITC","Eras Medium ITC","EucrosiaUPC","Euphemia","Euphemia UCAS","EUROSTILE","Exotc350 Bd BT","FangSong","Felix Titling","Fixedsys","FONTIN","Footlight MT Light","Forte","FrankRuehl","Fransiscan","Freefrm721 Blk BT","FreesiaUPC","Freestyle Script","French Script MT","FrnkGothITC Bk BT","Fruitger","FRUTIGER","Futura","Futura Bk BT","Futura Lt BT","Futura Md BT","Futura ZBlk BT","FuturaBlack BT","Gabriola","Galliard BT","Gautami","Geeza Pro","Geometr231 BT","Geometr231 Hv BT","Geometr231 Lt BT","GeoSlab 703 Lt BT","GeoSlab 703 XBd BT","Gigi","Gill Sans","Gill Sans MT","Gill Sans MT Condensed","Gill Sans MT Ext Condensed Bold","Gill Sans Ultra Bold","Gill Sans Ultra Bold Condensed","Gisha","Gloucester MT Extra Condensed","GOTHAM","GOTHAM BOLD","Goudy Old Style","Goudy Stout","GoudyHandtooled BT","GoudyOLSt BT","Gujarati Sangam MN","Gulim","GulimChe","Gungsuh","GungsuhChe","Gurmukhi MN","Haettenschweiler","Harlow Solid Italic","Harrington","Heather","Heiti SC","Heiti TC","HELV","Herald","High Tower Text","Hiragino Kaku Gothic ProN","Hiragino Mincho ProN","Hoefler Text","Humanst 521 Cn BT","Humanst521 BT","Humanst521 Lt BT","Imprint MT Shadow","Incised901 Bd BT","Incised901 BT","Incised901 Lt BT","INCONSOLATA","Informal Roman","Informal011 BT","INTERSTATE","IrisUPC","Iskoola Pota","JasmineUPC","Jazz LET","Jenson","Jester","Jokerman","Juice ITC","Kabel Bk BT","Kabel Ult BT","Kailasa","KaiTi","Kalinga","Kannada Sangam MN","Kartika","Kaufmann Bd BT","Kaufmann BT","Khmer UI","KodchiangUPC","Kokila","Korinna BT","Kristen ITC","Krungthep","Kunstler Script","Lao UI","Latha","Leelawadee","Letter Gothic","Levenim MT","LilyUPC","Lithograph","Lithograph Light","Long Island","Lydian BT","Magneto","Maiandra GD","Malayalam Sangam MN","Malgun Gothic","Mangal","Marigold","Marion","Marker Felt","Market","Marlett","Matisse ITC","Matura MT Script Capitals","Meiryo","Meiryo UI","Microsoft Himalaya","Microsoft JhengHei","Microsoft New Tai Lue","Microsoft PhagsPa","Microsoft Tai Le","Microsoft Uighur","Microsoft YaHei","Microsoft Yi Baiti","MingLiU","MingLiU_HKSCS","MingLiU_HKSCS-ExtB","MingLiU-ExtB","Minion","Minion Pro","Miriam","Miriam Fixed","Mistral","Modern","Modern No. 20","Mona Lisa Solid ITC TT","Mongolian Baiti","MONO","MoolBoran","Mrs Eaves","MS LineDraw","MS Mincho","MS PMincho","MS Reference Specialty","MS UI Gothic","MT Extra","MUSEO","MV Boli","Nadeem","Narkisim","NEVIS","News Gothic","News GothicMT","NewsGoth BT","Niagara Engraved","Niagara Solid","Noteworthy","NSimSun","Nyala","OCR A Extended","Old Century","Old English Text MT","Onyx","Onyx BT","OPTIMA","Oriya Sangam MN","OSAKA","OzHandicraft BT","Palace Script MT","Papyrus","Parchment","Party LET","Pegasus","Perpetua","Perpetua Titling MT","PetitaBold","Pickwick","Plantagenet Cherokee","Playbill","PMingLiU","PMingLiU-ExtB","Poor Richard","Poster","PosterBodoni BT","PRINCETOWN LET","Pristina","PTBarnum BT","Pythagoras","Raavi","Rage Italic","Ravie","Ribbon131 Bd BT","Rockwell","Rockwell Condensed","Rockwell Extra Bold","Rod","Roman","Sakkal Majalla","Santa Fe LET","Savoye LET","Sceptre","Script","Script MT Bold","SCRIPTINA","Serifa","Serifa BT","Serifa Th BT","ShelleyVolante BT","Sherwood","Shonar Bangla","Showcard Gothic","Shruti","Signboard","SILKSCREEN","SimHei","Simplified Arabic","Simplified Arabic Fixed","SimSun","SimSun-ExtB","Sinhala Sangam MN","Sketch Rockwell","Skia","Small Fonts","Snap ITC","Snell Roundhand","Socket","Souvenir Lt BT","Staccato222 BT","Steamer","Stencil","Storybook","Styllo","Subway","Swis721 BlkEx BT","Swiss911 XCm BT","Sylfaen","Synchro LET","System","Tamil Sangam MN","Technical","Teletype","Telugu Sangam MN","Tempus Sans ITC","Terminal","Thonburi","Traditional Arabic","Trajan","TRAJAN PRO","Tristan","Tubular","Tunga","Tw Cen MT","Tw Cen MT Condensed","Tw Cen MT Condensed Extra Bold","TypoUpright BT","Unicorn","Univers","Univers CE 55 Medium","Univers Condensed","Utsaah","Vagabond","Vani","Vijaya","Viner Hand ITC","VisualUI","Vivaldi","Vladimir Script","Vrinda","Westminster","WHITNEY","Wide Latin","ZapfEllipt BT","ZapfHumnst BT","ZapfHumnst Dm BT","Zapfino","Zurich BlkEx BT","Zurich Ex BT","ZWAdobeF"];i.options.extendedJsFonts&&(r=r.concat(n)),r=r.concat(i.options.userDefinedFonts);var o="mmmmmmmmmmlli",s="72px",l=document.getElementsByTagName("body")[0],h=document.createElement("div"),u=document.createElement("div"),c={},d={},g=function(){var e=document.createElement("span");return e.style.position="absolute",e.style.left="-9999px",e.style.fontSize=s,e.style.lineHeight="normal",e.innerHTML=o,e},p=function(e,t){var i=g();return i.style.fontFamily="'"+e+"',"+t,i},f=function(){for(var e=[],t=0,i=a.length;tt.name?1:e.name=0?"Windows Phone":t.indexOf("win")>=0?"Windows":t.indexOf("android")>=0?"Android":t.indexOf("linux")>=0?"Linux":t.indexOf("iphone")>=0||t.indexOf("ipad")>=0?"iOS":t.indexOf("mac")>=0?"Mac":"Other";var r;if(r="ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0,r&&"Windows Phone"!==e&&"Android"!==e&&"iOS"!==e&&"Other"!==e)return!0;if("undefined"!=typeof i){if(i=i.toLowerCase(),i.indexOf("win")>=0&&"Windows"!==e&&"Windows Phone"!==e)return!0;if(i.indexOf("linux")>=0&&"Linux"!==e&&"Android"!==e)return!0;if(i.indexOf("mac")>=0&&"Mac"!==e&&"iOS"!==e)return!0;if(0===i.indexOf("win")&&0===i.indexOf("linux")&&i.indexOf("mac")>=0&&"other"!==e)return!0}return a.indexOf("win")>=0&&"Windows"!==e&&"Windows Phone"!==e||((a.indexOf("linux")>=0||a.indexOf("android")>=0||a.indexOf("pike")>=0)&&"Linux"!==e&&"Android"!==e||((a.indexOf("mac")>=0||a.indexOf("ipad")>=0||a.indexOf("ipod")>=0||a.indexOf("iphone")>=0)&&"Mac"!==e&&"iOS"!==e||(0===a.indexOf("win")&&0===a.indexOf("linux")&&a.indexOf("mac")>=0&&"other"!==e||"undefined"==typeof navigator.plugins&&"Windows"!==e&&"Windows Phone"!==e)))},getHasLiedBrowser:function(){var e,t=navigator.userAgent.toLowerCase(),i=navigator.productSub;if(e=t.indexOf("firefox")>=0?"Firefox":t.indexOf("opera")>=0||t.indexOf("opr")>=0?"Opera":t.indexOf("chrome")>=0?"Chrome":t.indexOf("safari")>=0?"Safari":t.indexOf("trident")>=0?"Internet Explorer":"Other",("Chrome"===e||"Safari"===e||"Opera"===e)&&"20030107"!==i)return!0;var a=eval.toString().length;if(37===a&&"Safari"!==e&&"Firefox"!==e&&"Other"!==e)return!0;if(39===a&&"Internet Explorer"!==e&&"Other"!==e)return!0;if(33===a&&"Chrome"!==e&&"Opera"!==e&&"Other"!==e)return!0;var r;try{throw"a"}catch(n){try{n.toSource(),r=!0}catch(o){r=!1}}return!(!r||"Firefox"===e||"Other"===e)},isCanvasSupported:function(){var e=document.createElement("canvas");return!(!e.getContext||!e.getContext("2d"))},isWebGlSupported:function(){if(!this.isCanvasSupported())return!1;var e,t=document.createElement("canvas");try{e=t.getContext&&(t.getContext("webgl")||t.getContext("experimental-webgl"))}catch(i){e=!1}return!!window.WebGLRenderingContext&&!!e},isIE:function(){return"Microsoft Internet Explorer"===navigator.appName||!("Netscape"!==navigator.appName||!/Trident/.test(navigator.userAgent))},hasSwfObjectLoaded:function(){return"undefined"!=typeof window.swfobject},hasMinFlashInstalled:function(){return swfobject.hasFlashPlayerVersion("9.0.0")},addFlashDivNode:function(){var e=document.createElement("div");e.setAttribute("id",this.options.swfContainerId),document.body.appendChild(e)},loadSwfAndDetectFonts:function(e){var t="___fp_swf_loaded";window[t]=function(t){e(t)};var i=this.options.swfContainerId;this.addFlashDivNode();var a={onReady:t},r={allowScriptAccess:"always",menu:"false"};swfobject.embedSWF(this.options.swfPath,i,"1","1","9.0.0",!1,a,r,{})},getWebglCanvas:function(){var e=document.createElement("canvas"),t=null;try{t=e.getContext("webgl")||e.getContext("experimental-webgl")}catch(i){}return t||(t=null),t},each:function(e,t,i){if(null!==e)if(this.nativeForEach&&e.forEach===this.nativeForEach)e.forEach(t,i);else if(e.length===+e.length){for(var a=0,r=e.length;a>>16,65535&e[0],e[1]>>>16,65535&e[1]],t=[t[0]>>>16,65535&t[0],t[1]>>>16,65535&t[1]];var i=[0,0,0,0];return i[3]+=e[3]+t[3],i[2]+=i[3]>>>16,i[3]&=65535,i[2]+=e[2]+t[2],i[1]+=i[2]>>>16,i[2]&=65535,i[1]+=e[1]+t[1],i[0]+=i[1]>>>16,i[1]&=65535,i[0]+=e[0]+t[0],i[0]&=65535,[i[0]<<16|i[1],i[2]<<16|i[3]]},x64Multiply:function(e,t){e=[e[0]>>>16,65535&e[0],e[1]>>>16,65535&e[1]],t=[t[0]>>>16,65535&t[0],t[1]>>>16,65535&t[1]];var i=[0,0,0,0];return i[3]+=e[3]*t[3],i[2]+=i[3]>>>16,i[3]&=65535,i[2]+=e[2]*t[3],i[1]+=i[2]>>>16,i[2]&=65535,i[2]+=e[3]*t[2],i[1]+=i[2]>>>16,i[2]&=65535,i[1]+=e[1]*t[3],i[0]+=i[1]>>>16,i[1]&=65535,i[1]+=e[2]*t[2],i[0]+=i[1]>>>16,i[1]&=65535,i[1]+=e[3]*t[1],i[0]+=i[1]>>>16,i[1]&=65535,i[0]+=e[0]*t[3]+e[1]*t[2]+e[2]*t[1]+e[3]*t[0],i[0]&=65535,[i[0]<<16|i[1],i[2]<<16|i[3]]},x64Rotl:function(e,t){return t%=64,32===t?[e[1],e[0]]:t<32?[e[0]<>>32-t,e[1]<>>32-t]:(t-=32,[e[1]<>>32-t,e[0]<>>32-t])},x64LeftShift:function(e,t){return t%=64,0===t?e:t<32?[e[0]<>>32-t,e[1]<>>1]),e=this.x64Multiply(e,[4283543511,3981806797]),e=this.x64Xor(e,[0,e[0]>>>1]),e=this.x64Multiply(e,[3301882366,444984403]),e=this.x64Xor(e,[0,e[0]>>>1])},x64hash128:function(e,t){e=e||"",t=t||0;for(var i=e.length%16,a=e.length-i,r=[0,t],n=[0,t],o=[0,0],s=[0,0],l=[2277735313,289559509],h=[1291169091,658871167],u=0;u>>0).toString(16)).slice(-8)+("00000000"+(r[1]>>>0).toString(16)).slice(-8)+("00000000"+(n[0]>>>0).toString(16)).slice(-8)+("00000000"+(n[1]>>>0).toString(16)).slice(-8)}},e.VERSION="1.5.1",e}); -------------------------------------------------------------------------------- /flash/FontList.as: -------------------------------------------------------------------------------- 1 | package { 2 | import flash.display.Sprite; 3 | import flash.display.LoaderInfo; 4 | import flash.text.Font; 5 | import flash.external.ExternalInterface; 6 | 7 | public class FontList extends Sprite { 8 | 9 | public function FontList() { 10 | var params:Object = loadParams(); 11 | loadExternalInterface(params); 12 | } 13 | 14 | private function loadParams():Object { 15 | return LoaderInfo(this.root.loaderInfo).parameters; 16 | } 17 | 18 | private function loadExternalInterface(params:Object):void { 19 | ExternalInterface.call(params.onReady, fonts()); 20 | } 21 | 22 | private function fonts():Array { 23 | var fontNames:Array = []; 24 | for each (var font:Font in Font.enumerateFonts(true) ) 25 | { 26 | fontNames.push(font.fontName); 27 | } 28 | return fontNames; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /flash/Makefile: -------------------------------------------------------------------------------- 1 | all: FontList.swf 2 | 3 | FontList.swf: clean 4 | mxmlc -static-link-runtime-shared-libraries FontList.as 5 | 6 | clean: 7 | rm -f FontList.swf 8 | -------------------------------------------------------------------------------- /flash/compiled/FontList.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mestarshine/fingerprint2js/f079c473140f56971afc19f6ecbd9a40a0e588d8/flash/compiled/FontList.swf -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"), 2 | eslint = require("gulp-eslint"), 3 | rename = require("gulp-rename"), 4 | uglify = require("gulp-uglify"); 5 | 6 | gulp.task("lint", function() { 7 | return gulp 8 | .src("fingerprint2.js") 9 | .pipe(eslint()) 10 | .pipe(eslint.format()) 11 | .pipe(eslint.failOnError()); 12 | }); 13 | 14 | gulp.task("minify", function() { 15 | return gulp 16 | .src("fingerprint2.js") 17 | .pipe(rename({suffix: ".min"})) 18 | .pipe(uglify({ 19 | compress: { 20 | global_defs: {} 21 | }, 22 | output: { 23 | ascii_only: true 24 | } 25 | })) 26 | .pipe(gulp.dest("dist/")); 27 | }); 28 | 29 | 30 | gulp.task("default", ["lint", "minify"], function() {}); 31 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fingerprintjs2 test 5 | 6 | 15 | 16 | 66 | 67 | 68 |
69 | 70 | 71 |

Fingerprintjs2

72 | 73 |

Your browser fingerprint:

74 |

75 |

76 | 77 | 78 | Fork me on GitHub 79 | 80 | 81 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | { 5 | "raw": "fingerprintjs2@^1.5.1", 6 | "scope": null, 7 | "escapedName": "fingerprintjs2", 8 | "name": "fingerprintjs2", 9 | "rawSpec": "^1.5.1", 10 | "spec": ">=1.5.1 <2.0.0", 11 | "type": "range" 12 | }, 13 | "/home/neuro" 14 | ] 15 | ], 16 | "_from": "fingerprintjs2@>=1.5.1 <2.0.0", 17 | "_id": "fingerprintjs2@1.5.1", 18 | "_inCache": true, 19 | "_location": "/fingerprintjs2", 20 | "_nodeVersion": "4.8.0", 21 | "_npmOperationalInternal": { 22 | "host": "packages-18-east.internal.npmjs.com", 23 | "tmp": "tmp/fingerprintjs2-1.5.1.tgz_1490890052857_0.0629298856947571" 24 | }, 25 | "_npmUser": { 26 | "name": "valve", 27 | "email": "valentin.vasilyev@outlook.com" 28 | }, 29 | "_npmVersion": "2.15.11", 30 | "_phantomChildren": {}, 31 | "_requested": { 32 | "raw": "fingerprintjs2@^1.5.1", 33 | "scope": null, 34 | "escapedName": "fingerprintjs2", 35 | "name": "fingerprintjs2", 36 | "rawSpec": "^1.5.1", 37 | "spec": ">=1.5.1 <2.0.0", 38 | "type": "range" 39 | }, 40 | "_requiredBy": [ 41 | "#USER", 42 | "/" 43 | ], 44 | "_resolved": "https://registry.npmjs.org/fingerprintjs2/-/fingerprintjs2-1.5.1.tgz", 45 | "_shasum": "010691d425bc37fa0b5a7ec1ae85404bb3f934cb", 46 | "_shrinkwrap": null, 47 | "_spec": "fingerprintjs2@^1.5.1", 48 | "_where": "/home/neuro", 49 | "author": { 50 | "name": "Valentin Vasilyev" 51 | }, 52 | "bugs": { 53 | "url": "https://github.com/Valve/fingerprintjs2/issues" 54 | }, 55 | "dependencies": {}, 56 | "description": "Modern & flexible browser fingerprinting library", 57 | "devDependencies": { 58 | "eslint": "^4.18.2", 59 | "gulp": "^3.8.11", 60 | "gulp-eslint": "^0.4.2", 61 | "gulp-rename": "^1.2.2", 62 | "gulp-uglify": "^1.1.0" 63 | }, 64 | "directories": {}, 65 | "dist": { 66 | "shasum": "010691d425bc37fa0b5a7ec1ae85404bb3f934cb", 67 | "tarball": "https://registry.npmjs.org/fingerprintjs2/-/fingerprintjs2-1.5.1.tgz" 68 | }, 69 | "gitHead": "bcde614161e2ca328692fdcbe5bf2a8d0e38579a", 70 | "homepage": "https://github.com/Valve/fingerprintjs2", 71 | "keywords": [ 72 | "browser", 73 | "identification", 74 | "fingerprint", 75 | "fingerprinting", 76 | "privacy" 77 | ], 78 | "license": "MIT", 79 | "main": "dist/fingerprint2.min.js", 80 | "maintainers": [ 81 | { 82 | "name": "valve", 83 | "email": "valentin.vasilyev@outlook.com" 84 | } 85 | ], 86 | "name": "fingerprintjs2", 87 | "optionalDependencies": {}, 88 | "readme": "ERROR: No README data found!", 89 | "repository": { 90 | "type": "git", 91 | "url": "git+https://github.com/Valve/fingerprintjs2.git" 92 | }, 93 | "scripts": { 94 | "test": "specs/phantomjs.runner.sh specs/spec_runner.html" 95 | }, 96 | "version": "1.5.1" 97 | } 98 | -------------------------------------------------------------------------------- /specs/lib/jasmine-2.3.4/boot.js: -------------------------------------------------------------------------------- 1 | /** 2 | Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. 3 | 4 | If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms. 5 | 6 | The location of `boot.js` can be specified and/or overridden in `jasmine.yml`. 7 | 8 | [jasmine-gem]: http://github.com/pivotal/jasmine-gem 9 | */ 10 | 11 | (function() { 12 | 13 | /** 14 | * ## Require & Instantiate 15 | * 16 | * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. 17 | */ 18 | window.jasmine = jasmineRequire.core(jasmineRequire); 19 | 20 | /** 21 | * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. 22 | */ 23 | jasmineRequire.html(jasmine); 24 | 25 | /** 26 | * Create the Jasmine environment. This is used to run all specs in a project. 27 | */ 28 | var env = jasmine.getEnv(); 29 | 30 | /** 31 | * ## The Global Interface 32 | * 33 | * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. 34 | */ 35 | var jasmineInterface = jasmineRequire.interface(jasmine, env); 36 | 37 | /** 38 | * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. 39 | */ 40 | extend(window, jasmineInterface); 41 | 42 | /** 43 | * ## Runner Parameters 44 | * 45 | * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. 46 | */ 47 | 48 | var queryString = new jasmine.QueryString({ 49 | getWindowLocation: function() { return window.location; } 50 | }); 51 | 52 | var catchingExceptions = queryString.getParam("catch"); 53 | env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions); 54 | 55 | var throwingExpectationFailures = queryString.getParam("throwFailures"); 56 | env.throwOnExpectationFailure(throwingExpectationFailures); 57 | 58 | /** 59 | * ## Reporters 60 | * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). 61 | */ 62 | var htmlReporter = new jasmine.HtmlReporter({ 63 | env: env, 64 | onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); }, 65 | onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); }, 66 | addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, 67 | getContainer: function() { return document.body; }, 68 | createElement: function() { return document.createElement.apply(document, arguments); }, 69 | createTextNode: function() { return document.createTextNode.apply(document, arguments); }, 70 | timer: new jasmine.Timer() 71 | }); 72 | 73 | /** 74 | * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. 75 | */ 76 | env.addReporter(jasmineInterface.jsApiReporter); 77 | env.addReporter(htmlReporter); 78 | 79 | /** 80 | * Filter which specs will be run by matching the start of the full name against the `spec` query param. 81 | */ 82 | var specFilter = new jasmine.HtmlSpecFilter({ 83 | filterString: function() { return queryString.getParam("spec"); } 84 | }); 85 | 86 | env.specFilter = function(spec) { 87 | return specFilter.matches(spec.getFullName()); 88 | }; 89 | 90 | /** 91 | * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. 92 | */ 93 | window.setTimeout = window.setTimeout; 94 | window.setInterval = window.setInterval; 95 | window.clearTimeout = window.clearTimeout; 96 | window.clearInterval = window.clearInterval; 97 | 98 | /** 99 | * ## Execution 100 | * 101 | * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. 102 | */ 103 | var currentWindowOnload = window.onload; 104 | 105 | window.onload = function() { 106 | if (currentWindowOnload) { 107 | currentWindowOnload(); 108 | } 109 | htmlReporter.initialize(); 110 | env.execute(); 111 | }; 112 | 113 | /** 114 | * Helper function for readability above. 115 | */ 116 | function extend(destination, source) { 117 | for (var property in source) destination[property] = source[property]; 118 | return destination; 119 | } 120 | 121 | }()); 122 | -------------------------------------------------------------------------------- /specs/lib/jasmine-2.3.4/jasmine-html.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008-2015 Pivotal Labs 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | jasmineRequire.html = function(j$) { 24 | j$.ResultsNode = jasmineRequire.ResultsNode(); 25 | j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); 26 | j$.QueryString = jasmineRequire.QueryString(); 27 | j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); 28 | }; 29 | 30 | jasmineRequire.HtmlReporter = function(j$) { 31 | 32 | var noopTimer = { 33 | start: function() {}, 34 | elapsed: function() { return 0; } 35 | }; 36 | 37 | function HtmlReporter(options) { 38 | var env = options.env || {}, 39 | getContainer = options.getContainer, 40 | createElement = options.createElement, 41 | createTextNode = options.createTextNode, 42 | onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {}, 43 | onThrowExpectationsClick = options.onThrowExpectationsClick || function() {}, 44 | addToExistingQueryString = options.addToExistingQueryString || defaultQueryString, 45 | timer = options.timer || noopTimer, 46 | results = [], 47 | specsExecuted = 0, 48 | failureCount = 0, 49 | pendingSpecCount = 0, 50 | htmlReporterMain, 51 | symbols, 52 | failedSuites = []; 53 | 54 | this.initialize = function() { 55 | clearPrior(); 56 | htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'}, 57 | createDom('div', {className: 'banner'}, 58 | createDom('a', {className: 'title', href: 'http://jasmine.github.io/', target: '_blank'}), 59 | createDom('span', {className: 'version'}, j$.version) 60 | ), 61 | createDom('ul', {className: 'symbol-summary'}), 62 | createDom('div', {className: 'alert'}), 63 | createDom('div', {className: 'results'}, 64 | createDom('div', {className: 'failures'}) 65 | ) 66 | ); 67 | getContainer().appendChild(htmlReporterMain); 68 | 69 | symbols = find('.symbol-summary'); 70 | }; 71 | 72 | var totalSpecsDefined; 73 | this.jasmineStarted = function(options) { 74 | totalSpecsDefined = options.totalSpecsDefined || 0; 75 | timer.start(); 76 | }; 77 | 78 | var summary = createDom('div', {className: 'summary'}); 79 | 80 | var topResults = new j$.ResultsNode({}, '', null), 81 | currentParent = topResults; 82 | 83 | this.suiteStarted = function(result) { 84 | currentParent.addChild(result, 'suite'); 85 | currentParent = currentParent.last(); 86 | }; 87 | 88 | this.suiteDone = function(result) { 89 | if (result.status == 'failed') { 90 | failedSuites.push(result); 91 | } 92 | 93 | if (currentParent == topResults) { 94 | return; 95 | } 96 | 97 | currentParent = currentParent.parent; 98 | }; 99 | 100 | this.specStarted = function(result) { 101 | currentParent.addChild(result, 'spec'); 102 | }; 103 | 104 | var failures = []; 105 | this.specDone = function(result) { 106 | if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') { 107 | console.error('Spec \'' + result.fullName + '\' has no expectations.'); 108 | } 109 | 110 | if (result.status != 'disabled') { 111 | specsExecuted++; 112 | } 113 | 114 | symbols.appendChild(createDom('li', { 115 | className: noExpectations(result) ? 'empty' : result.status, 116 | id: 'spec_' + result.id, 117 | title: result.fullName 118 | } 119 | )); 120 | 121 | if (result.status == 'failed') { 122 | failureCount++; 123 | 124 | var failure = 125 | createDom('div', {className: 'spec-detail failed'}, 126 | createDom('div', {className: 'description'}, 127 | createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName) 128 | ), 129 | createDom('div', {className: 'messages'}) 130 | ); 131 | var messages = failure.childNodes[1]; 132 | 133 | for (var i = 0; i < result.failedExpectations.length; i++) { 134 | var expectation = result.failedExpectations[i]; 135 | messages.appendChild(createDom('div', {className: 'result-message'}, expectation.message)); 136 | messages.appendChild(createDom('div', {className: 'stack-trace'}, expectation.stack)); 137 | } 138 | 139 | failures.push(failure); 140 | } 141 | 142 | if (result.status == 'pending') { 143 | pendingSpecCount++; 144 | } 145 | }; 146 | 147 | this.jasmineDone = function() { 148 | var banner = find('.banner'); 149 | var alert = find('.alert'); 150 | alert.appendChild(createDom('span', {className: 'duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); 151 | 152 | banner.appendChild( 153 | createDom('div', { className: 'run-options' }, 154 | createDom('span', { className: 'trigger' }, 'Options'), 155 | createDom('div', { className: 'payload' }, 156 | createDom('div', { className: 'exceptions' }, 157 | createDom('input', { 158 | className: 'raise', 159 | id: 'raise-exceptions', 160 | type: 'checkbox' 161 | }), 162 | createDom('label', { className: 'label', 'for': 'raise-exceptions' }, 'raise exceptions')), 163 | createDom('div', { className: 'throw-failures' }, 164 | createDom('input', { 165 | className: 'throw', 166 | id: 'throw-failures', 167 | type: 'checkbox' 168 | }), 169 | createDom('label', { className: 'label', 'for': 'throw-failures' }, 'stop spec on expectation failure')) 170 | ) 171 | )); 172 | 173 | var raiseCheckbox = find('#raise-exceptions'); 174 | 175 | raiseCheckbox.checked = !env.catchingExceptions(); 176 | raiseCheckbox.onclick = onRaiseExceptionsClick; 177 | 178 | var throwCheckbox = find('#throw-failures'); 179 | throwCheckbox.checked = env.throwingExpectationFailures(); 180 | throwCheckbox.onclick = onThrowExpectationsClick; 181 | 182 | var optionsMenu = find('.run-options'), 183 | optionsTrigger = optionsMenu.querySelector('.trigger'), 184 | optionsPayload = optionsMenu.querySelector('.payload'), 185 | isOpen = /\bopen\b/; 186 | 187 | optionsTrigger.onclick = function() { 188 | if (isOpen.test(optionsPayload.className)) { 189 | optionsPayload.className = optionsPayload.className.replace(isOpen, ''); 190 | } else { 191 | optionsPayload.className += ' open'; 192 | } 193 | }; 194 | 195 | if (specsExecuted < totalSpecsDefined) { 196 | var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; 197 | alert.appendChild( 198 | createDom('span', {className: 'bar skipped'}, 199 | createDom('a', {href: '?', title: 'Run all specs'}, skippedMessage) 200 | ) 201 | ); 202 | } 203 | var statusBarMessage = ''; 204 | var statusBarClassName = 'bar '; 205 | 206 | if (totalSpecsDefined > 0) { 207 | statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount); 208 | if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); } 209 | statusBarClassName += (failureCount > 0) ? 'failed' : 'passed'; 210 | } else { 211 | statusBarClassName += 'skipped'; 212 | statusBarMessage += 'No specs found'; 213 | } 214 | 215 | alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage)); 216 | 217 | for(i = 0; i < failedSuites.length; i++) { 218 | var failedSuite = failedSuites[i]; 219 | for(var j = 0; j < failedSuite.failedExpectations.length; j++) { 220 | var errorBarMessage = 'AfterAll ' + failedSuite.failedExpectations[j].message; 221 | var errorBarClassName = 'bar errored'; 222 | alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessage)); 223 | } 224 | } 225 | 226 | var results = find('.results'); 227 | results.appendChild(summary); 228 | 229 | summaryList(topResults, summary); 230 | 231 | function summaryList(resultsTree, domParent) { 232 | var specListNode; 233 | for (var i = 0; i < resultsTree.children.length; i++) { 234 | var resultNode = resultsTree.children[i]; 235 | if (resultNode.type == 'suite') { 236 | var suiteListNode = createDom('ul', {className: 'suite', id: 'suite-' + resultNode.result.id}, 237 | createDom('li', {className: 'suite-detail'}, 238 | createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description) 239 | ) 240 | ); 241 | 242 | summaryList(resultNode, suiteListNode); 243 | domParent.appendChild(suiteListNode); 244 | } 245 | if (resultNode.type == 'spec') { 246 | if (domParent.getAttribute('class') != 'specs') { 247 | specListNode = createDom('ul', {className: 'specs'}); 248 | domParent.appendChild(specListNode); 249 | } 250 | var specDescription = resultNode.result.description; 251 | if(noExpectations(resultNode.result)) { 252 | specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; 253 | } 254 | if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') { 255 | specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; 256 | } 257 | specListNode.appendChild( 258 | createDom('li', { 259 | className: resultNode.result.status, 260 | id: 'spec-' + resultNode.result.id 261 | }, 262 | createDom('a', {href: specHref(resultNode.result)}, specDescription) 263 | ) 264 | ); 265 | } 266 | } 267 | } 268 | 269 | if (failures.length) { 270 | alert.appendChild( 271 | createDom('span', {className: 'menu bar spec-list'}, 272 | createDom('span', {}, 'Spec List | '), 273 | createDom('a', {className: 'failures-menu', href: '#'}, 'Failures'))); 274 | alert.appendChild( 275 | createDom('span', {className: 'menu bar failure-list'}, 276 | createDom('a', {className: 'spec-list-menu', href: '#'}, 'Spec List'), 277 | createDom('span', {}, ' | Failures '))); 278 | 279 | find('.failures-menu').onclick = function() { 280 | setMenuModeTo('failure-list'); 281 | }; 282 | find('.spec-list-menu').onclick = function() { 283 | setMenuModeTo('spec-list'); 284 | }; 285 | 286 | setMenuModeTo('failure-list'); 287 | 288 | var failureNode = find('.failures'); 289 | for (var i = 0; i < failures.length; i++) { 290 | failureNode.appendChild(failures[i]); 291 | } 292 | } 293 | }; 294 | 295 | return this; 296 | 297 | function find(selector) { 298 | return getContainer().querySelector('.jasmine_html-reporter ' + selector); 299 | } 300 | 301 | function clearPrior() { 302 | // return the reporter 303 | var oldReporter = find(''); 304 | 305 | if(oldReporter) { 306 | getContainer().removeChild(oldReporter); 307 | } 308 | } 309 | 310 | function createDom(type, attrs, childrenVarArgs) { 311 | var el = createElement(type); 312 | 313 | for (var i = 2; i < arguments.length; i++) { 314 | var child = arguments[i]; 315 | 316 | if (typeof child === 'string') { 317 | el.appendChild(createTextNode(child)); 318 | } else { 319 | if (child) { 320 | el.appendChild(child); 321 | } 322 | } 323 | } 324 | 325 | for (var attr in attrs) { 326 | if (attr == 'className') { 327 | el[attr] = attrs[attr]; 328 | } else { 329 | el.setAttribute(attr, attrs[attr]); 330 | } 331 | } 332 | 333 | return el; 334 | } 335 | 336 | function pluralize(singular, count) { 337 | var word = (count == 1 ? singular : singular + 's'); 338 | 339 | return '' + count + ' ' + word; 340 | } 341 | 342 | function specHref(result) { 343 | return addToExistingQueryString('spec', result.fullName); 344 | } 345 | 346 | function defaultQueryString(key, value) { 347 | return '?' + key + '=' + value; 348 | } 349 | 350 | function setMenuModeTo(mode) { 351 | htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode); 352 | } 353 | 354 | function noExpectations(result) { 355 | return (result.failedExpectations.length + result.passedExpectations.length) === 0 && 356 | result.status === 'passed'; 357 | } 358 | } 359 | 360 | return HtmlReporter; 361 | }; 362 | 363 | jasmineRequire.HtmlSpecFilter = function() { 364 | function HtmlSpecFilter(options) { 365 | var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); 366 | var filterPattern = new RegExp(filterString); 367 | 368 | this.matches = function(specName) { 369 | return filterPattern.test(specName); 370 | }; 371 | } 372 | 373 | return HtmlSpecFilter; 374 | }; 375 | 376 | jasmineRequire.ResultsNode = function() { 377 | function ResultsNode(result, type, parent) { 378 | this.result = result; 379 | this.type = type; 380 | this.parent = parent; 381 | 382 | this.children = []; 383 | 384 | this.addChild = function(result, type) { 385 | this.children.push(new ResultsNode(result, type, this)); 386 | }; 387 | 388 | this.last = function() { 389 | return this.children[this.children.length - 1]; 390 | }; 391 | } 392 | 393 | return ResultsNode; 394 | }; 395 | 396 | jasmineRequire.QueryString = function() { 397 | function QueryString(options) { 398 | 399 | this.navigateWithNewParam = function(key, value) { 400 | options.getWindowLocation().search = this.fullStringWithNewParam(key, value); 401 | }; 402 | 403 | this.fullStringWithNewParam = function(key, value) { 404 | var paramMap = queryStringToParamMap(); 405 | paramMap[key] = value; 406 | return toQueryString(paramMap); 407 | }; 408 | 409 | this.getParam = function(key) { 410 | return queryStringToParamMap()[key]; 411 | }; 412 | 413 | return this; 414 | 415 | function toQueryString(paramMap) { 416 | var qStrPairs = []; 417 | for (var prop in paramMap) { 418 | qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])); 419 | } 420 | return '?' + qStrPairs.join('&'); 421 | } 422 | 423 | function queryStringToParamMap() { 424 | var paramStr = options.getWindowLocation().search.substring(1), 425 | params = [], 426 | paramMap = {}; 427 | 428 | if (paramStr.length > 0) { 429 | params = paramStr.split('&'); 430 | for (var i = 0; i < params.length; i++) { 431 | var p = params[i].split('='); 432 | var value = decodeURIComponent(p[1]); 433 | if (value === 'true' || value === 'false') { 434 | value = JSON.parse(value); 435 | } 436 | paramMap[decodeURIComponent(p[0])] = value; 437 | } 438 | } 439 | 440 | return paramMap; 441 | } 442 | 443 | } 444 | 445 | return QueryString; 446 | }; 447 | -------------------------------------------------------------------------------- /specs/lib/jasmine-2.3.4/jasmine-matchers.js: -------------------------------------------------------------------------------- 1 | (function e(t, n, r) { 2 | function s(o, u) { 3 | if (!n[o]) { 4 | if (!t[o]) { 5 | var a = typeof require == 'function' && require; 6 | if (!u && a) { 7 | return a(o, !0); 8 | } 9 | if (i) { 10 | return i(o, !0); 11 | } 12 | var f = new Error('Cannot find module \'' + o + '\''); 13 | throw f.code = 'MODULE_NOT_FOUND', f 14 | } 15 | var l = n[o] = { 16 | exports: {} 17 | }; 18 | t[o][0].call(l.exports, function(e) { 19 | var n = t[o][1][e]; 20 | return s(n ? n : e); 21 | }, l, l.exports, e, t, n, r); 22 | } 23 | return n[o].exports; 24 | } 25 | var i = typeof require == 'function' && require; 26 | for (var o = 0; o < r.length; o++) { 27 | s(r[o]); 28 | } 29 | return s; 30 | })({ 31 | 1: [function(require, module, exports) { 32 | 'use strict'; 33 | 34 | /* 35 | * Copyright © Jamie Mason, @fold_left, 36 | * https://github.com/JamieMason 37 | * 38 | * Permission is hereby granted, free of charge, to any person 39 | * obtaining a copy of this software and associated documentation files 40 | * (the "Software"), to deal in the Software without restriction, 41 | * including without limitation the rights to use, copy, modify, merge, 42 | * publish, distribute, sublicense, and/or sell copies of the Software, 43 | * and to permit persons to whom the Software is furnished to do so, 44 | * subject to the following conditions: 45 | * 46 | * The above copyright notice and this permission notice shall be 47 | * included in all copies or substantial portions of the Software. 48 | * 49 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 50 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 51 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 52 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 53 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 54 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 55 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 56 | * SOFTWARE. 57 | */ 58 | 59 | var factory = require('./lib/factory'); 60 | 61 | var matchers = { 62 | toBeAfter: require('./toBeAfter'), 63 | toBeArray: require('./toBeArray'), 64 | toBeArrayOfBooleans: require('./toBeArrayOfBooleans'), 65 | toBeArrayOfNumbers: require('./toBeArrayOfNumbers'), 66 | toBeArrayOfObjects: require('./toBeArrayOfObjects'), 67 | toBeArrayOfSize: require('./toBeArrayOfSize'), 68 | toBeArrayOfStrings: require('./toBeArrayOfStrings'), 69 | toBeBefore: require('./toBeBefore'), 70 | toBeBoolean: require('./toBeBoolean'), 71 | toBeCalculable: require('./toBeCalculable'), 72 | toBeDate: require('./toBeDate'), 73 | toBeEmptyArray: require('./toBeEmptyArray'), 74 | toBeEmptyObject: require('./toBeEmptyObject'), 75 | toBeEmptyString: require('./toBeEmptyString'), 76 | toBeEvenNumber: require('./toBeEvenNumber'), 77 | toBeFalse: require('./toBeFalse'), 78 | toBeFunction: require('./toBeFunction'), 79 | toBeHtmlString: require('./toBeHtmlString'), 80 | toBeIso8601: require('./toBeIso8601'), 81 | toBeJsonString: require('./toBeJsonString'), 82 | toBeLongerThan: require('./toBeLongerThan'), 83 | toBeNonEmptyArray: require('./toBeNonEmptyArray'), 84 | toBeNonEmptyObject: require('./toBeNonEmptyObject'), 85 | toBeNonEmptyString: require('./toBeNonEmptyString'), 86 | toBeNumber: require('./toBeNumber'), 87 | toBeObject: require('./toBeObject'), 88 | toBeOddNumber: require('./toBeOddNumber'), 89 | toBeSameLengthAs: require('./toBeSameLengthAs'), 90 | toBeShorterThan: require('./toBeShorterThan'), 91 | toBeString: require('./toBeString'), 92 | toBeTrue: require('./toBeTrue'), 93 | toBeWhitespace: require('./toBeWhitespace'), 94 | toBeWholeNumber: require('./toBeWholeNumber'), 95 | toBeWithinRange: require('./toBeWithinRange'), 96 | toEndWith: require('./toEndWith'), 97 | toImplement: require('./toImplement'), 98 | toStartWith: require('./toStartWith'), 99 | toThrowAnyError: require('./toThrowAnyError'), 100 | toThrowErrorOfType: require('./toThrowErrorOfType'), 101 | toHaveArray: require('./toHaveArray'), 102 | toHaveArrayOfBooleans: require('./toHaveArrayOfBooleans'), 103 | toHaveArrayOfNumbers: require('./toHaveArrayOfNumbers'), 104 | toHaveArrayOfObjects: require('./toHaveArrayOfObjects'), 105 | toHaveArrayOfSize: require('./toHaveArrayOfSize'), 106 | toHaveArrayOfStrings: require('./toHaveArrayOfStrings'), 107 | toHaveBoolean: require('./toHaveBoolean'), 108 | toHaveCalculable: require('./toHaveCalculable'), 109 | toHaveDate: require('./toHaveDate'), 110 | toHaveDateAfter: require('./toHaveDateAfter'), 111 | toHaveDateBefore: require('./toHaveDateBefore'), 112 | toHaveEmptyArray: require('./toHaveEmptyArray'), 113 | toHaveEmptyObject: require('./toHaveEmptyObject'), 114 | toHaveEmptyString: require('./toHaveEmptyString'), 115 | toHaveEvenNumber: require('./toHaveEvenNumber'), 116 | toHaveFalse: require('./toHaveFalse'), 117 | toHaveHtmlString: require('./toHaveHtmlString'), 118 | toHaveIso8601: require('./toHaveIso8601'), 119 | toHaveJsonString: require('./toHaveJsonString'), 120 | toHaveMember: require('./toHaveMember'), 121 | toHaveMethod: require('./toHaveMethod'), 122 | toHaveNonEmptyArray: require('./toHaveNonEmptyArray'), 123 | toHaveNonEmptyObject: require('./toHaveNonEmptyObject'), 124 | toHaveNonEmptyString: require('./toHaveNonEmptyString'), 125 | toHaveNumber: require('./toHaveNumber'), 126 | toHaveNumberWithinRange: require('./toHaveNumberWithinRange'), 127 | toHaveObject: require('./toHaveObject'), 128 | toHaveOddNumber: require('./toHaveOddNumber'), 129 | toHaveString: require('./toHaveString'), 130 | toHaveStringLongerThan: require('./toHaveStringLongerThan'), 131 | toHaveStringSameLengthAs: require('./toHaveStringSameLengthAs'), 132 | toHaveStringShorterThan: require('./toHaveStringShorterThan'), 133 | toHaveTrue: require('./toHaveTrue'), 134 | toHaveWhitespaceString: require('./toHaveWhitespaceString'), 135 | toHaveWholeNumber: require('./toHaveWholeNumber') 136 | }; 137 | 138 | for (var matcherName in matchers) { 139 | factory(matcherName, matchers[matcherName]); 140 | } 141 | 142 | module.exports = matchers; 143 | 144 | }, { 145 | './lib/factory': 3, 146 | './toBeAfter': 10, 147 | './toBeArray': 11, 148 | './toBeArrayOfBooleans': 12, 149 | './toBeArrayOfNumbers': 13, 150 | './toBeArrayOfObjects': 14, 151 | './toBeArrayOfSize': 15, 152 | './toBeArrayOfStrings': 16, 153 | './toBeBefore': 17, 154 | './toBeBoolean': 18, 155 | './toBeCalculable': 19, 156 | './toBeDate': 20, 157 | './toBeEmptyArray': 21, 158 | './toBeEmptyObject': 22, 159 | './toBeEmptyString': 23, 160 | './toBeEvenNumber': 24, 161 | './toBeFalse': 25, 162 | './toBeFunction': 26, 163 | './toBeHtmlString': 27, 164 | './toBeIso8601': 28, 165 | './toBeJsonString': 29, 166 | './toBeLongerThan': 30, 167 | './toBeNonEmptyArray': 31, 168 | './toBeNonEmptyObject': 32, 169 | './toBeNonEmptyString': 33, 170 | './toBeNumber': 34, 171 | './toBeObject': 35, 172 | './toBeOddNumber': 36, 173 | './toBeSameLengthAs': 37, 174 | './toBeShorterThan': 38, 175 | './toBeString': 39, 176 | './toBeTrue': 40, 177 | './toBeWhitespace': 41, 178 | './toBeWholeNumber': 42, 179 | './toBeWithinRange': 43, 180 | './toEndWith': 44, 181 | './toHaveArray': 45, 182 | './toHaveArrayOfBooleans': 46, 183 | './toHaveArrayOfNumbers': 47, 184 | './toHaveArrayOfObjects': 48, 185 | './toHaveArrayOfSize': 49, 186 | './toHaveArrayOfStrings': 50, 187 | './toHaveBoolean': 51, 188 | './toHaveCalculable': 52, 189 | './toHaveDate': 53, 190 | './toHaveDateAfter': 54, 191 | './toHaveDateBefore': 55, 192 | './toHaveEmptyArray': 56, 193 | './toHaveEmptyObject': 57, 194 | './toHaveEmptyString': 58, 195 | './toHaveEvenNumber': 59, 196 | './toHaveFalse': 60, 197 | './toHaveHtmlString': 61, 198 | './toHaveIso8601': 62, 199 | './toHaveJsonString': 63, 200 | './toHaveMember': 64, 201 | './toHaveMethod': 65, 202 | './toHaveNonEmptyArray': 66, 203 | './toHaveNonEmptyObject': 67, 204 | './toHaveNonEmptyString': 68, 205 | './toHaveNumber': 69, 206 | './toHaveNumberWithinRange': 70, 207 | './toHaveObject': 71, 208 | './toHaveOddNumber': 72, 209 | './toHaveString': 73, 210 | './toHaveStringLongerThan': 74, 211 | './toHaveStringSameLengthAs': 75, 212 | './toHaveStringShorterThan': 76, 213 | './toHaveTrue': 77, 214 | './toHaveWhitespaceString': 78, 215 | './toHaveWholeNumber': 79, 216 | './toImplement': 80, 217 | './toStartWith': 81, 218 | './toThrowAnyError': 82, 219 | './toThrowErrorOfType': 83 220 | }], 221 | 2: [function(require, module, exports) { 222 | 'use strict'; 223 | 224 | module.exports = every; 225 | 226 | function every(array, truthTest) { 227 | for (var i = 0, len = array.length; i < len; i++) { 228 | if (!truthTest(array[i])) { 229 | return false; 230 | } 231 | } 232 | return true; 233 | } 234 | 235 | }, {}], 236 | 3: [function(require, module, exports) { 237 | 'use strict'; 238 | 239 | var adapters = typeof jasmine.addMatchers === 'function' ? 240 | require('./jasmine-v2') : 241 | require('./jasmine-v1'); 242 | 243 | module.exports = function(name, matcher) { 244 | var adapter = adapters[matcher.length]; 245 | return adapter(name, matcher); 246 | }; 247 | 248 | }, { 249 | './jasmine-v1': 4, 250 | './jasmine-v2': 5 251 | }], 252 | 4: [function(require, module, exports) { 253 | 'use strict'; 254 | 255 | module.exports = { 256 | 1: createFactory(forActual), 257 | 2: createFactory(forActualAndExpected), 258 | 3: createFactory(forActualAndTwoExpected), 259 | 4: createFactory(forKeyAndActualAndTwoExpected) 260 | }; 261 | 262 | function createFactory(adapter) { 263 | return function jasmineV1MatcherFactory(name, matcher) { 264 | var matcherByName = new JasmineV1Matcher(name, adapter, matcher); 265 | beforeEach(function() { 266 | this.addMatchers(matcherByName); 267 | }); 268 | return matcherByName; 269 | }; 270 | } 271 | 272 | function JasmineV1Matcher(name, adapter, matcher) { 273 | this[name] = adapter(name, matcher); 274 | } 275 | 276 | function forActual(name, matcher) { 277 | return function(optionalMessage) { 278 | return matcher(this.actual, optionalMessage); 279 | }; 280 | } 281 | 282 | function forActualAndExpected(name, matcher) { 283 | return function(expected, optionalMessage) { 284 | return matcher(expected, this.actual, optionalMessage); 285 | }; 286 | } 287 | 288 | function forActualAndTwoExpected(name, matcher) { 289 | return function(expected1, expected2, optionalMessage) { 290 | return matcher(expected1, expected2, this.actual, optionalMessage); 291 | }; 292 | } 293 | 294 | function forKeyAndActualAndTwoExpected(name, matcher) { 295 | return function(key, expected1, expected2, optionalMessage) { 296 | return matcher(key, expected1, expected2, this.actual, optionalMessage); 297 | }; 298 | } 299 | 300 | }, {}], 301 | 5: [function(require, module, exports) { 302 | 'use strict'; 303 | 304 | var matcherFactory = require('./matcherFactory'); 305 | var memberMatcherFactory = require('./memberMatcherFactory'); 306 | 307 | module.exports = { 308 | 1: createFactory(getAdapter(1)), 309 | 2: createFactory(getAdapter(2)), 310 | 3: createFactory(getAdapter(3)), 311 | 4: createFactory(getAdapter(4)) 312 | }; 313 | 314 | function createFactory(adapter) { 315 | return function jasmineV2MatcherFactory(name, matcher) { 316 | var matcherByName = new JasmineV2Matcher(name, adapter, matcher); 317 | beforeEach(function() { 318 | jasmine.addMatchers(matcherByName); 319 | }); 320 | return matcherByName; 321 | }; 322 | } 323 | 324 | function JasmineV2Matcher(name, adapter, matcher) { 325 | this[name] = adapter(name, matcher); 326 | } 327 | 328 | function getAdapter(argsCount) { 329 | return function adapter(name, matcher) { 330 | var factory = isMemberMatcher(name) ? memberMatcherFactory : matcherFactory; 331 | return factory[argsCount](name, matcher); 332 | }; 333 | } 334 | 335 | function isMemberMatcher(name) { 336 | return name.search(/^toHave/) !== -1; 337 | } 338 | 339 | }, { 340 | './matcherFactory': 6, 341 | './memberMatcherFactory': 7 342 | }], 343 | 6: [function(require, module, exports) { 344 | 'use strict'; 345 | 346 | module.exports = { 347 | 1: forActual, 348 | 2: forActualAndExpected, 349 | 3: forActualAndTwoExpected 350 | }; 351 | 352 | function forActual(name, matcher) { 353 | return function(util) { 354 | return { 355 | compare: function(actual, optionalMessage) { 356 | var passes = matcher(actual); 357 | return { 358 | pass: passes, 359 | message: ( 360 | optionalMessage ? 361 | util.buildFailureMessage(name, passes, actual, optionalMessage) : 362 | util.buildFailureMessage(name, passes, actual) 363 | ) 364 | }; 365 | } 366 | }; 367 | }; 368 | } 369 | 370 | function forActualAndExpected(name, matcher) { 371 | return function(util) { 372 | return { 373 | compare: function(actual, expected, optionalMessage) { 374 | var passes = matcher(expected, actual); 375 | return { 376 | pass: passes, 377 | message: ( 378 | optionalMessage ? 379 | util.buildFailureMessage(name, passes, actual, expected, optionalMessage) : 380 | util.buildFailureMessage(name, passes, actual, expected) 381 | ) 382 | }; 383 | } 384 | }; 385 | }; 386 | } 387 | 388 | function forActualAndTwoExpected(name, matcher) { 389 | return function(util) { 390 | return { 391 | compare: function(actual, expected1, expected2, optionalMessage) { 392 | var passes = matcher(expected1, expected2, actual); 393 | return { 394 | pass: passes, 395 | message: ( 396 | optionalMessage ? 397 | util.buildFailureMessage(name, passes, actual, expected1, expected2, optionalMessage) : 398 | util.buildFailureMessage(name, passes, actual, expected1, expected2) 399 | ) 400 | }; 401 | } 402 | }; 403 | }; 404 | } 405 | 406 | }, {}], 407 | 7: [function(require, module, exports) { 408 | 'use strict'; 409 | 410 | module.exports = { 411 | 2: forKeyAndActual, 412 | 3: forKeyAndActualAndExpected, 413 | 4: forKeyAndActualAndTwoExpected 414 | }; 415 | 416 | function forKeyAndActual(name, matcher) { 417 | return function(util) { 418 | return { 419 | compare: function(actual, key, optionalMessage) { 420 | var passes = matcher(key, actual); 421 | return { 422 | pass: passes, 423 | message: ( 424 | optionalMessage ? 425 | util.buildFailureMessage(name, passes, actual, optionalMessage) : 426 | util.buildFailureMessage(name, passes, actual) 427 | ) 428 | }; 429 | } 430 | }; 431 | }; 432 | } 433 | 434 | function forKeyAndActualAndExpected(name, matcher) { 435 | return function(util) { 436 | return { 437 | compare: function(actual, key, expected, optionalMessage) { 438 | var passes = matcher(key, expected, actual); 439 | return { 440 | pass: passes, 441 | message: ( 442 | optionalMessage ? 443 | util.buildFailureMessage(name, passes, actual, expected, optionalMessage) : 444 | util.buildFailureMessage(name, passes, actual, expected) 445 | ) 446 | }; 447 | } 448 | }; 449 | }; 450 | } 451 | 452 | function forKeyAndActualAndTwoExpected(name, matcher) { 453 | return function(util) { 454 | return { 455 | compare: function(actual, key, expected1, expected2, optionalMessage) { 456 | var passes = matcher(key, expected1, expected2, actual); 457 | return { 458 | pass: passes, 459 | message: ( 460 | optionalMessage ? 461 | util.buildFailureMessage(name, passes, actual, expected1, expected2, optionalMessage) : 462 | util.buildFailureMessage(name, passes, actual, expected1, expected2) 463 | ) 464 | }; 465 | } 466 | }; 467 | }; 468 | } 469 | 470 | }, {}], 471 | 8: [function(require, module, exports) { 472 | 'use strict'; 473 | 474 | module.exports = is; 475 | 476 | function is(value, type) { 477 | return Object.prototype.toString.call(value) === '[object ' + type + ']'; 478 | } 479 | 480 | }, {}], 481 | 9: [function(require, module, exports) { 482 | 'use strict'; 483 | 484 | module.exports = keys; 485 | 486 | function keys(object) { 487 | var list = []; 488 | for (var key in object) { 489 | if (object.hasOwnProperty(key)) { 490 | list.push(key); 491 | } 492 | } 493 | return list; 494 | } 495 | 496 | }, {}], 497 | 10: [function(require, module, exports) { 498 | 'use strict'; 499 | 500 | var toBeBefore = require('./toBeBefore'); 501 | 502 | module.exports = toBeAfter; 503 | 504 | function toBeAfter(otherDate, actual) { 505 | return toBeBefore(actual, otherDate); 506 | } 507 | 508 | }, { 509 | './toBeBefore': 17 510 | }], 511 | 11: [function(require, module, exports) { 512 | 'use strict'; 513 | 514 | var is = require('./lib/is'); 515 | 516 | module.exports = toBeArray; 517 | 518 | function toBeArray(actual) { 519 | return is(actual, 'Array'); 520 | } 521 | 522 | }, { 523 | './lib/is': 8 524 | }], 525 | 12: [function(require, module, exports) { 526 | 'use strict'; 527 | 528 | var every = require('./lib/every'); 529 | var toBeArray = require('./toBeArray'); 530 | var toBeBoolean = require('./toBeBoolean'); 531 | 532 | module.exports = toBeArrayOfBooleans; 533 | 534 | function toBeArrayOfBooleans(actual) { 535 | return toBeArray(actual) && 536 | every(actual, toBeBoolean); 537 | } 538 | 539 | }, { 540 | './lib/every': 2, 541 | './toBeArray': 11, 542 | './toBeBoolean': 18 543 | }], 544 | 13: [function(require, module, exports) { 545 | 'use strict'; 546 | 547 | var every = require('./lib/every'); 548 | var toBeArray = require('./toBeArray'); 549 | var toBeNumber = require('./toBeNumber'); 550 | 551 | module.exports = toBeArrayOfBooleans; 552 | 553 | function toBeArrayOfBooleans(actual) { 554 | return toBeArray(actual) && 555 | every(actual, toBeNumber); 556 | } 557 | 558 | }, { 559 | './lib/every': 2, 560 | './toBeArray': 11, 561 | './toBeNumber': 34 562 | }], 563 | 14: [function(require, module, exports) { 564 | 'use strict'; 565 | 566 | var every = require('./lib/every'); 567 | var toBeArray = require('./toBeArray'); 568 | var toBeObject = require('./toBeObject'); 569 | 570 | module.exports = toBeArrayOfBooleans; 571 | 572 | function toBeArrayOfBooleans(actual) { 573 | return toBeArray(actual) && 574 | every(actual, toBeObject); 575 | } 576 | 577 | }, { 578 | './lib/every': 2, 579 | './toBeArray': 11, 580 | './toBeObject': 35 581 | }], 582 | 15: [function(require, module, exports) { 583 | 'use strict'; 584 | 585 | var toBeArray = require('./toBeArray'); 586 | 587 | module.exports = toBeArrayOfSize; 588 | 589 | function toBeArrayOfSize(size, actual) { 590 | return toBeArray(actual) && 591 | actual.length === size; 592 | } 593 | 594 | }, { 595 | './toBeArray': 11 596 | }], 597 | 16: [function(require, module, exports) { 598 | 'use strict'; 599 | 600 | var every = require('./lib/every'); 601 | var toBeArray = require('./toBeArray'); 602 | var toBeString = require('./toBeString'); 603 | 604 | module.exports = toBeArrayOfStrings; 605 | 606 | function toBeArrayOfStrings(actual) { 607 | return toBeArray(actual) && 608 | every(actual, toBeString); 609 | } 610 | 611 | }, { 612 | './lib/every': 2, 613 | './toBeArray': 11, 614 | './toBeString': 39 615 | }], 616 | 17: [function(require, module, exports) { 617 | 'use strict'; 618 | 619 | var toBeDate = require('./toBeDate'); 620 | 621 | module.exports = toBeBefore; 622 | 623 | function toBeBefore(otherDate, actual) { 624 | return toBeDate(actual) && 625 | toBeDate(otherDate) && 626 | actual.getTime() < otherDate.getTime(); 627 | } 628 | 629 | }, { 630 | './toBeDate': 20 631 | }], 632 | 18: [function(require, module, exports) { 633 | 'use strict'; 634 | 635 | var is = require('./lib/is'); 636 | 637 | module.exports = toBeBoolean; 638 | 639 | function toBeBoolean(actual) { 640 | return is(actual, 'Boolean'); 641 | } 642 | 643 | }, { 644 | './lib/is': 8 645 | }], 646 | 19: [function(require, module, exports) { 647 | 'use strict'; 648 | 649 | module.exports = toBeCalculable; 650 | 651 | // Assert subject can be used in Mathemetic 652 | // calculations despite not being a Number, 653 | // for example `"1" * "2" === 2` whereas 654 | // `"wut?" * 2 === NaN`. 655 | function toBeCalculable(actual) { 656 | return !isNaN(actual * 2); 657 | } 658 | 659 | }, {}], 660 | 20: [function(require, module, exports) { 661 | 'use strict'; 662 | 663 | var is = require('./lib/is'); 664 | 665 | module.exports = toBeDate; 666 | 667 | function toBeDate(actual) { 668 | return is(actual, 'Date'); 669 | } 670 | 671 | }, { 672 | './lib/is': 8 673 | }], 674 | 21: [function(require, module, exports) { 675 | 'use strict'; 676 | 677 | var toBeArrayOfSize = require('./toBeArrayOfSize'); 678 | 679 | module.exports = toBeEmptyArray; 680 | 681 | function toBeEmptyArray(actual) { 682 | return toBeArrayOfSize(0, actual); 683 | } 684 | 685 | }, { 686 | './toBeArrayOfSize': 15 687 | }], 688 | 22: [function(require, module, exports) { 689 | 'use strict'; 690 | 691 | var keys = require('./lib/keys'); 692 | var is = require('./lib/is'); 693 | 694 | module.exports = toBeEmptyObject; 695 | 696 | function toBeEmptyObject(actual) { 697 | return is(actual, 'Object') && 698 | keys(actual).length === 0; 699 | } 700 | 701 | }, { 702 | './lib/is': 8, 703 | './lib/keys': 9 704 | }], 705 | 23: [function(require, module, exports) { 706 | 'use strict'; 707 | 708 | module.exports = toBeEmptyString; 709 | 710 | function toBeEmptyString(actual) { 711 | return actual === ''; 712 | } 713 | 714 | }, {}], 715 | 24: [function(require, module, exports) { 716 | 'use strict'; 717 | 718 | var toBeNumber = require('./toBeNumber'); 719 | 720 | module.exports = toBeEvenNumber; 721 | 722 | function toBeEvenNumber(actual) { 723 | return toBeNumber(actual) && 724 | actual % 2 === 0; 725 | } 726 | 727 | }, { 728 | './toBeNumber': 34 729 | }], 730 | 25: [function(require, module, exports) { 731 | 'use strict'; 732 | 733 | var is = require('./lib/is'); 734 | 735 | module.exports = toBeFalse; 736 | 737 | function toBeFalse(actual) { 738 | return actual === false || 739 | is(actual, 'Boolean') && 740 | !actual.valueOf(); 741 | } 742 | 743 | }, { 744 | './lib/is': 8 745 | }], 746 | 26: [function(require, module, exports) { 747 | 'use strict'; 748 | 749 | module.exports = toBeFunction; 750 | 751 | function toBeFunction(actual) { 752 | return typeof actual === 'function'; 753 | } 754 | 755 | }, {}], 756 | 27: [function(require, module, exports) { 757 | 'use strict'; 758 | 759 | var toBeString = require('./toBeString'); 760 | 761 | module.exports = toBeHtmlString; 762 | 763 | function toBeHtmlString(actual) { 764 | // < start with opening tag "<" 765 | // ( start group 1 766 | // "[^"]*" allow string in "double quotes" 767 | // | OR 768 | // '[^']*' allow string in "single quotes" 769 | // | OR 770 | // [^'">] cant contains one single quotes, double quotes and ">" 771 | // ) end group 1 772 | // * 0 or more 773 | // > end with closing tag ">" 774 | return toBeString(actual) && 775 | actual.search(/<("[^"]*"|'[^']*'|[^'">])*>/) !== -1; 776 | } 777 | 778 | }, { 779 | './toBeString': 39 780 | }], 781 | 28: [function(require, module, exports) { 782 | 'use strict'; 783 | 784 | var toBeString = require('./toBeString'); 785 | 786 | module.exports = toBeIso8601; 787 | 788 | function toBeIso8601(actual) { 789 | 790 | if (!toBeString(actual)) { 791 | return false; 792 | } 793 | 794 | if ( 795 | isIso8601(actual, [ 796 | // 2013-07-08 797 | 4, '-', 2, '-', 2 798 | ]) || isIso8601(actual, [ 799 | // 2013-07-08T07:29 800 | 4, '-', 2, '-', 2, 'T', 2, ':', 2 801 | ]) || isIso8601(actual, [ 802 | // 2013-07-08T07:29:15 803 | 4, '-', 2, '-', 2, 'T', 2, ':', 2, ':', 2 804 | ]) || isIso8601(actual, [ 805 | // 2013-07-08T07:29:15.863 806 | 4, '-', 2, '-', 2, 'T', 2, ':', 2, ':', 2, '.', 3 807 | ]) || isIso8601(actual, [ 808 | // 2013-07-08T07:29:15.863Z 809 | 4, '-', 2, '-', 2, 'T', 2, ':', 2, ':', 2, '.', 3, 'Z' 810 | ]) 811 | ) { 812 | return new Date(actual).toString() !== 'Invalid Date'; 813 | } 814 | 815 | return false; 816 | 817 | } 818 | 819 | function isIso8601(string, pattern) { 820 | var returnValue = string.search( 821 | new RegExp('^' + pattern.map(function(term) { 822 | if (term === '-') { 823 | return '\\-'; 824 | } else if (typeof term === 'string') { 825 | return term; 826 | } else { 827 | return '([0-9]{' + term + '})'; 828 | } 829 | }).join('') + '$') 830 | ) !== -1; 831 | return returnValue; 832 | } 833 | 834 | }, { 835 | './toBeString': 39 836 | }], 837 | 29: [function(require, module, exports) { 838 | 'use strict'; 839 | 840 | module.exports = toBeJsonString; 841 | 842 | function toBeJsonString(actual) { 843 | var isParseable; 844 | var json; 845 | try { 846 | json = JSON.parse(actual); 847 | } catch (e) { 848 | isParseable = false; 849 | } 850 | return isParseable !== false && 851 | json !== null; 852 | } 853 | 854 | }, {}], 855 | 30: [function(require, module, exports) { 856 | 'use strict'; 857 | 858 | var toBeString = require('./toBeString'); 859 | 860 | module.exports = toBeLongerThan; 861 | 862 | function toBeLongerThan(otherString, actual) { 863 | return toBeString(actual) && 864 | toBeString(otherString) && 865 | actual.length > otherString.length; 866 | } 867 | 868 | }, { 869 | './toBeString': 39 870 | }], 871 | 31: [function(require, module, exports) { 872 | 'use strict'; 873 | 874 | var is = require('./lib/is'); 875 | 876 | module.exports = toBeNonEmptyArray; 877 | 878 | function toBeNonEmptyArray(actual) { 879 | return is(actual, 'Array') && 880 | actual.length > 0; 881 | } 882 | 883 | }, { 884 | './lib/is': 8 885 | }], 886 | 32: [function(require, module, exports) { 887 | 'use strict'; 888 | 889 | var keys = require('./lib/keys'); 890 | var is = require('./lib/is'); 891 | 892 | module.exports = toBeNonEmptyObject; 893 | 894 | function toBeNonEmptyObject(actual) { 895 | return is(actual, 'Object') && 896 | keys(actual).length > 0; 897 | } 898 | 899 | }, { 900 | './lib/is': 8, 901 | './lib/keys': 9 902 | }], 903 | 33: [function(require, module, exports) { 904 | 'use strict'; 905 | 906 | var toBeString = require('./toBeString'); 907 | 908 | module.exports = toBeNonEmptyString; 909 | 910 | function toBeNonEmptyString(actual) { 911 | return toBeString(actual) && 912 | actual.length > 0; 913 | } 914 | 915 | }, { 916 | './toBeString': 39 917 | }], 918 | 34: [function(require, module, exports) { 919 | 'use strict'; 920 | 921 | var is = require('./lib/is'); 922 | 923 | module.exports = toBeNumber; 924 | 925 | function toBeNumber(actual) { 926 | return !isNaN(parseFloat(actual)) && 927 | !is(actual, 'String'); 928 | } 929 | 930 | }, { 931 | './lib/is': 8 932 | }], 933 | 35: [function(require, module, exports) { 934 | 'use strict'; 935 | 936 | var is = require('./lib/is'); 937 | 938 | module.exports = toBeObject; 939 | 940 | function toBeObject(actual) { 941 | return is(actual, 'Object'); 942 | } 943 | 944 | }, { 945 | './lib/is': 8 946 | }], 947 | 36: [function(require, module, exports) { 948 | 'use strict'; 949 | 950 | var toBeNumber = require('./toBeNumber'); 951 | 952 | module.exports = toBeOddNumber; 953 | 954 | function toBeOddNumber(actual) { 955 | return toBeNumber(actual) && 956 | actual % 2 !== 0; 957 | } 958 | 959 | }, { 960 | './toBeNumber': 34 961 | }], 962 | 37: [function(require, module, exports) { 963 | 'use strict'; 964 | 965 | var toBeString = require('./toBeString'); 966 | 967 | module.exports = toBeSameLengthAs; 968 | 969 | function toBeSameLengthAs(otherString, actual) { 970 | return toBeString(actual) && 971 | toBeString(otherString) && 972 | actual.length === otherString.length; 973 | } 974 | 975 | }, { 976 | './toBeString': 39 977 | }], 978 | 38: [function(require, module, exports) { 979 | 'use strict'; 980 | 981 | var toBeString = require('./toBeString'); 982 | 983 | module.exports = toBeShorterThan; 984 | 985 | function toBeShorterThan(otherString, actual) { 986 | return toBeString(actual) && 987 | toBeString(otherString) && 988 | actual.length < otherString.length; 989 | } 990 | 991 | }, { 992 | './toBeString': 39 993 | }], 994 | 39: [function(require, module, exports) { 995 | 'use strict'; 996 | 997 | var is = require('./lib/is'); 998 | 999 | module.exports = toBeString; 1000 | 1001 | function toBeString(actual) { 1002 | return is(actual, 'String'); 1003 | } 1004 | 1005 | }, { 1006 | './lib/is': 8 1007 | }], 1008 | 40: [function(require, module, exports) { 1009 | 'use strict'; 1010 | 1011 | var is = require('./lib/is'); 1012 | 1013 | module.exports = toBeTrue; 1014 | 1015 | function toBeTrue(actual) { 1016 | return actual === true || 1017 | is(actual, 'Boolean') && 1018 | actual.valueOf(); 1019 | } 1020 | 1021 | }, { 1022 | './lib/is': 8 1023 | }], 1024 | 41: [function(require, module, exports) { 1025 | 'use strict'; 1026 | 1027 | var toBeString = require('./toBeString'); 1028 | 1029 | module.exports = toBeWhitespace; 1030 | 1031 | function toBeWhitespace(actual) { 1032 | return toBeString(actual) && 1033 | actual.search(/\S/) === -1; 1034 | } 1035 | 1036 | }, { 1037 | './toBeString': 39 1038 | }], 1039 | 42: [function(require, module, exports) { 1040 | 'use strict'; 1041 | 1042 | var toBeNumber = require('./toBeNumber'); 1043 | 1044 | module.exports = toBeWholeNumber; 1045 | 1046 | function toBeWholeNumber(actual) { 1047 | return toBeNumber(actual) && ( 1048 | actual === 0 || actual % 1 === 0 1049 | ); 1050 | } 1051 | 1052 | }, { 1053 | './toBeNumber': 34 1054 | }], 1055 | 43: [function(require, module, exports) { 1056 | 'use strict'; 1057 | 1058 | var toBeNumber = require('./toBeNumber'); 1059 | 1060 | module.exports = toBeWithinRange; 1061 | 1062 | function toBeWithinRange(floor, ceiling, actual) { 1063 | return toBeNumber(actual) && 1064 | actual >= floor && 1065 | actual <= ceiling; 1066 | } 1067 | 1068 | }, { 1069 | './toBeNumber': 34 1070 | }], 1071 | 44: [function(require, module, exports) { 1072 | 'use strict'; 1073 | 1074 | var toBeNonEmptyString = require('./toBeNonEmptyString'); 1075 | 1076 | module.exports = toEndWith; 1077 | 1078 | function toEndWith(subString, actual) { 1079 | if (!toBeNonEmptyString(actual) || !toBeNonEmptyString(subString)) { 1080 | return false; 1081 | } 1082 | return actual.slice(actual.length - subString.length, actual.length) === subString; 1083 | } 1084 | 1085 | }, { 1086 | './toBeNonEmptyString': 33 1087 | }], 1088 | 45: [function(require, module, exports) { 1089 | 'use strict'; 1090 | 1091 | var toBeObject = require('./toBeObject'); 1092 | var toBeArray = require('./toBeArray'); 1093 | 1094 | module.exports = toHaveArray; 1095 | 1096 | function toHaveArray(key, actual) { 1097 | return toBeObject(actual) && 1098 | toBeArray(actual[key]); 1099 | } 1100 | 1101 | }, { 1102 | './toBeArray': 11, 1103 | './toBeObject': 35 1104 | }], 1105 | 46: [function(require, module, exports) { 1106 | 'use strict'; 1107 | 1108 | var toBeObject = require('./toBeObject'); 1109 | var toBeArrayOfBooleans = require('./toBeArrayOfBooleans'); 1110 | 1111 | module.exports = toHaveArrayOfBooleans; 1112 | 1113 | function toHaveArrayOfBooleans(key, actual) { 1114 | return toBeObject(actual) && 1115 | toBeArrayOfBooleans(actual[key]); 1116 | } 1117 | 1118 | }, { 1119 | './toBeArrayOfBooleans': 12, 1120 | './toBeObject': 35 1121 | }], 1122 | 47: [function(require, module, exports) { 1123 | 'use strict'; 1124 | 1125 | var toBeObject = require('./toBeObject'); 1126 | var toBeArrayOfNumbers = require('./toBeArrayOfNumbers'); 1127 | 1128 | module.exports = toHaveArrayOfNumbers; 1129 | 1130 | function toHaveArrayOfNumbers(key, actual) { 1131 | return toBeObject(actual) && 1132 | toBeArrayOfNumbers(actual[key]); 1133 | } 1134 | 1135 | }, { 1136 | './toBeArrayOfNumbers': 13, 1137 | './toBeObject': 35 1138 | }], 1139 | 48: [function(require, module, exports) { 1140 | 'use strict'; 1141 | 1142 | var toBeObject = require('./toBeObject'); 1143 | var toBeArrayOfObjects = require('./toBeArrayOfObjects'); 1144 | 1145 | module.exports = toHaveArrayOfObjects; 1146 | 1147 | function toHaveArrayOfObjects(key, actual) { 1148 | return toBeObject(actual) && 1149 | toBeArrayOfObjects(actual[key]); 1150 | } 1151 | 1152 | }, { 1153 | './toBeArrayOfObjects': 14, 1154 | './toBeObject': 35 1155 | }], 1156 | 49: [function(require, module, exports) { 1157 | 'use strict'; 1158 | 1159 | var toBeObject = require('./toBeObject'); 1160 | var toBeArrayOfSize = require('./toBeArrayOfSize'); 1161 | 1162 | module.exports = toHaveArrayOfSize; 1163 | 1164 | function toHaveArrayOfSize(key, size, actual) { 1165 | return toBeObject(actual) && 1166 | toBeArrayOfSize(size, actual[key]); 1167 | } 1168 | 1169 | }, { 1170 | './toBeArrayOfSize': 15, 1171 | './toBeObject': 35 1172 | }], 1173 | 50: [function(require, module, exports) { 1174 | 'use strict'; 1175 | 1176 | var toBeObject = require('./toBeObject'); 1177 | var toBeArrayOfStrings = require('./toBeArrayOfStrings'); 1178 | 1179 | module.exports = toHaveArrayOfStrings; 1180 | 1181 | function toHaveArrayOfStrings(key, actual) { 1182 | return toBeObject(actual) && 1183 | toBeArrayOfStrings(actual[key]); 1184 | } 1185 | 1186 | }, { 1187 | './toBeArrayOfStrings': 16, 1188 | './toBeObject': 35 1189 | }], 1190 | 51: [function(require, module, exports) { 1191 | 'use strict'; 1192 | 1193 | var toBeObject = require('./toBeObject'); 1194 | var toBeBoolean = require('./toBeBoolean'); 1195 | 1196 | module.exports = toHaveBoolean; 1197 | 1198 | function toHaveBoolean(key, actual) { 1199 | return toBeObject(actual) && 1200 | toBeBoolean(actual[key]); 1201 | } 1202 | 1203 | }, { 1204 | './toBeBoolean': 18, 1205 | './toBeObject': 35 1206 | }], 1207 | 52: [function(require, module, exports) { 1208 | 'use strict'; 1209 | 1210 | var toBeObject = require('./toBeObject'); 1211 | var toBeCalculable = require('./toBeCalculable'); 1212 | 1213 | module.exports = toHaveCalculable; 1214 | 1215 | function toHaveCalculable(key, actual) { 1216 | return toBeObject(actual) && 1217 | toBeCalculable(actual[key]); 1218 | } 1219 | 1220 | }, { 1221 | './toBeCalculable': 19, 1222 | './toBeObject': 35 1223 | }], 1224 | 53: [function(require, module, exports) { 1225 | 'use strict'; 1226 | 1227 | var toBeObject = require('./toBeObject'); 1228 | var toBeDate = require('./toBeDate'); 1229 | 1230 | module.exports = toHaveDate; 1231 | 1232 | function toHaveDate(key, actual) { 1233 | return toBeObject(actual) && 1234 | toBeDate(actual[key]); 1235 | } 1236 | 1237 | }, { 1238 | './toBeDate': 20, 1239 | './toBeObject': 35 1240 | }], 1241 | 54: [function(require, module, exports) { 1242 | 'use strict'; 1243 | 1244 | var toBeObject = require('./toBeObject'); 1245 | var toBeAfter = require('./toBeAfter'); 1246 | 1247 | module.exports = toHaveDateAfter; 1248 | 1249 | function toHaveDateAfter(key, date, actual) { 1250 | return toBeObject(actual) && 1251 | toBeAfter(date, actual[key]); 1252 | } 1253 | 1254 | }, { 1255 | './toBeAfter': 10, 1256 | './toBeObject': 35 1257 | }], 1258 | 55: [function(require, module, exports) { 1259 | 'use strict'; 1260 | 1261 | var toBeObject = require('./toBeObject'); 1262 | var toBeBefore = require('./toBeBefore'); 1263 | 1264 | module.exports = toHaveDateBefore; 1265 | 1266 | function toHaveDateBefore(key, date, actual) { 1267 | return toBeObject(actual) && 1268 | toBeBefore(date, actual[key]); 1269 | } 1270 | 1271 | }, { 1272 | './toBeBefore': 17, 1273 | './toBeObject': 35 1274 | }], 1275 | 56: [function(require, module, exports) { 1276 | 'use strict'; 1277 | 1278 | var toBeObject = require('./toBeObject'); 1279 | var toBeEmptyArray = require('./toBeEmptyArray'); 1280 | 1281 | module.exports = toHaveEmptyArray; 1282 | 1283 | function toHaveEmptyArray(key, actual) { 1284 | return toBeObject(actual) && 1285 | toBeEmptyArray(actual[key]); 1286 | } 1287 | 1288 | }, { 1289 | './toBeEmptyArray': 21, 1290 | './toBeObject': 35 1291 | }], 1292 | 57: [function(require, module, exports) { 1293 | 'use strict'; 1294 | 1295 | var toBeObject = require('./toBeObject'); 1296 | var toBeEmptyObject = require('./toBeEmptyObject'); 1297 | 1298 | module.exports = toHaveEmptyObject; 1299 | 1300 | function toHaveEmptyObject(key, actual) { 1301 | return toBeObject(actual) && 1302 | toBeEmptyObject(actual[key]); 1303 | } 1304 | 1305 | }, { 1306 | './toBeEmptyObject': 22, 1307 | './toBeObject': 35 1308 | }], 1309 | 58: [function(require, module, exports) { 1310 | 'use strict'; 1311 | 1312 | var toBeObject = require('./toBeObject'); 1313 | var toBeEmptyString = require('./toBeEmptyString'); 1314 | 1315 | module.exports = toHaveEmptyString; 1316 | 1317 | function toHaveEmptyString(key, actual) { 1318 | return toBeObject(actual) && 1319 | toBeEmptyString(actual[key]); 1320 | } 1321 | 1322 | }, { 1323 | './toBeEmptyString': 23, 1324 | './toBeObject': 35 1325 | }], 1326 | 59: [function(require, module, exports) { 1327 | 'use strict'; 1328 | 1329 | var toBeObject = require('./toBeObject'); 1330 | var toBeEvenNumber = require('./toBeEvenNumber'); 1331 | 1332 | module.exports = toHaveEvenNumber; 1333 | 1334 | function toHaveEvenNumber(key, actual) { 1335 | return toBeObject(actual) && 1336 | toBeEvenNumber(actual[key]); 1337 | } 1338 | 1339 | }, { 1340 | './toBeEvenNumber': 24, 1341 | './toBeObject': 35 1342 | }], 1343 | 60: [function(require, module, exports) { 1344 | 'use strict'; 1345 | 1346 | var toBeObject = require('./toBeObject'); 1347 | var toBeFalse = require('./toBeFalse'); 1348 | 1349 | module.exports = toHaveFalse; 1350 | 1351 | function toHaveFalse(key, actual) { 1352 | return toBeObject(actual) && 1353 | toBeFalse(actual[key]); 1354 | } 1355 | 1356 | }, { 1357 | './toBeFalse': 25, 1358 | './toBeObject': 35 1359 | }], 1360 | 61: [function(require, module, exports) { 1361 | 'use strict'; 1362 | 1363 | var toBeObject = require('./toBeObject'); 1364 | var toBeHtmlString = require('./toBeHtmlString'); 1365 | 1366 | module.exports = toHaveHtmlString; 1367 | 1368 | function toHaveHtmlString(key, actual) { 1369 | return toBeObject(actual) && 1370 | toBeHtmlString(actual[key]); 1371 | } 1372 | 1373 | }, { 1374 | './toBeHtmlString': 27, 1375 | './toBeObject': 35 1376 | }], 1377 | 62: [function(require, module, exports) { 1378 | 'use strict'; 1379 | 1380 | var toBeObject = require('./toBeObject'); 1381 | var toBeIso8601 = require('./toBeIso8601'); 1382 | 1383 | module.exports = toHaveIso8601; 1384 | 1385 | function toHaveIso8601(key, actual) { 1386 | return toBeObject(actual) && 1387 | toBeIso8601(actual[key]); 1388 | } 1389 | 1390 | }, { 1391 | './toBeIso8601': 28, 1392 | './toBeObject': 35 1393 | }], 1394 | 63: [function(require, module, exports) { 1395 | 'use strict'; 1396 | 1397 | var toBeObject = require('./toBeObject'); 1398 | var toBeJsonString = require('./toBeJsonString'); 1399 | 1400 | module.exports = toHaveJsonString; 1401 | 1402 | function toHaveJsonString(key, actual) { 1403 | return toBeObject(actual) && 1404 | toBeJsonString(actual[key]); 1405 | } 1406 | 1407 | }, { 1408 | './toBeJsonString': 29, 1409 | './toBeObject': 35 1410 | }], 1411 | 64: [function(require, module, exports) { 1412 | 'use strict'; 1413 | 1414 | var toBeObject = require('./toBeObject'); 1415 | var toBeString = require('./toBeString'); 1416 | 1417 | module.exports = toHaveMember; 1418 | 1419 | function toHaveMember(key, actual) { 1420 | return toBeString(key) && 1421 | toBeObject(actual) && 1422 | key in actual; 1423 | } 1424 | 1425 | }, { 1426 | './toBeObject': 35, 1427 | './toBeString': 39 1428 | }], 1429 | 65: [function(require, module, exports) { 1430 | 'use strict'; 1431 | 1432 | var toBeObject = require('./toBeObject'); 1433 | var toBeFunction = require('./toBeFunction'); 1434 | 1435 | module.exports = toHaveMethod; 1436 | 1437 | function toHaveMethod(key, actual) { 1438 | return toBeObject(actual) && 1439 | toBeFunction(actual[key]); 1440 | } 1441 | 1442 | }, { 1443 | './toBeFunction': 26, 1444 | './toBeObject': 35 1445 | }], 1446 | 66: [function(require, module, exports) { 1447 | 'use strict'; 1448 | 1449 | var toBeObject = require('./toBeObject'); 1450 | var toBeNonEmptyArray = require('./toBeNonEmptyArray'); 1451 | 1452 | module.exports = toHaveNonEmptyArray; 1453 | 1454 | function toHaveNonEmptyArray(key, actual) { 1455 | return toBeObject(actual) && 1456 | toBeNonEmptyArray(actual[key]); 1457 | } 1458 | 1459 | }, { 1460 | './toBeNonEmptyArray': 31, 1461 | './toBeObject': 35 1462 | }], 1463 | 67: [function(require, module, exports) { 1464 | 'use strict'; 1465 | 1466 | var toBeObject = require('./toBeObject'); 1467 | var toBeNonEmptyObject = require('./toBeNonEmptyObject'); 1468 | 1469 | module.exports = toHaveNonEmptyObject; 1470 | 1471 | function toHaveNonEmptyObject(key, actual) { 1472 | return toBeObject(actual) && 1473 | toBeNonEmptyObject(actual[key]); 1474 | } 1475 | 1476 | }, { 1477 | './toBeNonEmptyObject': 32, 1478 | './toBeObject': 35 1479 | }], 1480 | 68: [function(require, module, exports) { 1481 | 'use strict'; 1482 | 1483 | var toBeObject = require('./toBeObject'); 1484 | var toBeNonEmptyString = require('./toBeNonEmptyString'); 1485 | 1486 | module.exports = toHaveNonEmptyString; 1487 | 1488 | function toHaveNonEmptyString(key, actual) { 1489 | return toBeObject(actual) && 1490 | toBeNonEmptyString(actual[key]); 1491 | } 1492 | 1493 | }, { 1494 | './toBeNonEmptyString': 33, 1495 | './toBeObject': 35 1496 | }], 1497 | 69: [function(require, module, exports) { 1498 | 'use strict'; 1499 | 1500 | var toBeObject = require('./toBeObject'); 1501 | var toBeNumber = require('./toBeNumber'); 1502 | 1503 | module.exports = toHaveNumber; 1504 | 1505 | function toHaveNumber(key, actual) { 1506 | return toBeObject(actual) && 1507 | toBeNumber(actual[key]); 1508 | } 1509 | 1510 | }, { 1511 | './toBeNumber': 34, 1512 | './toBeObject': 35 1513 | }], 1514 | 70: [function(require, module, exports) { 1515 | 'use strict'; 1516 | 1517 | var toBeObject = require('./toBeObject'); 1518 | var toBeWithinRange = require('./toBeWithinRange'); 1519 | 1520 | module.exports = toHaveNumberWithinRange; 1521 | 1522 | function toHaveNumberWithinRange(key, floor, ceiling, actual) { 1523 | return toBeObject(actual) && 1524 | toBeWithinRange(floor, ceiling, actual[key]); 1525 | } 1526 | 1527 | }, { 1528 | './toBeObject': 35, 1529 | './toBeWithinRange': 43 1530 | }], 1531 | 71: [function(require, module, exports) { 1532 | 'use strict'; 1533 | 1534 | var toBeObject = require('./toBeObject'); 1535 | 1536 | module.exports = toHaveObject; 1537 | 1538 | function toHaveObject(key, actual) { 1539 | return toBeObject(actual) && 1540 | toBeObject(actual[key]); 1541 | } 1542 | 1543 | }, { 1544 | './toBeObject': 35 1545 | }], 1546 | 72: [function(require, module, exports) { 1547 | 'use strict'; 1548 | 1549 | var toBeObject = require('./toBeObject'); 1550 | var toBeOddNumber = require('./toBeOddNumber'); 1551 | 1552 | module.exports = toHaveOddNumber; 1553 | 1554 | function toHaveOddNumber(key, actual) { 1555 | return toBeObject(actual) && 1556 | toBeOddNumber(actual[key]); 1557 | } 1558 | 1559 | }, { 1560 | './toBeObject': 35, 1561 | './toBeOddNumber': 36 1562 | }], 1563 | 73: [function(require, module, exports) { 1564 | 'use strict'; 1565 | 1566 | var toBeObject = require('./toBeObject'); 1567 | var toBeString = require('./toBeString'); 1568 | 1569 | module.exports = toHaveString; 1570 | 1571 | function toHaveString(key, actual) { 1572 | return toBeObject(actual) && 1573 | toBeString(actual[key]); 1574 | } 1575 | 1576 | }, { 1577 | './toBeObject': 35, 1578 | './toBeString': 39 1579 | }], 1580 | 74: [function(require, module, exports) { 1581 | 'use strict'; 1582 | 1583 | var toBeObject = require('./toBeObject'); 1584 | var toBeLongerThan = require('./toBeLongerThan'); 1585 | 1586 | module.exports = toHaveStringLongerThan; 1587 | 1588 | function toHaveStringLongerThan(key, other, actual) { 1589 | return toBeObject(actual) && 1590 | toBeLongerThan(other, actual[key]); 1591 | } 1592 | 1593 | }, { 1594 | './toBeLongerThan': 30, 1595 | './toBeObject': 35 1596 | }], 1597 | 75: [function(require, module, exports) { 1598 | 'use strict'; 1599 | 1600 | var toBeObject = require('./toBeObject'); 1601 | var toBeSameLengthAs = require('./toBeSameLengthAs'); 1602 | 1603 | module.exports = toHaveStringSameLengthAs; 1604 | 1605 | function toHaveStringSameLengthAs(key, other, actual) { 1606 | return toBeObject(actual) && 1607 | toBeSameLengthAs(other, actual[key]); 1608 | } 1609 | 1610 | }, { 1611 | './toBeObject': 35, 1612 | './toBeSameLengthAs': 37 1613 | }], 1614 | 76: [function(require, module, exports) { 1615 | 'use strict'; 1616 | 1617 | var toBeObject = require('./toBeObject'); 1618 | var toBeShorterThan = require('./toBeShorterThan'); 1619 | 1620 | module.exports = toHaveStringShorterThan; 1621 | 1622 | function toHaveStringShorterThan(key, other, actual) { 1623 | return toBeObject(actual) && 1624 | toBeShorterThan(other, actual[key]); 1625 | } 1626 | 1627 | }, { 1628 | './toBeObject': 35, 1629 | './toBeShorterThan': 38 1630 | }], 1631 | 77: [function(require, module, exports) { 1632 | 'use strict'; 1633 | 1634 | var toBeObject = require('./toBeObject'); 1635 | var toBeTrue = require('./toBeTrue'); 1636 | 1637 | module.exports = toHaveTrue; 1638 | 1639 | function toHaveTrue(key, actual) { 1640 | return toBeObject(actual) && 1641 | toBeTrue(actual[key]); 1642 | } 1643 | 1644 | }, { 1645 | './toBeObject': 35, 1646 | './toBeTrue': 40 1647 | }], 1648 | 78: [function(require, module, exports) { 1649 | 'use strict'; 1650 | 1651 | var toBeObject = require('./toBeObject'); 1652 | var toBeWhitespace = require('./toBeWhitespace'); 1653 | 1654 | module.exports = toHaveWhitespaceString; 1655 | 1656 | function toHaveWhitespaceString(key, actual) { 1657 | return toBeObject(actual) && 1658 | toBeWhitespace(actual[key]); 1659 | } 1660 | 1661 | }, { 1662 | './toBeObject': 35, 1663 | './toBeWhitespace': 41 1664 | }], 1665 | 79: [function(require, module, exports) { 1666 | 'use strict'; 1667 | 1668 | var toBeObject = require('./toBeObject'); 1669 | var toBeWholeNumber = require('./toBeWholeNumber'); 1670 | 1671 | module.exports = toHaveWholeNumber; 1672 | 1673 | function toHaveWholeNumber(key, actual) { 1674 | return toBeObject(actual) && 1675 | toBeWholeNumber(actual[key]); 1676 | } 1677 | 1678 | }, { 1679 | './toBeObject': 35, 1680 | './toBeWholeNumber': 42 1681 | }], 1682 | 80: [function(require, module, exports) { 1683 | 'use strict'; 1684 | 1685 | var toBeObject = require('./toBeObject'); 1686 | 1687 | module.exports = toImplement; 1688 | 1689 | function toImplement(api, actual) { 1690 | return toBeObject(api) && 1691 | toBeObject(actual) && 1692 | featuresAll(api, actual); 1693 | } 1694 | 1695 | function featuresAll(api, actual) { 1696 | for (var key in api) { 1697 | if (api.hasOwnProperty(key) && 1698 | key in actual === false) { 1699 | return false; 1700 | } 1701 | } 1702 | return true; 1703 | } 1704 | 1705 | }, { 1706 | './toBeObject': 35 1707 | }], 1708 | 81: [function(require, module, exports) { 1709 | 'use strict'; 1710 | 1711 | var toBeNonEmptyString = require('./toBeNonEmptyString'); 1712 | 1713 | module.exports = toStartWith; 1714 | 1715 | function toStartWith(subString, actual) { 1716 | if (!toBeNonEmptyString(actual) || !toBeNonEmptyString(subString)) { 1717 | return false; 1718 | } 1719 | return actual.slice(0, subString.length) === subString; 1720 | } 1721 | 1722 | }, { 1723 | './toBeNonEmptyString': 33 1724 | }], 1725 | 82: [function(require, module, exports) { 1726 | 'use strict'; 1727 | 1728 | module.exports = toThrowAnyError; 1729 | 1730 | function toThrowAnyError(actual) { 1731 | var threwError = false; 1732 | try { 1733 | actual(); 1734 | } catch (e) { 1735 | threwError = true; 1736 | } 1737 | return threwError; 1738 | } 1739 | 1740 | }, {}], 1741 | 83: [function(require, module, exports) { 1742 | 'use strict'; 1743 | 1744 | module.exports = toThrowErrorOfType; 1745 | 1746 | function toThrowErrorOfType(type, actual) { 1747 | var threwErrorOfType = false; 1748 | try { 1749 | actual(); 1750 | } catch (e) { 1751 | threwErrorOfType = (e.name === type); 1752 | } 1753 | return threwErrorOfType; 1754 | } 1755 | 1756 | }, {}] 1757 | }, {}, [1]); 1758 | -------------------------------------------------------------------------------- /specs/lib/jasmine-2.3.4/jasmine.css: -------------------------------------------------------------------------------- 1 | body { overflow-y: scroll; } 2 | 3 | .jasmine_html-reporter { background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; } 4 | .jasmine_html-reporter a { text-decoration: none; } 5 | .jasmine_html-reporter a:hover { text-decoration: underline; } 6 | .jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; } 7 | .jasmine_html-reporter .banner, .jasmine_html-reporter .symbol-summary, .jasmine_html-reporter .summary, .jasmine_html-reporter .result-message, .jasmine_html-reporter .spec .description, .jasmine_html-reporter .spec-detail .description, .jasmine_html-reporter .alert .bar, .jasmine_html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; } 8 | .jasmine_html-reporter .banner { position: relative; } 9 | .jasmine_html-reporter .banner .title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; } 10 | .jasmine_html-reporter .banner .version { margin-left: 14px; position: relative; top: 6px; } 11 | .jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; } 12 | .jasmine_html-reporter .version { color: #aaa; } 13 | .jasmine_html-reporter .banner { margin-top: 14px; } 14 | .jasmine_html-reporter .duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; } 15 | .jasmine_html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } 16 | .jasmine_html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; } 17 | .jasmine_html-reporter .symbol-summary li.passed { font-size: 14px; } 18 | .jasmine_html-reporter .symbol-summary li.passed:before { color: #007069; content: "\02022"; } 19 | .jasmine_html-reporter .symbol-summary li.failed { line-height: 9px; } 20 | .jasmine_html-reporter .symbol-summary li.failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; } 21 | .jasmine_html-reporter .symbol-summary li.disabled { font-size: 14px; } 22 | .jasmine_html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; } 23 | .jasmine_html-reporter .symbol-summary li.pending { line-height: 17px; } 24 | .jasmine_html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; } 25 | .jasmine_html-reporter .symbol-summary li.empty { font-size: 14px; } 26 | .jasmine_html-reporter .symbol-summary li.empty:before { color: #ba9d37; content: "\02022"; } 27 | .jasmine_html-reporter .run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; } 28 | .jasmine_html-reporter .run-options .trigger { cursor: pointer; padding: 8px 16px; } 29 | .jasmine_html-reporter .run-options .payload { position: absolute; display: none; right: -1px; border: 1px solid #8a4182; background-color: #eee; white-space: nowrap; padding: 4px 8px; } 30 | .jasmine_html-reporter .run-options .payload.open { display: block; } 31 | .jasmine_html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 32 | .jasmine_html-reporter .bar.failed { background-color: #ca3a11; } 33 | .jasmine_html-reporter .bar.passed { background-color: #007069; } 34 | .jasmine_html-reporter .bar.skipped { background-color: #bababa; } 35 | .jasmine_html-reporter .bar.errored { background-color: #ca3a11; } 36 | .jasmine_html-reporter .bar.menu { background-color: #fff; color: #aaa; } 37 | .jasmine_html-reporter .bar.menu a { color: #333; } 38 | .jasmine_html-reporter .bar a { color: white; } 39 | .jasmine_html-reporter.spec-list .bar.menu.failure-list, .jasmine_html-reporter.spec-list .results .failures { display: none; } 40 | .jasmine_html-reporter.failure-list .bar.menu.spec-list, .jasmine_html-reporter.failure-list .summary { display: none; } 41 | .jasmine_html-reporter .results { margin-top: 14px; } 42 | .jasmine_html-reporter .summary { margin-top: 14px; } 43 | .jasmine_html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } 44 | .jasmine_html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } 45 | .jasmine_html-reporter .summary li.passed a { color: #007069; } 46 | .jasmine_html-reporter .summary li.failed a { color: #ca3a11; } 47 | .jasmine_html-reporter .summary li.empty a { color: #ba9d37; } 48 | .jasmine_html-reporter .summary li.pending a { color: #ba9d37; } 49 | .jasmine_html-reporter .summary li.disabled a { color: #bababa; } 50 | .jasmine_html-reporter .description + .suite { margin-top: 0; } 51 | .jasmine_html-reporter .suite { margin-top: 14px; } 52 | .jasmine_html-reporter .suite a { color: #333; } 53 | .jasmine_html-reporter .failures .spec-detail { margin-bottom: 28px; } 54 | .jasmine_html-reporter .failures .spec-detail .description { background-color: #ca3a11; } 55 | .jasmine_html-reporter .failures .spec-detail .description a { color: white; } 56 | .jasmine_html-reporter .result-message { padding-top: 14px; color: #333; white-space: pre; } 57 | .jasmine_html-reporter .result-message span.result { display: block; } 58 | .jasmine_html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; } 59 | -------------------------------------------------------------------------------- /specs/lib/jasmine-2.3.4/jasmine_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mestarshine/fingerprint2js/f079c473140f56971afc19f6ecbd9a40a0e588d8/specs/lib/jasmine-2.3.4/jasmine_favicon.png -------------------------------------------------------------------------------- /specs/lib/jasmine-2.3.4/terminal.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | var UNDEFINED, 3 | exportObject; 4 | 5 | if (typeof module !== "undefined" && module.exports) { 6 | exportObject = exports; 7 | } else { 8 | exportObject = global.jasmineReporters = global.jasmineReporters || {}; 9 | } 10 | 11 | function elapsed(start, end) { return (end - start)/1000; } 12 | function isFailed(obj) { return obj.status === "failed"; } 13 | function isSkipped(obj) { return obj.status === "pending"; } 14 | function isDisabled(obj) { return obj.status === "disabled"; } 15 | function extend(dupe, obj) { // performs a shallow copy of all props of `obj` onto `dupe` 16 | for (var prop in obj) { 17 | if (obj.hasOwnProperty(prop)) { 18 | dupe[prop] = obj[prop]; 19 | } 20 | } 21 | return dupe; 22 | } 23 | function log(str) { 24 | var con = global.console || console; 25 | if (con && con.log && str && str.length) { 26 | con.log(str); 27 | } 28 | } 29 | 30 | 31 | /** 32 | * Basic reporter that outputs spec results to the terminal. 33 | * Use this reporter in your build pipeline. 34 | * 35 | * Usage: 36 | * 37 | * jasmine.getEnv().addReporter(new jasmineReporters.TerminalReporter(options); 38 | * 39 | * @param {object} [options] 40 | * @param {number} [options.verbosity] meaningful values are 0 through 3; anything 41 | * greater than 3 is treated as 3 (default: 2) 42 | * @param {boolean} [options.color] print in color or not (default: true) 43 | * @param {boolean} [opts.showStack] show stack trace for failed specs (default: false) 44 | */ 45 | var DEFAULT_VERBOSITY = 2, 46 | ATTRIBUTES_TO_ANSI = { 47 | "off": 0, 48 | "bold": 1, 49 | "red": 31, 50 | "green": 32, 51 | "yellow": 33, 52 | "blue": 34, 53 | "magenta": 35, 54 | "cyan": 36 55 | }; 56 | 57 | exportObject.TerminalReporter = function(options) { 58 | var self = this; 59 | self.started = false; 60 | self.finished = false; 61 | 62 | // sanitize arguments 63 | options = options || {}; 64 | self.verbosity = typeof options.verbosity === "number" ? options.verbosity : DEFAULT_VERBOSITY; 65 | self.color = options.color; 66 | self.showStack = options.showStack; 67 | 68 | var indent_string = ' ', 69 | startTime, 70 | currentSuite = null, 71 | totalSpecsExecuted = 0, 72 | totalSpecsSkipped = 0, 73 | totalSpecsDisabled = 0, 74 | totalSpecsFailed = 0, 75 | totalSpecsDefined, 76 | // when use use fit, jasmine never calls suiteStarted / suiteDone, so make a fake one to use 77 | fakeFocusedSuite = { 78 | id: 'focused', 79 | description: 'focused specs', 80 | fullName: 'focused specs' 81 | }; 82 | 83 | var __suites = {}, __specs = {}; 84 | function getSuite(suite) { 85 | __suites[suite.id] = extend(__suites[suite.id] || {}, suite); 86 | return __suites[suite.id]; 87 | } 88 | function getSpec(spec) { 89 | __specs[spec.id] = extend(__specs[spec.id] || {}, spec); 90 | return __specs[spec.id]; 91 | } 92 | 93 | self.jasmineStarted = function(summary) { 94 | totalSpecsDefined = summary && summary.totalSpecsDefined || NaN; 95 | startTime = exportObject.startTime = new Date(); 96 | self.started = true; 97 | }; 98 | self.suiteStarted = function(suite) { 99 | suite = getSuite(suite); 100 | suite._specs = 0; 101 | suite._nestedSpecs = 0; 102 | suite._failures = 0; 103 | suite._nestedFailures = 0; 104 | suite._skipped = 0; 105 | suite._nestedSkipped = 0; 106 | suite._disabled = 0; 107 | suite._nestedDisabled = 0; 108 | suite._depth = currentSuite ? currentSuite._depth+1 : 1; 109 | suite._parent = currentSuite; 110 | currentSuite = suite; 111 | if (self.verbosity > 2) { 112 | log(indentWithLevel(suite._depth, inColor(suite.description, "bold"))); 113 | } 114 | }; 115 | self.specStarted = function(spec) { 116 | if (!currentSuite) { 117 | // focused spec (fit) -- suiteStarted was never called 118 | self.suiteStarted(fakeFocusedSuite); 119 | } 120 | spec = getSpec(spec); 121 | spec._suite = currentSuite; 122 | spec._depth = currentSuite._depth+1; 123 | currentSuite._specs++; 124 | if (self.verbosity > 2) { 125 | log(indentWithLevel(spec._depth, spec.description + ' ...')); 126 | } 127 | }; 128 | self.specDone = function(spec) { 129 | spec = getSpec(spec); 130 | var failed = false, 131 | skipped = false, 132 | disabled = false, 133 | color = 'green', 134 | resultText = ''; 135 | if (isSkipped(spec)) { 136 | skipped = true; 137 | color = ''; 138 | spec._suite._skipped++; 139 | totalSpecsSkipped++; 140 | } 141 | if (isFailed(spec)) { 142 | failed = true; 143 | color = 'red'; 144 | spec._suite._failures++; 145 | totalSpecsFailed++; 146 | } 147 | if (isDisabled(spec)) { 148 | disabled = true; 149 | color = 'yellow'; 150 | spec._suite._disabled++; 151 | totalSpecsDisabled++; 152 | } 153 | totalSpecsExecuted++; 154 | 155 | if (self.verbosity === 2) { 156 | resultText = failed ? 'F' : skipped ? 'S' : disabled ? 'D' : '.'; 157 | } else if (self.verbosity > 2) { 158 | resultText = ' ' + (failed ? 'Failed' : skipped ? 'Skipped' : disabled ? 'Disabled' : 'Passed'); 159 | } 160 | log(inColor(resultText, color)); 161 | 162 | if (failed) { 163 | if (self.verbosity === 1) { 164 | log(spec.fullName); 165 | } else if (self.verbosity === 2) { 166 | log(' '); 167 | log(indentWithLevel(spec._depth, spec.fullName)); 168 | } 169 | 170 | for (var i = 0; i < spec.failedExpectations.length; i++) { 171 | log(inColor(indentWithLevel(spec._depth, indent_string + spec.failedExpectations[i].message), color)); 172 | if (self.showStack){ 173 | logStackLines(spec._depth, spec.failedExpectations[i].stack.split('\n')); 174 | } 175 | } 176 | } 177 | }; 178 | self.suiteDone = function(suite) { 179 | suite = getSuite(suite); 180 | if (suite._parent === UNDEFINED) { 181 | // disabled suite (xdescribe) -- suiteStarted was never called 182 | self.suiteStarted(suite); 183 | } 184 | if (suite._parent) { 185 | suite._parent._specs += suite._specs + suite._nestedSpecs; 186 | suite._parent._failures += suite._failures + suite._nestedFailures; 187 | suite._parent._skipped += suite._skipped + suite._nestedSkipped; 188 | suite._parent._disabled += suite._disabled + suite._nestedDisabled; 189 | 190 | } 191 | currentSuite = suite._parent; 192 | if (self.verbosity < 3) { 193 | return; 194 | } 195 | 196 | var total = suite._specs + suite._nestedSpecs, 197 | failed = suite._failures + suite._nestedFailures, 198 | skipped = suite._skipped + suite._nestedSkipped, 199 | disabled = suite._disabled + suite._nestedDisabled, 200 | passed = total - failed - skipped, 201 | color = failed ? 'red+bold' : 'green+bold', 202 | str = passed + ' of ' + total + ' passed (' + skipped + ' skipped, ' + disabled + ' disabled)'; 203 | log(indentWithLevel(suite._depth, inColor(str+'.', color))); 204 | }; 205 | self.jasmineDone = function() { 206 | if (currentSuite) { 207 | // focused spec (fit) -- suiteDone was never called 208 | self.suiteDone(fakeFocusedSuite); 209 | } 210 | var now = new Date(), 211 | dur = elapsed(startTime, now), 212 | total = totalSpecsDefined || totalSpecsExecuted, 213 | disabled = total - totalSpecsExecuted + totalSpecsDisabled, 214 | skipped = totalSpecsSkipped, 215 | spec_str = total + (total === 1 ? " spec, " : " specs, "), 216 | fail_str = totalSpecsFailed + (totalSpecsFailed === 1 ? " failure, " : " failures, "), 217 | skip_str = skipped + " skipped, ", 218 | disabled_str = disabled + " disabled in ", 219 | summary_str = spec_str + fail_str + skip_str + disabled_str + dur + "s.", 220 | result_str = (totalSpecsFailed && "FAILURE: " || "SUCCESS: ") + summary_str, 221 | result_color = totalSpecsFailed && "red+bold" || "green+bold"; 222 | 223 | if (self.verbosity === 2) { 224 | log(''); 225 | } 226 | 227 | if (self.verbosity > 0) { 228 | log(inColor(result_str, result_color)); 229 | } 230 | //log("Specs skipped but not reported (entire suite skipped or targeted to specific specs)", totalSpecsDefined - totalSpecsExecuted + totalSpecsDisabled); 231 | 232 | self.finished = true; 233 | // this is so phantomjs-testrunner.js can tell if we're done executing 234 | exportObject.endTime = now; 235 | }; 236 | function indentWithLevel(level, string) { 237 | return new Array(level).join(indent_string) + string; 238 | } 239 | function logStackLines(depth, lines) { 240 | lines.forEach(function(line){ 241 | log(inColor(indentWithLevel(depth, indent_string + line), 'magenta')); 242 | }); 243 | } 244 | function inColor(string, color) { 245 | var color_attributes = color && color.split("+"), 246 | ansi_string = "", 247 | i; 248 | 249 | if (!self.color || !color_attributes) { 250 | return string; 251 | } 252 | 253 | for(i = 0; i < color_attributes.length; i++) { 254 | ansi_string += "\033[" + ATTRIBUTES_TO_ANSI[color_attributes[i]] + "m"; 255 | } 256 | ansi_string += string + "\033[" + ATTRIBUTES_TO_ANSI["off"] + "m"; 257 | 258 | return ansi_string; 259 | } 260 | }; 261 | })(this); 262 | -------------------------------------------------------------------------------- /specs/phantomjs-testrunner.js: -------------------------------------------------------------------------------- 1 | /* globals jasmineRequire, phantom */ 2 | // Verify arguments 3 | var system = require('system'); 4 | var args; 5 | 6 | if(phantom.args) { 7 | args = phantom.args; 8 | } else { 9 | args = system.args.slice(1);//use system args for phantom 2.0+ 10 | } 11 | 12 | if (args.length === 0) { 13 | console.log("Simple JasmineBDD test runner for phantom.js"); 14 | console.log("Usage: phantomjs-testrunner.js url_to_runner.html"); 15 | console.log("Accepts http:// and file:// urls"); 16 | console.log(""); 17 | console.log("NOTE: This script depends on jasmine.HtmlReporter being used\non the page, for the DOM elements it creates.\n"); 18 | phantom.exit(2); 19 | } 20 | else { 21 | var fs = require("fs"), 22 | pages = [], 23 | page, address, resultsKey, i, l; 24 | 25 | 26 | var setupPageFn = function(p, k) { 27 | return function() { 28 | overloadPageEvaluate(p); 29 | setupWriteFileFunction(p, k, fs.separator); 30 | }; 31 | }; 32 | 33 | for (i = 0, l = args.length; i < l; i++) { 34 | address = args[i]; 35 | console.log("Loading " + address); 36 | 37 | // if provided a url without a protocol, try to use file:// 38 | address = address.indexOf("://") === -1 ? "file://" + address : address; 39 | 40 | // create a WebPage object to work with 41 | page = require("webpage").create(); 42 | page.url = address; 43 | 44 | // When initialized, inject the reporting functions before the page is loaded 45 | // (and thus before it will try to utilize the functions) 46 | resultsKey = "__jr" + Math.ceil(Math.random() * 1000000); 47 | page.onInitialized = setupPageFn(page, resultsKey); 48 | page.open(address, processPage(null, page, resultsKey)); 49 | pages.push(page); 50 | 51 | page.onConsoleMessage = logAndWorkAroundDefaultLineBreaking; 52 | } 53 | 54 | // bail when all pages have been processed 55 | setInterval(function(){ 56 | var exit_code = 0; 57 | for (i = 0, l = pages.length; i < l; i++) { 58 | page = pages[i]; 59 | if (page.__exit_code === null) { 60 | // wait until later 61 | return; 62 | } 63 | exit_code |= page.__exit_code; 64 | } 65 | phantom.exit(exit_code); 66 | }, 100); 67 | } 68 | 69 | // Thanks to hoisting, these helpers are still available when needed above 70 | /** 71 | * Logs a message. Does not add a line-break for single characters '.' and 'F' or lines ending in ' ...' 72 | * 73 | * @param msg 74 | */ 75 | function logAndWorkAroundDefaultLineBreaking(msg) { 76 | var interpretAsWithoutNewline = /(^(\033\[\d+m)*[\.F](\033\[\d+m)*$)|( \.\.\.$)/; 77 | if (navigator.userAgent.indexOf("Windows") < 0 && interpretAsWithoutNewline.test(msg)) { 78 | try { 79 | system.stdout.write(msg); 80 | } catch (e) { 81 | var fs = require('fs'); 82 | fs.write('/dev/stdout', msg, 'w'); 83 | } 84 | } else { 85 | console.log(msg); 86 | } 87 | } 88 | 89 | /** 90 | * Stringifies the function, replacing any %placeholders% with mapped values. 91 | * 92 | * @param {function} fn The function to replace occurrences within. 93 | * @param {object} replacements Key => Value object of string replacements. 94 | */ 95 | function replaceFunctionPlaceholders(fn, replacements) { 96 | if (replacements && typeof replacements === "object") { 97 | fn = fn.toString(); 98 | for (var p in replacements) { 99 | if (replacements.hasOwnProperty(p)) { 100 | var match = new RegExp("%" + p + "%", "g"); 101 | do { 102 | fn = fn.replace(match, replacements[p]); 103 | } while(fn.indexOf(match) !== -1); 104 | } 105 | } 106 | } 107 | return fn; 108 | } 109 | 110 | /** 111 | * Replaces the "evaluate" method with one we can easily do substitution with. 112 | * 113 | * @param {phantomjs.WebPage} page The WebPage object to overload 114 | */ 115 | function overloadPageEvaluate(page) { 116 | page._evaluate = page.evaluate; 117 | page.evaluate = function(fn, replacements) { return page._evaluate(replaceFunctionPlaceholders(fn, replacements)); }; 118 | return page; 119 | } 120 | 121 | /** Stubs a fake writeFile function into the test runner. 122 | * 123 | * @param {phantomjs.WebPage} page The WebPage object to inject functions into. 124 | * @param {string} key The name of the global object in which file data should 125 | * be stored for later retrieval. 126 | */ 127 | // TODO: not bothering with error checking for now (closed environment) 128 | function setupWriteFileFunction(page, key, path_separator) { 129 | page.evaluate(function(){ 130 | window["%resultsObj%"] = {}; 131 | window.fs_path_separator = "%fs_path_separator%"; 132 | window.__phantom_writeFile = function(filename, text) { 133 | window["%resultsObj%"][filename] = text; 134 | }; 135 | }, {resultsObj: key, fs_path_separator: path_separator.replace("\\", "\\\\")}); 136 | } 137 | 138 | /** 139 | * Returns the loaded page's filename => output object. 140 | * 141 | * @param {phantomjs.WebPage} page The WebPage object to retrieve data from. 142 | * @param {string} key The name of the global object to be returned. Should 143 | * be the same key provided to setupWriteFileFunction. 144 | */ 145 | function getXmlResults(page, key) { 146 | return page.evaluate(function(){ 147 | return window["%resultsObj%"] || {}; 148 | }, {resultsObj: key}); 149 | } 150 | 151 | /** 152 | * Processes a page. 153 | * 154 | * @param {string} status The status from opening the page via WebPage#open. 155 | * @param {phantomjs.WebPage} page The WebPage to be processed. 156 | */ 157 | function processPage(status, page, resultsKey) { 158 | if (status === null && page) { 159 | page.__exit_code = null; 160 | return function(stat){ 161 | processPage(stat, page, resultsKey); 162 | }; 163 | } 164 | if (status !== "success") { 165 | console.error("Unable to load resource: " + address); 166 | page.__exit_code = 2; 167 | } 168 | else { 169 | var isFinished = function() { 170 | return page.evaluate(function(){ 171 | // if there's a JUnitXmlReporter, return a boolean indicating if it is finished 172 | if (window.jasmineReporters && window.jasmineReporters.startTime) { 173 | return !!window.jasmineReporters.endTime; 174 | } 175 | // otherwise, scrape the DOM for the HtmlReporter "finished in ..." output 176 | var durElem = document.querySelector(".html-reporter .duration"); 177 | if (!durElem) { 178 | durElem = document.querySelector(".jasmine_html-reporter .duration"); 179 | } 180 | return durElem && durElem.textContent && durElem.textContent.toLowerCase().indexOf("finished in") === 0; 181 | }); 182 | }; 183 | var getResultsFromHtmlRunner = function() { 184 | return page.evaluate(function(){ 185 | var resultElem = document.querySelector(".html-reporter .alert .bar"); 186 | if (!resultElem) { 187 | resultElem = document.querySelector(".jasmine_html-reporter .alert .bar"); 188 | } 189 | return resultElem && resultElem.textContent && 190 | resultElem.textContent.match(/(\d+) spec.* (\d+) failure.*/) || 191 | ["Unable to determine success or failure."]; 192 | }); 193 | }; 194 | var timeout = 60000; 195 | var loopInterval = 100; 196 | var ival = setInterval(function(){ 197 | if (isFinished()) { 198 | // get the results that need to be written to disk 199 | var fs = require("fs"), 200 | xml_results = getXmlResults(page, resultsKey), 201 | output; 202 | for (var filename in xml_results) { 203 | if (xml_results.hasOwnProperty(filename) && (output = xml_results[filename]) && typeof(output) === "string") { 204 | fs.write(filename, output, "w"); 205 | } 206 | } 207 | 208 | // print out a success / failure message of the results 209 | var results = getResultsFromHtmlRunner(); 210 | var failures = Number(results[2]); 211 | if (failures > 0) { 212 | page.__exit_code = 1; 213 | clearInterval(ival); 214 | } 215 | else { 216 | page.__exit_code = 0; 217 | clearInterval(ival); 218 | } 219 | } 220 | else { 221 | timeout -= loopInterval; 222 | if (timeout <= 0) { 223 | console.log('Page has timed out; aborting.'); 224 | page.__exit_code = 2; 225 | clearInterval(ival); 226 | } 227 | } 228 | }, loopInterval); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /specs/phantomjs.runner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # sanity check to make sure phantomjs exists in the PATH 4 | hash /usr/bin/env phantomjs &> /dev/null 5 | if [ $? -eq 1 ]; then 6 | echo "ERROR: phantomjs is not installed" 7 | echo "Please visit http://www.phantomjs.org/" 8 | exit 1 9 | fi 10 | 11 | # sanity check number of args 12 | if [ $# -lt 1 ] 13 | then 14 | echo "Usage: `basename $0` path_to_runner.html" 15 | echo 16 | exit 1 17 | fi 18 | 19 | SCRIPTDIR=$(dirname `perl -e 'use Cwd "abs_path";print abs_path(shift)' $0`) 20 | TESTFILE="" 21 | while (( "$#" )); do 22 | if [ ${1:0:7} == "http://" -o ${1:0:8} == "https://" ]; then 23 | TESTFILE="$TESTFILE $1" 24 | else 25 | TESTFILE="$TESTFILE `perl -e 'use Cwd "abs_path";print abs_path(shift)' $1`" 26 | fi 27 | shift 28 | done 29 | 30 | # cleanup previous test runs 31 | cd $SCRIPTDIR 32 | rm -f *.xml 33 | 34 | # make sure phantomjs submodule is initialized 35 | cd .. 36 | git submodule update --init 37 | 38 | # fire up the phantomjs environment and run the test 39 | cd $SCRIPTDIR 40 | /usr/bin/env phantomjs $SCRIPTDIR/phantomjs-testrunner.js $TESTFILE 41 | -------------------------------------------------------------------------------- /specs/spec_runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Fingerprint2 spec runner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /specs/specs.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | describe("Fingerprint2", function () { 3 | describe("new", function () { 4 | it("creates a new instance of FP2", function () { 5 | expect(new Fingerprint2()).not.toBeNull(); 6 | }); 7 | 8 | it("accepts an empty options object", function () { 9 | expect(new Fingerprint2({})).not.toBeNull(); 10 | }); 11 | 12 | it("uses default options", function () { 13 | var fp2 = new Fingerprint2(); 14 | expect(fp2.options.swfContainerId).toEqual("fingerprintjs2"); 15 | expect(fp2.options.swfPath).toEqual("flash/compiled/FontList.swf"); 16 | expect(fp2.options.userDefinedFonts).toEqual([]); 17 | }); 18 | 19 | it("allows to override default options", function () { 20 | var fp2 = new Fingerprint2({swfPath: "newpath", userDefinedFonts: ["Ethos", "Quenda"]}); 21 | expect(fp2.options.swfContainerId).toEqual("fingerprintjs2"); 22 | expect(fp2.options.swfPath).toEqual("newpath"); 23 | expect(fp2.options.userDefinedFonts).toEqual(["Ethos", "Quenda"]); 24 | }); 25 | 26 | it("allows to add new options", function () { 27 | var fp2 = new Fingerprint2({excludeUserAgent: true}); 28 | expect(fp2.options.swfContainerId).toEqual("fingerprintjs2"); 29 | expect(fp2.options.swfPath).toEqual("flash/compiled/FontList.swf"); 30 | expect(fp2.options.excludeUserAgent).toBe(true); 31 | }); 32 | 33 | describe("sortPluginsFor", function () { 34 | it("has default value", function () { 35 | var fp2 = new Fingerprint2(); 36 | expect(fp2.options.sortPluginsFor).toEqual([/palemoon/i]); 37 | }); 38 | 39 | it("allows to set new array of regexes", function () { 40 | var fp2 = new Fingerprint2({sortPluginsFor: [/firefox/i, /chrome/i]}); 41 | expect(fp2.options.sortPluginsFor).toEqual([/firefox/i, /chrome/i]); 42 | }); 43 | }); 44 | }); 45 | 46 | describe("without new keyword", function () { 47 | it("creates a new instance of FP2", function () { 48 | expect(Fingerprint2()).not.toBeNull(); 49 | }); 50 | }) 51 | 52 | describe("get", function () { 53 | describe("default options", function () { 54 | it("calculates fingerprint", function (done) { 55 | var fp2 = new Fingerprint2(); 56 | fp2.get(function (result) { 57 | expect(result).toMatch(/^[0-9a-f]{32}$/i); 58 | done(); 59 | }); 60 | }); 61 | 62 | it("does not try calling flash font detection", function (done) { 63 | var fp2 = new Fingerprint2(); 64 | spyOn(fp2, "flashFontsKey"); 65 | fp2.get(function (result) { 66 | expect(fp2.flashFontsKey).not.toHaveBeenCalled(); 67 | done(); 68 | }); 69 | }); 70 | }); 71 | 72 | describe("non-default options", function () { 73 | it("does not use userAgent when excluded", function (done) { 74 | var fp2 = new Fingerprint2({excludeUserAgent: true}); 75 | spyOn(fp2, "getUserAgent"); 76 | fp2.get(function (result) { 77 | expect(fp2.getUserAgent).not.toHaveBeenCalled(); 78 | done(); 79 | }); 80 | }); 81 | 82 | it("does not use pixelRatio when excluded", function (done) { 83 | var fp2 = new Fingerprint2({excludePixelRatio: true}); 84 | spyOn(fp2, "getPixelRatio"); 85 | fp2.get(function (result) { 86 | expect(fp2.getPixelRatio).not.toHaveBeenCalled(); 87 | done(); 88 | }); 89 | }); 90 | 91 | it("does not use screen resolution when excluded", function (done) { 92 | var fp2 = new Fingerprint2({excludeScreenResolution: true}); 93 | spyOn(fp2, "getScreenResolution"); 94 | fp2.get(function (result) { 95 | expect(fp2.getScreenResolution).not.toHaveBeenCalled(); 96 | done(); 97 | }); 98 | }); 99 | 100 | it("does not use available screen resolution when excluded", function (done) { 101 | var fp2 = new Fingerprint2({excludeAvailableScreenResolution: true}); 102 | spyOn(fp2, "getAvailableScreenResolution"); 103 | fp2.get(function (result) { 104 | expect(fp2.getAvailableScreenResolution).not.toHaveBeenCalled(); 105 | done(); 106 | }); 107 | }); 108 | 109 | it("does not use plugins info when excluded", function (done) { 110 | var fp2 = new Fingerprint2({excludePlugins: true}); 111 | spyOn(fp2, "getRegularPlugins"); 112 | fp2.get(function (result) { 113 | expect(fp2.getRegularPlugins).not.toHaveBeenCalled(); 114 | done(); 115 | }); 116 | }); 117 | 118 | it("does not use IE plugins info when excluded", function (done) { 119 | var fp2 = new Fingerprint2({excludeIEPlugins: true}); 120 | spyOn(fp2, "getIEPlugins"); 121 | fp2.get(function (result) { 122 | expect(fp2.getIEPlugins).not.toHaveBeenCalled(); 123 | done(); 124 | }); 125 | }); 126 | 127 | }); 128 | 129 | describe("returns components", function () { 130 | it("does it return components as a second argument to callback", function (done) { 131 | var fp2 = new Fingerprint2(); 132 | fp2.get(function (result, components) { 133 | expect(components).not.toBeNull(); 134 | done(); 135 | }); 136 | }); 137 | 138 | it("checks if returned components is array", function (done) { 139 | var fp2 = new Fingerprint2(); 140 | fp2.get(function (result, components) { 141 | expect(components).toBeArrayOfObjects(); 142 | done(); 143 | }); 144 | }); 145 | 146 | it("checks if js_fonts component is array", function (done) { 147 | var fp2 = new Fingerprint2(); 148 | fp2.get(function (result, components) { 149 | for (var x = 0; x < components.length; x++) { 150 | if (components[x].key == "js_fonts") { 151 | expect(components[x].value).toBeArray(); 152 | } 153 | } 154 | done(); 155 | }); 156 | }); 157 | 158 | it("returns user_agent as the first element", function (done) { 159 | var fp2 = new Fingerprint2(); 160 | fp2.get(function (result, components) { 161 | expect(components[0].key).toEqual("user_agent"); 162 | done(); 163 | }); 164 | }); 165 | }); 166 | 167 | describe("baseFontArray iteration", function () { 168 | it("only iterates specified items", function (done) { 169 | var baseFonts = ["monospace", "sans-serif", "serif"]; 170 | var ctr = 0; 171 | for (var x in baseFonts) { 172 | ctr++; 173 | } 174 | 175 | expect(baseFonts.length).toEqual(3); 176 | expect(ctr).toEqual(baseFonts.length); 177 | 178 | // Somewhere deep in your JavaScript library... 179 | Array.prototype.foo = 1; 180 | Array.prototype.bar = 2; 181 | ctr = 0; 182 | for (var x in baseFonts) { 183 | console.log(x); 184 | ctr++; 185 | // Now foo & bar is a part of EVERY array and 186 | // will show up here as a value of 'x'. 187 | } 188 | 189 | expect(baseFonts.length).toEqual(3); 190 | // sadface 191 | expect(ctr).not.toEqual(baseFonts.length); 192 | expect(ctr).toEqual(5); 193 | done(); 194 | }); 195 | }); 196 | 197 | describe("userDefinedFonts option", function () { 198 | it("concatinates existing fonts with user-defined", function (done) { 199 | var fontList = [ 200 | "Andale Mono", "Arial", "Arial Black", "Arial Hebrew", "Arial MT", "Arial Narrow", "Arial Rounded MT Bold", 201 | "Arial Unicode MS", 202 | "Bitstream Vera Sans Mono", "Book Antiqua", "Bookman Old Style", 203 | "Calibri", "Cambria", "Cambria Math", "Century", "Century Gothic", "Century Schoolbook", "Comic Sans", 204 | "Comic Sans MS", "Consolas", "Courier", "Courier New", 205 | "Garamond", "Geneva", "Georgia", 206 | "Helvetica", "Helvetica Neue", 207 | "Impact", 208 | "Lucida Bright", "Lucida Calligraphy", "Lucida Console", "Lucida Fax", "LUCIDA GRANDE", "Lucida Handwriting", 209 | "Lucida Sans", "Lucida Sans Typewriter", "Lucida Sans Unicode", 210 | "Microsoft Sans Serif", "Monaco", "Monotype Corsiva", "MS Gothic", "MS Outlook", "MS PGothic", 211 | "MS Reference Sans Serif", "MS Sans Serif", "MS Serif", "MYRIAD", "MYRIAD PRO", 212 | "Palatino", "Palatino Linotype", 213 | "Segoe Print", "Segoe Script", "Segoe UI", "Segoe UI Light", "Segoe UI Semibold", "Segoe UI Symbol", 214 | "Tahoma", "Times", "Times New Roman", "Times New Roman PS", "Trebuchet MS", 215 | "Verdana", "Wingdings", "Wingdings 2", "Wingdings 3" 216 | ]; 217 | 218 | expect(fontList.length).toEqual(65); 219 | var userDefinedFonts = []; 220 | fontList.concat(userDefinedFonts); 221 | expect(fontList.length).toEqual(65); 222 | 223 | userDefinedFonts = ["Adria Grotesk", "Butler", "Nimbus Mono"]; 224 | expect(userDefinedFonts.length).toEqual(3); 225 | fontList = fontList.concat(userDefinedFonts); 226 | expect(fontList.length).toEqual(65 + 3); 227 | done(); 228 | }); 229 | }); 230 | describe("customFunction option", function () { 231 | 232 | it("concatinates the current keys with the customFunction output", function (done) { 233 | function customFunction () { 234 | return "RANDOM_STRING"; 235 | } 236 | var spy = jasmine.createSpy('customFunction', customFunction).and.callThrough(); 237 | var fp = new Fingerprint2({ 238 | "customFunction": spy 239 | }); 240 | fp.get(function (result, keys) { 241 | expect(spy).toHaveBeenCalled(); 242 | done() 243 | }); 244 | }); 245 | }); 246 | }); 247 | }); 248 | -------------------------------------------------------------------------------- /test.php: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------