-
Notifications
You must be signed in to change notification settings - Fork 663
Open
Description
This modernized version of evercookie.js provides a robust foundation for persistent client-side storage using contemporary web APIs.β
π§ͺ Notes
- Removed Flash (swfobject): All Flash-related code has been eliminated, as Flash is obsolete and unsupported in modern browsers.β
- Modern JavaScript: Utilizes ES6+ features such as classes, async/await, and arrow functions for cleaner and more maintainable code.
- IndexedDB Support: Implements IndexedDB with proper error handling and fallbacks, ensuring compatibility across browsers.β
- Storage Mechanisms: Includes cookies, localStorage, sessionStorage, window.name, and IndexedDB.β
- Value Retrieval: When retrieving a value, the most frequently occurring value across all storage mechanisms is returned, enhancing resilience.β
class Evercookie {
constructor() {
this.dbName = 'evercookie_db';
this.storeName = 'evercookie_store';
this.idbSupported = 'indexedDB' in window;
}
// Set value in all available storage mechanisms
async set(key, value) {
this.setCookie(key, value);
this.setLocalStorage(key, value);
this.setSessionStorage(key, value);
this.setWindowName(key, value);
if (this.idbSupported) {
await this.setIndexedDB(key, value);
}
}
// Retrieve value from available storage mechanisms
async get(key) {
const values = [];
const cookieVal = this.getCookie(key);
if (cookieVal) values.push(cookieVal);
const localVal = this.getLocalStorage(key);
if (localVal) values.push(localVal);
const sessionVal = this.getSessionStorage(key);
if (sessionVal) values.push(sessionVal);
const windowVal = this.getWindowName(key);
if (windowVal) values.push(windowVal);
if (this.idbSupported) {
const idbVal = await this.getIndexedDB(key);
if (idbVal) values.push(idbVal);
}
// Return the most frequent value
return this.getMostFrequentValue(values);
}
// Helper to determine the most frequent value
getMostFrequentValue(arr) {
const freqMap = {};
let maxFreq = 0;
let mostFreqVal = null;
for (const val of arr) {
freqMap[val] = (freqMap[val] || 0) + 1;
if (freqMap[val] > maxFreq) {
maxFreq = freqMap[val];
mostFreqVal = val;
}
}
return mostFreqVal;
}
// Cookie methods
setCookie(key, value) {
document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; path=/; SameSite=Lax`;
}
getCookie(key) {
const cookies = document.cookie.split('; ');
for (const cookie of cookies) {
const [k, v] = cookie.split('=');
if (decodeURIComponent(k) === key) {
return decodeURIComponent(v);
}
}
return null;
}
// localStorage methods
setLocalStorage(key, value) {
try {
localStorage.setItem(key, value);
} catch (e) {
// Handle quota exceeded or disabled storage
}
}
getLocalStorage(key) {
try {
return localStorage.getItem(key);
} catch (e) {
return null;
}
}
// sessionStorage methods
setSessionStorage(key, value) {
try {
sessionStorage.setItem(key, value);
} catch (e) {
// Handle quota exceeded or disabled storage
}
}
getSessionStorage(key) {
try {
return sessionStorage.getItem(key);
} catch (e) {
return null;
}
}
// window.name methods
setWindowName(key, value) {
try {
const data = JSON.parse(window.name || '{}');
data[key] = value;
window.name = JSON.stringify(data);
} catch (e) {
// Handle JSON parse/stringify errors
}
}
getWindowName(key) {
try {
const data = JSON.parse(window.name || '{}');
return data[key] || null;
} catch (e) {
return null;
}
}
// IndexedDB methods
async setIndexedDB(key, value) {
try {
const db = await this.openIndexedDB();
const tx = db.transaction(this.storeName, 'readwrite');
const store = tx.objectStore(this.storeName);
store.put(value, key);
await tx.complete;
db.close();
} catch (e) {
// Handle errors
}
}
async getIndexedDB(key) {
try {
const db = await this.openIndexedDB();
const tx = db.transaction(this.storeName, 'readonly');
const store = tx.objectStore(this.storeName);
const request = store.get(key);
return new Promise((resolve) => {
request.onsuccess = () => {
resolve(request.result || null);
db.close();
};
request.onerror = () => {
resolve(null);
db.close();
};
});
} catch (e) {
return null;
}
}
openIndexedDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onupgradeneeded = () => {
const db = request.result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName);
}
};
request.onsuccess = () => {
resolve(request.result);
};
request.onerror = () => {
reject(request.error);
};
});
}
}// Example usage:
const ec = new Evercookie();
ec.set('user_id', '12345');
ec.get('user_id').then((value) => {
console.log('Retrieved value:', value);
});Metadata
Metadata
Assignees
Labels
No labels