// // // // // global variables var p1 = performance.now(); var timer; var artTypes = ['🎨','πŸ§‘','🏞️']; var artTypesAlt = ['1️⃣','2️⃣','3️⃣']; var artTitles = ['artwork','portraits','landscapes']; var models = [ // path, short display name, full display name // short display name should have max of ~36 characters // order here needs to match db for censoring function to work ['SDXL_1_0','SDXL 1.0 Vanilla','SDXL 1.0 Stability.ai official'], ['Ground_Truth','Actual artwork','Screenshots of the actual artwork'], ['SDXL_Crystal_Clear','SDXL Crystal Clear','Crystal Clear XL vCCXL'], ['SDXL_DynaVision','SDXL DynaVision','SDXL DynaVision beta v0.4.1.1'], ]; var modelSelected = 0; var artistPinned = []; var initialPosX = -1; var initialPosY = -1; var prevScrollTop = -1; // used for lazyLoad var newPosX = -1; var newPosY = -1; var imgTypeShown = 0; var log = ''; var editMostUsedMode = false; var informationMode = false; var windowWidth = 0; var gutterStartPosX, mouseStartPosX, gutterEndPercentX var style, tempStyle, stylesheet, tempStylesheet, imgHoverRule, teaseRules; var theTime = new Date; var startUpTime; var tagsConcatenated = new Set(); var tagCountsPermissive = []; var editedArtists = new Set(); var storingAccessType = 'none'; var missingFiles = ''; // the longer prompt is better for non-photographers var promptStyleWords = ['artwork in the style of','by|||'] const lowCountThreshold = 3; const unloadedImgSrc = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAkA4JaQAA3AA/vgfgAA='; // a 1x1 pixel const maxAristsToBeLoaded = 40; // each artist loads 3 images const artistLoadingChunk = 15; // artists loaded per lazy load call const missingInterval = setInterval(checkMissingInterval, 5000); var imageItemUnloadQueue = []; // // // // wait for DOM document.addEventListener("DOMContentLoaded", function() { checkStoringAccessType().then(state => { startUp(); }); startUpTime = theTime.getTime(); }); // functions async function startUp() { makeConcatenatedTagSet(); await loadEditedArtists(); insertArtists(); insertCheckboxesFromArtistsData(); insertCheckboxesFromCategories(); await loadCheckboxesState(); showHideCategories(); await loadOptionsState(); await loadArtistPinned(); await loadFavoritesState(); hideAllArtists(); rotatePromptsImages(); updateArtistsImgSrc(false); blurUnblurCensored(); updateTags('start'); makeStyleRuleForDrag(); // teasePartition(); promptBuilderAddArtist(true); updatePromptBuilderParts(); addAllListeners(); } function checkStoringAccessType() { return new Promise((resolve, reject) => { try { localStorage.setItem('testKey', 'testValue'); localStorage.removeItem('testKey'); storingAccessType = 'localStorage'; console.log('all settings saved using localStorage'); resolve(); } catch (error) { return caches.open('testCache') .then(cache => { const blob = new Blob([JSON.stringify('test')], { type: 'application/json' }); const responseToCache = new Response(blob); cache.put('testCache', responseToCache).then(response => { storingAccessType = 'dataCache'; console.log('all settings saved using dataCache'); return; }) .catch(error => { console.warn('no settings can be saved; we only have read access to cache: ' + error); resolve(); }); }) .catch(error => { console.warn('no settings can be saved; no access to any storage method: ' + error); resolve(); }); } }).catch(error => { console.warn('had error writing to localStorage: ', error); }); } function loadItemBasedOnAccessType(item) { if(storingAccessType == 'localStorage') { return new Promise((resolve, reject) => { try { const state = JSON.parse(localStorage.getItem(item)); resolve(state || {}); } catch (error) { reject(error); } }).catch(error => { console.warn(item + ' had error loading from localStorage: ', error); return {}; }); } else if(storingAccessType == 'dataCache') { return caches.open('dataCache') .then(cache => { return cache.match(item); }) .then(response => { if(response) { return response.json(); } return {}; }) .catch(error => { console.warn(item + ' had error loading from cache: ', error); }); } else if(storingAccessType == 'none') { return Promise.resolve({}); } } function storeItemBasedOnAccessType(item, stateArray, key, value) { if(storingAccessType == 'localStorage') { try { if(stateArray) { localStorage.setItem(item, JSON.stringify(stateArray)); } else { let state = JSON.parse(localStorage.getItem(item)) || {}; state[key] = value; localStorage.setItem(item, JSON.stringify(state)); } } catch (error) { console.warn(item + ' had error saving localStorage: ', error); } } else if(storingAccessType = 'dataCache') { caches.open('dataCache').then(cache => { if(stateArray) { const blob = new Blob([JSON.stringify(stateArray)], { type: 'application/json' }); const responseToCache = new Response(blob); return cache.put(item, responseToCache); } else { // try to get the item state from the cache cache.match(item).then(response => { let state = {}; if(response) { return response.json().then(cachedData => { state = cachedData || {}; return state; }); } else { return state; } }).then(state => { state[key] = value; // store the updated state back to the cache const blob = new Blob([JSON.stringify(state)], { type: 'application/json' }); const responseToCache = new Response(blob); return cache.put(item, responseToCache); }); } }).catch(error => { console.warn(item + ' had error saving to cache: ', error); }); } else if(storingAccessType == 'none') { alertNoStoringAccess(0); } } async function deleteItemBasedOnAccessType(item) { if(storingAccessType == 'localStorage') { localStorage.removeItem(item); } else if(storingAccessType = 'dataCache') { await caches.delete(item); } else if(storingAccessType == 'none') { // nothing to do } } function alertNoStoringAccess(wait) { window.setTimeout(function(){ let msg = ''; msg += 'My apologies, your browser settings block the ability to save settings and favorites. Suggestions:\n'; msg += '1. Try Firefox, Safari, or Edge\n' msg += '2. Download the app to use offline\n'; msg += '3. On Chrome, enable 3rd-party cookies (not recommended)\n\n'; msg += 'This app doesn\'t use cookies, never sends data to any server, and saves all data locally. But since this app is hosted on Hugging Face, Chrome treats it as a "3rd-party". Other browsers give you more nuanced control of your privacy settings.'; alert(msg); },wait); } function makeConcatenatedTagSet() { // this set is used for tag editing mode for (var i=0, il=tagCategories.length; i { editedArtists = new Set(Array.from(state)); let proto = window.location.protocol; let anyChanges = false; for (let i=0, il=artistsData.length; i editedA[0] === artist[0] && editedA[1] === artist[1]); if(artistFound) { // check if the edit now matches the original let match = true; for (let j=0, jl=artist.length; j