scratch – Diff between revs 58 and 125

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 58 Rev 125
1 /* 1 /*
2 * Fingerprintjs2 1.5.0 - Modern & flexible browser fingerprint library v2 2 * Fingerprintjs2 1.5.1 - Modern & flexible browser fingerprint library v2
3 * https://github.com/Valve/fingerprintjs2 3 * https://github.com/Valve/fingerprintjs2
4 * Copyright (c) 2015 Valentin Vasilyev (valentin.vasilyev@outlook.com) 4 * Copyright (c) 2015 Valentin Vasilyev (valentin.vasilyev@outlook.com)
5 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. 5 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6 * 6 *
7 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 7 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
8 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 8 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
9 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 9 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
10 * ARE DISCLAIMED. IN NO EVENT SHALL VALENTIN VASILYEV BE LIABLE FOR ANY 10 * ARE DISCLAIMED. IN NO EVENT SHALL VALENTIN VASILYEV BE LIABLE FOR ANY
11 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 11 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
12 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 12 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
13 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 13 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
14 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 14 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
15 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 15 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
16 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17 */ 17 */
18   18  
19 (function (name, context, definition) { 19 (function (name, context, definition) {
20 "use strict"; 20 "use strict";
21 if (typeof define === "function" && define.amd) { define(definition); } 21 if (typeof define === "function" && define.amd) { define(definition); }
22 else if (typeof module !== "undefined" && module.exports) { module.exports = definition(); } 22 else if (typeof module !== "undefined" && module.exports) { module.exports = definition(); }
23 else if (context.exports) { context.exports = definition(); } 23 else if (context.exports) { context.exports = definition(); }
24 else { context[name] = definition(); } 24 else { context[name] = definition(); }
25 })("Fingerprint2", this, function() { 25 })("Fingerprint2", this, function() {
26 "use strict"; 26 "use strict";
27 // This will only be polyfilled for IE8 and older -  
28 // Taken from Mozilla MDC -  
29 if (!Array.prototype.indexOf) { -  
30 Array.prototype.indexOf = function(searchElement, fromIndex) { -  
31 var k; -  
32 if (this == null) { -  
33 throw new TypeError("'this' is null or undefined"); -  
34 } -  
35 var O = Object(this); -  
36 var len = O.length >>> 0; -  
37 if (len === 0) { -  
38 return -1; -  
39 } -  
40 var n = +fromIndex || 0; -  
41 if (Math.abs(n) === Infinity) { -  
42 n = 0; -  
43 } -  
44 if (n >= len) { -  
45 return -1; -  
46 } -  
47 k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); -  
48 while (k < len) { -  
49 if (k in O && O[k] === searchElement) { -  
50 return k; -  
51 } -  
52 k++; -  
53 } -  
54 return -1; -  
55 }; -  
56 } -  
57 var Fingerprint2 = function(options) { 27 var Fingerprint2 = function(options) {
58   28  
59 if (!(this instanceof Fingerprint2)) { 29 if (!(this instanceof Fingerprint2)) {
60 return new Fingerprint2(options); 30 return new Fingerprint2(options);
61 } 31 }
62   32  
63 var defaultOptions = { 33 var defaultOptions = {
64 swfContainerId: "fingerprintjs2", 34 swfContainerId: "fingerprintjs2",
65 swfPath: "flash/compiled/FontList.swf", 35 swfPath: "flash/compiled/FontList.swf",
66 detectScreenOrientation: true, 36 detectScreenOrientation: true,
67 sortPluginsFor: [/palemoon/i], 37 sortPluginsFor: [/palemoon/i],
68 userDefinedFonts: [] 38 userDefinedFonts: []
69 }; 39 };
70 this.options = this.extend(options, defaultOptions); 40 this.options = this.extend(options, defaultOptions);
71 this.nativeForEach = Array.prototype.forEach; 41 this.nativeForEach = Array.prototype.forEach;
72 this.nativeMap = Array.prototype.map; 42 this.nativeMap = Array.prototype.map;
73 }; 43 };
74 Fingerprint2.prototype = { 44 Fingerprint2.prototype = {
75 extend: function(source, target) { 45 extend: function(source, target) {
76 if (source == null) { return target; } 46 if (source == null) { return target; }
77 for (var k in source) { 47 for (var k in source) {
78 if(source[k] != null && target[k] !== source[k]) { 48 if(source[k] != null && target[k] !== source[k]) {
79 target[k] = source[k]; 49 target[k] = source[k];
80 } 50 }
81 } 51 }
82 return target; 52 return target;
83 }, 53 },
84 log: function(msg){ -  
85 if(window.console){ -  
86 console.log(msg); -  
87 } -  
88 }, -  
89 get: function(done){ 54 get: function(done){
90 var keys = []; 55 var keys = [];
91 keys = this.userAgentKey(keys); 56 keys = this.userAgentKey(keys);
92 keys = this.languageKey(keys); 57 keys = this.languageKey(keys);
93 keys = this.colorDepthKey(keys); 58 keys = this.colorDepthKey(keys);
94 keys = this.pixelRatioKey(keys); 59 keys = this.pixelRatioKey(keys);
95 keys = this.hardwareConcurrencyKey(keys); 60 keys = this.hardwareConcurrencyKey(keys);
96 keys = this.screenResolutionKey(keys); 61 keys = this.screenResolutionKey(keys);
97 keys = this.availableScreenResolutionKey(keys); 62 keys = this.availableScreenResolutionKey(keys);
98 keys = this.timezoneOffsetKey(keys); 63 keys = this.timezoneOffsetKey(keys);
99 keys = this.sessionStorageKey(keys); 64 keys = this.sessionStorageKey(keys);
100 keys = this.localStorageKey(keys); 65 keys = this.localStorageKey(keys);
101 keys = this.indexedDbKey(keys); 66 keys = this.indexedDbKey(keys);
102 keys = this.addBehaviorKey(keys); 67 keys = this.addBehaviorKey(keys);
103 keys = this.openDatabaseKey(keys); 68 keys = this.openDatabaseKey(keys);
104 keys = this.cpuClassKey(keys); 69 keys = this.cpuClassKey(keys);
105 keys = this.platformKey(keys); 70 keys = this.platformKey(keys);
106 keys = this.doNotTrackKey(keys); 71 keys = this.doNotTrackKey(keys);
107 keys = this.pluginsKey(keys); 72 keys = this.pluginsKey(keys);
108 keys = this.canvasKey(keys); 73 keys = this.canvasKey(keys);
109 keys = this.webglKey(keys); 74 keys = this.webglKey(keys);
110 keys = this.adBlockKey(keys); 75 keys = this.adBlockKey(keys);
111 keys = this.hasLiedLanguagesKey(keys); 76 keys = this.hasLiedLanguagesKey(keys);
112 keys = this.hasLiedResolutionKey(keys); 77 keys = this.hasLiedResolutionKey(keys);
113 keys = this.hasLiedOsKey(keys); 78 keys = this.hasLiedOsKey(keys);
114 keys = this.hasLiedBrowserKey(keys); 79 keys = this.hasLiedBrowserKey(keys);
115 keys = this.touchSupportKey(keys); 80 keys = this.touchSupportKey(keys);
-   81 keys = this.customEntropyFunction(keys);
116 var that = this; 82 var that = this;
117 this.fontsKey(keys, function(newKeys){ 83 this.fontsKey(keys, function(newKeys){
118 var values = []; 84 var values = [];
119 that.each(newKeys, function(pair) { 85 that.each(newKeys, function(pair) {
120 var value = pair.value; 86 var value = pair.value;
121 if (typeof pair.value.join !== "undefined") { 87 if (typeof pair.value.join !== "undefined") {
122 value = pair.value.join(";"); 88 value = pair.value.join(";");
123 } 89 }
124 values.push(value); 90 values.push(value);
125 }); 91 });
126 var murmur = that.x64hash128(values.join("~~~"), 31); 92 var murmur = that.x64hash128(values.join("~~~"), 31);
127 return done(murmur, newKeys); 93 return done(murmur, newKeys);
128 }); 94 });
129 }, 95 },
-   96 customEntropyFunction: function (keys) {
-   97 if (typeof this.options.customFunction === "function") {
-   98 keys.push({key: "custom", value: this.options.customFunction()});
-   99 }
-   100 return keys;
-   101 },
130 userAgentKey: function(keys) { 102 userAgentKey: function(keys) {
131 if(!this.options.excludeUserAgent) { 103 if(!this.options.excludeUserAgent) {
132 keys.push({key: "user_agent", value: this.getUserAgent()}); 104 keys.push({key: "user_agent", value: this.getUserAgent()});
133 } 105 }
134 return keys; 106 return keys;
135 }, 107 },
136 // for tests 108 // for tests
137 getUserAgent: function(){ 109 getUserAgent: function(){
138 return navigator.userAgent; 110 return navigator.userAgent;
139 }, 111 },
140 languageKey: function(keys) { 112 languageKey: function(keys) {
141 if(!this.options.excludeLanguage) { 113 if(!this.options.excludeLanguage) {
142 // IE 9,10 on Windows 10 does not have the `navigator.language` property any longer 114 // IE 9,10 on Windows 10 does not have the `navigator.language` property any longer
143 keys.push({ key: "language", value: navigator.language || navigator.userLanguage || navigator.browserLanguage || navigator.systemLanguage || "" }); 115 keys.push({ key: "language", value: navigator.language || navigator.userLanguage || navigator.browserLanguage || navigator.systemLanguage || "" });
144 } 116 }
145 return keys; 117 return keys;
146 }, 118 },
147 colorDepthKey: function(keys) { 119 colorDepthKey: function(keys) {
148 if(!this.options.excludeColorDepth) { 120 if(!this.options.excludeColorDepth) {
149 keys.push({key: "color_depth", value: screen.colorDepth || -1}); 121 keys.push({key: "color_depth", value: screen.colorDepth || -1});
150 } 122 }
151 return keys; 123 return keys;
152 }, 124 },
153 pixelRatioKey: function(keys) { 125 pixelRatioKey: function(keys) {
154 if(!this.options.excludePixelRatio) { 126 if(!this.options.excludePixelRatio) {
155 keys.push({key: "pixel_ratio", value: this.getPixelRatio()}); 127 keys.push({key: "pixel_ratio", value: this.getPixelRatio()});
156 } 128 }
157 return keys; 129 return keys;
158 }, 130 },
159 getPixelRatio: function() { 131 getPixelRatio: function() {
160 return window.devicePixelRatio || ""; 132 return window.devicePixelRatio || "";
161 }, 133 },
162 screenResolutionKey: function(keys) { 134 screenResolutionKey: function(keys) {
163 if(!this.options.excludeScreenResolution) { 135 if(!this.options.excludeScreenResolution) {
164 return this.getScreenResolution(keys); 136 return this.getScreenResolution(keys);
165 } 137 }
166 return keys; 138 return keys;
167 }, 139 },
168 getScreenResolution: function(keys) { 140 getScreenResolution: function(keys) {
169 var resolution; 141 var resolution;
170 if(this.options.detectScreenOrientation) { 142 if(this.options.detectScreenOrientation) {
171 resolution = (screen.height > screen.width) ? [screen.height, screen.width] : [screen.width, screen.height]; 143 resolution = (screen.height > screen.width) ? [screen.height, screen.width] : [screen.width, screen.height];
172 } else { 144 } else {
173 resolution = [screen.width, screen.height]; 145 resolution = [screen.width, screen.height];
174 } 146 }
175 if(typeof resolution !== "undefined") { // headless browsers 147 if(typeof resolution !== "undefined") { // headless browsers
176 keys.push({key: "resolution", value: resolution}); 148 keys.push({key: "resolution", value: resolution});
177 } 149 }
178 return keys; 150 return keys;
179 }, 151 },
180 availableScreenResolutionKey: function(keys) { 152 availableScreenResolutionKey: function(keys) {
181 if (!this.options.excludeAvailableScreenResolution) { 153 if (!this.options.excludeAvailableScreenResolution) {
182 return this.getAvailableScreenResolution(keys); 154 return this.getAvailableScreenResolution(keys);
183 } 155 }
184 return keys; 156 return keys;
185 }, 157 },
186 getAvailableScreenResolution: function(keys) { 158 getAvailableScreenResolution: function(keys) {
187 var available; 159 var available;
188 if(screen.availWidth && screen.availHeight) { 160 if(screen.availWidth && screen.availHeight) {
189 if(this.options.detectScreenOrientation) { 161 if(this.options.detectScreenOrientation) {
190 available = (screen.availHeight > screen.availWidth) ? [screen.availHeight, screen.availWidth] : [screen.availWidth, screen.availHeight]; 162 available = (screen.availHeight > screen.availWidth) ? [screen.availHeight, screen.availWidth] : [screen.availWidth, screen.availHeight];
191 } else { 163 } else {
192 available = [screen.availHeight, screen.availWidth]; 164 available = [screen.availHeight, screen.availWidth];
193 } 165 }
194 } 166 }
195 if(typeof available !== "undefined") { // headless browsers 167 if(typeof available !== "undefined") { // headless browsers
196 keys.push({key: "available_resolution", value: available}); 168 keys.push({key: "available_resolution", value: available});
197 } 169 }
198 return keys; 170 return keys;
199 }, 171 },
200 timezoneOffsetKey: function(keys) { 172 timezoneOffsetKey: function(keys) {
201 if(!this.options.excludeTimezoneOffset) { 173 if(!this.options.excludeTimezoneOffset) {
202 keys.push({key: "timezone_offset", value: new Date().getTimezoneOffset()}); 174 keys.push({key: "timezone_offset", value: new Date().getTimezoneOffset()});
203 } 175 }
204 return keys; 176 return keys;
205 }, 177 },
206 sessionStorageKey: function(keys) { 178 sessionStorageKey: function(keys) {
207 if(!this.options.excludeSessionStorage && this.hasSessionStorage()) { 179 if(!this.options.excludeSessionStorage && this.hasSessionStorage()) {
208 keys.push({key: "session_storage", value: 1}); 180 keys.push({key: "session_storage", value: 1});
209 } 181 }
210 return keys; 182 return keys;
211 }, 183 },
212 localStorageKey: function(keys) { 184 localStorageKey: function(keys) {
213 if(!this.options.excludeSessionStorage && this.hasLocalStorage()) { 185 if(!this.options.excludeSessionStorage && this.hasLocalStorage()) {
214 keys.push({key: "local_storage", value: 1}); 186 keys.push({key: "local_storage", value: 1});
215 } 187 }
216 return keys; 188 return keys;
217 }, 189 },
218 indexedDbKey: function(keys) { 190 indexedDbKey: function(keys) {
219 if(!this.options.excludeIndexedDB && this.hasIndexedDB()) { 191 if(!this.options.excludeIndexedDB && this.hasIndexedDB()) {
220 keys.push({key: "indexed_db", value: 1}); 192 keys.push({key: "indexed_db", value: 1});
221 } 193 }
222 return keys; 194 return keys;
223 }, 195 },
224 addBehaviorKey: function(keys) { 196 addBehaviorKey: function(keys) {
225 //body might not be defined at this point or removed programmatically 197 //body might not be defined at this point or removed programmatically
226 if(document.body && !this.options.excludeAddBehavior && document.body.addBehavior) { 198 if(document.body && !this.options.excludeAddBehavior && document.body.addBehavior) {
227 keys.push({key: "add_behavior", value: 1}); 199 keys.push({key: "add_behavior", value: 1});
228 } 200 }
229 return keys; 201 return keys;
230 }, 202 },
231 openDatabaseKey: function(keys) { 203 openDatabaseKey: function(keys) {
232 if(!this.options.excludeOpenDatabase && window.openDatabase) { 204 if(!this.options.excludeOpenDatabase && window.openDatabase) {
233 keys.push({key: "open_database", value: 1}); 205 keys.push({key: "open_database", value: 1});
234 } 206 }
235 return keys; 207 return keys;
236 }, 208 },
237 cpuClassKey: function(keys) { 209 cpuClassKey: function(keys) {
238 if(!this.options.excludeCpuClass) { 210 if(!this.options.excludeCpuClass) {
239 keys.push({key: "cpu_class", value: this.getNavigatorCpuClass()}); 211 keys.push({key: "cpu_class", value: this.getNavigatorCpuClass()});
240 } 212 }
241 return keys; 213 return keys;
242 }, 214 },
243 platformKey: function(keys) { 215 platformKey: function(keys) {
244 if(!this.options.excludePlatform) { 216 if(!this.options.excludePlatform) {
245 keys.push({key: "navigator_platform", value: this.getNavigatorPlatform()}); 217 keys.push({key: "navigator_platform", value: this.getNavigatorPlatform()});
246 } 218 }
247 return keys; 219 return keys;
248 }, 220 },
249 doNotTrackKey: function(keys) { 221 doNotTrackKey: function(keys) {
250 if(!this.options.excludeDoNotTrack) { 222 if(!this.options.excludeDoNotTrack) {
251 keys.push({key: "do_not_track", value: this.getDoNotTrack()}); 223 keys.push({key: "do_not_track", value: this.getDoNotTrack()});
252 } 224 }
253 return keys; 225 return keys;
254 }, 226 },
255 canvasKey: function(keys) { 227 canvasKey: function(keys) {
256 if(!this.options.excludeCanvas && this.isCanvasSupported()) { 228 if(!this.options.excludeCanvas && this.isCanvasSupported()) {
257 keys.push({key: "canvas", value: this.getCanvasFp()}); 229 keys.push({key: "canvas", value: this.getCanvasFp()});
258 } 230 }
259 return keys; 231 return keys;
260 }, 232 },
261 webglKey: function(keys) { 233 webglKey: function(keys) {
262 if(this.options.excludeWebGL) { 234 if(this.options.excludeWebGL) {
263 if(typeof NODEBUG === "undefined"){ -  
264 this.log("Skipping WebGL fingerprinting per excludeWebGL configuration option"); -  
265 } -  
266 return keys; 235 return keys;
267 } 236 }
268 if(!this.isWebGlSupported()) { 237 if(!this.isWebGlSupported()) {
269 if(typeof NODEBUG === "undefined"){ -  
270 this.log("Skipping WebGL fingerprinting because it is not supported in this browser"); -  
271 } -  
272 return keys; 238 return keys;
273 } 239 }
274 keys.push({key: "webgl", value: this.getWebglFp()}); 240 keys.push({key: "webgl", value: this.getWebglFp()});
275 return keys; 241 return keys;
276 }, 242 },
277 adBlockKey: function(keys){ 243 adBlockKey: function(keys){
278 if(!this.options.excludeAdBlock) { 244 if(!this.options.excludeAdBlock) {
279 keys.push({key: "adblock", value: this.getAdBlock()}); 245 keys.push({key: "adblock", value: this.getAdBlock()});
280 } 246 }
281 return keys; 247 return keys;
282 }, 248 },
283 hasLiedLanguagesKey: function(keys){ 249 hasLiedLanguagesKey: function(keys){
284 if(!this.options.excludeHasLiedLanguages){ 250 if(!this.options.excludeHasLiedLanguages){
285 keys.push({key: "has_lied_languages", value: this.getHasLiedLanguages()}); 251 keys.push({key: "has_lied_languages", value: this.getHasLiedLanguages()});
286 } 252 }
287 return keys; 253 return keys;
288 }, 254 },
289 hasLiedResolutionKey: function(keys){ 255 hasLiedResolutionKey: function(keys){
290 if(!this.options.excludeHasLiedResolution){ 256 if(!this.options.excludeHasLiedResolution){
291 keys.push({key: "has_lied_resolution", value: this.getHasLiedResolution()}); 257 keys.push({key: "has_lied_resolution", value: this.getHasLiedResolution()});
292 } 258 }
293 return keys; 259 return keys;
294 }, 260 },
295 hasLiedOsKey: function(keys){ 261 hasLiedOsKey: function(keys){
296 if(!this.options.excludeHasLiedOs){ 262 if(!this.options.excludeHasLiedOs){
297 keys.push({key: "has_lied_os", value: this.getHasLiedOs()}); 263 keys.push({key: "has_lied_os", value: this.getHasLiedOs()});
298 } 264 }
299 return keys; 265 return keys;
300 }, 266 },
301 hasLiedBrowserKey: function(keys){ 267 hasLiedBrowserKey: function(keys){
302 if(!this.options.excludeHasLiedBrowser){ 268 if(!this.options.excludeHasLiedBrowser){
303 keys.push({key: "has_lied_browser", value: this.getHasLiedBrowser()}); 269 keys.push({key: "has_lied_browser", value: this.getHasLiedBrowser()});
304 } 270 }
305 return keys; 271 return keys;
306 }, 272 },
307 fontsKey: function(keys, done) { 273 fontsKey: function(keys, done) {
308 if (this.options.excludeJsFonts) { 274 if (this.options.excludeJsFonts) {
309 return this.flashFontsKey(keys, done); 275 return this.flashFontsKey(keys, done);
310 } 276 }
311 return this.jsFontsKey(keys, done); 277 return this.jsFontsKey(keys, done);
312 }, 278 },
313 // flash fonts (will increase fingerprinting time 20X to ~ 130-150ms) 279 // flash fonts (will increase fingerprinting time 20X to ~ 130-150ms)
314 flashFontsKey: function(keys, done) { 280 flashFontsKey: function(keys, done) {
315 if(this.options.excludeFlashFonts) { 281 if(this.options.excludeFlashFonts) {
316 if(typeof NODEBUG === "undefined"){ -  
317 this.log("Skipping flash fonts detection per excludeFlashFonts configuration option"); -  
318 } -  
319 return done(keys); 282 return done(keys);
320 } 283 }
321 // we do flash if swfobject is loaded 284 // we do flash if swfobject is loaded
322 if(!this.hasSwfObjectLoaded()){ 285 if(!this.hasSwfObjectLoaded()){
323 if(typeof NODEBUG === "undefined"){ -  
324 this.log("Swfobject is not detected, Flash fonts enumeration is skipped"); -  
325 } -  
326 return done(keys); 286 return done(keys);
327 } 287 }
328 if(!this.hasMinFlashInstalled()){ 288 if(!this.hasMinFlashInstalled()){
329 if(typeof NODEBUG === "undefined"){ -  
330 this.log("Flash is not installed, skipping Flash fonts enumeration"); -  
331 } -  
332 return done(keys); 289 return done(keys);
333 } 290 }
334 if(typeof this.options.swfPath === "undefined"){ 291 if(typeof this.options.swfPath === "undefined"){
335 if(typeof NODEBUG === "undefined"){ -  
336 this.log("To use Flash fonts detection, you must pass a valid swfPath option, skipping Flash fonts enumeration"); -  
337 } -  
338 return done(keys); 292 return done(keys);
339 } 293 }
340 this.loadSwfAndDetectFonts(function(fonts){ 294 this.loadSwfAndDetectFonts(function(fonts){
341 keys.push({key: "swf_fonts", value: fonts.join(";")}); 295 keys.push({key: "swf_fonts", value: fonts.join(";")});
342 done(keys); 296 done(keys);
343 }); 297 });
344 }, 298 },
345 // kudos to http://www.lalit.org/lab/javascript-css-font-detect/ 299 // kudos to http://www.lalit.org/lab/javascript-css-font-detect/
346 jsFontsKey: function(keys, done) { 300 jsFontsKey: function(keys, done) {
347 var that = this; 301 var that = this;
348 // doing js fonts detection in a pseudo-async fashion 302 // doing js fonts detection in a pseudo-async fashion
349 return setTimeout(function(){ 303 return setTimeout(function(){
350   304  
351 // a font will be compared against all the three default fonts. 305 // a font will be compared against all the three default fonts.
352 // and if it doesn't match all 3 then that font is not available. 306 // and if it doesn't match all 3 then that font is not available.
353 var baseFonts = ["monospace", "sans-serif", "serif"]; 307 var baseFonts = ["monospace", "sans-serif", "serif"];
354   308  
355 var fontList = [ 309 var fontList = [
356 "Andale Mono", "Arial", "Arial Black", "Arial Hebrew", "Arial MT", "Arial Narrow", "Arial Rounded MT Bold", "Arial Unicode MS", 310 "Andale Mono", "Arial", "Arial Black", "Arial Hebrew", "Arial MT", "Arial Narrow", "Arial Rounded MT Bold", "Arial Unicode MS",
357 "Bitstream Vera Sans Mono", "Book Antiqua", "Bookman Old Style", 311 "Bitstream Vera Sans Mono", "Book Antiqua", "Bookman Old Style",
358 "Calibri", "Cambria", "Cambria Math", "Century", "Century Gothic", "Century Schoolbook", "Comic Sans", "Comic Sans MS", "Consolas", "Courier", "Courier New", 312 "Calibri", "Cambria", "Cambria Math", "Century", "Century Gothic", "Century Schoolbook", "Comic Sans", "Comic Sans MS", "Consolas", "Courier", "Courier New",
359 "Garamond", "Geneva", "Georgia", 313 "Garamond", "Geneva", "Georgia",
360 "Helvetica", "Helvetica Neue", 314 "Helvetica", "Helvetica Neue",
361 "Impact", 315 "Impact",
362 "Lucida Bright", "Lucida Calligraphy", "Lucida Console", "Lucida Fax", "LUCIDA GRANDE", "Lucida Handwriting", "Lucida Sans", "Lucida Sans Typewriter", "Lucida Sans Unicode", 316 "Lucida Bright", "Lucida Calligraphy", "Lucida Console", "Lucida Fax", "LUCIDA GRANDE", "Lucida Handwriting", "Lucida Sans", "Lucida Sans Typewriter", "Lucida Sans Unicode",
363 "Microsoft Sans Serif", "Monaco", "Monotype Corsiva", "MS Gothic", "MS Outlook", "MS PGothic", "MS Reference Sans Serif", "MS Sans Serif", "MS Serif", "MYRIAD", "MYRIAD PRO", 317 "Microsoft Sans Serif", "Monaco", "Monotype Corsiva", "MS Gothic", "MS Outlook", "MS PGothic", "MS Reference Sans Serif", "MS Sans Serif", "MS Serif", "MYRIAD", "MYRIAD PRO",
364 "Palatino", "Palatino Linotype", 318 "Palatino", "Palatino Linotype",
365 "Segoe Print", "Segoe Script", "Segoe UI", "Segoe UI Light", "Segoe UI Semibold", "Segoe UI Symbol", 319 "Segoe Print", "Segoe Script", "Segoe UI", "Segoe UI Light", "Segoe UI Semibold", "Segoe UI Symbol",
366 "Tahoma", "Times", "Times New Roman", "Times New Roman PS", "Trebuchet MS", 320 "Tahoma", "Times", "Times New Roman", "Times New Roman PS", "Trebuchet MS",
367 "Verdana", "Wingdings", "Wingdings 2", "Wingdings 3" 321 "Verdana", "Wingdings", "Wingdings 2", "Wingdings 3"
368 ]; 322 ];
369 var extendedFontList = [ 323 var extendedFontList = [
370 "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", 324 "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",
371 "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", 325 "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",
372 "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", 326 "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",
373 "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", 327 "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",
374 "Blackadder ITC", "BlairMdITC TT", "Bodoni 72", "Bodoni 72 Oldstyle", "Bodoni 72 Smallcaps", "Bodoni MT", "Bodoni MT Black", "Bodoni MT Condensed", "Bodoni MT Poster Compressed", 328 "Blackadder ITC", "BlairMdITC TT", "Bodoni 72", "Bodoni 72 Oldstyle", "Bodoni 72 Smallcaps", "Bodoni MT", "Bodoni MT Black", "Bodoni MT Condensed", "Bodoni MT Poster Compressed",
375 "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", 329 "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",
376 "CaslonOpnface BT", "Castellar", "Centaur", "Cezanne", "CG Omega", "CG Times", "Chalkboard", "Chalkboard SE", "Chalkduster", "Charlesworth", "Charter Bd BT", "Charter BT", "Chaucer", 330 "CaslonOpnface BT", "Castellar", "Centaur", "Cezanne", "CG Omega", "CG Times", "Chalkboard", "Chalkboard SE", "Chalkduster", "Charlesworth", "Charter Bd BT", "Charter BT", "Chaucer",
377 "ChelthmITC Bk BT", "Chiller", "Clarendon", "Clarendon Condensed", "CloisterBlack BT", "Cochin", "Colonna MT", "Constantia", "Cooper Black", "Copperplate", "Copperplate Gothic", "Copperplate Gothic Bold", 331 "ChelthmITC Bk BT", "Chiller", "Clarendon", "Clarendon Condensed", "CloisterBlack BT", "Cochin", "Colonna MT", "Constantia", "Cooper Black", "Copperplate", "Copperplate Gothic", "Copperplate Gothic Bold",
378 "Copperplate Gothic Light", "CopperplGoth Bd BT", "Corbel", "Cordia New", "CordiaUPC", "Cornerstone", "Coronet", "Cuckoo", "Curlz MT", "DaunPenh", "Dauphin", "David", "DB LCD Temp", "DELICIOUS", "Denmark", 332 "Copperplate Gothic Light", "CopperplGoth Bd BT", "Corbel", "Cordia New", "CordiaUPC", "Cornerstone", "Coronet", "Cuckoo", "Curlz MT", "DaunPenh", "Dauphin", "David", "DB LCD Temp", "DELICIOUS", "Denmark",
379 "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", 333 "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",
380 "EucrosiaUPC", "Euphemia", "Euphemia UCAS", "EUROSTILE", "Exotc350 Bd BT", "FangSong", "Felix Titling", "Fixedsys", "FONTIN", "Footlight MT Light", "Forte", 334 "EucrosiaUPC", "Euphemia", "Euphemia UCAS", "EUROSTILE", "Exotc350 Bd BT", "FangSong", "Felix Titling", "Fixedsys", "FONTIN", "Footlight MT Light", "Forte",
381 "FrankRuehl", "Fransiscan", "Freefrm721 Blk BT", "FreesiaUPC", "Freestyle Script", "French Script MT", "FrnkGothITC Bk BT", "Fruitger", "FRUTIGER", 335 "FrankRuehl", "Fransiscan", "Freefrm721 Blk BT", "FreesiaUPC", "Freestyle Script", "French Script MT", "FrnkGothITC Bk BT", "Fruitger", "FRUTIGER",
382 "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", 336 "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",
383 "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", 337 "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",
384 "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", 338 "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",
385 "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", 339 "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",
386 "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", 340 "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",
387 "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", 341 "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",
388 "Lydian BT", "Magneto", "Maiandra GD", "Malayalam Sangam MN", "Malgun Gothic", 342 "Lydian BT", "Magneto", "Maiandra GD", "Malayalam Sangam MN", "Malgun Gothic",
389 "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", 343 "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",
390 "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", 344 "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",
391 "MONO", "MoolBoran", "Mrs Eaves", "MS LineDraw", "MS Mincho", "MS PMincho", "MS Reference Specialty", "MS UI Gothic", "MT Extra", "MUSEO", "MV Boli", 345 "MONO", "MoolBoran", "Mrs Eaves", "MS LineDraw", "MS Mincho", "MS PMincho", "MS Reference Specialty", "MS UI Gothic", "MT Extra", "MUSEO", "MV Boli",
392 "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", 346 "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",
393 "OSAKA", "OzHandicraft BT", "Palace Script MT", "Papyrus", "Parchment", "Party LET", "Pegasus", "Perpetua", "Perpetua Titling MT", "PetitaBold", "Pickwick", "Plantagenet Cherokee", "Playbill", "PMingLiU", "PMingLiU-ExtB", 347 "OSAKA", "OzHandicraft BT", "Palace Script MT", "Papyrus", "Parchment", "Party LET", "Pegasus", "Perpetua", "Perpetua Titling MT", "PetitaBold", "Pickwick", "Plantagenet Cherokee", "Playbill", "PMingLiU", "PMingLiU-ExtB",
394 "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", 348 "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",
395 "Santa Fe LET", "Savoye LET", "Sceptre", "Script", "Script MT Bold", "SCRIPTINA", "Serifa", "Serifa BT", "Serifa Th BT", "ShelleyVolante BT", "Sherwood", 349 "Santa Fe LET", "Savoye LET", "Sceptre", "Script", "Script MT Bold", "SCRIPTINA", "Serifa", "Serifa BT", "Serifa Th BT", "ShelleyVolante BT", "Sherwood",
396 "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", 350 "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",
397 "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", 351 "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",
398 "Terminal", "Thonburi", "Traditional Arabic", "Trajan", "TRAJAN PRO", "Tristan", "Tubular", "Tunga", "Tw Cen MT", "Tw Cen MT Condensed", "Tw Cen MT Condensed Extra Bold", 352 "Terminal", "Thonburi", "Traditional Arabic", "Trajan", "TRAJAN PRO", "Tristan", "Tubular", "Tunga", "Tw Cen MT", "Tw Cen MT Condensed", "Tw Cen MT Condensed Extra Bold",
399 "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", 353 "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",
400 "ZapfEllipt BT", "ZapfHumnst BT", "ZapfHumnst Dm BT", "Zapfino", "Zurich BlkEx BT", "Zurich Ex BT", "ZWAdobeF"]; 354 "ZapfEllipt BT", "ZapfHumnst BT", "ZapfHumnst Dm BT", "Zapfino", "Zurich BlkEx BT", "Zurich Ex BT", "ZWAdobeF"];
401   355  
402 if(that.options.extendedJsFonts) { 356 if(that.options.extendedJsFonts) {
403 fontList = fontList.concat(extendedFontList); 357 fontList = fontList.concat(extendedFontList);
404 } 358 }
405   359  
406 fontList = fontList.concat(that.options.userDefinedFonts); 360 fontList = fontList.concat(that.options.userDefinedFonts);
407   361  
408 //we use m or w because these two characters take up the maximum width. 362 //we use m or w because these two characters take up the maximum width.
409 // And we use a LLi so that the same matching fonts can get separated 363 // And we use a LLi so that the same matching fonts can get separated
410 var testString = "mmmmmmmmmmlli"; 364 var testString = "mmmmmmmmmmlli";
411   365  
412 //we test using 72px font size, we may use any size. I guess larger the better. 366 //we test using 72px font size, we may use any size. I guess larger the better.
413 var testSize = "72px"; 367 var testSize = "72px";
414   368  
415 var h = document.getElementsByTagName("body")[0]; 369 var h = document.getElementsByTagName("body")[0];
416   370  
417 // div to load spans for the base fonts 371 // div to load spans for the base fonts
418 var baseFontsDiv = document.createElement("div"); 372 var baseFontsDiv = document.createElement("div");
419   373  
420 // div to load spans for the fonts to detect 374 // div to load spans for the fonts to detect
421 var fontsDiv = document.createElement("div"); 375 var fontsDiv = document.createElement("div");
422   376  
423 var defaultWidth = {}; 377 var defaultWidth = {};
424 var defaultHeight = {}; 378 var defaultHeight = {};
425   379  
426 // creates a span where the fonts will be loaded 380 // creates a span where the fonts will be loaded
427 var createSpan = function() { 381 var createSpan = function() {
428 var s = document.createElement("span"); 382 var s = document.createElement("span");
429 /* 383 /*
430 * We need this css as in some weird browser this 384 * We need this css as in some weird browser this
431 * span elements shows up for a microSec which creates a 385 * span elements shows up for a microSec which creates a
432 * bad user experience 386 * bad user experience
433 */ 387 */
434 s.style.position = "absolute"; 388 s.style.position = "absolute";
435 s.style.left = "-9999px"; 389 s.style.left = "-9999px";
436 s.style.fontSize = testSize; 390 s.style.fontSize = testSize;
437 s.style.lineHeight = "normal"; 391 s.style.lineHeight = "normal";
438 s.innerHTML = testString; 392 s.innerHTML = testString;
439 return s; 393 return s;
440 }; 394 };
441   395  
442 // creates a span and load the font to detect and a base font for fallback 396 // creates a span and load the font to detect and a base font for fallback
443 var createSpanWithFonts = function(fontToDetect, baseFont) { 397 var createSpanWithFonts = function(fontToDetect, baseFont) {
444 var s = createSpan(); 398 var s = createSpan();
445 s.style.fontFamily = "'" + fontToDetect + "'," + baseFont; 399 s.style.fontFamily = "'" + fontToDetect + "'," + baseFont;
446 return s; 400 return s;
447 }; 401 };
448   402  
449 // creates spans for the base fonts and adds them to baseFontsDiv 403 // creates spans for the base fonts and adds them to baseFontsDiv
450 var initializeBaseFontsSpans = function() { 404 var initializeBaseFontsSpans = function() {
451 var spans = []; 405 var spans = [];
452 for (var index = 0, length = baseFonts.length; index < length; index++) { 406 for (var index = 0, length = baseFonts.length; index < length; index++) {
453 var s = createSpan(); 407 var s = createSpan();
454 s.style.fontFamily = baseFonts[index]; 408 s.style.fontFamily = baseFonts[index];
455 baseFontsDiv.appendChild(s); 409 baseFontsDiv.appendChild(s);
456 spans.push(s); 410 spans.push(s);
457 } 411 }
458 return spans; 412 return spans;
459 }; 413 };
460   414  
461 // creates spans for the fonts to detect and adds them to fontsDiv 415 // creates spans for the fonts to detect and adds them to fontsDiv
462 var initializeFontsSpans = function() { 416 var initializeFontsSpans = function() {
463 var spans = {}; 417 var spans = {};
464 for(var i = 0, l = fontList.length; i < l; i++) { 418 for(var i = 0, l = fontList.length; i < l; i++) {
465 var fontSpans = []; 419 var fontSpans = [];
466 for(var j = 0, numDefaultFonts = baseFonts.length; j < numDefaultFonts; j++) { 420 for(var j = 0, numDefaultFonts = baseFonts.length; j < numDefaultFonts; j++) {
467 var s = createSpanWithFonts(fontList[i], baseFonts[j]); 421 var s = createSpanWithFonts(fontList[i], baseFonts[j]);
468 fontsDiv.appendChild(s); 422 fontsDiv.appendChild(s);
469 fontSpans.push(s); 423 fontSpans.push(s);
470 } 424 }
471 spans[fontList[i]] = fontSpans; // Stores {fontName : [spans for that font]} 425 spans[fontList[i]] = fontSpans; // Stores {fontName : [spans for that font]}
472 } 426 }
473 return spans; 427 return spans;
474 }; 428 };
475   429  
476 // checks if a font is available 430 // checks if a font is available
477 var isFontAvailable = function(fontSpans) { 431 var isFontAvailable = function(fontSpans) {
478 var detected = false; 432 var detected = false;
479 for(var i = 0; i < baseFonts.length; i++) { 433 for(var i = 0; i < baseFonts.length; i++) {
480 detected = (fontSpans[i].offsetWidth !== defaultWidth[baseFonts[i]] || fontSpans[i].offsetHeight !== defaultHeight[baseFonts[i]]); 434 detected = (fontSpans[i].offsetWidth !== defaultWidth[baseFonts[i]] || fontSpans[i].offsetHeight !== defaultHeight[baseFonts[i]]);
481 if(detected) { 435 if(detected) {
482 return detected; 436 return detected;
483 } 437 }
484 } 438 }
485 return detected; 439 return detected;
486 }; 440 };
487   441  
488 // create spans for base fonts 442 // create spans for base fonts
489 var baseFontsSpans = initializeBaseFontsSpans(); 443 var baseFontsSpans = initializeBaseFontsSpans();
490   444  
491 // add the spans to the DOM 445 // add the spans to the DOM
492 h.appendChild(baseFontsDiv); 446 h.appendChild(baseFontsDiv);
493   447  
494 // get the default width for the three base fonts 448 // get the default width for the three base fonts
495 for (var index = 0, length = baseFonts.length; index < length; index++) { 449 for (var index = 0, length = baseFonts.length; index < length; index++) {
496 defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font 450 defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth; // width for the default font
497 defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font 451 defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight; // height for the default font
498 } 452 }
499   453  
500 // create spans for fonts to detect 454 // create spans for fonts to detect
501 var fontsSpans = initializeFontsSpans(); 455 var fontsSpans = initializeFontsSpans();
502   456  
503 // add all the spans to the DOM 457 // add all the spans to the DOM
504 h.appendChild(fontsDiv); 458 h.appendChild(fontsDiv);
505   459  
506 // check available fonts 460 // check available fonts
507 var available = []; 461 var available = [];
508 for(var i = 0, l = fontList.length; i < l; i++) { 462 for(var i = 0, l = fontList.length; i < l; i++) {
509 if(isFontAvailable(fontsSpans[fontList[i]])) { 463 if(isFontAvailable(fontsSpans[fontList[i]])) {
510 available.push(fontList[i]); 464 available.push(fontList[i]);
511 } 465 }
512 } 466 }
513   467  
514 // remove spans from DOM 468 // remove spans from DOM
515 h.removeChild(fontsDiv); 469 h.removeChild(fontsDiv);
516 h.removeChild(baseFontsDiv); 470 h.removeChild(baseFontsDiv);
517   471  
518 keys.push({key: "js_fonts", value: available}); 472 keys.push({key: "js_fonts", value: available});
519 done(keys); 473 done(keys);
520 }, 1); 474 }, 1);
521 }, 475 },
522 pluginsKey: function(keys) { 476 pluginsKey: function(keys) {
523 if(!this.options.excludePlugins){ 477 if(!this.options.excludePlugins){
524 if(this.isIE()){ 478 if(this.isIE()){
525 if(!this.options.excludeIEPlugins) { 479 if(!this.options.excludeIEPlugins) {
526 keys.push({key: "ie_plugins", value: this.getIEPlugins()}); 480 keys.push({key: "ie_plugins", value: this.getIEPlugins()});
527 } 481 }
528 } else { 482 } else {
529 keys.push({key: "regular_plugins", value: this.getRegularPlugins()}); 483 keys.push({key: "regular_plugins", value: this.getRegularPlugins()});
530 } 484 }
531 } 485 }
532 return keys; 486 return keys;
533 }, 487 },
534 getRegularPlugins: function () { 488 getRegularPlugins: function () {
535 var plugins = []; 489 var plugins = [];
536 for(var i = 0, l = navigator.plugins.length; i < l; i++) { 490 for(var i = 0, l = navigator.plugins.length; i < l; i++) {
537 plugins.push(navigator.plugins[i]); 491 plugins.push(navigator.plugins[i]);
538 } 492 }
539 // sorting plugins only for those user agents, that we know randomize the plugins 493 // sorting plugins only for those user agents, that we know randomize the plugins
540 // every time we try to enumerate them 494 // every time we try to enumerate them
541 if(this.pluginsShouldBeSorted()) { 495 if(this.pluginsShouldBeSorted()) {
542 plugins = plugins.sort(function(a, b) { 496 plugins = plugins.sort(function(a, b) {
543 if(a.name > b.name){ return 1; } 497 if(a.name > b.name){ return 1; }
544 if(a.name < b.name){ return -1; } 498 if(a.name < b.name){ return -1; }
545 return 0; 499 return 0;
546 }); 500 });
547 } 501 }
548 return this.map(plugins, function (p) { 502 return this.map(plugins, function (p) {
549 var mimeTypes = this.map(p, function(mt){ 503 var mimeTypes = this.map(p, function(mt){
550 return [mt.type, mt.suffixes].join("~"); 504 return [mt.type, mt.suffixes].join("~");
551 }).join(","); 505 }).join(",");
552 return [p.name, p.description, mimeTypes].join("::"); 506 return [p.name, p.description, mimeTypes].join("::");
553 }, this); 507 }, this);
554 }, 508 },
555 getIEPlugins: function () { 509 getIEPlugins: function () {
556 var result = []; 510 var result = [];
557 if((Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(window, "ActiveXObject")) || ("ActiveXObject" in window)) { 511 if((Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(window, "ActiveXObject")) || ("ActiveXObject" in window)) {
558 var names = [ 512 var names = [
559 "AcroPDF.PDF", // Adobe PDF reader 7+ 513 "AcroPDF.PDF", // Adobe PDF reader 7+
560 "Adodb.Stream", 514 "Adodb.Stream",
561 "AgControl.AgControl", // Silverlight 515 "AgControl.AgControl", // Silverlight
562 "DevalVRXCtrl.DevalVRXCtrl.1", 516 "DevalVRXCtrl.DevalVRXCtrl.1",
563 "MacromediaFlashPaper.MacromediaFlashPaper", 517 "MacromediaFlashPaper.MacromediaFlashPaper",
564 "Msxml2.DOMDocument", 518 "Msxml2.DOMDocument",
565 "Msxml2.XMLHTTP", 519 "Msxml2.XMLHTTP",
566 "PDF.PdfCtrl", // Adobe PDF reader 6 and earlier, brrr 520 "PDF.PdfCtrl", // Adobe PDF reader 6 and earlier, brrr
567 "QuickTime.QuickTime", // QuickTime 521 "QuickTime.QuickTime", // QuickTime
568 "QuickTimeCheckObject.QuickTimeCheck.1", 522 "QuickTimeCheckObject.QuickTimeCheck.1",
569 "RealPlayer", 523 "RealPlayer",
570 "RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)", 524 "RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)",
571 "RealVideo.RealVideo(tm) ActiveX Control (32-bit)", 525 "RealVideo.RealVideo(tm) ActiveX Control (32-bit)",
572 "Scripting.Dictionary", 526 "Scripting.Dictionary",
573 "SWCtl.SWCtl", // ShockWave player 527 "SWCtl.SWCtl", // ShockWave player
574 "Shell.UIHelper", 528 "Shell.UIHelper",
575 "ShockwaveFlash.ShockwaveFlash", //flash plugin 529 "ShockwaveFlash.ShockwaveFlash", //flash plugin
576 "Skype.Detection", 530 "Skype.Detection",
577 "TDCCtl.TDCCtl", 531 "TDCCtl.TDCCtl",
578 "WMPlayer.OCX", // Windows media player 532 "WMPlayer.OCX", // Windows media player
579 "rmocx.RealPlayer G2 Control", 533 "rmocx.RealPlayer G2 Control",
580 "rmocx.RealPlayer G2 Control.1" 534 "rmocx.RealPlayer G2 Control.1"
581 ]; 535 ];
582 // starting to detect plugins in IE 536 // starting to detect plugins in IE
583 result = this.map(names, function(name) { 537 result = this.map(names, function(name) {
584 try { 538 try {
585 new ActiveXObject(name); // eslint-disable-no-new 539 new ActiveXObject(name); // eslint-disable-no-new
586 return name; 540 return name;
587 } catch(e) { 541 } catch(e) {
588 return null; 542 return null;
589 } 543 }
590 }); 544 });
591 } 545 }
592 if(navigator.plugins) { 546 if(navigator.plugins) {
593 result = result.concat(this.getRegularPlugins()); 547 result = result.concat(this.getRegularPlugins());
594 } 548 }
595 return result; 549 return result;
596 }, 550 },
597 pluginsShouldBeSorted: function () { 551 pluginsShouldBeSorted: function () {
598 var should = false; 552 var should = false;
599 for(var i = 0, l = this.options.sortPluginsFor.length; i < l; i++) { 553 for(var i = 0, l = this.options.sortPluginsFor.length; i < l; i++) {
600 var re = this.options.sortPluginsFor[i]; 554 var re = this.options.sortPluginsFor[i];
601 if(navigator.userAgent.match(re)) { 555 if(navigator.userAgent.match(re)) {
602 should = true; 556 should = true;
603 break; 557 break;
604 } 558 }
605 } 559 }
606 return should; 560 return should;
607 }, 561 },
608 touchSupportKey: function (keys) { 562 touchSupportKey: function (keys) {
609 if(!this.options.excludeTouchSupport){ 563 if(!this.options.excludeTouchSupport){
610 keys.push({key: "touch_support", value: this.getTouchSupport()}); 564 keys.push({key: "touch_support", value: this.getTouchSupport()});
611 } 565 }
612 return keys; 566 return keys;
613 }, 567 },
614 hardwareConcurrencyKey: function(keys){ 568 hardwareConcurrencyKey: function(keys){
615 if(!this.options.excludeHardwareConcurrency){ 569 if(!this.options.excludeHardwareConcurrency){
616 keys.push({key: "hardware_concurrency", value: this.getHardwareConcurrency()}); 570 keys.push({key: "hardware_concurrency", value: this.getHardwareConcurrency()});
617 } 571 }
618 return keys; 572 return keys;
619 }, 573 },
620 hasSessionStorage: function () { 574 hasSessionStorage: function () {
621 try { 575 try {
622 return !!window.sessionStorage; 576 return !!window.sessionStorage;
623 } catch(e) { 577 } catch(e) {
624 return true; // SecurityError when referencing it means it exists 578 return true; // SecurityError when referencing it means it exists
625 } 579 }
626 }, 580 },
627 // https://bugzilla.mozilla.org/show_bug.cgi?id=781447 581 // https://bugzilla.mozilla.org/show_bug.cgi?id=781447
628 hasLocalStorage: function () { 582 hasLocalStorage: function () {
629 try { 583 try {
630 return !!window.localStorage; 584 return !!window.localStorage;
631 } catch(e) { 585 } catch(e) {
632 return true; // SecurityError when referencing it means it exists 586 return true; // SecurityError when referencing it means it exists
633 } 587 }
634 }, 588 },
635 hasIndexedDB: function (){ 589 hasIndexedDB: function (){
636 try { 590 try {
637 return !!window.indexedDB; 591 return !!window.indexedDB;
638 } catch(e) { 592 } catch(e) {
639 return true; // SecurityError when referencing it means it exists 593 return true; // SecurityError when referencing it means it exists
640 } 594 }
641 }, 595 },
642 getHardwareConcurrency: function(){ 596 getHardwareConcurrency: function(){
643 if(navigator.hardwareConcurrency){ 597 if(navigator.hardwareConcurrency){
644 return navigator.hardwareConcurrency; 598 return navigator.hardwareConcurrency;
645 } 599 }
646 return "unknown"; 600 return "unknown";
647 }, 601 },
648 getNavigatorCpuClass: function () { 602 getNavigatorCpuClass: function () {
649 if(navigator.cpuClass){ 603 if(navigator.cpuClass){
650 return navigator.cpuClass; 604 return navigator.cpuClass;
651 } else { 605 } else {
652 return "unknown"; 606 return "unknown";
653 } 607 }
654 }, 608 },
655 getNavigatorPlatform: function () { 609 getNavigatorPlatform: function () {
656 if(navigator.platform) { 610 if(navigator.platform) {
657 return navigator.platform; 611 return navigator.platform;
658 } else { 612 } else {
659 return "unknown"; 613 return "unknown";
660 } 614 }
661 }, 615 },
662 getDoNotTrack: function () { 616 getDoNotTrack: function () {
663 if(navigator.doNotTrack) { 617 if(navigator.doNotTrack) {
664 return navigator.doNotTrack; 618 return navigator.doNotTrack;
665 } else if (navigator.msDoNotTrack) { 619 } else if (navigator.msDoNotTrack) {
666 return navigator.msDoNotTrack; 620 return navigator.msDoNotTrack;
667 } else if (window.doNotTrack) { 621 } else if (window.doNotTrack) {
668 return window.doNotTrack; 622 return window.doNotTrack;
669 } else { 623 } else {
670 return "unknown"; 624 return "unknown";
671 } 625 }
672 }, 626 },
673 // This is a crude and primitive touch screen detection. 627 // This is a crude and primitive touch screen detection.
674 // It's not possible to currently reliably detect the availability of a touch screen 628 // It's not possible to currently reliably detect the availability of a touch screen
675 // with a JS, without actually subscribing to a touch event. 629 // with a JS, without actually subscribing to a touch event.
676 // http://www.stucox.com/blog/you-cant-detect-a-touchscreen/ 630 // http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
677 // https://github.com/Modernizr/Modernizr/issues/548 631 // https://github.com/Modernizr/Modernizr/issues/548
678 // method returns an array of 3 values: 632 // method returns an array of 3 values:
679 // maxTouchPoints, the success or failure of creating a TouchEvent, 633 // maxTouchPoints, the success or failure of creating a TouchEvent,
680 // and the availability of the 'ontouchstart' property 634 // and the availability of the 'ontouchstart' property
681 getTouchSupport: function () { 635 getTouchSupport: function () {
682 var maxTouchPoints = 0; 636 var maxTouchPoints = 0;
683 var touchEvent = false; 637 var touchEvent = false;
684 if(typeof navigator.maxTouchPoints !== "undefined") { 638 if(typeof navigator.maxTouchPoints !== "undefined") {
685 maxTouchPoints = navigator.maxTouchPoints; 639 maxTouchPoints = navigator.maxTouchPoints;
686 } else if (typeof navigator.msMaxTouchPoints !== "undefined") { 640 } else if (typeof navigator.msMaxTouchPoints !== "undefined") {
687 maxTouchPoints = navigator.msMaxTouchPoints; 641 maxTouchPoints = navigator.msMaxTouchPoints;
688 } 642 }
689 try { 643 try {
690 document.createEvent("TouchEvent"); 644 document.createEvent("TouchEvent");
691 touchEvent = true; 645 touchEvent = true;
692 } catch(_) { /* squelch */ } 646 } catch(_) { /* squelch */ }
693 var touchStart = "ontouchstart" in window; 647 var touchStart = "ontouchstart" in window;
694 return [maxTouchPoints, touchEvent, touchStart]; 648 return [maxTouchPoints, touchEvent, touchStart];
695 }, 649 },
696 // https://www.browserleaks.com/canvas#how-does-it-work 650 // https://www.browserleaks.com/canvas#how-does-it-work
697 getCanvasFp: function() { 651 getCanvasFp: function() {
698 var result = []; 652 var result = [];
699 // Very simple now, need to make it more complex (geo shapes etc) 653 // Very simple now, need to make it more complex (geo shapes etc)
700 var canvas = document.createElement("canvas"); 654 var canvas = document.createElement("canvas");
701 canvas.width = 2000; 655 canvas.width = 2000;
702 canvas.height = 200; 656 canvas.height = 200;
703 canvas.style.display = "inline"; 657 canvas.style.display = "inline";
704 var ctx = canvas.getContext("2d"); 658 var ctx = canvas.getContext("2d");
705 // detect browser support of canvas winding 659 // detect browser support of canvas winding
706 // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ 660 // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/
707 // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js 661 // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/canvas/winding.js
708 ctx.rect(0, 0, 10, 10); 662 ctx.rect(0, 0, 10, 10);
709 ctx.rect(2, 2, 6, 6); 663 ctx.rect(2, 2, 6, 6);
710 result.push("canvas winding:" + ((ctx.isPointInPath(5, 5, "evenodd") === false) ? "yes" : "no")); 664 result.push("canvas winding:" + ((ctx.isPointInPath(5, 5, "evenodd") === false) ? "yes" : "no"));
711   665  
712 ctx.textBaseline = "alphabetic"; 666 ctx.textBaseline = "alphabetic";
713 ctx.fillStyle = "#f60"; 667 ctx.fillStyle = "#f60";
714 ctx.fillRect(125, 1, 62, 20); 668 ctx.fillRect(125, 1, 62, 20);
715 ctx.fillStyle = "#069"; 669 ctx.fillStyle = "#069";
716 // https://github.com/Valve/fingerprintjs2/issues/66 670 // https://github.com/Valve/fingerprintjs2/issues/66
717 if(this.options.dontUseFakeFontInCanvas) { 671 if(this.options.dontUseFakeFontInCanvas) {
718 ctx.font = "11pt Arial"; 672 ctx.font = "11pt Arial";
719 } else { 673 } else {
720 ctx.font = "11pt no-real-font-123"; 674 ctx.font = "11pt no-real-font-123";
721 } 675 }
722 ctx.fillText("Cwm fjordbank glyphs vext quiz, \ud83d\ude03", 2, 15); 676 ctx.fillText("Cwm fjordbank glyphs vext quiz, \ud83d\ude03", 2, 15);
723 ctx.fillStyle = "rgba(102, 204, 0, 0.2)"; 677 ctx.fillStyle = "rgba(102, 204, 0, 0.2)";
724 ctx.font = "18pt Arial"; 678 ctx.font = "18pt Arial";
725 ctx.fillText("Cwm fjordbank glyphs vext quiz, \ud83d\ude03", 4, 45); 679 ctx.fillText("Cwm fjordbank glyphs vext quiz, \ud83d\ude03", 4, 45);
726   680  
727 // canvas blending 681 // canvas blending
728 // http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ 682 // http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/
729 // http://jsfiddle.net/NDYV8/16/ 683 // http://jsfiddle.net/NDYV8/16/
730 ctx.globalCompositeOperation = "multiply"; 684 ctx.globalCompositeOperation = "multiply";
731 ctx.fillStyle = "rgb(255,0,255)"; 685 ctx.fillStyle = "rgb(255,0,255)";
732 ctx.beginPath(); 686 ctx.beginPath();
733 ctx.arc(50, 50, 50, 0, Math.PI * 2, true); 687 ctx.arc(50, 50, 50, 0, Math.PI * 2, true);
734 ctx.closePath(); 688 ctx.closePath();
735 ctx.fill(); 689 ctx.fill();
736 ctx.fillStyle = "rgb(0,255,255)"; 690 ctx.fillStyle = "rgb(0,255,255)";
737 ctx.beginPath(); 691 ctx.beginPath();
738 ctx.arc(100, 50, 50, 0, Math.PI * 2, true); 692 ctx.arc(100, 50, 50, 0, Math.PI * 2, true);
739 ctx.closePath(); 693 ctx.closePath();
740 ctx.fill(); 694 ctx.fill();
741 ctx.fillStyle = "rgb(255,255,0)"; 695 ctx.fillStyle = "rgb(255,255,0)";
742 ctx.beginPath(); 696 ctx.beginPath();
743 ctx.arc(75, 100, 50, 0, Math.PI * 2, true); 697 ctx.arc(75, 100, 50, 0, Math.PI * 2, true);
744 ctx.closePath(); 698 ctx.closePath();
745 ctx.fill(); 699 ctx.fill();
746 ctx.fillStyle = "rgb(255,0,255)"; 700 ctx.fillStyle = "rgb(255,0,255)";
747 // canvas winding 701 // canvas winding
748 // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/ 702 // http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/
749 // http://jsfiddle.net/NDYV8/19/ 703 // http://jsfiddle.net/NDYV8/19/
750 ctx.arc(75, 75, 75, 0, Math.PI * 2, true); 704 ctx.arc(75, 75, 75, 0, Math.PI * 2, true);
751 ctx.arc(75, 75, 25, 0, Math.PI * 2, true); 705 ctx.arc(75, 75, 25, 0, Math.PI * 2, true);
752 ctx.fill("evenodd"); 706 ctx.fill("evenodd");
753   707  
754 result.push("canvas fp:" + canvas.toDataURL()); 708 result.push("canvas fp:" + canvas.toDataURL());
755 return result.join("~"); 709 return result.join("~");
756 }, 710 },
757   711  
758 getWebglFp: function() { 712 getWebglFp: function() {
759 var gl; 713 var gl;
760 var fa2s = function(fa) { 714 var fa2s = function(fa) {
761 gl.clearColor(0.0, 0.0, 0.0, 1.0); 715 gl.clearColor(0.0, 0.0, 0.0, 1.0);
762 gl.enable(gl.DEPTH_TEST); 716 gl.enable(gl.DEPTH_TEST);
763 gl.depthFunc(gl.LEQUAL); 717 gl.depthFunc(gl.LEQUAL);
764 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 718 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
765 return "[" + fa[0] + ", " + fa[1] + "]"; 719 return "[" + fa[0] + ", " + fa[1] + "]";
766 }; 720 };
767 var maxAnisotropy = function(gl) { 721 var maxAnisotropy = function(gl) {
768 var anisotropy, ext = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic"); 722 var anisotropy, ext = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic");
769 return ext ? (anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT), 0 === anisotropy && (anisotropy = 2), anisotropy) : null; 723 return ext ? (anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT), 0 === anisotropy && (anisotropy = 2), anisotropy) : null;
770 }; 724 };
771 gl = this.getWebglCanvas(); 725 gl = this.getWebglCanvas();
772 if(!gl) { return null; } 726 if(!gl) { return null; }
773 // WebGL fingerprinting is a combination of techniques, found in MaxMind antifraud script & Augur fingerprinting. 727 // WebGL fingerprinting is a combination of techniques, found in MaxMind antifraud script & Augur fingerprinting.
774 // First it draws a gradient object with shaders and convers the image to the Base64 string. 728 // First it draws a gradient object with shaders and convers the image to the Base64 string.
775 // Then it enumerates all WebGL extensions & capabilities and appends them to the Base64 string, resulting in a huge WebGL string, potentially very unique on each device 729 // Then it enumerates all WebGL extensions & capabilities and appends them to the Base64 string, resulting in a huge WebGL string, potentially very unique on each device
776 // Since iOS supports webgl starting from version 8.1 and 8.1 runs on several graphics chips, the results may be different across ios devices, but we need to verify it. 730 // Since iOS supports webgl starting from version 8.1 and 8.1 runs on several graphics chips, the results may be different across ios devices, but we need to verify it.
777 var result = []; 731 var result = [];
778 var vShaderTemplate = "attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}"; 732 var vShaderTemplate = "attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}";
779 var fShaderTemplate = "precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}"; 733 var fShaderTemplate = "precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}";
780 var vertexPosBuffer = gl.createBuffer(); 734 var vertexPosBuffer = gl.createBuffer();
781 gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer); 735 gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
782 var vertices = new Float32Array([-.2, -.9, 0, .4, -.26, 0, 0, .732134444, 0]); 736 var vertices = new Float32Array([-.2, -.9, 0, .4, -.26, 0, 0, .732134444, 0]);
783 gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); 737 gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
784 vertexPosBuffer.itemSize = 3; 738 vertexPosBuffer.itemSize = 3;
785 vertexPosBuffer.numItems = 3; 739 vertexPosBuffer.numItems = 3;
786 var program = gl.createProgram(), vshader = gl.createShader(gl.VERTEX_SHADER); 740 var program = gl.createProgram(), vshader = gl.createShader(gl.VERTEX_SHADER);
787 gl.shaderSource(vshader, vShaderTemplate); 741 gl.shaderSource(vshader, vShaderTemplate);
788 gl.compileShader(vshader); 742 gl.compileShader(vshader);
789 var fshader = gl.createShader(gl.FRAGMENT_SHADER); 743 var fshader = gl.createShader(gl.FRAGMENT_SHADER);
790 gl.shaderSource(fshader, fShaderTemplate); 744 gl.shaderSource(fshader, fShaderTemplate);
791 gl.compileShader(fshader); 745 gl.compileShader(fshader);
792 gl.attachShader(program, vshader); 746 gl.attachShader(program, vshader);
793 gl.attachShader(program, fshader); 747 gl.attachShader(program, fshader);
794 gl.linkProgram(program); 748 gl.linkProgram(program);
795 gl.useProgram(program); 749 gl.useProgram(program);
796 program.vertexPosAttrib = gl.getAttribLocation(program, "attrVertex"); 750 program.vertexPosAttrib = gl.getAttribLocation(program, "attrVertex");
797 program.offsetUniform = gl.getUniformLocation(program, "uniformOffset"); 751 program.offsetUniform = gl.getUniformLocation(program, "uniformOffset");
798 gl.enableVertexAttribArray(program.vertexPosArray); 752 gl.enableVertexAttribArray(program.vertexPosArray);
799 gl.vertexAttribPointer(program.vertexPosAttrib, vertexPosBuffer.itemSize, gl.FLOAT, !1, 0, 0); 753 gl.vertexAttribPointer(program.vertexPosAttrib, vertexPosBuffer.itemSize, gl.FLOAT, !1, 0, 0);
800 gl.uniform2f(program.offsetUniform, 1, 1); 754 gl.uniform2f(program.offsetUniform, 1, 1);
801 gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexPosBuffer.numItems); 755 gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexPosBuffer.numItems);
802 if (gl.canvas != null) { result.push(gl.canvas.toDataURL()); } 756 if (gl.canvas != null) { result.push(gl.canvas.toDataURL()); }
803 result.push("extensions:" + gl.getSupportedExtensions().join(";")); 757 result.push("extensions:" + gl.getSupportedExtensions().join(";"));
804 result.push("webgl aliased line width range:" + fa2s(gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE))); 758 result.push("webgl aliased line width range:" + fa2s(gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE)));
805 result.push("webgl aliased point size range:" + fa2s(gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE))); 759 result.push("webgl aliased point size range:" + fa2s(gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)));
806 result.push("webgl alpha bits:" + gl.getParameter(gl.ALPHA_BITS)); 760 result.push("webgl alpha bits:" + gl.getParameter(gl.ALPHA_BITS));
807 result.push("webgl antialiasing:" + (gl.getContextAttributes().antialias ? "yes" : "no")); 761 result.push("webgl antialiasing:" + (gl.getContextAttributes().antialias ? "yes" : "no"));
808 result.push("webgl blue bits:" + gl.getParameter(gl.BLUE_BITS)); 762 result.push("webgl blue bits:" + gl.getParameter(gl.BLUE_BITS));
809 result.push("webgl depth bits:" + gl.getParameter(gl.DEPTH_BITS)); 763 result.push("webgl depth bits:" + gl.getParameter(gl.DEPTH_BITS));
810 result.push("webgl green bits:" + gl.getParameter(gl.GREEN_BITS)); 764 result.push("webgl green bits:" + gl.getParameter(gl.GREEN_BITS));
811 result.push("webgl max anisotropy:" + maxAnisotropy(gl)); 765 result.push("webgl max anisotropy:" + maxAnisotropy(gl));
812 result.push("webgl max combined texture image units:" + gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS)); 766 result.push("webgl max combined texture image units:" + gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS));
813 result.push("webgl max cube map texture size:" + gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE)); 767 result.push("webgl max cube map texture size:" + gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE));
814 result.push("webgl max fragment uniform vectors:" + gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS)); 768 result.push("webgl max fragment uniform vectors:" + gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS));
815 result.push("webgl max render buffer size:" + gl.getParameter(gl.MAX_RENDERBUFFER_SIZE)); 769 result.push("webgl max render buffer size:" + gl.getParameter(gl.MAX_RENDERBUFFER_SIZE));
816 result.push("webgl max texture image units:" + gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)); 770 result.push("webgl max texture image units:" + gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));
817 result.push("webgl max texture size:" + gl.getParameter(gl.MAX_TEXTURE_SIZE)); 771 result.push("webgl max texture size:" + gl.getParameter(gl.MAX_TEXTURE_SIZE));
818 result.push("webgl max varying vectors:" + gl.getParameter(gl.MAX_VARYING_VECTORS)); 772 result.push("webgl max varying vectors:" + gl.getParameter(gl.MAX_VARYING_VECTORS));
819 result.push("webgl max vertex attribs:" + gl.getParameter(gl.MAX_VERTEX_ATTRIBS)); 773 result.push("webgl max vertex attribs:" + gl.getParameter(gl.MAX_VERTEX_ATTRIBS));
820 result.push("webgl max vertex texture image units:" + gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS)); 774 result.push("webgl max vertex texture image units:" + gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS));
821 result.push("webgl max vertex uniform vectors:" + gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS)); 775 result.push("webgl max vertex uniform vectors:" + gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS));
822 result.push("webgl max viewport dims:" + fa2s(gl.getParameter(gl.MAX_VIEWPORT_DIMS))); 776 result.push("webgl max viewport dims:" + fa2s(gl.getParameter(gl.MAX_VIEWPORT_DIMS)));
823 result.push("webgl red bits:" + gl.getParameter(gl.RED_BITS)); 777 result.push("webgl red bits:" + gl.getParameter(gl.RED_BITS));
824 result.push("webgl renderer:" + gl.getParameter(gl.RENDERER)); 778 result.push("webgl renderer:" + gl.getParameter(gl.RENDERER));
825 result.push("webgl shading language version:" + gl.getParameter(gl.SHADING_LANGUAGE_VERSION)); 779 result.push("webgl shading language version:" + gl.getParameter(gl.SHADING_LANGUAGE_VERSION));
826 result.push("webgl stencil bits:" + gl.getParameter(gl.STENCIL_BITS)); 780 result.push("webgl stencil bits:" + gl.getParameter(gl.STENCIL_BITS));
827 result.push("webgl vendor:" + gl.getParameter(gl.VENDOR)); 781 result.push("webgl vendor:" + gl.getParameter(gl.VENDOR));
828 result.push("webgl version:" + gl.getParameter(gl.VERSION)); 782 result.push("webgl version:" + gl.getParameter(gl.VERSION));
829   783  
830 try { 784 try {
831 // Add the unmasked vendor and unmasked renderer if the debug_renderer_info extension is available 785 // Add the unmasked vendor and unmasked renderer if the debug_renderer_info extension is available
832 var extensionDebugRendererInfo = gl.getExtension("WEBGL_debug_renderer_info"); 786 var extensionDebugRendererInfo = gl.getExtension("WEBGL_debug_renderer_info");
833 if (!extensionDebugRendererInfo) { 787 if (extensionDebugRendererInfo) {
834 if (typeof NODEBUG === "undefined") { -  
835 this.log("WebGL fingerprinting is incomplete, because your browser does not have the extension WEBGL_debug_renderer_info"); -  
836 } -  
837 } else { -  
838 result.push("webgl unmasked vendor:" + gl.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL)); 788 result.push("webgl unmasked vendor:" + gl.getParameter(extensionDebugRendererInfo.UNMASKED_VENDOR_WEBGL));
839 result.push("webgl unmasked renderer:" + gl.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL)); 789 result.push("webgl unmasked renderer:" + gl.getParameter(extensionDebugRendererInfo.UNMASKED_RENDERER_WEBGL));
840 } 790 }
841 } catch(e) { /* squelch */ } 791 } catch(e) { /* squelch */ }
842   792  
843 if (!gl.getShaderPrecisionFormat) { 793 if (!gl.getShaderPrecisionFormat) {
844 if (typeof NODEBUG === "undefined") { -  
845 this.log("WebGL fingerprinting is incomplete, because your browser does not support getShaderPrecisionFormat"); -  
846 } -  
847 return result.join("~"); 794 return result.join("~");
848 } 795 }
849   796  
850 result.push("webgl vertex shader high float precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision); 797 result.push("webgl vertex shader high float precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision);
851 result.push("webgl vertex shader high float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT ).rangeMin); 798 result.push("webgl vertex shader high float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT ).rangeMin);
852 result.push("webgl vertex shader high float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT ).rangeMax); 799 result.push("webgl vertex shader high float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT ).rangeMax);
853 result.push("webgl vertex shader medium float precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision); 800 result.push("webgl vertex shader medium float precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision);
854 result.push("webgl vertex shader medium float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).rangeMin); 801 result.push("webgl vertex shader medium float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).rangeMin);
855 result.push("webgl vertex shader medium float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).rangeMax); 802 result.push("webgl vertex shader medium float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).rangeMax);
856 result.push("webgl vertex shader low float precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT ).precision); 803 result.push("webgl vertex shader low float precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT ).precision);
857 result.push("webgl vertex shader low float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT ).rangeMin); 804 result.push("webgl vertex shader low float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT ).rangeMin);
858 result.push("webgl vertex shader low float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT ).rangeMax); 805 result.push("webgl vertex shader low float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT ).rangeMax);
859 result.push("webgl fragment shader high float precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision); 806 result.push("webgl fragment shader high float precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision);
860 result.push("webgl fragment shader high float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).rangeMin); 807 result.push("webgl fragment shader high float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).rangeMin);
861 result.push("webgl fragment shader high float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).rangeMax); 808 result.push("webgl fragment shader high float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).rangeMax);
862 result.push("webgl fragment shader medium float precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision); 809 result.push("webgl fragment shader medium float precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision);
863 result.push("webgl fragment shader medium float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).rangeMin); 810 result.push("webgl fragment shader medium float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).rangeMin);
864 result.push("webgl fragment shader medium float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).rangeMax); 811 result.push("webgl fragment shader medium float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).rangeMax);
865 result.push("webgl fragment shader low float precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT ).precision); 812 result.push("webgl fragment shader low float precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT ).precision);
866 result.push("webgl fragment shader low float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT ).rangeMin); 813 result.push("webgl fragment shader low float precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT ).rangeMin);
867 result.push("webgl fragment shader low float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT ).rangeMax); 814 result.push("webgl fragment shader low float precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT ).rangeMax);
868 result.push("webgl vertex shader high int precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_INT ).precision); 815 result.push("webgl vertex shader high int precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_INT ).precision);
869 result.push("webgl vertex shader high int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_INT ).rangeMin); 816 result.push("webgl vertex shader high int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_INT ).rangeMin);
870 result.push("webgl vertex shader high int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_INT ).rangeMax); 817 result.push("webgl vertex shader high int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_INT ).rangeMax);
871 result.push("webgl vertex shader medium int precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_INT ).precision); 818 result.push("webgl vertex shader medium int precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_INT ).precision);
872 result.push("webgl vertex shader medium int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_INT ).rangeMin); 819 result.push("webgl vertex shader medium int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_INT ).rangeMin);
873 result.push("webgl vertex shader medium int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_INT ).rangeMax); 820 result.push("webgl vertex shader medium int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_INT ).rangeMax);
874 result.push("webgl vertex shader low int precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_INT ).precision); 821 result.push("webgl vertex shader low int precision:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_INT ).precision);
875 result.push("webgl vertex shader low int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_INT ).rangeMin); 822 result.push("webgl vertex shader low int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_INT ).rangeMin);
876 result.push("webgl vertex shader low int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_INT ).rangeMax); 823 result.push("webgl vertex shader low int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_INT ).rangeMax);
877 result.push("webgl fragment shader high int precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT ).precision); 824 result.push("webgl fragment shader high int precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT ).precision);
878 result.push("webgl fragment shader high int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT ).rangeMin); 825 result.push("webgl fragment shader high int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT ).rangeMin);
879 result.push("webgl fragment shader high int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT ).rangeMax); 826 result.push("webgl fragment shader high int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_INT ).rangeMax);
880 result.push("webgl fragment shader medium int precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT ).precision); 827 result.push("webgl fragment shader medium int precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT ).precision);
881 result.push("webgl fragment shader medium int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT ).rangeMin); 828 result.push("webgl fragment shader medium int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT ).rangeMin);
882 result.push("webgl fragment shader medium int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT ).rangeMax); 829 result.push("webgl fragment shader medium int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_INT ).rangeMax);
883 result.push("webgl fragment shader low int precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT ).precision); 830 result.push("webgl fragment shader low int precision:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT ).precision);
884 result.push("webgl fragment shader low int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT ).rangeMin); 831 result.push("webgl fragment shader low int precision rangeMin:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT ).rangeMin);
885 result.push("webgl fragment shader low int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT ).rangeMax); 832 result.push("webgl fragment shader low int precision rangeMax:" + gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_INT ).rangeMax);
886 return result.join("~"); 833 return result.join("~");
887 }, 834 },
888 getAdBlock: function(){ 835 getAdBlock: function(){
889 var ads = document.createElement("div"); 836 var ads = document.createElement("div");
890 ads.innerHTML = "&nbsp;"; 837 ads.innerHTML = "&nbsp;";
891 ads.className = "adsbox"; 838 ads.className = "adsbox";
892 var result = false; 839 var result = false;
893 try { 840 try {
894 // body may not exist, that's why we need try/catch 841 // body may not exist, that's why we need try/catch
895 document.body.appendChild(ads); 842 document.body.appendChild(ads);
896 result = document.getElementsByClassName("adsbox")[0].offsetHeight === 0; 843 result = document.getElementsByClassName("adsbox")[0].offsetHeight === 0;
897 document.body.removeChild(ads); 844 document.body.removeChild(ads);
898 } catch (e) { 845 } catch (e) {
899 result = false; 846 result = false;
900 } 847 }
901 return result; 848 return result;
902 }, 849 },
903 getHasLiedLanguages: function(){ 850 getHasLiedLanguages: function(){
904 //We check if navigator.language is equal to the first language of navigator.languages 851 //We check if navigator.language is equal to the first language of navigator.languages
905 if(typeof navigator.languages !== "undefined"){ 852 if(typeof navigator.languages !== "undefined"){
906 try { 853 try {
907 var firstLanguages = navigator.languages[0].substr(0, 2); 854 var firstLanguages = navigator.languages[0].substr(0, 2);
908 if(firstLanguages !== navigator.language.substr(0, 2)){ 855 if(firstLanguages !== navigator.language.substr(0, 2)){
909 return true; 856 return true;
910 } 857 }
911 } catch(err) { 858 } catch(err) {
912 return true; 859 return true;
913 } 860 }
914 } 861 }
915 return false; 862 return false;
916 }, 863 },
917 getHasLiedResolution: function(){ 864 getHasLiedResolution: function(){
918 if(screen.width < screen.availWidth){ 865 if(screen.width < screen.availWidth){
919 return true; 866 return true;
920 } 867 }
921 if(screen.height < screen.availHeight){ 868 if(screen.height < screen.availHeight){
922 return true; 869 return true;
923 } 870 }
924 return false; 871 return false;
925 }, 872 },
926 getHasLiedOs: function(){ 873 getHasLiedOs: function(){
927 var userAgent = navigator.userAgent.toLowerCase(); 874 var userAgent = navigator.userAgent.toLowerCase();
928 var oscpu = navigator.oscpu; 875 var oscpu = navigator.oscpu;
929 var platform = navigator.platform.toLowerCase(); 876 var platform = navigator.platform.toLowerCase();
930 var os; 877 var os;
931 //We extract the OS from the user agent (respect the order of the if else if statement) 878 //We extract the OS from the user agent (respect the order of the if else if statement)
932 if(userAgent.indexOf("windows phone") >= 0){ 879 if(userAgent.indexOf("windows phone") >= 0){
933 os = "Windows Phone"; 880 os = "Windows Phone";
934 } else if(userAgent.indexOf("win") >= 0){ 881 } else if(userAgent.indexOf("win") >= 0){
935 os = "Windows"; 882 os = "Windows";
936 } else if(userAgent.indexOf("android") >= 0){ 883 } else if(userAgent.indexOf("android") >= 0){
937 os = "Android"; 884 os = "Android";
938 } else if(userAgent.indexOf("linux") >= 0){ 885 } else if(userAgent.indexOf("linux") >= 0){
939 os = "Linux"; 886 os = "Linux";
940 } else if(userAgent.indexOf("iphone") >= 0 || userAgent.indexOf("ipad") >= 0 ){ 887 } else if(userAgent.indexOf("iphone") >= 0 || userAgent.indexOf("ipad") >= 0 ){
941 os = "iOS"; 888 os = "iOS";
942 } else if(userAgent.indexOf("mac") >= 0){ 889 } else if(userAgent.indexOf("mac") >= 0){
943 os = "Mac"; 890 os = "Mac";
944 } else{ 891 } else{
945 os = "Other"; 892 os = "Other";
946 } 893 }
947 // We detect if the person uses a mobile device 894 // We detect if the person uses a mobile device
948 var mobileDevice; 895 var mobileDevice;
949 if (("ontouchstart" in window) || 896 if (("ontouchstart" in window) ||
950 (navigator.maxTouchPoints > 0) || 897 (navigator.maxTouchPoints > 0) ||
951 (navigator.msMaxTouchPoints > 0)) { 898 (navigator.msMaxTouchPoints > 0)) {
952 mobileDevice = true; 899 mobileDevice = true;
953 } else{ 900 } else{
954 mobileDevice = false; 901 mobileDevice = false;
955 } 902 }
956   903  
957 if(mobileDevice && os !== "Windows Phone" && os !== "Android" && os !== "iOS" && os !== "Other"){ 904 if(mobileDevice && os !== "Windows Phone" && os !== "Android" && os !== "iOS" && os !== "Other"){
958 return true; 905 return true;
959 } 906 }
960   907  
961 // We compare oscpu with the OS extracted from the UA 908 // We compare oscpu with the OS extracted from the UA
962 if(typeof oscpu !== "undefined"){ 909 if(typeof oscpu !== "undefined"){
963 oscpu = oscpu.toLowerCase(); 910 oscpu = oscpu.toLowerCase();
964 if(oscpu.indexOf("win") >= 0 && os !== "Windows" && os !== "Windows Phone"){ 911 if(oscpu.indexOf("win") >= 0 && os !== "Windows" && os !== "Windows Phone"){
965 return true; 912 return true;
966 } else if(oscpu.indexOf("linux") >= 0 && os !== "Linux" && os !== "Android"){ 913 } else if(oscpu.indexOf("linux") >= 0 && os !== "Linux" && os !== "Android"){
967 return true; 914 return true;
968 } else if(oscpu.indexOf("mac") >= 0 && os !== "Mac" && os !== "iOS"){ 915 } else if(oscpu.indexOf("mac") >= 0 && os !== "Mac" && os !== "iOS"){
969 return true; 916 return true;
970 } else if(oscpu.indexOf("win") === 0 && oscpu.indexOf("linux") === 0 && oscpu.indexOf("mac") >= 0 && os !== "other"){ 917 } else if(oscpu.indexOf("win") === 0 && oscpu.indexOf("linux") === 0 && oscpu.indexOf("mac") >= 0 && os !== "other"){
971 return true; 918 return true;
972 } 919 }
973 } 920 }
974   921  
975 //We compare platform with the OS extracted from the UA 922 //We compare platform with the OS extracted from the UA
976 if(platform.indexOf("win") >= 0 && os !== "Windows" && os !== "Windows Phone"){ 923 if(platform.indexOf("win") >= 0 && os !== "Windows" && os !== "Windows Phone"){
977 return true; 924 return true;
978 } else if((platform.indexOf("linux") >= 0 || platform.indexOf("android") >= 0 || platform.indexOf("pike") >= 0) && os !== "Linux" && os !== "Android"){ 925 } else if((platform.indexOf("linux") >= 0 || platform.indexOf("android") >= 0 || platform.indexOf("pike") >= 0) && os !== "Linux" && os !== "Android"){
979 return true; 926 return true;
980 } else if((platform.indexOf("mac") >= 0 || platform.indexOf("ipad") >= 0 || platform.indexOf("ipod") >= 0 || platform.indexOf("iphone") >= 0) && os !== "Mac" && os !== "iOS"){ 927 } else if((platform.indexOf("mac") >= 0 || platform.indexOf("ipad") >= 0 || platform.indexOf("ipod") >= 0 || platform.indexOf("iphone") >= 0) && os !== "Mac" && os !== "iOS"){
981 return true; 928 return true;
982 } else if(platform.indexOf("win") === 0 && platform.indexOf("linux") === 0 && platform.indexOf("mac") >= 0 && os !== "other"){ 929 } else if(platform.indexOf("win") === 0 && platform.indexOf("linux") === 0 && platform.indexOf("mac") >= 0 && os !== "other"){
983 return true; 930 return true;
984 } 931 }
985   932  
986 if(typeof navigator.plugins === "undefined" && os !== "Windows" && os !== "Windows Phone"){ 933 if(typeof navigator.plugins === "undefined" && os !== "Windows" && os !== "Windows Phone"){
987 //We are are in the case where the person uses ie, therefore we can infer that it's windows 934 //We are are in the case where the person uses ie, therefore we can infer that it's windows
988 return true; 935 return true;
989 } 936 }
990   937  
991 return false; 938 return false;
992 }, 939 },
993 getHasLiedBrowser: function () { 940 getHasLiedBrowser: function () {
994 var userAgent = navigator.userAgent.toLowerCase(); 941 var userAgent = navigator.userAgent.toLowerCase();
995 var productSub = navigator.productSub; 942 var productSub = navigator.productSub;
996   943  
997 //we extract the browser from the user agent (respect the order of the tests) 944 //we extract the browser from the user agent (respect the order of the tests)
998 var browser; 945 var browser;
999 if(userAgent.indexOf("firefox") >= 0){ 946 if(userAgent.indexOf("firefox") >= 0){
1000 browser = "Firefox"; 947 browser = "Firefox";
1001 } else if(userAgent.indexOf("opera") >= 0 || userAgent.indexOf("opr") >= 0){ 948 } else if(userAgent.indexOf("opera") >= 0 || userAgent.indexOf("opr") >= 0){
1002 browser = "Opera"; 949 browser = "Opera";
1003 } else if(userAgent.indexOf("chrome") >= 0){ 950 } else if(userAgent.indexOf("chrome") >= 0){
1004 browser = "Chrome"; 951 browser = "Chrome";
1005 } else if(userAgent.indexOf("safari") >= 0){ 952 } else if(userAgent.indexOf("safari") >= 0){
1006 browser = "Safari"; 953 browser = "Safari";
1007 } else if(userAgent.indexOf("trident") >= 0){ 954 } else if(userAgent.indexOf("trident") >= 0){
1008 browser = "Internet Explorer"; 955 browser = "Internet Explorer";
1009 } else{ 956 } else{
1010 browser = "Other"; 957 browser = "Other";
1011 } 958 }
1012   959  
1013 if((browser === "Chrome" || browser === "Safari" || browser === "Opera") && productSub !== "20030107"){ 960 if((browser === "Chrome" || browser === "Safari" || browser === "Opera") && productSub !== "20030107"){
1014 return true; 961 return true;
1015 } 962 }
1016   963  
1017 var tempRes = eval.toString().length; 964 var tempRes = eval.toString().length;
1018 if(tempRes === 37 && browser !== "Safari" && browser !== "Firefox" && browser !== "Other"){ 965 if(tempRes === 37 && browser !== "Safari" && browser !== "Firefox" && browser !== "Other"){
1019 return true; 966 return true;
1020 } else if(tempRes === 39 && browser !== "Internet Explorer" && browser !== "Other"){ 967 } else if(tempRes === 39 && browser !== "Internet Explorer" && browser !== "Other"){
1021 return true; 968 return true;
1022 } else if(tempRes === 33 && browser !== "Chrome" && browser !== "Opera" && browser !== "Other"){ 969 } else if(tempRes === 33 && browser !== "Chrome" && browser !== "Opera" && browser !== "Other"){
1023 return true; 970 return true;
1024 } 971 }
1025   972  
1026 //We create an error to see how it is handled 973 //We create an error to see how it is handled
1027 var errFirefox; 974 var errFirefox;
1028 try { 975 try {
1029 throw "a"; 976 throw "a";
1030 } catch(err){ 977 } catch(err){
1031 try{ 978 try{
1032 err.toSource(); 979 err.toSource();
1033 errFirefox = true; 980 errFirefox = true;
1034 } catch(errOfErr){ 981 } catch(errOfErr){
1035 errFirefox = false; 982 errFirefox = false;
1036 } 983 }
1037 } 984 }
1038 if(errFirefox && browser !== "Firefox" && browser !== "Other"){ 985 if(errFirefox && browser !== "Firefox" && browser !== "Other"){
1039 return true; 986 return true;
1040 } 987 }
1041 return false; 988 return false;
1042 }, 989 },
1043 isCanvasSupported: function () { 990 isCanvasSupported: function () {
1044 var elem = document.createElement("canvas"); 991 var elem = document.createElement("canvas");
1045 return !!(elem.getContext && elem.getContext("2d")); 992 return !!(elem.getContext && elem.getContext("2d"));
1046 }, 993 },
1047 isWebGlSupported: function() { 994 isWebGlSupported: function() {
1048 // code taken from Modernizr 995 // code taken from Modernizr
1049 if (!this.isCanvasSupported()) { 996 if (!this.isCanvasSupported()) {
1050 return false; 997 return false;
1051 } 998 }
1052   999  
1053 var canvas = document.createElement("canvas"), 1000 var canvas = document.createElement("canvas"),
1054 glContext; 1001 glContext;
1055   1002  
1056 try { 1003 try {
1057 glContext = canvas.getContext && (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")); 1004 glContext = canvas.getContext && (canvas.getContext("webgl") || canvas.getContext("experimental-webgl"));
1058 } catch(e) { 1005 } catch(e) {
1059 glContext = false; 1006 glContext = false;
1060 } 1007 }
1061   1008  
1062 return !!window.WebGLRenderingContext && !!glContext; 1009 return !!window.WebGLRenderingContext && !!glContext;
1063 }, 1010 },
1064 isIE: function () { 1011 isIE: function () {
1065 if(navigator.appName === "Microsoft Internet Explorer") { 1012 if(navigator.appName === "Microsoft Internet Explorer") {
1066 return true; 1013 return true;
1067 } else if(navigator.appName === "Netscape" && /Trident/.test(navigator.userAgent)) { // IE 11 1014 } else if(navigator.appName === "Netscape" && /Trident/.test(navigator.userAgent)) { // IE 11
1068 return true; 1015 return true;
1069 } 1016 }
1070 return false; 1017 return false;
1071 }, 1018 },
1072 hasSwfObjectLoaded: function(){ 1019 hasSwfObjectLoaded: function(){
1073 return typeof window.swfobject !== "undefined"; 1020 return typeof window.swfobject !== "undefined";
1074 }, 1021 },
1075 hasMinFlashInstalled: function () { 1022 hasMinFlashInstalled: function () {
1076 return swfobject.hasFlashPlayerVersion("9.0.0"); 1023 return swfobject.hasFlashPlayerVersion("9.0.0");
1077 }, 1024 },
1078 addFlashDivNode: function() { 1025 addFlashDivNode: function() {
1079 var node = document.createElement("div"); 1026 var node = document.createElement("div");
1080 node.setAttribute("id", this.options.swfContainerId); 1027 node.setAttribute("id", this.options.swfContainerId);
1081 document.body.appendChild(node); 1028 document.body.appendChild(node);
1082 }, 1029 },
1083 loadSwfAndDetectFonts: function(done) { 1030 loadSwfAndDetectFonts: function(done) {
1084 var hiddenCallback = "___fp_swf_loaded"; 1031 var hiddenCallback = "___fp_swf_loaded";
1085 window[hiddenCallback] = function(fonts) { 1032 window[hiddenCallback] = function(fonts) {
1086 done(fonts); 1033 done(fonts);
1087 }; 1034 };
1088 var id = this.options.swfContainerId; 1035 var id = this.options.swfContainerId;
1089 this.addFlashDivNode(); 1036 this.addFlashDivNode();
1090 var flashvars = { onReady: hiddenCallback}; 1037 var flashvars = { onReady: hiddenCallback};
1091 var flashparams = { allowScriptAccess: "always", menu: "false" }; 1038 var flashparams = { allowScriptAccess: "always", menu: "false" };
1092 swfobject.embedSWF(this.options.swfPath, id, "1", "1", "9.0.0", false, flashvars, flashparams, {}); 1039 swfobject.embedSWF(this.options.swfPath, id, "1", "1", "9.0.0", false, flashvars, flashparams, {});
1093 }, 1040 },
1094 getWebglCanvas: function() { 1041 getWebglCanvas: function() {
1095 var canvas = document.createElement("canvas"); 1042 var canvas = document.createElement("canvas");
1096 var gl = null; 1043 var gl = null;
1097 try { 1044 try {
1098 gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); 1045 gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
1099 } catch(e) { /* squelch */ } 1046 } catch(e) { /* squelch */ }
1100 if (!gl) { gl = null; } 1047 if (!gl) { gl = null; }
1101 return gl; 1048 return gl;
1102 }, 1049 },
1103 each: function (obj, iterator, context) { 1050 each: function (obj, iterator, context) {
1104 if (obj === null) { 1051 if (obj === null) {
1105 return; 1052 return;
1106 } 1053 }
1107 if (this.nativeForEach && obj.forEach === this.nativeForEach) { 1054 if (this.nativeForEach && obj.forEach === this.nativeForEach) {
1108 obj.forEach(iterator, context); 1055 obj.forEach(iterator, context);
1109 } else if (obj.length === +obj.length) { 1056 } else if (obj.length === +obj.length) {
1110 for (var i = 0, l = obj.length; i < l; i++) { 1057 for (var i = 0, l = obj.length; i < l; i++) {
1111 if (iterator.call(context, obj[i], i, obj) === {}) { return; } 1058 if (iterator.call(context, obj[i], i, obj) === {}) { return; }
1112 } 1059 }
1113 } else { 1060 } else {
1114 for (var key in obj) { 1061 for (var key in obj) {
1115 if (obj.hasOwnProperty(key)) { 1062 if (obj.hasOwnProperty(key)) {
1116 if (iterator.call(context, obj[key], key, obj) === {}) { return; } 1063 if (iterator.call(context, obj[key], key, obj) === {}) { return; }
1117 } 1064 }
1118 } 1065 }
1119 } 1066 }
1120 }, 1067 },
1121   1068  
1122 map: function(obj, iterator, context) { 1069 map: function(obj, iterator, context) {
1123 var results = []; 1070 var results = [];
1124 // Not using strict equality so that this acts as a 1071 // Not using strict equality so that this acts as a
1125 // shortcut to checking for `null` and `undefined`. 1072 // shortcut to checking for `null` and `undefined`.
1126 if (obj == null) { return results; } 1073 if (obj == null) { return results; }
1127 if (this.nativeMap && obj.map === this.nativeMap) { return obj.map(iterator, context); } 1074 if (this.nativeMap && obj.map === this.nativeMap) { return obj.map(iterator, context); }
1128 this.each(obj, function(value, index, list) { 1075 this.each(obj, function(value, index, list) {
1129 results[results.length] = iterator.call(context, value, index, list); 1076 results[results.length] = iterator.call(context, value, index, list);
1130 }); 1077 });
1131 return results; 1078 return results;
1132 }, 1079 },
1133   1080  
1134 /// MurmurHash3 related functions 1081 /// MurmurHash3 related functions
1135   1082  
1136 // 1083 //
1137 // Given two 64bit ints (as an array of two 32bit ints) returns the two 1084 // Given two 64bit ints (as an array of two 32bit ints) returns the two
1138 // added together as a 64bit int (as an array of two 32bit ints). 1085 // added together as a 64bit int (as an array of two 32bit ints).
1139 // 1086 //
1140 x64Add: function(m, n) { 1087 x64Add: function(m, n) {
1141 m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; 1088 m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff];
1142 n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; 1089 n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff];
1143 var o = [0, 0, 0, 0]; 1090 var o = [0, 0, 0, 0];
1144 o[3] += m[3] + n[3]; 1091 o[3] += m[3] + n[3];
1145 o[2] += o[3] >>> 16; 1092 o[2] += o[3] >>> 16;
1146 o[3] &= 0xffff; 1093 o[3] &= 0xffff;
1147 o[2] += m[2] + n[2]; 1094 o[2] += m[2] + n[2];
1148 o[1] += o[2] >>> 16; 1095 o[1] += o[2] >>> 16;
1149 o[2] &= 0xffff; 1096 o[2] &= 0xffff;
1150 o[1] += m[1] + n[1]; 1097 o[1] += m[1] + n[1];
1151 o[0] += o[1] >>> 16; 1098 o[0] += o[1] >>> 16;
1152 o[1] &= 0xffff; 1099 o[1] &= 0xffff;
1153 o[0] += m[0] + n[0]; 1100 o[0] += m[0] + n[0];
1154 o[0] &= 0xffff; 1101 o[0] &= 0xffff;
1155 return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; 1102 return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]];
1156 }, 1103 },
1157   1104  
1158 // 1105 //
1159 // Given two 64bit ints (as an array of two 32bit ints) returns the two 1106 // Given two 64bit ints (as an array of two 32bit ints) returns the two
1160 // multiplied together as a 64bit int (as an array of two 32bit ints). 1107 // multiplied together as a 64bit int (as an array of two 32bit ints).
1161 // 1108 //
1162 x64Multiply: function(m, n) { 1109 x64Multiply: function(m, n) {
1163 m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff]; 1110 m = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff];
1164 n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff]; 1111 n = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff];
1165 var o = [0, 0, 0, 0]; 1112 var o = [0, 0, 0, 0];
1166 o[3] += m[3] * n[3]; 1113 o[3] += m[3] * n[3];
1167 o[2] += o[3] >>> 16; 1114 o[2] += o[3] >>> 16;
1168 o[3] &= 0xffff; 1115 o[3] &= 0xffff;
1169 o[2] += m[2] * n[3]; 1116 o[2] += m[2] * n[3];
1170 o[1] += o[2] >>> 16; 1117 o[1] += o[2] >>> 16;
1171 o[2] &= 0xffff; 1118 o[2] &= 0xffff;
1172 o[2] += m[3] * n[2]; 1119 o[2] += m[3] * n[2];
1173 o[1] += o[2] >>> 16; 1120 o[1] += o[2] >>> 16;
1174 o[2] &= 0xffff; 1121 o[2] &= 0xffff;
1175 o[1] += m[1] * n[3]; 1122 o[1] += m[1] * n[3];
1176 o[0] += o[1] >>> 16; 1123 o[0] += o[1] >>> 16;
1177 o[1] &= 0xffff; 1124 o[1] &= 0xffff;
1178 o[1] += m[2] * n[2]; 1125 o[1] += m[2] * n[2];
1179 o[0] += o[1] >>> 16; 1126 o[0] += o[1] >>> 16;
1180 o[1] &= 0xffff; 1127 o[1] &= 0xffff;
1181 o[1] += m[3] * n[1]; 1128 o[1] += m[3] * n[1];
1182 o[0] += o[1] >>> 16; 1129 o[0] += o[1] >>> 16;
1183 o[1] &= 0xffff; 1130 o[1] &= 0xffff;
1184 o[0] += (m[0] * n[3]) + (m[1] * n[2]) + (m[2] * n[1]) + (m[3] * n[0]); 1131 o[0] += (m[0] * n[3]) + (m[1] * n[2]) + (m[2] * n[1]) + (m[3] * n[0]);
1185 o[0] &= 0xffff; 1132 o[0] &= 0xffff;
1186 return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]]; 1133 return [(o[0] << 16) | o[1], (o[2] << 16) | o[3]];
1187 }, 1134 },
1188 // 1135 //
1189 // Given a 64bit int (as an array of two 32bit ints) and an int 1136 // Given a 64bit int (as an array of two 32bit ints) and an int
1190 // representing a number of bit positions, returns the 64bit int (as an 1137 // representing a number of bit positions, returns the 64bit int (as an
1191 // array of two 32bit ints) rotated left by that number of positions. 1138 // array of two 32bit ints) rotated left by that number of positions.
1192 // 1139 //
1193 x64Rotl: function(m, n) { 1140 x64Rotl: function(m, n) {
1194 n %= 64; 1141 n %= 64;
1195 if (n === 32) { 1142 if (n === 32) {
1196 return [m[1], m[0]]; 1143 return [m[1], m[0]];
1197 } 1144 }
1198 else if (n < 32) { 1145 else if (n < 32) {
1199 return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))]; 1146 return [(m[0] << n) | (m[1] >>> (32 - n)), (m[1] << n) | (m[0] >>> (32 - n))];
1200 } 1147 }
1201 else { 1148 else {
1202 n -= 32; 1149 n -= 32;
1203 return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))]; 1150 return [(m[1] << n) | (m[0] >>> (32 - n)), (m[0] << n) | (m[1] >>> (32 - n))];
1204 } 1151 }
1205 }, 1152 },
1206 // 1153 //
1207 // Given a 64bit int (as an array of two 32bit ints) and an int 1154 // Given a 64bit int (as an array of two 32bit ints) and an int
1208 // representing a number of bit positions, returns the 64bit int (as an 1155 // representing a number of bit positions, returns the 64bit int (as an
1209 // array of two 32bit ints) shifted left by that number of positions. 1156 // array of two 32bit ints) shifted left by that number of positions.
1210 // 1157 //
1211 x64LeftShift: function(m, n) { 1158 x64LeftShift: function(m, n) {
1212 n %= 64; 1159 n %= 64;
1213 if (n === 0) { 1160 if (n === 0) {
1214 return m; 1161 return m;
1215 } 1162 }
1216 else if (n < 32) { 1163 else if (n < 32) {
1217 return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n]; 1164 return [(m[0] << n) | (m[1] >>> (32 - n)), m[1] << n];
1218 } 1165 }
1219 else { 1166 else {
1220 return [m[1] << (n - 32), 0]; 1167 return [m[1] << (n - 32), 0];
1221 } 1168 }
1222 }, 1169 },
1223 // 1170 //
1224 // Given two 64bit ints (as an array of two 32bit ints) returns the two 1171 // Given two 64bit ints (as an array of two 32bit ints) returns the two
1225 // xored together as a 64bit int (as an array of two 32bit ints). 1172 // xored together as a 64bit int (as an array of two 32bit ints).
1226 // 1173 //
1227 x64Xor: function(m, n) { 1174 x64Xor: function(m, n) {
1228 return [m[0] ^ n[0], m[1] ^ n[1]]; 1175 return [m[0] ^ n[0], m[1] ^ n[1]];
1229 }, 1176 },
1230 // 1177 //
1231 // Given a block, returns murmurHash3's final x64 mix of that block. 1178 // Given a block, returns murmurHash3's final x64 mix of that block.
1232 // (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the 1179 // (`[0, h[0] >>> 1]` is a 33 bit unsigned right shift. This is the
1233 // only place where we need to right shift 64bit ints.) 1180 // only place where we need to right shift 64bit ints.)
1234 // 1181 //
1235 x64Fmix: function(h) { 1182 x64Fmix: function(h) {
1236 h = this.x64Xor(h, [0, h[0] >>> 1]); 1183 h = this.x64Xor(h, [0, h[0] >>> 1]);
1237 h = this.x64Multiply(h, [0xff51afd7, 0xed558ccd]); 1184 h = this.x64Multiply(h, [0xff51afd7, 0xed558ccd]);
1238 h = this.x64Xor(h, [0, h[0] >>> 1]); 1185 h = this.x64Xor(h, [0, h[0] >>> 1]);
1239 h = this.x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]); 1186 h = this.x64Multiply(h, [0xc4ceb9fe, 0x1a85ec53]);
1240 h = this.x64Xor(h, [0, h[0] >>> 1]); 1187 h = this.x64Xor(h, [0, h[0] >>> 1]);
1241 return h; 1188 return h;
1242 }, 1189 },
1243   1190  
1244 // 1191 //
1245 // Given a string and an optional seed as an int, returns a 128 bit 1192 // Given a string and an optional seed as an int, returns a 128 bit
1246 // hash using the x64 flavor of MurmurHash3, as an unsigned hex. 1193 // hash using the x64 flavor of MurmurHash3, as an unsigned hex.
1247 // 1194 //
1248 x64hash128: function (key, seed) { 1195 x64hash128: function (key, seed) {
1249 key = key || ""; 1196 key = key || "";
1250 seed = seed || 0; 1197 seed = seed || 0;
1251 var remainder = key.length % 16; 1198 var remainder = key.length % 16;
1252 var bytes = key.length - remainder; 1199 var bytes = key.length - remainder;
1253 var h1 = [0, seed]; 1200 var h1 = [0, seed];
1254 var h2 = [0, seed]; 1201 var h2 = [0, seed];
1255 var k1 = [0, 0]; 1202 var k1 = [0, 0];
1256 var k2 = [0, 0]; 1203 var k2 = [0, 0];
1257 var c1 = [0x87c37b91, 0x114253d5]; 1204 var c1 = [0x87c37b91, 0x114253d5];
1258 var c2 = [0x4cf5ad43, 0x2745937f]; 1205 var c2 = [0x4cf5ad43, 0x2745937f];
1259 for (var i = 0; i < bytes; i = i + 16) { 1206 for (var i = 0; i < bytes; i = i + 16) {
1260 k1 = [((key.charCodeAt(i + 4) & 0xff)) | ((key.charCodeAt(i + 5) & 0xff) << 8) | ((key.charCodeAt(i + 6) & 0xff) << 16) | ((key.charCodeAt(i + 7) & 0xff) << 24), ((key.charCodeAt(i) & 0xff)) | ((key.charCodeAt(i + 1) & 0xff) << 8) | ((key.charCodeAt(i + 2) & 0xff) << 16) | ((key.charCodeAt(i + 3) & 0xff) << 24)]; 1207 k1 = [((key.charCodeAt(i + 4) & 0xff)) | ((key.charCodeAt(i + 5) & 0xff) << 8) | ((key.charCodeAt(i + 6) & 0xff) << 16) | ((key.charCodeAt(i + 7) & 0xff) << 24), ((key.charCodeAt(i) & 0xff)) | ((key.charCodeAt(i + 1) & 0xff) << 8) | ((key.charCodeAt(i + 2) & 0xff) << 16) | ((key.charCodeAt(i + 3) & 0xff) << 24)];
1261 k2 = [((key.charCodeAt(i + 12) & 0xff)) | ((key.charCodeAt(i + 13) & 0xff) << 8) | ((key.charCodeAt(i + 14) & 0xff) << 16) | ((key.charCodeAt(i + 15) & 0xff) << 24), ((key.charCodeAt(i + 8) & 0xff)) | ((key.charCodeAt(i + 9) & 0xff) << 8) | ((key.charCodeAt(i + 10) & 0xff) << 16) | ((key.charCodeAt(i + 11) & 0xff) << 24)]; 1208 k2 = [((key.charCodeAt(i + 12) & 0xff)) | ((key.charCodeAt(i + 13) & 0xff) << 8) | ((key.charCodeAt(i + 14) & 0xff) << 16) | ((key.charCodeAt(i + 15) & 0xff) << 24), ((key.charCodeAt(i + 8) & 0xff)) | ((key.charCodeAt(i + 9) & 0xff) << 8) | ((key.charCodeAt(i + 10) & 0xff) << 16) | ((key.charCodeAt(i + 11) & 0xff) << 24)];
1262 k1 = this.x64Multiply(k1, c1); 1209 k1 = this.x64Multiply(k1, c1);
1263 k1 = this.x64Rotl(k1, 31); 1210 k1 = this.x64Rotl(k1, 31);
1264 k1 = this.x64Multiply(k1, c2); 1211 k1 = this.x64Multiply(k1, c2);
1265 h1 = this.x64Xor(h1, k1); 1212 h1 = this.x64Xor(h1, k1);
1266 h1 = this.x64Rotl(h1, 27); 1213 h1 = this.x64Rotl(h1, 27);
1267 h1 = this.x64Add(h1, h2); 1214 h1 = this.x64Add(h1, h2);
1268 h1 = this.x64Add(this.x64Multiply(h1, [0, 5]), [0, 0x52dce729]); 1215 h1 = this.x64Add(this.x64Multiply(h1, [0, 5]), [0, 0x52dce729]);
1269 k2 = this.x64Multiply(k2, c2); 1216 k2 = this.x64Multiply(k2, c2);
1270 k2 = this.x64Rotl(k2, 33); 1217 k2 = this.x64Rotl(k2, 33);
1271 k2 = this.x64Multiply(k2, c1); 1218 k2 = this.x64Multiply(k2, c1);
1272 h2 = this.x64Xor(h2, k2); 1219 h2 = this.x64Xor(h2, k2);
1273 h2 = this.x64Rotl(h2, 31); 1220 h2 = this.x64Rotl(h2, 31);
1274 h2 = this.x64Add(h2, h1); 1221 h2 = this.x64Add(h2, h1);
1275 h2 = this.x64Add(this.x64Multiply(h2, [0, 5]), [0, 0x38495ab5]); 1222 h2 = this.x64Add(this.x64Multiply(h2, [0, 5]), [0, 0x38495ab5]);
1276 } 1223 }
1277 k1 = [0, 0]; 1224 k1 = [0, 0];
1278 k2 = [0, 0]; 1225 k2 = [0, 0];
1279 switch(remainder) { 1226 switch(remainder) {
1280 case 15: 1227 case 15:
1281 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 14)], 48)); 1228 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 14)], 48));
1282 case 14: 1229 case 14:
1283 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 13)], 40)); 1230 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 13)], 40));
1284 case 13: 1231 case 13:
1285 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 12)], 32)); 1232 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 12)], 32));
1286 case 12: 1233 case 12:
1287 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 11)], 24)); 1234 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 11)], 24));
1288 case 11: 1235 case 11:
1289 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 10)], 16)); 1236 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 10)], 16));
1290 case 10: 1237 case 10:
1291 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 9)], 8)); 1238 k2 = this.x64Xor(k2, this.x64LeftShift([0, key.charCodeAt(i + 9)], 8));
1292 case 9: 1239 case 9:
1293 k2 = this.x64Xor(k2, [0, key.charCodeAt(i + 8)]); 1240 k2 = this.x64Xor(k2, [0, key.charCodeAt(i + 8)]);
1294 k2 = this.x64Multiply(k2, c2); 1241 k2 = this.x64Multiply(k2, c2);
1295 k2 = this.x64Rotl(k2, 33); 1242 k2 = this.x64Rotl(k2, 33);
1296 k2 = this.x64Multiply(k2, c1); 1243 k2 = this.x64Multiply(k2, c1);
1297 h2 = this.x64Xor(h2, k2); 1244 h2 = this.x64Xor(h2, k2);
1298 case 8: 1245 case 8:
1299 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 7)], 56)); 1246 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 7)], 56));
1300 case 7: 1247 case 7:
1301 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 6)], 48)); 1248 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 6)], 48));
1302 case 6: 1249 case 6:
1303 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 5)], 40)); 1250 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 5)], 40));
1304 case 5: 1251 case 5:
1305 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 4)], 32)); 1252 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 4)], 32));
1306 case 4: 1253 case 4:
1307 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 3)], 24)); 1254 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 3)], 24));
1308 case 3: 1255 case 3:
1309 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 2)], 16)); 1256 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 2)], 16));
1310 case 2: 1257 case 2:
1311 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 1)], 8)); 1258 k1 = this.x64Xor(k1, this.x64LeftShift([0, key.charCodeAt(i + 1)], 8));
1312 case 1: 1259 case 1:
1313 k1 = this.x64Xor(k1, [0, key.charCodeAt(i)]); 1260 k1 = this.x64Xor(k1, [0, key.charCodeAt(i)]);
1314 k1 = this.x64Multiply(k1, c1); 1261 k1 = this.x64Multiply(k1, c1);
1315 k1 = this.x64Rotl(k1, 31); 1262 k1 = this.x64Rotl(k1, 31);
1316 k1 = this.x64Multiply(k1, c2); 1263 k1 = this.x64Multiply(k1, c2);
1317 h1 = this.x64Xor(h1, k1); 1264 h1 = this.x64Xor(h1, k1);
1318 } 1265 }
1319 h1 = this.x64Xor(h1, [0, key.length]); 1266 h1 = this.x64Xor(h1, [0, key.length]);
1320 h2 = this.x64Xor(h2, [0, key.length]); 1267 h2 = this.x64Xor(h2, [0, key.length]);
1321 h1 = this.x64Add(h1, h2); 1268 h1 = this.x64Add(h1, h2);
1322 h2 = this.x64Add(h2, h1); 1269 h2 = this.x64Add(h2, h1);
1323 h1 = this.x64Fmix(h1); 1270 h1 = this.x64Fmix(h1);
1324 h2 = this.x64Fmix(h2); 1271 h2 = this.x64Fmix(h2);
1325 h1 = this.x64Add(h1, h2); 1272 h1 = this.x64Add(h1, h2);
1326 h2 = this.x64Add(h2, h1); 1273 h2 = this.x64Add(h2, h1);
1327 return ("00000000" + (h1[0] >>> 0).toString(16)).slice(-8) + ("00000000" + (h1[1] >>> 0).toString(16)).slice(-8) + ("00000000" + (h2[0] >>> 0).toString(16)).slice(-8) + ("00000000" + (h2[1] >>> 0).toString(16)).slice(-8); 1274 return ("00000000" + (h1[0] >>> 0).toString(16)).slice(-8) + ("00000000" + (h1[1] >>> 0).toString(16)).slice(-8) + ("00000000" + (h2[0] >>> 0).toString(16)).slice(-8) + ("00000000" + (h2[1] >>> 0).toString(16)).slice(-8);
1328 } 1275 }
1329 }; 1276 };
1330 Fingerprint2.VERSION = "1.5.0"; 1277 Fingerprint2.VERSION = "1.5.1";
1331 return Fingerprint2; 1278 return Fingerprint2;
1332 }); 1279 });
1333   1280