/** * @flow */ const dataUriPattern = /^data:/; export default class ImageUriCache { static _maximumEntries: number = 256; static _entries = {}; static has(uri: string) { const entries = ImageUriCache._entries; const isDataUri = dataUriPattern.test(uri); return isDataUri || Boolean(entries[uri]); } static add(uri: string) { const entries = ImageUriCache._entries; const lastUsedTimestamp = Date.now(); if (entries[uri]) { entries[uri].lastUsedTimestamp = lastUsedTimestamp; entries[uri].refCount += 1; } else { entries[uri] = { lastUsedTimestamp, refCount: 1 }; } } static remove(uri: string) { const entries = ImageUriCache._entries; if (entries[uri]) { entries[uri].refCount -= 1; } // Free up entries when the cache is "full" ImageUriCache._cleanUpIfNeeded(); } static _cleanUpIfNeeded() { const entries = ImageUriCache._entries; const imageUris = Object.keys(entries); if (imageUris.length + 1 > ImageUriCache._maximumEntries) { let leastRecentlyUsedKey; let leastRecentlyUsedEntry; imageUris.forEach(uri => { const entry = entries[uri]; if ( (!leastRecentlyUsedEntry || entry.lastUsedTimestamp < leastRecentlyUsedEntry.lastUsedTimestamp) && entry.refCount === 0 ) { leastRecentlyUsedKey = uri; leastRecentlyUsedEntry = entry; } }); if (leastRecentlyUsedKey) { delete entries[leastRecentlyUsedKey]; } } } }