scratch – Blame information for rev 125

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