(function () { 'use strict'; const root = window.PixelIslandModules ||= {}; const utils = root.CoreUtils || {}; const fnv1a = utils.fnv1a || ((value) => { let hash = 0x811c9dc5; for (const ch of String(value)) { hash ^= ch.charCodeAt(0); hash = Math.imul(hash, 0x01000193); } return (hash >>> 0).toString(16).padStart(8, '0'); }); const DEFAULT_CONFIG = { defaultDisplayLimit: 250, newArrivalSlots: 150, revivalSlots: 100, publishLimitFirstDay: 5, publishLimitTrusted: 10, upvoteDelaySlots: 20, downvoteAdvanceSlots: 25, upvoteRankCap: 50 }; function objectPublicAt(object) { return Number(object?.publishedAt || object?.placedAt || object?.createdAt || Date.now()); } function entry(kind, object, baseIndex, votes, config = DEFAULT_CONFIG) { const rawUp = Number(votes?.up) || 0; const up = Math.min(rawUp, config.upvoteRankCap); const down = Number(votes?.down) || 0; return { kind, object, id: object?.id, assetId: object?.assetId, publicAt: objectPublicAt(object), baseIndex, up, rawUp, down, effectiveSlot: baseIndex + up * config.upvoteDelaySlots - down * config.downvoteAdvanceSlots }; } function seededScore(id, salt, rotationAt = Date.now()) { const day = Math.floor((rotationAt || Date.now()) / (24 * 60 * 60 * 1000)); return parseInt(fnv1a(`${id}|${salt}|${day}`).slice(0, 8), 16) / 0xffffffff; } function buckets(entries, localLimit, config = DEFAULT_CONFIG, rotationAt = Date.now()) { const newCap = Math.min(config.newArrivalSlots, localLimit); const revivalCap = Math.max(0, Math.min(config.revivalSlots, localLimit - newCap)); const newest = entries .slice() .sort((a, b) => b.effectiveSlot - a.effectiveSlot || b.publicAt - a.publicAt || String(b.id).localeCompare(String(a.id))) .slice(0, newCap); const newestIds = new Set(newest.map((item) => item.id)); const revival = entries .filter((item) => !newestIds.has(item.id)) .sort((a, b) => seededScore(b.id, 'revival', rotationAt) - seededScore(a.id, 'revival', rotationAt) || b.up - a.up || String(b.id).localeCompare(String(a.id))) .slice(0, revivalCap); return { newest, revival, entries, visibleIds: new Set([...newest, ...revival].map((item) => item.id)) }; } function publishLimit(account, now = Date.now(), config = DEFAULT_CONFIG) { if (!account?.createdAt) return 0; return now - Number(account.createdAt) < 24 * 60 * 60 * 1000 ? config.publishLimitFirstDay : config.publishLimitTrusted; } root.RotationPolicy = { DEFAULT_CONFIG, objectPublicAt, entry, buckets, seededScore, publishLimit }; })();