Event Listeners
The Feral File Artwork JS Library uses a custom event system to notify your artwork when data is ready or errors occur. All events are dispatched on the window object.
Attach listeners before calling the loaders—events may fire shortly after the request resolves.
Event Types
Provenance Events
feralfile:provenance-ready
Fired when provenance data has been successfully loaded from the blockchain.
Event Detail:
{
detail: {
provenances: Array<ProvenanceRecord>
}
}
Example Usage:
window.addEventListener('feralfile:provenance-ready', (event) => {
const provenances = event.detail.provenances;
console.log(`Found ${provenances.length} transactions`);
if (!Array.isArray(provenances)) return;
// Example: Change artwork based on number of transfers
const transfers = provenances.filter(p => p.type === 'transfer');
if (transfers.length > 5) {
// This is a well-traveled artwork!
addSpecialEffect();
}
// Example: Use first owner's address to influence design
const mintRecord = provenances.find(p => p.type === 'mint');
if (mintRecord) {
const firstOwner = mintRecord.owner;
const ownerInfluence = parseInt(firstOwner.slice(-4), 16) / 65535;
adjustColorSaturation(ownerInfluence);
}
});
feralfile:provenance-request-error
Fired when provenance data fails to load.
Event Detail:
{
detail: {
error: Error
}
}
Example Usage:
window.addEventListener('feralfile:provenance-request-error', (event) => {
console.error('Provenance failed:', event.detail.error);
// Provide fallback behavior
useDefaultProvenance();
showOfflineIndicator();
});
Blockchain Events
feralfile:blockchain-info-ready
Fired when blockchain information (like current block height) is available.
Note: the library exposes Ethereum block height; Tezos height is not fetched.
Event Detail:
{
detail: {
height: number
}
}
Example Usage:
window.addEventListener('feralfile:blockchain-info-ready', (event) => {
const blockHeight = event.detail.height;
console.log(`Current block: ${blockHeight}`);
// Example: Use block height to create time-based variations
const timeVariation = blockHeight % 100;
if (timeVariation < 10) {
enableNightMode();
} else if (timeVariation > 90) {
enableDayMode();
}
// Example: Create periodic changes
const cycle = Math.floor(blockHeight / 1000);
setCycleTheme(cycle % 4);
});
feralfile:blockchain-info-request-error
Fired when blockchain data fails to load.
Event Detail:
{
detail: {
error: Error
}
}
Example Usage:
window.addEventListener('feralfile:blockchain-info-request-error', (event) => {
console.error('Blockchain info failed:', event.detail.error);
// Use local time as fallback
const fallbackHeight = Date.now() / 1000; // Rough block simulation
useFallbackBlockHeight(fallbackHeight);
});
Provenance Data Structure
Each provenance record has this structure:
interface ProvenanceRecord {
type: 'mint' | 'transfer';
owner: string; // Wallet address
blockchain: string; // 'ethereum' or 'tezos'
blockNumber: number; // Block where transaction occurred
timestamp: string; // ISO 8601 timestamp
txid: string; // Transaction hash
txURL: string; // Link to blockchain explorer
}
Provenance Data Example:
[
{
"type": "transfer",
"owner": "0x5151f4b48CeE4f7dcB7714E7b4b836aa847Bf4e8",
"blockchain": "ethereum",
"blockNumber": 20134677,
"timestamp": "2024-06-20T18:18:23Z",
"txid": "0x52750ea1b7efeece11e8d10f2b5c0e3f4db854b6e8eac8e82f89b03a2b39f52f",
"txURL": "https://etherscan.io/tx/0x52750ea1b7efeece11e8d10f2b5c0e3f4db854b6e8eac8e82f89b03a2b39f52f"
},
{
"type": "mint",
"owner": "0x457ee5f723C7606c12a7264b52e285906F91eEA6",
"blockchain": "ethereum",
"blockNumber": 18582877,
"timestamp": "2023-11-16T07:12:59Z",
"txid": "0xedfc4eca7c95911ee7c07cdfda14361998d84e5ea812404d42279665c0c74cf1",
"txURL": "https://etherscan.io/tx/0xedfc4eca7c95911ee7c07cdfda14361998d84e5ea812404d42279665c0c74cf1"
}
]
Best Practices
1. Always Set Up Listeners First
// ✅ Correct: Set up listeners before loading data
window.addEventListener('feralfile:provenance-ready', handleProvenance);
window.addEventListener('feralfile:blockchain-info-ready', handleBlockchain);
FeralFile.loadProvenance();
FeralFile.loadBlockchainInfo();
// ❌ Wrong: Race condition possible
FeralFile.loadProvenance();
window.addEventListener('feralfile:provenance-ready', handleProvenance);
2. Handle All Event Types
Always provide handlers for both success and error events:
// Handle success
window.addEventListener('feralfile:provenance-ready', (event) => {
// Use the data
});
// Handle failure
window.addEventListener('feralfile:provenance-request-error', (event) => {
// Provide fallback behavior
});
3. Implement Progressive Enhancement
// Show base artwork immediately
renderBaseArtwork();
// Enhance when data becomes available
window.addEventListener('feralfile:provenance-ready', () => {
addProvenanceVisualization();
});
window.addEventListener('feralfile:blockchain-info-ready', () => {
addBlockchainVisualization();
});
4. Clean Up Event Listeners
If you're creating dynamic artworks or single-page applications:
function destroyArtwork() {
window.removeEventListener('feralfile:provenance-ready', handleProvenance);
window.removeEventListener('feralfile:provenance-request-error', handleProvenanceError);
window.removeEventListener('feralfile:blockchain-info-ready', handleBlockchain);
window.removeEventListener('feralfile:blockchain-info-request-error', handleBlockchainError);
}
5. Timeout Handling
Don't wait indefinitely for external data:
const DATA_TIMEOUT = 10000; // 10 seconds
const timeoutId = setTimeout(() => {
if (!hasAllData()) {
console.log('Proceeding without complete data');
proceedWithFallback();
}
}, DATA_TIMEOUT);
// Clear timeout when all data is loaded
function onAllDataLoaded() {
clearTimeout(timeoutId);
proceedWithFullData();
}