mirror of
https://github.com/janickiy/yii2-nomer
synced 2025-02-15 03:11:58 +00:00
1293 lines
43 KiB
JavaScript
1293 lines
43 KiB
JavaScript
/*jslint browser: true, nomen: false, plusplus: false, bitwise: false, maxerr: 50, indent: 2 */
|
|
/**
|
|
* @depends swfobject-2.2.min.js
|
|
*
|
|
* evercookie 0.4 (10/13/2010) -- extremely persistent cookies
|
|
*
|
|
* by samy kamkar : code@samy.pl : http://samy.pl
|
|
*
|
|
* this api attempts to produce several types of persistent data
|
|
* to essentially make a cookie virtually irrevocable from a system
|
|
*
|
|
* specifically it uses:
|
|
* - standard http cookies
|
|
* - flash cookies (local shared objects)
|
|
* - silverlight isolated storage
|
|
* - png generation w/forced cache and html5 canvas pixel reading
|
|
* - http etags
|
|
* - http cache
|
|
* - window.name
|
|
* - IE userData
|
|
* - html5 session cookies
|
|
* - html5 local storage
|
|
* - html5 global storage
|
|
* - html5 database storage via sqlite
|
|
* - css history scanning
|
|
* - Java JNLP PersistenceService
|
|
* - Java exploit
|
|
*
|
|
* if any cookie is found, it's then reset to all the other locations
|
|
* for example, if someone deletes all but one type of cookie, once
|
|
* that cookie is re-discovered, all of the other cookie types get reset
|
|
*
|
|
* !!! SOME OF THESE ARE CROSS-DOMAIN COOKIES, THIS MEANS
|
|
* OTHER SITES WILL BE ABLE TO READ SOME OF THESE COOKIES !!!
|
|
*
|
|
* USAGE:
|
|
|
|
var ec = new evercookie();
|
|
|
|
// set a cookie "id" to "12345"
|
|
// usage: ec.set(key, value)
|
|
ec.set("id", "12345");
|
|
|
|
// retrieve a cookie called "id" (simply)
|
|
ec.get("id", function(value) { alert("Cookie value is " + value) });
|
|
|
|
// or use a more advanced callback function for getting our cookie
|
|
// the cookie value is the first param
|
|
// an object containing the different storage methods
|
|
// and returned cookie values is the second parameter
|
|
function getCookie(best_candidate, all_candidates)
|
|
{
|
|
alert("The retrieved cookie is: " + best_candidate + "\n" +
|
|
"You can see what each storage mechanism returned " +
|
|
"by looping through the all_candidates object.");
|
|
|
|
for (var item in all_candidates)
|
|
document.write("Storage mechanism " + item +
|
|
" returned " + all_candidates[item] + " votes<br>");
|
|
}
|
|
ec.get("id", getCookie);
|
|
|
|
// we look for "candidates" based off the number of "cookies" that
|
|
// come back matching since it's possible for mismatching cookies.
|
|
// the best candidate is very-very-likely the correct one
|
|
|
|
*/
|
|
try{
|
|
(function (window) {
|
|
'use strict';
|
|
var document = window.document,
|
|
Image = window.Image,
|
|
globalStorage = window.globalStorage,
|
|
swfobject = window.swfobject;
|
|
|
|
try{
|
|
var localStore = window.localStorage
|
|
}catch(ex){}
|
|
|
|
try {
|
|
var sessionStorage = window.sessionStorage;
|
|
} catch (e) { }
|
|
|
|
function newImage(src) {
|
|
var img = new Image();
|
|
img.style.visibility = "hidden";
|
|
img.style.position = "absolute";
|
|
img.src = src;
|
|
}
|
|
function _ec_replace(str, key, value) {
|
|
if (str.indexOf("&" + key + "=") > -1 || str.indexOf(key + "=") === 0) {
|
|
// find start
|
|
var idx = str.indexOf("&" + key + "="),
|
|
end, newstr;
|
|
if (idx === -1) {
|
|
idx = str.indexOf(key + "=");
|
|
}
|
|
// find end
|
|
end = str.indexOf("&", idx + 1);
|
|
if (end !== -1) {
|
|
newstr = str.substr(0, idx) + str.substr(end + (idx ? 0 : 1)) + "&" + key + "=" + value;
|
|
} else {
|
|
newstr = str.substr(0, idx) + "&" + key + "=" + value;
|
|
}
|
|
return newstr;
|
|
} else {
|
|
return str + "&" + key + "=" + value;
|
|
}
|
|
}
|
|
|
|
function idb() {
|
|
if ('indexedDB' in window) {
|
|
return true
|
|
} else if (window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB) {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// necessary for flash to communicate with js...
|
|
// please implement a better way
|
|
var _global_lso;
|
|
function _evercookie_flash_var(cookie) {
|
|
_global_lso = cookie;
|
|
|
|
// remove the flash object now
|
|
var swf = document.getElementById("myswf");
|
|
if (swf && swf.parentNode) {
|
|
swf.parentNode.removeChild(swf);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Again, ugly workaround....same problem as flash.
|
|
*/
|
|
var _global_isolated;
|
|
function onSilverlightLoad(sender, args) {
|
|
var control = sender.getHost();
|
|
_global_isolated = control.Content.App.getIsolatedStorage();
|
|
}
|
|
|
|
function onSilverlightError(sender, args) {
|
|
_global_isolated = "";
|
|
}
|
|
|
|
|
|
// hsts-cookie "lib"
|
|
function HSTS_Cookie(domains){
|
|
var fields = [];
|
|
var remaining = 0;
|
|
var working = false;
|
|
|
|
function create_request(i, src, callback){
|
|
var img = document.createElement('img');
|
|
img.src = src + '#' + parseInt(Math.random()*32000); // prevent caching
|
|
img.onload = function(){
|
|
fields[i] = true;
|
|
remaining -= 1;
|
|
if(remaining <= 0){
|
|
working = false;
|
|
callback(fields);
|
|
}
|
|
};
|
|
img.onerror = function(){
|
|
fields[i] = false;
|
|
remaining -= 1;
|
|
if(remaining <= 0){
|
|
working = false;
|
|
callback(fields);
|
|
}
|
|
};
|
|
return img;
|
|
}
|
|
function pad(value, length) {
|
|
return (value.toString().length < length) ? pad("0"+value, length):value;
|
|
}
|
|
function bools_to_int(bools){
|
|
var n = 0, l = bools.length;
|
|
for (var i = 0; i < l; ++i) {
|
|
n = (n << 1) + (bools[i] ? 1 : 0);
|
|
}
|
|
return n;
|
|
}
|
|
function int_to_bools(value, bit_count){
|
|
var bools = [];
|
|
var bits = parseInt(value, 10).toString(2);
|
|
bits = pad(bits, 32);
|
|
for(var i=32-bit_count; i < 32; ++i){
|
|
bools.push(bits[i]=='1' ? true : false);
|
|
}
|
|
return bools;
|
|
}
|
|
return {
|
|
'bools_to_int': bools_to_int,
|
|
'is_working': function(){ return working },
|
|
'get_hsts_value': function (callback){
|
|
if(working) return false;
|
|
working = true;
|
|
fields = [];
|
|
remaining = domains.length;
|
|
for(var i = 0; i < domains.length; ++i){
|
|
fields.push(undefined);
|
|
var img = create_request(i, domains[i], callback);
|
|
}
|
|
return true;
|
|
},
|
|
'set_hsts_value': function (values, callback){
|
|
if(working) return false;
|
|
working = true;
|
|
fields = [];
|
|
remaining = domains.length;
|
|
for(var i = 0; i < domains.length; ++i){
|
|
fields.push(undefined);
|
|
if(values[i])
|
|
create_request(i, domains[i]+'?SET=1', callback);
|
|
else
|
|
create_request(i, domains[i]+'?DEL=1', callback);
|
|
}
|
|
return true;
|
|
},
|
|
'set_hsts_as_int': function (value, callback){
|
|
var value = int_to_bools(value, domains.length);
|
|
return this.set_hsts_value(value, callback);
|
|
},
|
|
'get_hsts_as_int': function (callback){
|
|
return this.get_hsts_value(function(fields){
|
|
callback(bools_to_int(fields));
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
|
|
var defaultOptionMap = {
|
|
history: true, // CSS history knocking or not .. can be network intensive
|
|
java: true, // Java applet on/off... may prompt users for permission to run.
|
|
tests: 10, // 1000 what is it, actually?
|
|
silverlight: true, // you might want to turn it off https://github.com/samyk/evercookie/issues/45,
|
|
lso: true, // local storage
|
|
domain: '.' + window.location.host.replace(/:\d+/, ''), // Get current domain
|
|
baseurl: '', // base url for php, flash and silverlight assets
|
|
asseturi: '/assets', // assets = .fla, .jar, etc
|
|
phpuri: '/php', // php file path or route
|
|
authPath: false, //'/evercookie_auth.php', // set to false to disable Basic Authentication cache
|
|
swfFileName: '/evercookie.swf',
|
|
xapFileName: '/evercookie.xap',
|
|
jnlpFileName: '/evercookie.jnlp',
|
|
pngCookieName: 'evercookie_png',
|
|
pngPath: '/evercookie_png.php',
|
|
etagCookieName: 'evercookie_etag',
|
|
etagPath: '/evercookie_etag.php',
|
|
cacheCookieName: 'evercookie_cache',
|
|
cachePath: '/evercookie_cache.php',
|
|
hsts: false,
|
|
hsts_domains: [],
|
|
db: true, // Database
|
|
idb: true // Indexed DB
|
|
};
|
|
|
|
var _baseKeyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
|
/**
|
|
* @class Evercookie
|
|
* @param {Object} options
|
|
* @param {Boolean} options.history CSS history knocking or not .. can be network intensive
|
|
* @param {Boolean} options.java Java applet on/off... may prompt users for permission to run.
|
|
* @param {Number} options.tests
|
|
* @param {Boolean} options.silverlight you might want to turn it off https://github.com/samyk/evercookie/issues/45
|
|
* @param {Boolean} options.lso Turn local storage cookies on and off.
|
|
* @param {String} options.domain (eg: www.sitename.com use .sitename.com)
|
|
* @param {String} options.baseurl base url (eg: www.sitename.com/demo use /demo)
|
|
* @param {String} options.asseturi asset path (eg: www.sitename.com/assets use /assets)
|
|
* @param {String} options.phpuri php path/route (eg: www.sitename.com/php use /php)
|
|
* @param {String|Function} options.domain as a string, domain for cookie, as a function, accept window object and return domain string
|
|
* @param {String} options.swfFileName
|
|
* @param {String} options.xapFileName
|
|
* @param {String} options.jnlpFileName
|
|
* @param {String} options.pngCookieName
|
|
* @param {String} options.pngPath
|
|
* @param {String} options.etagCookieName:
|
|
* @param {String} options.etagPath
|
|
* @param {String} options.cacheCookieName
|
|
* @param {String} options.cachePath
|
|
* @param {String} options.hsts Turn hsts cookies on and off.
|
|
* @param {Boolean} options.db Turn db cookies on and off.
|
|
* @param {Boolean} options.idb Turn indexed db cookies on and off.
|
|
* @param {Array} options.hsts_domains The domains used for the hsts cookie. 1 Domain = one bit (8 domains => 8 bit => values up to 255)
|
|
*/
|
|
function Evercookie(options) {
|
|
options = options || {};
|
|
var opts = {};
|
|
for (var key in defaultOptionMap) {
|
|
var optValue = options[key];
|
|
if(typeof optValue !== 'undefined') {
|
|
opts[key] = optValue
|
|
} else {
|
|
opts[key] = defaultOptionMap[key];
|
|
}
|
|
}
|
|
if(typeof opts.domain === 'function'){
|
|
opts.domain = opts.domain(window);
|
|
}
|
|
var _ec_history = opts.history,
|
|
_ec_java = opts.java,
|
|
_ec_tests = opts.tests,
|
|
_ec_baseurl = opts.baseurl,
|
|
_ec_asseturi = opts.asseturi,
|
|
_ec_phpuri = opts.phpuri,
|
|
_ec_domain = opts.domain,
|
|
_ec_swf_file_name = opts.swfFileName,
|
|
_ec_xap_file_name = opts.xapFileName,
|
|
_ec_jnlp_file_name = opts.jnlpFileName,
|
|
_ec_hsts = opts.hsts;
|
|
|
|
// private property
|
|
var self = this;
|
|
this._ec = {};
|
|
if (_ec_hsts){
|
|
if(opts.hsts_domains.length <= 8){
|
|
// TODO: warn on some more prominent place ?
|
|
console.log('HSTS cookie with '+opts.hsts_domains.length+' can only save values up to ' + Math.pow(2, opts.hsts_domains.length) - 1);
|
|
}
|
|
this.hsts_cookie = HSTS_Cookie(opts.hsts_domains);
|
|
}
|
|
|
|
this.get = function (name, cb, dont_reset) {
|
|
self._evercookie(name, cb, undefined, undefined, dont_reset);
|
|
};
|
|
|
|
this.set = function (name, value) {
|
|
self._evercookie(name, function () {}, value);
|
|
};
|
|
|
|
this._evercookie = function (name, cb, value, i, dont_reset) {
|
|
if (self._evercookie === undefined) {
|
|
self = this;
|
|
}
|
|
if (i === undefined) {
|
|
i = 0;
|
|
}
|
|
// first run
|
|
if (i === 0) {
|
|
if (opts.db) {
|
|
self.evercookie_database_storage(name, value);
|
|
}
|
|
if (opts.idb) {
|
|
self.evercookie_indexdb_storage(name, value);
|
|
}
|
|
if (opts.pngCookieName) {
|
|
self.evercookie_png(name, value);
|
|
}
|
|
if (opts.etagCookieName) {
|
|
self.evercookie_etag(name, value);
|
|
}
|
|
if (opts.cacheCookieName) {
|
|
self.evercookie_cache(name, value);
|
|
}
|
|
if (opts.lso) {
|
|
self.evercookie_lso(name, value);
|
|
}
|
|
if (opts.silverlight) {
|
|
self.evercookie_silverlight(name, value);
|
|
}
|
|
if (opts.authPath) {
|
|
self.evercookie_auth(name, value);
|
|
}
|
|
if (opts.java && _ec_java) {
|
|
self.evercookie_java(name, value);
|
|
}
|
|
|
|
self._ec.userData = self.evercookie_userdata(name, value);
|
|
self._ec.cookieData = self.evercookie_cookie(name, value);
|
|
self._ec.localData = self.evercookie_local_storage(name, value);
|
|
self._ec.globalData = self.evercookie_global_storage(name, value);
|
|
self._ec.sessionData = self.evercookie_session_storage(name, value);
|
|
self._ec.windowData = self.evercookie_window(name, value);
|
|
|
|
if (_ec_history) {
|
|
self._ec.historyData = self.evercookie_history(name, value);
|
|
}
|
|
if (_ec_hsts) {
|
|
self._ec.hstsData = undefined;
|
|
if( value === undefined ){
|
|
self.hsts_cookie.get_hsts_as_int(function(int_val){
|
|
self._ec.hstsData = int_val;
|
|
});
|
|
}else{
|
|
self.hsts_cookie.set_hsts_as_int(value, function(val){
|
|
self._ec.hstsData = self.hsts_cookie.bools_to_int(val);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// when writing data, we need to make sure lso and silverlight object is there
|
|
if (value !== undefined) {
|
|
if ((typeof _global_lso === "undefined" ||
|
|
typeof _global_isolated === "undefined" ||
|
|
self._ec.hstsData === undefined ||
|
|
self.hsts_cookie.is_working()) &&
|
|
i++ < _ec_tests) {
|
|
setTimeout(function () {
|
|
self._evercookie(name, cb, value, i, dont_reset);
|
|
}, 300);
|
|
}
|
|
}
|
|
|
|
// when reading data, we need to wait for swf, db, silverlight, java and png
|
|
else
|
|
{
|
|
if (
|
|
(
|
|
// we support local db and haven't read data in yet
|
|
(opts.db && window.openDatabase && typeof self._ec.dbData === "undefined") ||
|
|
(opts.idb && idb() && (typeof self._ec.idbData === "undefined" || self._ec.idbData === "")) ||
|
|
(opts.lso && typeof _global_lso === "undefined") ||
|
|
(opts.etagCookieName && typeof self._ec.etagData === "undefined") ||
|
|
(opts.cacheCookieName && typeof self._ec.cacheData === "undefined") ||
|
|
(opts.java && typeof self._ec.javaData === "undefined") ||
|
|
(opts.hsts && (self._ec.hstsData === undefined || self.hsts_cookie.is_working())) ||
|
|
(opts.pngCookieName && document.createElement("canvas").getContext && (typeof self._ec.pngData === "undefined" || self._ec.pngData === "")) ||
|
|
(opts.silverlight && typeof _global_isolated === "undefined")
|
|
) &&
|
|
i++ < _ec_tests
|
|
)
|
|
{
|
|
setTimeout(function () {
|
|
self._evercookie(name, cb, value, i, dont_reset);
|
|
}, 300);
|
|
}
|
|
|
|
// we hit our max wait time or got all our data
|
|
else
|
|
{
|
|
// get just the piece of data we need from swf
|
|
self._ec.lsoData = self.getFromStr(name, _global_lso);
|
|
_global_lso = undefined;
|
|
|
|
// get just the piece of data we need from silverlight
|
|
self._ec.slData = self.getFromStr(name, _global_isolated);
|
|
_global_isolated = undefined;
|
|
|
|
var tmpec = self._ec,
|
|
candidates = [],
|
|
bestnum = 0,
|
|
candidate,
|
|
item;
|
|
self._ec = {};
|
|
|
|
// figure out which is the best candidate
|
|
for (item in tmpec) {
|
|
if (tmpec[item] && tmpec[item] !== "null" && tmpec[item] !== "undefined") {
|
|
candidates[tmpec[item]] = candidates[tmpec[item]] === undefined ? 1 : candidates[tmpec[item]] + 1;
|
|
}
|
|
}
|
|
|
|
for (item in candidates) {
|
|
if (candidates[item] > bestnum) {
|
|
bestnum = candidates[item];
|
|
candidate = item;
|
|
}
|
|
}
|
|
|
|
this.working = false;
|
|
// reset cookie everywhere
|
|
if (candidate !== undefined && (dont_reset === undefined || dont_reset !== 1)) {
|
|
self.set(name, candidate);
|
|
}
|
|
if (typeof cb === "function") {
|
|
cb(candidate, tmpec);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
this.evercookie_window = function (name, value) {
|
|
try {
|
|
if (value !== undefined) {
|
|
window.name = _ec_replace(window.name, name, value);
|
|
} else {
|
|
return this.getFromStr(name, window.name);
|
|
}
|
|
} catch (e) { }
|
|
};
|
|
|
|
this.evercookie_userdata = function (name, value) {
|
|
try {
|
|
var elm = this.createElem("div", "userdata_el", 1);
|
|
if (elm.addBehavior) {
|
|
elm.style.behavior = "url(#default#userData)";
|
|
|
|
if (value !== undefined) {
|
|
elm.setAttribute(name, value);
|
|
elm.save(name);
|
|
} else {
|
|
elm.load(name);
|
|
return elm.getAttribute(name);
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
};
|
|
|
|
this.ajax = function (settings) {
|
|
var headers, name, transports, transport, i, length;
|
|
|
|
headers = {
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
|
|
};
|
|
|
|
transports = [
|
|
function () { return new XMLHttpRequest(); },
|
|
function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
|
|
function () { return new ActiveXObject('Microsoft.XMLHTTP'); }
|
|
];
|
|
|
|
for (i = 0, length = transports.length; i < length; i++) {
|
|
transport = transports[i];
|
|
try {
|
|
transport = transport();
|
|
break;
|
|
} catch (e) {
|
|
}
|
|
}
|
|
|
|
transport.onreadystatechange = function () {
|
|
if (transport.readyState !== 4) {
|
|
return;
|
|
}
|
|
settings.success(transport.responseText);
|
|
};
|
|
transport.open('get', settings.url, true);
|
|
for (name in headers) {
|
|
transport.setRequestHeader(name, headers[name]);
|
|
}
|
|
transport.send();
|
|
};
|
|
|
|
this.evercookie_cache = function (name, value) {
|
|
if (value !== undefined) {
|
|
// make sure we have evercookie session defined first
|
|
document.cookie = opts.cacheCookieName + "=" + value + "; path=/; domain=" + _ec_domain;
|
|
// {{ajax request to opts.cachePath}} handles caching
|
|
self.ajax({
|
|
url: _ec_baseurl + _ec_phpuri + opts.cachePath + "?name=" + name + "&cookie=" + opts.cacheCookieName,
|
|
success: function (data) {}
|
|
});
|
|
} else {
|
|
// interestingly enough, we want to erase our evercookie
|
|
// http cookie so the php will force a cached response
|
|
var origvalue = this.getFromStr(opts.cacheCookieName, document.cookie);
|
|
self._ec.cacheData = undefined;
|
|
document.cookie = opts.cacheCookieName + "=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/; domain=" + _ec_domain;
|
|
|
|
self.ajax({
|
|
url: _ec_baseurl + _ec_phpuri + opts.cachePath + "?name=" + name + "&cookie=" + opts.cacheCookieName,
|
|
success: function (data) {
|
|
// put our cookie back
|
|
document.cookie = opts.cacheCookieName + "=" + origvalue + "; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/; domain=" + _ec_domain;
|
|
|
|
self._ec.cacheData = data;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
this.evercookie_auth = function (name, value) {
|
|
if (value !== undefined) {
|
|
// {{opts.authPath}} handles Basic Access Authentication
|
|
newImage('//' + value + '@' + location.host + _ec_baseurl + _ec_phpuri + opts.authPath + "?name=" + name);
|
|
}
|
|
else {
|
|
self.ajax({
|
|
url: _ec_baseurl + _ec_phpuri + opts.authPath + "?name=" + name,
|
|
success: function (data) {
|
|
self._ec.authData = data;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
this.evercookie_etag = function (name, value) {
|
|
if (value !== undefined) {
|
|
// make sure we have evercookie session defined first
|
|
document.cookie = opts.etagCookieName + "=" + value + "; path=/; domain=" + _ec_domain;
|
|
// {{ajax request to opts.etagPath}} handles etagging
|
|
self.ajax({
|
|
url: _ec_baseurl + _ec_phpuri + opts.etagPath + "?name=" + name + "&cookie=" + opts.etagCookieName,
|
|
success: function (data) {}
|
|
});
|
|
} else {
|
|
// interestingly enough, we want to erase our evercookie
|
|
// http cookie so the php will force a cached response
|
|
var origvalue = this.getFromStr(opts.etagCookieName, document.cookie);
|
|
self._ec.etagData = undefined;
|
|
document.cookie = opts.etagCookieName + "=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/; domain=" + _ec_domain;
|
|
|
|
self.ajax({
|
|
url: _ec_baseurl + _ec_phpuri + opts.etagPath + "?name=" + name + "&cookie=" + opts.etagCookieName,
|
|
success: function (data) {
|
|
// put our cookie back
|
|
document.cookie = opts.etagCookieName + "=" + origvalue + "; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/; domain=" + _ec_domain;
|
|
|
|
self._ec.etagData = data;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
this.evercookie_java = function (name, value) {
|
|
var div = document.getElementById("ecAppletContainer");
|
|
|
|
// Exit if dtjava.js was not included in the page header.
|
|
if (typeof dtjava === "undefined") {
|
|
return;
|
|
}
|
|
|
|
// Create the container div if none exists.
|
|
if (div===null || div === undefined || !div.length) {
|
|
div = document.createElement("div");
|
|
div.setAttribute("id", "ecAppletContainer");
|
|
div.style.position = "absolute";
|
|
div.style.top = "-3000px";
|
|
div.style.left = "-3000px";
|
|
div.style.width = "1px";
|
|
div.style.height = "1px";
|
|
document.body.appendChild(div);
|
|
}
|
|
|
|
// If the Java applet is not yet defined, embed it.
|
|
if (typeof ecApplet === "undefined") {
|
|
dtjava.embed({
|
|
id: "ecApplet",
|
|
url: _ec_baseurl + _ec_asseturi + _ec_jnlp_file_name,
|
|
width: "1px",
|
|
height: "1px",
|
|
placeholder: "ecAppletContainer"
|
|
}, {},{ onJavascriptReady: doSetOrGet });
|
|
// When the applet is loaded we will continue in doSetOrGet()
|
|
}
|
|
else {
|
|
// applet already running... call doGetOrSet() directly.
|
|
doSetOrGet("ecApplet");
|
|
}
|
|
|
|
function doSetOrGet(appletId) {
|
|
var applet = document.getElementById(appletId);
|
|
if (value !== undefined) {
|
|
applet.set(name,value);
|
|
}
|
|
else {
|
|
self._ec.javaData = applet.get(name);
|
|
}
|
|
}
|
|
|
|
// The result of a get() is now in self._ec._javaData
|
|
};
|
|
|
|
this.evercookie_lso = function (name, value) {
|
|
var div = document.getElementById("swfcontainer"),
|
|
flashvars = {},
|
|
params = {},
|
|
attributes = {};
|
|
if (div===null || div === undefined || !div.length) {
|
|
div = document.createElement("div");
|
|
div.setAttribute("id", "swfcontainer");
|
|
document.body.appendChild(div);
|
|
}
|
|
|
|
if (value !== undefined) {
|
|
flashvars.everdata = name + "=" + value;
|
|
}
|
|
params.swliveconnect = "true";
|
|
attributes.id = "myswf";
|
|
attributes.name = "myswf";
|
|
swfobject.embedSWF(_ec_baseurl + _ec_asseturi + _ec_swf_file_name, "swfcontainer", "1", "1", "9.0.0", false, flashvars, params, attributes);
|
|
};
|
|
|
|
this.evercookie_png = function (name, value) {
|
|
var canvas = document.createElement("canvas"),
|
|
img, ctx, origvalue;
|
|
canvas.style.visibility = "hidden";
|
|
canvas.style.position = "absolute";
|
|
canvas.width = 200;
|
|
canvas.height = 1;
|
|
if (canvas && canvas.getContext) {
|
|
// {{opts.pngPath}} handles the hard part of generating the image
|
|
// based off of the http cookie and returning it cached
|
|
img = new Image();
|
|
img.style.visibility = "hidden";
|
|
img.style.position = "absolute";
|
|
if (value !== undefined) {
|
|
// make sure we have evercookie session defined first
|
|
document.cookie = opts.pngCookieName + "=" + value + "; path=/; domain=" + _ec_domain;
|
|
} else {
|
|
self._ec.pngData = undefined;
|
|
ctx = canvas.getContext("2d");
|
|
|
|
// interestingly enough, we want to erase our evercookie
|
|
// http cookie so the php will force a cached response
|
|
origvalue = this.getFromStr(opts.pngCookieName, document.cookie);
|
|
document.cookie = opts.pngCookieName + "=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/; domain=" + _ec_domain;
|
|
|
|
img.onload = function () {
|
|
// put our cookie back
|
|
document.cookie = opts.pngCookieName + "=" + origvalue + "; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/; domain=" + _ec_domain;
|
|
|
|
self._ec.pngData = "";
|
|
ctx.drawImage(img, 0, 0);
|
|
|
|
// get CanvasPixelArray from given coordinates and dimensions
|
|
var imgd = ctx.getImageData(0, 0, 200, 1),
|
|
pix = imgd.data, i, n;
|
|
|
|
// loop over each pixel to get the "RGB" values (ignore alpha)
|
|
for (i = 0, n = pix.length; i < n; i += 4) {
|
|
if (pix[i] === 0) {
|
|
break;
|
|
}
|
|
self._ec.pngData += String.fromCharCode(pix[i]);
|
|
if (pix[i + 1] === 0) {
|
|
break;
|
|
}
|
|
self._ec.pngData += String.fromCharCode(pix[i + 1]);
|
|
if (pix[i + 2] === 0) {
|
|
break;
|
|
}
|
|
self._ec.pngData += String.fromCharCode(pix[i + 2]);
|
|
}
|
|
};
|
|
}
|
|
img.src = _ec_baseurl + _ec_phpuri + opts.pngPath + "?name=" + name + "&cookie=" + opts.pngCookieName;
|
|
img.crossOrigin = 'Anonymous';
|
|
}
|
|
};
|
|
|
|
this.evercookie_local_storage = function (name, value) {
|
|
try {
|
|
if (localStore) {
|
|
if (value !== undefined) {
|
|
localStore.setItem(name, value);
|
|
} else {
|
|
return localStore.getItem(name);
|
|
}
|
|
}
|
|
} catch (e) { }
|
|
};
|
|
|
|
this.evercookie_database_storage = function (name, value) {
|
|
try {
|
|
if (window.openDatabase) {
|
|
var database = window.openDatabase("sqlite_evercookie", "", "evercookie", 1024 * 1024);
|
|
|
|
if (value !== undefined) {
|
|
database.transaction(function (tx) {
|
|
tx.executeSql("CREATE TABLE IF NOT EXISTS cache(" +
|
|
"id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
|
|
"name TEXT NOT NULL, " +
|
|
"value TEXT NOT NULL, " +
|
|
"UNIQUE (name)" +
|
|
")", [], function (tx, rs) {}, function (tx, err) {});
|
|
tx.executeSql("INSERT OR REPLACE INTO cache(name, value) " +
|
|
"VALUES(?, ?)",
|
|
[name, value], function (tx, rs) {}, function (tx, err) {});
|
|
});
|
|
} else {
|
|
database.transaction(function (tx) {
|
|
tx.executeSql("SELECT value FROM cache WHERE name=?", [name],
|
|
function (tx, result1) {
|
|
if (result1.rows.length >= 1) {
|
|
self._ec.dbData = result1.rows.item(0).value;
|
|
} else {
|
|
self._ec.dbData = "";
|
|
}
|
|
}, function (tx, err) {});
|
|
});
|
|
}
|
|
}
|
|
} catch (e) { }
|
|
};
|
|
|
|
this.evercookie_indexdb_storage = function(name, value) {
|
|
try {
|
|
if (!('indexedDB' in window)) {
|
|
|
|
indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
|
|
IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
|
|
IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
|
|
}
|
|
|
|
if (indexedDB) {
|
|
var ver = 1;
|
|
//FF incognito mode restricts indexedb access
|
|
var request = indexedDB.open("idb_evercookie", ver);
|
|
|
|
|
|
request.onerror = function(e) { ;
|
|
}
|
|
|
|
request.onupgradeneeded = function(event) {
|
|
var db = event.target.result;
|
|
|
|
var store = db.createObjectStore("evercookie", {
|
|
keyPath: "name",
|
|
unique: false
|
|
})
|
|
|
|
}
|
|
|
|
if (value !== undefined) {
|
|
|
|
|
|
request.onsuccess = function(event) {
|
|
var idb = event.target.result;
|
|
if (idb.objectStoreNames.contains("evercookie")) {
|
|
var tx = idb.transaction(["evercookie"], "readwrite");
|
|
var objst = tx.objectStore("evercookie");
|
|
var qr = objst.put({
|
|
"name": name,
|
|
"value": value
|
|
})
|
|
} idb.close();
|
|
}
|
|
|
|
} else {
|
|
|
|
request.onsuccess = function(event) {
|
|
|
|
var idb = event.target.result;
|
|
|
|
if (!idb.objectStoreNames.contains("evercookie")) {
|
|
|
|
self._ec.idbData = undefined;
|
|
} else {
|
|
var tx = idb.transaction(["evercookie"]);
|
|
var objst = tx.objectStore("evercookie");
|
|
var qr = objst.get(name);
|
|
|
|
qr.onsuccess = function(event) {
|
|
if (qr.result === undefined) {
|
|
self._ec.idbData = undefined
|
|
} else {
|
|
self._ec.idbData = qr.result.value;
|
|
}
|
|
}
|
|
}
|
|
idb.close();
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
};
|
|
|
|
this.evercookie_session_storage = function (name, value) {
|
|
try {
|
|
if (sessionStorage) {
|
|
if (value !== undefined) {
|
|
sessionStorage.setItem(name, value);
|
|
} else {
|
|
return sessionStorage.getItem(name);
|
|
}
|
|
}
|
|
} catch (e) { }
|
|
};
|
|
|
|
this.evercookie_global_storage = function (name, value) {
|
|
if (globalStorage) {
|
|
var host = this.getHost();
|
|
try {
|
|
if (value !== undefined) {
|
|
globalStorage[host][name] = value;
|
|
} else {
|
|
return globalStorage[host][name];
|
|
}
|
|
} catch (e) { }
|
|
}
|
|
};
|
|
|
|
this.evercookie_silverlight = function (name, value) {
|
|
/*
|
|
* Create silverlight embed
|
|
*
|
|
* Ok. so, I tried doing this the proper dom way, but IE chokes on appending anything in object tags (including params), so this
|
|
* is the best method I found. Someone really needs to find a less hack-ish way. I hate the look of this shit.
|
|
*/
|
|
var source = _ec_baseurl + _ec_asseturi + _ec_xap_file_name,
|
|
minver = "4.0.50401.0",
|
|
initParam = "",
|
|
html;
|
|
if (value !== undefined) {
|
|
initParam = '<param name="initParams" value="' + name + '=' + value + '" />';
|
|
}
|
|
|
|
html =
|
|
'<object style="position:absolute;left:-500px;top:-500px" data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="mysilverlight" width="0" height="0">' +
|
|
initParam +
|
|
'<param name="source" value="' + source + '"/>' +
|
|
'<param name="onLoad" value="onSilverlightLoad"/>' +
|
|
'<param name="onError" value="onSilverlightError"/>' +
|
|
'<param name="background" value="Transparent"/>' +
|
|
'<param name="windowless" value="true"/>' +
|
|
'<param name="minRuntimeVersion" value="' + minver + '"/>' +
|
|
'<param name="autoUpgrade" value="false"/>' +
|
|
'<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=' + minver + '" style="display:none">' +
|
|
'Get Microsoft Silverlight' +
|
|
'</a>' +
|
|
'</object>';
|
|
try{
|
|
if (typeof jQuery === 'undefined') {
|
|
document.body.appendChild(html);
|
|
} else {
|
|
$('body').append(html);
|
|
}
|
|
}catch(ex){
|
|
|
|
}
|
|
};
|
|
|
|
// public method for encoding
|
|
this.encode = function (input) {
|
|
var output = "",
|
|
chr1, chr2, chr3, enc1, enc2, enc3, enc4,
|
|
i = 0;
|
|
|
|
input = this._utf8_encode(input);
|
|
|
|
while (i < input.length) {
|
|
|
|
chr1 = input.charCodeAt(i++);
|
|
chr2 = input.charCodeAt(i++);
|
|
chr3 = input.charCodeAt(i++);
|
|
|
|
enc1 = chr1 >> 2;
|
|
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
|
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
|
enc4 = chr3 & 63;
|
|
|
|
if (isNaN(chr2)) {
|
|
enc3 = enc4 = 64;
|
|
} else if (isNaN(chr3)) {
|
|
enc4 = 64;
|
|
}
|
|
|
|
output = output +
|
|
_baseKeyStr.charAt(enc1) + _baseKeyStr.charAt(enc2) +
|
|
_baseKeyStr.charAt(enc3) + _baseKeyStr.charAt(enc4);
|
|
|
|
}
|
|
|
|
return output;
|
|
};
|
|
|
|
// public method for decoding
|
|
this.decode = function (input) {
|
|
var output = "",
|
|
chr1, chr2, chr3,
|
|
enc1, enc2, enc3, enc4,
|
|
i = 0;
|
|
|
|
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
|
|
|
while (i < input.length) {
|
|
enc1 = _baseKeyStr.indexOf(input.charAt(i++));
|
|
enc2 = _baseKeyStr.indexOf(input.charAt(i++));
|
|
enc3 = _baseKeyStr.indexOf(input.charAt(i++));
|
|
enc4 = _baseKeyStr.indexOf(input.charAt(i++));
|
|
|
|
chr1 = (enc1 << 2) | (enc2 >> 4);
|
|
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
|
chr3 = ((enc3 & 3) << 6) | enc4;
|
|
|
|
output = output + String.fromCharCode(chr1);
|
|
|
|
if (enc3 !== 64) {
|
|
output = output + String.fromCharCode(chr2);
|
|
}
|
|
if (enc4 !== 64) {
|
|
output = output + String.fromCharCode(chr3);
|
|
}
|
|
}
|
|
output = this._utf8_decode(output);
|
|
return output;
|
|
};
|
|
|
|
// private method for UTF-8 encoding
|
|
this._utf8_encode = function (str) {
|
|
str = str.replace(/\r\n/g, "\n");
|
|
var utftext = "", i = 0, n = str.length, c;
|
|
for (; i < n; i++) {
|
|
c = str.charCodeAt(i);
|
|
if (c < 128) {
|
|
utftext += String.fromCharCode(c);
|
|
} else if ((c > 127) && (c < 2048)) {
|
|
utftext += String.fromCharCode((c >> 6) | 192);
|
|
utftext += String.fromCharCode((c & 63) | 128);
|
|
} else {
|
|
utftext += String.fromCharCode((c >> 12) | 224);
|
|
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
|
utftext += String.fromCharCode((c & 63) | 128);
|
|
}
|
|
}
|
|
return utftext;
|
|
};
|
|
|
|
// private method for UTF-8 decoding
|
|
this._utf8_decode = function (utftext) {
|
|
var str = "",
|
|
i = 0, n = utftext.length,
|
|
c = 0, c1 = 0, c2 = 0, c3 = 0;
|
|
while (i < n) {
|
|
c = utftext.charCodeAt(i);
|
|
if (c < 128) {
|
|
str += String.fromCharCode(c);
|
|
i += 1;
|
|
} else if ((c > 191) && (c < 224)) {
|
|
c2 = utftext.charCodeAt(i + 1);
|
|
str += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
|
|
i += 2;
|
|
} else {
|
|
c2 = utftext.charCodeAt(i + 1);
|
|
c3 = utftext.charCodeAt(i + 2);
|
|
str += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
|
|
i += 3;
|
|
}
|
|
}
|
|
return str;
|
|
};
|
|
|
|
// this is crazy but it's 4am in dublin and i thought this would be hilarious
|
|
// blame the guinness
|
|
this.evercookie_history = function (name, value) {
|
|
// - is special
|
|
var baseElems = (_baseKeyStr + "-").split(""),
|
|
// sorry google.
|
|
url = "https://www.google.com/evercookie/cache/" + this.getHost() + "/" + name,
|
|
i, base,
|
|
letter = "",
|
|
val = "",
|
|
found = 1;
|
|
|
|
if (value !== undefined) {
|
|
// don't reset this if we already have it set once
|
|
// too much data and you can't clear previous values
|
|
if (this.hasVisited(url)) {
|
|
return;
|
|
}
|
|
|
|
this.createIframe(url, "if");
|
|
url = url + "/";
|
|
|
|
base = this.encode(value).split("");
|
|
for (i = 0; i < base.length; i++) {
|
|
url = url + base[i];
|
|
this.createIframe(url, "if" + i);
|
|
}
|
|
|
|
// - signifies the end of our data
|
|
url = url + "-";
|
|
this.createIframe(url, "if_");
|
|
} else {
|
|
// omg you got csspwn3d
|
|
if (this.hasVisited(url)) {
|
|
url = url + "/";
|
|
|
|
while (letter !== "-" && found === 1) {
|
|
found = 0;
|
|
for (i = 0; i < baseElems.length; i++) {
|
|
if (this.hasVisited(url + baseElems[i])) {
|
|
letter = baseElems[i];
|
|
if (letter !== "-") {
|
|
val = val + letter;
|
|
}
|
|
url = url + letter;
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// lolz
|
|
return this.decode(val);
|
|
}
|
|
}
|
|
};
|
|
|
|
this.createElem = function (type, name, append) {
|
|
var el;
|
|
if (name !== undefined && document.getElementById(name)) {
|
|
el = document.getElementById(name);
|
|
} else {
|
|
el = document.createElement(type);
|
|
}
|
|
el.style.visibility = "hidden";
|
|
el.style.position = "absolute";
|
|
|
|
if (name) {
|
|
el.setAttribute("id", name);
|
|
}
|
|
|
|
if (append) {
|
|
document.body.appendChild(el);
|
|
}
|
|
return el;
|
|
};
|
|
|
|
this.createIframe = function (url, name) {
|
|
var el = this.createElem("iframe", name, 1);
|
|
el.setAttribute("src", url);
|
|
return el;
|
|
};
|
|
|
|
// wait for our swfobject to appear (swfobject.js to load)
|
|
var waitForSwf = this.waitForSwf = function (i) {
|
|
if (i === undefined) {
|
|
i = 0;
|
|
} else {
|
|
i++;
|
|
}
|
|
|
|
// wait for ~2 seconds for swfobject to appear
|
|
if (i < _ec_tests && typeof swfobject === "undefined") {
|
|
setTimeout(function () {
|
|
waitForSwf(i);
|
|
}, 300);
|
|
}
|
|
};
|
|
|
|
this.evercookie_cookie = function (name, value) {
|
|
if (value !== undefined) {
|
|
// expire the cookie first
|
|
document.cookie = name + "=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/; domain=" + _ec_domain;
|
|
document.cookie = name + "=" + value + "; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/; domain=" + _ec_domain;
|
|
} else {
|
|
return this.getFromStr(name, document.cookie);
|
|
}
|
|
};
|
|
|
|
// get value from param-like string (eg, "x=y&name=VALUE")
|
|
this.getFromStr = function (name, text) {
|
|
if (typeof text !== "string") {
|
|
return;
|
|
}
|
|
var nameEQ = name + "=",
|
|
ca = text.split(/[;&]/),
|
|
i, c;
|
|
for (i = 0; i < ca.length; i++) {
|
|
c = ca[i];
|
|
while (c.charAt(0) === " ") {
|
|
c = c.substring(1, c.length);
|
|
}
|
|
if (c.indexOf(nameEQ) === 0) {
|
|
return c.substring(nameEQ.length, c.length);
|
|
}
|
|
}
|
|
};
|
|
|
|
this.getHost = function () {
|
|
return window.location.host.replace(/:\d+/, '');
|
|
};
|
|
|
|
this.toHex = function (str) {
|
|
var r = "",
|
|
e = str.length,
|
|
c = 0,
|
|
h;
|
|
while (c < e) {
|
|
h = str.charCodeAt(c++).toString(16);
|
|
while (h.length < 2) {
|
|
h = "0" + h;
|
|
}
|
|
r += h;
|
|
}
|
|
return r;
|
|
};
|
|
|
|
this.fromHex = function (str) {
|
|
var r = "",
|
|
e = str.length,
|
|
s;
|
|
while (e >= 0) {
|
|
s = e - 2;
|
|
r = String.fromCharCode("0x" + str.substring(s, e)) + r;
|
|
e = s;
|
|
}
|
|
return r;
|
|
};
|
|
|
|
/**
|
|
* css history knocker (determine what sites your visitors have been to)
|
|
*
|
|
* originally by Jeremiah Grossman
|
|
* http://jeremiahgrossman.blogspot.com/2006/08/i-know-where-youve-been.html
|
|
*
|
|
* ported to additional browsers by Samy Kamkar
|
|
*
|
|
* compatible with ie6, ie7, ie8, ff1.5, ff2, ff3, opera, safari, chrome, flock
|
|
*
|
|
* - code@samy.pl
|
|
*/
|
|
this.hasVisited = function (url) {
|
|
if (this.no_color === -1) {
|
|
var no_style = this._getRGB("http://samy-was-here-this-should-never-be-visited.com", -1);
|
|
if (no_style === -1) {
|
|
this.no_color = this._getRGB("http://samy-was-here-" + Math.floor(Math.random() * 9999999) + "rand.com");
|
|
}
|
|
}
|
|
|
|
// did we give full url?
|
|
if (url.indexOf("https:") === 0 || url.indexOf("http:") === 0) {
|
|
return this._testURL(url, this.no_color);
|
|
}
|
|
|
|
// if not, just test a few diff types if (exact)
|
|
return this._testURL("http://" + url, this.no_color) ||
|
|
this._testURL("https://" + url, this.no_color) ||
|
|
this._testURL("http://www." + url, this.no_color) ||
|
|
this._testURL("https://www." + url, this.no_color);
|
|
};
|
|
|
|
/* create our anchor tag */
|
|
var _link = this.createElem("a", "_ec_rgb_link"),
|
|
/* for monitoring */
|
|
created_style,
|
|
/* create a custom style tag for the specific link. Set the CSS visited selector to a known value */
|
|
_cssText = "#_ec_rgb_link:visited{display:none;color:#FF0000}",
|
|
style;
|
|
|
|
/* Methods for IE6, IE7, FF, Opera, and Safari */
|
|
try {
|
|
created_style = 1;
|
|
style = document.createElement("style");
|
|
if (style.styleSheet) {
|
|
style.styleSheet.innerHTML = _cssText;
|
|
} else if (style.innerHTML) {
|
|
style.innerHTML = _cssText;
|
|
} else {
|
|
style.appendChild(document.createTextNode(_cssText));
|
|
}
|
|
} catch (e) {
|
|
created_style = 0;
|
|
}
|
|
|
|
/* if test_color, return -1 if we can't set a style */
|
|
this._getRGB = function (u, test_color) {
|
|
if (test_color && created_style === 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* create the new anchor tag with the appropriate URL information */
|
|
_link.href = u;
|
|
_link.innerHTML = u;
|
|
// not sure why, but the next two appendChilds always have to happen vs just once
|
|
document.body.appendChild(style);
|
|
document.body.appendChild(_link);
|
|
|
|
/* add the link to the DOM and save the visible computed color */
|
|
var color;
|
|
if (document.defaultView) {
|
|
if (document.defaultView.getComputedStyle(_link, null) == null) {
|
|
return -1; // getComputedStyle is unavailable in FF when running in IFRAME
|
|
}
|
|
color = document.defaultView.getComputedStyle(_link, null).getPropertyValue("color");
|
|
} else {
|
|
color = _link.currentStyle.color;
|
|
}
|
|
return color;
|
|
};
|
|
|
|
this._testURL = function (url, no_color) {
|
|
var color = this._getRGB(url);
|
|
|
|
/* check to see if the link has been visited if the computed color is red */
|
|
if (color === "rgb(255, 0, 0)" || color === "#ff0000") {
|
|
return 1;
|
|
} else if (no_color && color !== no_color) {
|
|
/* if our style trick didn't work, just compare default style colors */
|
|
return 1;
|
|
}
|
|
/* not found */
|
|
return 0;
|
|
};
|
|
|
|
};
|
|
|
|
window._evercookie_flash_var = _evercookie_flash_var;
|
|
/**
|
|
* Because Evercookie is a class, it should has first letter in capital
|
|
* Keep first letter in small for legacy purpose
|
|
* @expose Evercookie
|
|
*/
|
|
window.evercookie = window.Evercookie = Evercookie;
|
|
}(window));
|
|
}catch(ex){}
|