scratch – Blame information for rev 58

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